Midnight Sun CTF 2019 Quals writeup
はじめに
Gissa2、 Hfsipc、 Hfs-dosを解いた。 どれも良い問題だったと思う。
Gissa2
stack overflowがあり、 canaryがないのでropができる。 しかしseccomp filterによってシステムコールに制限がかかっている。 設定されるフィルターは以下の通りで、 open/openat/ execve/execeatが塞がれておりフラグを開いたりシェルを立ち上げることができない。
line CODE JT JF K ================================= 0000: 0x20 0x00 0x00 0x00000004 A = arch 0001: 0x15 0x01 0x00 0xc000003e if (A == ARCH_X86_64) goto 0003 0002: 0x06 0x00 0x00 0x00000000 return KILL 0003: 0x20 0x00 0x00 0x00000000 A = sys_number 0004: 0x15 0x00 0x01 0x00000002 if (A != open) goto 0006 0005: 0x06 0x00 0x00 0x00000000 return KILL 0006: 0x15 0x00 0x01 0x00000038 if (A != clone) goto 0008 0007: 0x06 0x00 0x00 0x00000000 return KILL 0008: 0x15 0x00 0x01 0x00000039 if (A != fork) goto 0010 0009: 0x06 0x00 0x00 0x00000000 return KILL 0010: 0x15 0x00 0x01 0x0000003a if (A != vfork) goto 0012 0011: 0x06 0x00 0x00 0x00000000 return KILL 0012: 0x15 0x00 0x01 0x0000003b if (A != execve) goto 0014 0013: 0x06 0x00 0x00 0x00000000 return KILL 0014: 0x15 0x00 0x01 0x00000055 if (A != creat) goto 0016 0015: 0x06 0x00 0x00 0x00000000 return KILL 0016: 0x15 0x00 0x01 0x00000101 if (A != openat) goto 0018 0017: 0x06 0x00 0x00 0x00000000 return KILL 0018: 0x15 0x00 0x01 0x00000142 if (A != execveat) goto 0020 0019: 0x06 0x00 0x00 0x00000000 return KILL 0020: 0x06 0x00 0x00 0x7fff0000 return ALLOW
フィルターをバイパスする方法は単純でx32の方のシステムコールを呼ぶ。 システムコールを呼ぶ際に設定するシステムコール番号を+0x40000000すると、 x32として扱われる。 x32でopenを呼ぶ場合は、 システムコール番号は2+0x40000000で良い。 この時上記のseccomp filterでのsys_numberの制限に引っかかることはない。
x64におけるx32でのシステムコールについては下記のリンクに詳細がある。
RFD: x32 ABI system call numbers [LWN.net]
exploit
システムコールの制限は回避可能なので、 フラグを開いて中身を読むだけである。 Midnight Sun CTF 2019 Quals Gissa2 · GitHub
Hfsipc
Linuxのkernel exploit。
脆弱なLKMがロードされているので、 これを攻略する。
ioctlで諸々の操作ができ、 kernel heap上にオブジェクトを作ったり破棄したり編集したりできる。
扱うオブジェクトの構造は以下のようになっていた。
どのオブジェクトを操作するのかをkey
で指定する。
buf
は、 オブジェクトを作成するときに指定するサイズで確保したkmalloc()の返り値が入る。
size
にそのサイズが入る。
struct obj { long key; char* buf; long size; };
ioctl()経由で、 オブジェクトのbuf
の中身を読み書きできる。
書き込む際に、 作成したサイズよりも1byte多く書き込むことができるのでoff-by-one overflowとなっている。
exploit
linux kernelで使用されるSLUB allocatorでは、 解放済みのチャンクがリンクドリストで繋がっている。 off-by-one overflowを利用すると、 リンクドリストのポインタの最下位1byteを書き換えることができる。 これを利用して、 kmalloc()で取得する領域をuserlandに強制することができる。 あとは適当にkernelspaceのアドレスをリークして、 modprobe_pathを改変してrootを取った。
Midnight Sun CTF 2019 Quals Hfsipc · GitHub
Hfs-dos
改変されたCOMMAND.COM
が動作するFreeDOS。
COMMAND.COM
では、 入力した文字列に対応する文字列を返すようになっている。
削除文字(0x7f)を入力すると、 こちらからの入力を保存するバッファへのポインタをデクリメントすることができる。
このデクリメントに制限がなく、 COMMAND.COM
のtext領域まで持っていくことができる。
古い時代のアーキテクチャ(i8086)で動作している、 かつ古のOSであるため、 メモリの保護機構など存在せずtext領域でも書き換えることが可能となっている。
exploit
jmpする先を改変し、 文字列FLAG1をFLAG2に改変した。
jmpする先は、 COMMAND.COM
が最初にFLAG1を表示する関数群へと変えており、FLAG1をFLAG2にしているので、 2つ目のフラグを得られる。