论文地址:https://www.usenix.org/system/files/conference/usenixsecurity14/sec14-paper-kemerlis.pdf
作者:哥伦比亚大学
发表于: USENIX Security(%,计算机安全顶会)
ret2usr由于smep、smap等缓解机制导致内核与用户空间的隔离,使得原生的ret2usr难以直接利用。
而论文中提出关键的一点:
However, the implicit physical memory sharing between user processes and the kernel allows an attacker to deconstruct the isolation guarantees offered by ret2usr protection mechanisms, and redirect the kernel’s control or data flow to user-controlled code or data .
翻译一下就是:由于内核空间与用户和空间的 隐形内存共享 导致攻击者可以绕过ret2usr的保护机制,使得内核空间的执行流最终流向一块区域,这一块区域中的数据or指令是可以由用户控制的。
紧接着,作者提出:
A key facility that enables the implicit sharing of physical memory is physmap :a large, contiguous virtual memory region inside kernel address space that contains a direct mapping of part or all (depending on the architecture) physical memory.
重点在于physmap这一块在内核中连续的一块空间,并且physmap映射了部分或所有(取决于具体架构)的物理内存。
文章还指出:物理内存分配给用户和内核,可能产生别名现象,具体表现为:多块虚存映射到同一块物理内存地址。
而假设physmap能映射内核中大部分或全部物理地址,那么攻击者控制的用户进程就有可能通过地址别名来访问。
(感觉这个讲的比较清楚Orz)
总体来说:ret2dir利用kernel space与user space的隐形地址共享,使得要劫持的内核数据或者执行流进入这一别名空间,类似一种不用返回用户空间的ret2usr改进版。更重要的是我们的payload不需要显式的放入内核空间(比如使用堆喷等技术),当page frame被给予attack process的时候,我们的攻击payload就会 “emerges in kernel space”
但是这种技术仍有一些限制。
系统会对能分配给内核的kernel-resident buffer加以限制,使得我们的payload不一定出现在kernel space中,进而要求我们尽量将payload封装在一些内核数据对象中。
而这一块区域有以下极为重要的特性:
(i) there exists a direct mapping of part or all physical memory in kernel space.
(ii) the mapping starts at a fixed, known location . The latter is true even in the case where kernelspace ASLR (KASLR) [35] is employed
由后一条我们可以知道,即使开启了ASLR,这一块区域的位置也是固定的。那么我们只要找到基地址 ,就可以通过线性加减来得到其他的位置。
攻击过程:
1.申请大量内存(利用堆喷等),提高命中率。
2.泄露slab地址,计算physmap基地址。
3.劫持执行流到physmap。
https://github.com/bsauce/CTF/tree/master/ret2dir/kpwn_driver
kpwn.c
实现了几个漏洞
POC主要做了如下事情:
1.首先调用kmalloc申请0x200空间,计算出slab基地址。
2.在 用户态 喷射一大片空间,并用 'K' 填充,作为标记。
3.从内核 slab基地址开始,在 内核空间 以页为单位向下搜索,直到找到标记了 'KKKKK...' 的空间,即找到了physmap的地方。
4.利用write函数在 内核空间 中修改被标记的 'KKKKK...'
5.验证 用户空间 中的 'KKKK...' 是否有发生变化。若发生变化,则说明POC成功。
效果:
以图说明:
http://c.biancheng.net/view/1272.html
https://www.jianshu.com/p/3c662b6163a7
https://www.anquanke.com/post/id/185408
MODULE_LICENSE(
"Dual BSD/GPL"
);
struct in_args{
uint64_t addr;
uint64_t size;
char __user
*
buf;
};
static
long
read_any(struct in_args
*
args){
long
ret
=
0
;
char
*
addr
=
(void
*
)args
-
>addr;
if
(copy_to_user(args
-
>buf,addr,args
-
>size)){
return
-
EINVAL;
}
return
ret;
}
static
long
write_any(struct in_args
*
args){
long
ret
=
0
;
char
*
addr
=
(void
*
)args
-
>addr;
if
(copy_from_user(addr,args
-
>buf,args
-
>size)){
return
-
EINVAL;
}
return
ret;
}
static
long
add_any(struct in_args
*
args){
long
ret
=
0
;
char
*
buffer
=
kmalloc(args
-
>size,GFP_KERNEL);
if
(
buffer
=
=
NULL){
return
-
ENOMEM;
}
if
(copy_to_user(args
-
>buf,&
buffer
,
0x8
)){
/
/
(void
*
)
buffer
return
-
EINVAL;
}
return
ret;
}
static
long
del_any(struct in_args
*
args){
long
ret
=
0
;
kfree((void
*
)args
-
>addr);
return
ret;
}
static
long
kpwn_ioctl(struct
file
*
file
, unsigned
int
cmd, unsigned
long
arg){
long
ret
=
-
EINVAL;
struct in_args
in
;
if
(copy_from_user(&
in
,(void
*
)arg,sizeof(
in
))){
return
ret;
}
switch(cmd){
case READ_ANY:
ret
=
read_any(&
in
);
break
;
case WRITE_ANY:
ret
=
write_any(&
in
);
break
;
case DEL_ANY:
ret
=
del_any(&
in
);
break
;
case ADD_ANY:
ret
=
add_any(&
in
);
break
;
default:
ret
=
-
1
;
}
return
ret;
}
static struct file_operations fops
=
{
.owner
=
THIS_MODULE,
.
open
=
NULL,
.release
=
NULL,
.read
=
NULL,
.write
=
NULL,
.unlocked_ioctl
=
kpwn_ioctl
};
static struct miscdevice misc
=
{
.minor
=
MISC_DYNAMIC_MINOR,
.name
=
"kpwn"
,
.fops
=
&fops
};
int
kpwn_init(void)
{
misc_register(&misc);
return
0
;
}
void kpwn_exit(void)
{
printk(KERN_INFO
"Goodbye hackern"
);
misc_deregister(&misc);
}
module_init(kpwn_init);
module_exit(kpwn_exit);
MODULE_LICENSE(
"Dual BSD/GPL"
);
struct in_args{
uint64_t addr;
uint64_t size;
char __user
*
buf;
};
static
long
read_any(struct in_args
*
args){
long
ret
=
0
;
char
*
addr
=
(void
*
)args
-
>addr;
if
(copy_to_user(args
-
>buf,addr,args
-
>size)){
return
-
EINVAL;
}
return
ret;
}
static
long
write_any(struct in_args
*
args){
long
ret
=
0
;
char
*
addr
=
(void
*
)args
-
>addr;
if
(copy_from_user(addr,args
-
>buf,args
-
>size)){
return
-
EINVAL;
}
return
ret;
}
static
long
add_any(struct in_args
*
args){
long
ret
=
0
;
char
*
buffer
=
kmalloc(args
-
>size,GFP_KERNEL);
if
(
buffer
=
=
NULL){
return
-
ENOMEM;
}
if
(copy_to_user(args
-
>buf,&
buffer
,
0x8
)){
/
/
(void
*
)
buffer
return
-
EINVAL;
}
return
ret;
}
static
long
del_any(struct in_args
*
args){
long
ret
=
0
;
kfree((void
*
)args
-
>addr);
return
ret;
}
static
long
kpwn_ioctl(struct
file
*
file
, unsigned
int
cmd, unsigned
long
arg){
long
ret
=
-
EINVAL;
struct in_args
in
;
if
(copy_from_user(&
in
,(void
*
)arg,sizeof(
in
))){
return
ret;
}
switch(cmd){
case READ_ANY:
ret
=
read_any(&
in
);
break
;
case WRITE_ANY:
ret
=
write_any(&
in
);
break
;
case DEL_ANY:
ret
=
del_any(&
in
);
break
;
case ADD_ANY:
ret
=
add_any(&
in
);
break
;
default:
ret
=
-
1
;
}
return
ret;
}
static struct file_operations fops
=
{
.owner
=
THIS_MODULE,
.
open
=
NULL,
.release
=
NULL,
.read
=
NULL,
.write
=
NULL,
.unlocked_ioctl
=
kpwn_ioctl
};
static struct miscdevice misc
=
{
.minor
=
MISC_DYNAMIC_MINOR,
.name
=
"kpwn"
,
.fops
=
&fops
};
int
kpwn_init(void)
{
misc_register(&misc);
return
0
;
}
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课