問題ファイルの分析
与えられたファイルを解凍すると「seccompare」 というファイルが出てくる。 このファイルを fileコマンドで確認する。
$ file seccompare seccompare: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=4a607c82ea263205071c80295afe633412cda6f7, not stripped
出力結果から、ELF形式の実行ファイルであることがわかる。
gdbで処理の流れを確認する
まずは、gdm を使ってファイルを読み込ませる。
$ gdb ./seccompare
disasコマンドを使用して main処理の流れを確認する。
(gdb) disas main Dump of assembler code for function main: 0x00000000004005e7 <+0>: push %rbp 0x00000000004005e8 <+1>: mov %rsp,%rbp 0x00000000004005eb <+4>: sub $0x40,%rsp 0x00000000004005ef <+8>: mov %edi,-0x34(%rbp) 0x00000000004005f2 <+11>: mov %rsi,-0x40(%rbp) 0x00000000004005f6 <+15>: mov %fs:0x28,%rax 0x00000000004005ff <+24>: mov %rax,-0x8(%rbp) 0x0000000000400603 <+28>: xor %eax,%eax 0x0000000000400605 <+30>: cmpl $0x1,-0x34(%rbp) 0x0000000000400609 <+34>: jg 0x400630 <main+73> 0x000000000040060b <+36>: mov -0x40(%rbp),%rax 0x000000000040060f <+40>: mov (%rax),%rax 0x0000000000400612 <+43>: mov %rax,%rsi 0x0000000000400615 <+46>: lea 0x168(%rip),%rdi # 0x400784 0x000000000040061c <+53>: mov $0x0,%eax 0x0000000000400621 <+58>: callq 0x4004e0 <printf@plt> 0x0000000000400626 <+63>: mov $0x1,%eax 0x000000000040062b <+68>: jmpq 0x4006e1 <main+250> 0x0000000000400630 <+73>: movb $0x63,-0x30(%rbp) 0x0000000000400634 <+77>: movb $0x74,-0x2f(%rbp) 0x0000000000400638 <+81>: movb $0x66,-0x2e(%rbp) 0x000000000040063c <+85>: movb $0x34,-0x2d(%rbp) 0x0000000000400640 <+89>: movb $0x62,-0x2c(%rbp) 0x0000000000400644 <+93>: movb $0x7b,-0x2b(%rbp) 0x0000000000400648 <+97>: movb $0x35,-0x2a(%rbp) 0x000000000040064c <+101>: movb $0x74,-0x29(%rbp) 0x0000000000400650 <+105>: movb $0x72,-0x28(%rbp) 0x0000000000400654 <+109>: movb $0x31,-0x27(%rbp) 0x0000000000400658 <+113>: movb $0x6e,-0x26(%rbp) 0x000000000040065c <+117>: movb $0x67,-0x25(%rbp) 0x0000000000400660 <+121>: movb $0x73,-0x24(%rbp) 0x0000000000400664 <+125>: movb $0x5f,-0x23(%rbp) 0x0000000000400668 <+129>: movb $0x31,-0x22(%rbp) 0x000000000040066c <+133>: movb $0x73,-0x21(%rbp) 0x0000000000400670 <+137>: movb $0x5f,-0x20(%rbp) 0x0000000000400674 <+141>: movb $0x6e,-0x1f(%rbp) 0x0000000000400678 <+145>: movb $0x30,-0x1e(%rbp) 0x000000000040067c <+149>: movb $0x74,-0x1d(%rbp) 0x0000000000400680 <+153>: movb $0x5f,-0x1c(%rbp) 0x0000000000400684 <+157>: movb $0x65,-0x1b(%rbp) 0x0000000000400688 <+161>: movb $0x6e,-0x1a(%rbp) 0x000000000040068c <+165>: movb $0x30,-0x19(%rbp) 0x0000000000400690 <+169>: movb $0x75,-0x18(%rbp) 0x0000000000400694 <+173>: movb $0x67,-0x17(%rbp) 0x0000000000400698 <+177>: movb $0x68,-0x16(%rbp) 0x000000000040069c <+181>: movb $0x7d,-0x15(%rbp) 0x00000000004006a0 <+185>: movb $0x0,-0x14(%rbp) 0x00000000004006a4 <+189>: mov -0x40(%rbp),%rax 0x00000000004006a8 <+193>: add $0x8,%rax 0x00000000004006ac <+197>: mov (%rax),%rdx 0x00000000004006af <+200>: lea -0x30(%rbp),%rax 0x00000000004006b3 <+204>: mov %rdx,%rsi 0x00000000004006b6 <+207>: mov %rax,%rdi 0x00000000004006b9 <+210>: callq 0x4004f0 <strcmp@plt> 0x00000000004006be <+215>: test %eax,%eax 0x00000000004006c0 <+217>: jne 0x4006d0 <main+233> 0x00000000004006c2 <+219>: lea 0xcb(%rip),%rdi # 0x400794 0x00000000004006c9 <+226>: callq 0x4004c0 <puts@plt> 0x00000000004006ce <+231>: jmp 0x4006dc <main+245> 0x00000000004006d0 <+233>: lea 0xc5(%rip),%rdi # 0x40079c 0x00000000004006d7 <+240>: callq 0x4004c0 <puts@plt> 0x00000000004006dc <+245>: mov $0x0,%eax 0x00000000004006e1 <+250>: mov -0x8(%rbp),%rcx 0x00000000004006e5 <+254>: xor %fs:0x28,%rcx 0x00000000004006ee <+263>: je 0x4006f5 <main+270> 0x00000000004006f0 <+265>: callq 0x4004d0 <__stack_chk_fail@plt> 0x00000000004006f5 <+270>: leaveq 0x00000000004006f6 <+271>: retq End of assembler dump.
0x4005e7〜0x40062b で初期処理と、起動時の引数がなかったときの usage表示をする処理。
0x400630〜 flag をメモリ内に生成して 0x4006b9 で strcmp して合否の判断をしている模様。
実際に実行させて flag を得るまで
とりあえず、strcmp をしている 0x4006b9 にブレークポイントを置く。
アドレス指定なので、アドレスの前にアスタリスクをつける。
(gdb) break *0x4006b9 Breakpoint 1 at 0x4006b9
次に、実行をさせるが usage が表示されないようにダミーの引数(“hoge”)を与えておく。
(gdb) r hoge Starting program: /home/nekotaro/ctf/seccompare/seccompare hoge Breakpoint 1, 0x00000000004006b9 in main () (gdb)
ブレークポイントで無事停止。
必須ではないが、disas すると「=>」 の矢印マークがついて、ブレークポイントで止まっている場所を確認できる。
(gdb) disas main Dump of assembler code for function main: 0x00000000004005e7 <+0>: push %rbp 0x00000000004005e8 <+1>: mov %rsp,%rbp 0x00000000004005eb <+4>: sub $0x40,%rsp (中略) 0x00000000004006b6 <+207>: mov %rax,%rdi => 0x00000000004006b9 <+210>: callq 0x4004f0 <strcmp@plt> 0x00000000004006be <+215>: test %eax,%eax
strcmpに渡そうとしている第1引数と第2引数を確認する。
第1引数は rdi の先に、第2引数は rsi の先にある。
(gdb) x/s $rdi 0x7fffffffdf70: "ctf4b{5tr1ngs_1s_n0t_en0ugh}" (gdb) x/s $rsi 0x7fffffffe3d9: "hoge"
ということで、無事 flag の「ctf4b{5tr1ngs_1s_n0t_en0ugh}」を得ることができた。
ちなみに、余談だがx64のSystem V では、関数(ここれはstrcmp)に渡す引数の対応付はは下記の通り。(第7引数以降はスタックに積まれる)
引数 | レジスタ |
第1引数 | rdi |
第2引数 | rsi |
第3引数 | rdx |
第4引数 | rcx |
第5引数 | r8 |
第6引数 | r9 |
確認
最後に実際に動作を確認してみる。
$ ./seccompare ctf4b{5tr1ngs_1s_n0t_en0ugh} correct
無事 correct が表示されたので、flagの提出をしておしまい。