原理:攻击利用了 用户空间的进程不能访问内核空间,但内核空间能访问用户空间 这个特性来定向内核代码或数据流指向用户控件,以 ring 0 特权执行用户空间代码完成提权等操作。
可以看到比较重要的就是:
注意,此时开启了Kaslr保护。但是没有smep保护
这里遇到了一个插曲,当我尝试启动内核的时候,提示:
属实令我感到非常奇怪。然后我尝试将qemu启动脚本 中的 -m 64M改成:-m 256M。
成功启动!
保护如下:
通过ioctl来切换到不同的函数执行
如果我们可以控制拷贝的长度到canary,那么就可以把内核中的canary泄漏到用户态
(1) 通过kallsmys获取 commit_creds(),prepare_kernel_cred() 的地址。同时拿到加载的时的地址偏移。(以及gadgets)
(2) 通过ioctl设置off,通过copy_to_user把canary拉到用户态,达到泄漏canary的目的。
(3) 通过core_write向内核态全局变量name上写rop链。
(4) 通过core_copy_func把name上的内容写到内核栈上,bypass canary,覆盖返回地址。
(5) 通过执行ROP链来进行:调用commit_creds(prepare_kernel_cred(0))完成提权。
(6) 重新返回用户态,“着陆”起shell。
https://www.anquanke.com/post/id/172216#h2-9
https://blog.csdn.net/qq_41071646/article/details/95768194
https://www.jianshu.com/p/8d950a9d8974
mv core.cpio core.cpio.gz
gzip
-
d .
/
core.cpio.gz
cpio
-
idmv < core.cpio
mv core.cpio core.cpio.gz
gzip
-
d .
/
core.cpio.gz
cpio
-
idmv < core.cpio
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
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
/
core.ko
poweroff
-
d
120
-
f &
setsid
/
bin
/
cttyhack setuidgid
1000
/
bin
/
sh
echo
'sh end!\n'
umount
/
proc
umount
/
sys
poweroff
-
d
0
-
f
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
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
/
core.ko
poweroff
-
d
120
-
f &
setsid
/
bin
/
cttyhack setuidgid
1000
/
bin
/
sh
echo
'sh end!\n'
umount
/
proc
umount
/
sys
poweroff
-
d
0
-
f
qemu
-
system
-
x86_64 \
-
m
64M
\
-
kernel .
/
bzImage \
-
initrd .
/
core.cpio \
-
append
"root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr"
\
-
s \
-
netdev user,
id
=
t0,
-
device e1000,netdev
=
t0,
id
=
nic0 \
-
nographic \
-
gdb tcp::
1234
qemu
-
system
-
x86_64 \
-
m
64M
\
-
kernel .
/
bzImage \
-
initrd .
/
core.cpio \
-
append
"root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr"
\
-
s \
-
netdev user,
id
=
t0,
-
device e1000,netdev
=
t0,
id
=
nic0 \
-
nographic \
-
gdb tcp::
1234
__int64 init_module()
{
core_proc
=
proc_create(
"core"
,
0x1B6LL
,
0LL
, &core_fops);
/
/
创建一个proc虚拟文件,应用层通过读写该文件,即可实现与内核的交互。
printk(&unk_2DE);
/
/
输出提示符:
6core
:created
/
proc
/
core entry
return
0LL
;
}
__int64 init_module()
{
core_proc
=
proc_create(
"core"
,
0x1B6LL
,
0LL
, &core_fops);
/
/
创建一个proc虚拟文件,应用层通过读写该文件,即可实现与内核的交互。
printk(&unk_2DE);
/
/
输出提示符:
6core
:created
/
proc
/
core entry
return
0LL
;
}
__int64 __fastcall core_ioctl(__int64 a1,
int
choice, __int64 a3)
{
__int64 v3;
/
/
rbx
v3
=
a3;
switch ( choice )
{
case
0x6677889B
:
core_read(a3);
/
/
从内核态拷贝栈上数据到用户态
break
;
case
0x6677889C
:
printk(&unk_2CD);
off
=
v3;
/
/
根据ioctl传进来的参数设置off
break
;
case
0x6677889A
:
printk(&unk_2B3);
core_copy_func(v3);
break
;
}
return
0LL
;
}
__int64 __fastcall core_ioctl(__int64 a1,
int
choice, __int64 a3)
{
__int64 v3;
/
/
rbx
v3
=
a3;
switch ( choice )
{
case
0x6677889B
:
core_read(a3);
/
/
从内核态拷贝栈上数据到用户态
break
;
case
0x6677889C
:
printk(&unk_2CD);
off
=
v3;
/
/
根据ioctl传进来的参数设置off
break
;
case
0x6677889A
:
printk(&unk_2B3);
core_copy_func(v3);
break
;
}
return
0LL
;
}
unsigned __int64 __fastcall core_read(__int64 new_off)
{
__int64 v1;
/
/
rbx
__int64
*
v2;
/
/
rdi
signed __int64 i;
/
/
rcx
unsigned __int64 result;
/
/
rax
__int64 v5;
/
/
[rsp
+
0h
] [rbp
-
50h
]
unsigned __int64 v6;
/
/
[rsp
+
40h
] [rbp
-
10h
]
v1
=
new_off;
v6
=
__readgsqword(
0x28u
);
printk(&unk_25B);
printk(&unk_275);
v2
=
&v5;
for
( i
=
16LL
; i;
-
-
i )
{
*
(_DWORD
*
)v2
=
0
;
v2
=
(__int64
*
)((char
*
)v2
+
4
);
}
strcpy((char
*
)&v5,
"Welcome to the QWB CTF challenge.\n"
);
result
=
copy_to_user(v1, (char
*
)&v5
+
off,
0x40LL
);
/
/
向用户态拷贝
0x40
个字节的内容,内核空间的拷贝起始地址可控(通过off)
if
( !result )
return
__readgsqword(
0x28u
) ^ v6;
__asm { swapgs }
return
result;
}
/
*
内核空间
-
-
>用户空间
copy_to_user函数
unsigned
long
copy_to_user(void
*
to, const void
*
from
, unsigned
long
n)
to:目标地址(用户空间)
from
:源地址(内核空间)
n:将要拷贝数据的字节数
返回:成功返回
0
,失败返回没有拷贝成功的数据字节数
*
/
unsigned __int64 __fastcall core_read(__int64 new_off)
{
__int64 v1;
/
/
rbx
__int64
*
v2;
/
/
rdi
signed __int64 i;
/
/
rcx
unsigned __int64 result;
/
/
rax
__int64 v5;
/
/
[rsp
+
0h
] [rbp
-
50h
]
unsigned __int64 v6;
/
/
[rsp
+
40h
] [rbp
-
10h
]
v1
=
new_off;
v6
=
__readgsqword(
0x28u
);
printk(&unk_25B);
printk(&unk_275);
v2
=
&v5;
for
( i
=
16LL
; i;
-
-
i )
{
*
(_DWORD
*
)v2
=
0
;
v2
=
(__int64
*
)((char
*
)v2
+
4
);
}
strcpy((char
*
)&v5,
"Welcome to the QWB CTF challenge.\n"
);
result
=
copy_to_user(v1, (char
*
)&v5
+
off,
0x40LL
);
/
/
向用户态拷贝
0x40
个字节的内容,内核空间的拷贝起始地址可控(通过off)
if
( !result )
return
__readgsqword(
0x28u
) ^ v6;
__asm { swapgs }
return
result;
}
/
*
内核空间
-
-
>用户空间
copy_to_user函数
unsigned
long
copy_to_user(void
*
to, const void
*
from
, unsigned
long
n)
to:目标地址(用户空间)
from
:源地址(内核空间)
n:将要拷贝数据的字节数
返回:成功返回
0
,失败返回没有拷贝成功的数据字节数
*
/
signed __int64 __fastcall core_copy_func(signed __int64 my_off)
{
signed __int64 result;
/
/
rax
__int64 v2;
/
/
[rsp
+
0h
] [rbp
-
50h
]
unsigned __int64 v3;
/
/
[rsp
+
40h
] [rbp
-
10h
]
v3
=
__readgsqword(
0x28u
);
printk(&unk_215);
if
( my_off >
0x3F
)
/
/
检测是否拷贝溢出?
{
printk(&unk_2A1);
result
=
0xFFFFFFFFLL
;
}
else
{
result
=
0LL
;
qmemcpy(&v2, &name, (unsigned __int16)my_off);
/
/
拷贝bss上的全局变量name,长度为my_off,目标栈上参数v2的地址(rbp
-
50h
)
/
/
注意这里的my off本来是有符号数字结果被强制转成了无符号数字
}
return
result;
signed __int64 __fastcall core_copy_func(signed __int64 my_off)
{
signed __int64 result;
/
/
rax
__int64 v2;
/
/
[rsp
+
0h
] [rbp
-
50h
]
unsigned __int64 v3;
/
/
[rsp
+
40h
] [rbp
-
10h
]
v3
=
__readgsqword(
0x28u
);
printk(&unk_215);
if
( my_off >
0x3F
)
/
/
检测是否拷贝溢出?
{
printk(&unk_2A1);
result
=
0xFFFFFFFFLL
;
}
else
{
result
=
0LL
;
qmemcpy(&v2, &name, (unsigned __int16)my_off);
/
/
拷贝bss上的全局变量name,长度为my_off,目标栈上参数v2的地址(rbp
-
50h
)
/
/
注意这里的my off本来是有符号数字结果被强制转成了无符号数字
}
return
result;
signed __int64 __fastcall core_write(__int64 a1, __int64 a2, unsigned __int64 a3)
{
unsigned __int64 size;
/
/
rbx
size
=
a3;
printk(&unk_215);
if
( size <
=
0x800
&& !copy_from_user(&name, a2, size) )
/
/
从用户空间拷贝内容到内核bss段上
return
(unsigned
int
)size;
printk(&unk_230);
return
0xFFFFFFF2LL
;
}
signed __int64 __fastcall core_write(__int64 a1, __int64 a2, unsigned __int64 a3)
{
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2020-10-4 01:30
被Roland_编辑
,原因: