Midnight Sun CTF Finals Flitbip writeup
はじめに
一人writeup advent calendarの7日目です。 1日1問分のwriteupを目標に頑張っていきます。 7日目の問題は、Midnight Sun CTF Finalsで出題された「Flitbip」。 初めての人にオススメです。
カーネルの情報 (セキュリティ機構など)
カーネルのバージョンは、4.17。 smep, smap、kaslr、kptiが無効になっている。
/ $ uname -a Linux (none) 4.17.0 #1 Fri Jun 15 18:23:33 CEST 2018 x86_64 GNU/Linux / $ cat /proc/cpuinfo | grep flags flags : fpu de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx lm nopl cpuid pni cx16 hypervisor lahf_lm svm 3dnowprefetch vmmcall
解析
この問題では脆弱性を持つシステムコールが新しく追加されている。 親切にも、新しいシステムコールのソースコードが存在するので、カーネルのバイナリを解析する必要は特にない。 システムコールは、引数で指定したアドレスの指定したビットを反転できる。 しかし反転できる回数に制限があり、flit_countがMAXFLIT(=1)以上の場合ビットを反転させることができない。
#include <linux/kernel.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/syscalls.h> #define MAXFLIT 1 #ifndef __NR_FLITBIP #define FLITBIP 333 #endif long flit_count = 0; EXPORT_SYMBOL(flit_count); SYSCALL_DEFINE2(flitbip, long *, addr, long, bit) { if (flit_count >= MAXFLIT) { printk(KERN_INFO "flitbip: sorry :/\n"); return -EPERM; } *addr ^= (1ULL << (bit)); flit_count++; return 0; }
exploit
kaslrがないため、カーネルがロードされいているアドレスは固定である。 よって、.text、.data、.bssといった領域が存在するアドレスは固定となっている。
まずは、そのままでは1度しかビット反転させることができないので、flit_countの最上位ビットを反転させ、ビット反転の回数の条件を解決する。 最上位ビットを反転させて1を立てることで、flit_countは負数となり、符号付の比較を突破でき、複数回のビット反転が可能になる。
任意の箇所に複数回のビット反転が行えるので、書き込み可能でグローバルな領域に存在する関数テーブルを書き換えていく。 最近のLinuxカーネルexploit問に対するテクニック集3 - HackMD によると、n_tty_opsという関数テーブルが書き込み可能でグローバルである。 smepが無効となっているので、ユーザ空間に諸々の処理を行う関数へとn_tty_opsのreadを向けさせる。
root権限を取るまでの流れだが、基本に忠実にcredのuid系の値を全て0で書き換える方針を取った。 current_taskは、現在実行しているタスクのtask_structを指していており、グローバル変数である。 current_task->cred->uidといった感じで参照していくことで、uidを書き換えられる。