where is my rop
利用思路
login.cgi

在登录时使用POST和login.cgi交互,其中Authorization头格式是username:加密的password,载荷是session id。
在login.cgi中会使用libCgi.so中的send_cmd函数和basement使用socket交互,但调用函数之前有检查:
session id长度要是24,这里直接按正常流程GET cgi-bin/gen_enc?id获取session id。

header中Authorization的值取”Basic “后的检查:要在base64加密字符集内

漏洞点:在login.cgi中main函数对Authorization请求头解析时对内容长度的限制非常宽松,允许最大0x400长度的base64密文输入,而base64解码后存储的位置仅0x200大小,存在溢出,可以篡改_BYTE opcode全局变量


base64解密后检查是否有三个冒号分隔,且各个分段长度不超过0x80。
检查通过以后会调用send_cmd函数

由以上条件可知auth_parms一定得是类似Basic AAAA:BBBB:CCCC:DDDD的格式才能执行到send_cmd与basement交互
send_cmd函数功能是向basement监听的/tmp/tpctf.sock建立socket连接并传输这几个参数
send_cmd定义如下:
1 | char *__fastcall send_cmd(int opcode, int length, char *payload) |
第一个参数代表了要访问basement的功能编号
第二个是payload长度
第三个是指向payload的指针
basement
basement的main函数先读取opcode,然后是length,最后payload , 然后按照opcode调用分别的功能函数
由于我们能完全控制opcode,可以指定调用1-12任意的功能函数
1-5 sqlite3数据库操作
6-9 iperf3, ping, traceroute 命令执行,存在命令注入
10-12 假装使用acme.sh注册证书命令执行, 题目中的acme.sh其实是一个空的脚本文件
所有命令注入的位置都使用isSafeCommandArg()过滤字符
1 | from ctypes import * |
使用脚本获取的允许的字符集如下:
1 | \r\t!"#%'()*+,-./0123456789:=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]_abcdefghijklmnopqrstuvwxyz{}~% |
禁用了所有的管道符、分号、换行等,不能在原指令基础上另起一个新的指令,只能试着改程序的参数
其中iperf3可以传输文件,但是机器不出网,无法连接到服务器端口
但iperf3有可以输出文件的选项
1 | --logfile f send output to a log file |
与此同时还支持自定义日志每行最左端时间戳的样式,使得日志每行开头都可控,注入指令后利用#注释可以将无关部分去除,实现给指定文件写入任意shell指令
1 | --timestamps<=format> emit a timestamp at the start of each output line |
10-12 功能运行了acme.sh这个sh脚本,考虑先用功能6将要执行的shell指令写入acme.sh,再使用功能10运行acme.sh
尝试 cp /*flag* /var/www/html/flag,本地成功但远程访问/flag时404,怀疑flag文件名有改动,使用
1 | tar -zcvf /var/www/html/flag.tar --exclude=/proc --exclude=/sys / |
将根目录下除过滤的目录外全部压缩,然后浏览器访问/flag.tar下载即可得到flag
远程flag文件名被改成了the_flaG_is_here056480436103548697634
文件名中的G大写导致了之前没有成功复制文件
exp
1 | import requests |