サイズ: 7845
コメント:
|
← 2014-12-19 12:12:46時点のリビジョン3 ⇥
サイズ: 7848
コメント:
|
削除された箇所はこのように表示されます。 | 追加された箇所はこのように表示されます。 |
行 43: | 行 43: |
概ね次のようなプログラムが実行されていると考えれる。 | 概ね次のようなプログラムが実行されていると考えられる。 |
password checker
問題
This checker tells me "wrong" everytime. There is something strange, you can discover hidden flag in the binary.
password-checker.xz
メモ
perlccされた物のDeparse方法: http://stackoverflow.com/questions/4048614/how-can-i-reverse-engineer-a-perl-program-that-has-been-compiled-with-perlcc
解法
gdb上で実行して,適宜ブレークポイントを仕掛けながら内部状態を観察する。
Perl_runops_standardで止めて1ステップずつ実行してどのような関数が呼ばれるか調べた。 Perl_pp_constに関しては次の方法で積まれた文字列を特定した。
(gdb) printf "%s %d\n", ((char**)((**sp)->sv_any))[0], ((int*)((**sp)->sv_any))[1] password: 10
以下のような順で呼び出されていた。
Perl_pp_enter Perl_pp_nextstate Perl_pp_pushmark Perl_pp_const ("password: ") Perl_pp_print Perl_pp_nextstate Perl_pp_gv Perl_pp_readline Perl_pp_nextstate Perl_pp_pushmark Perl_pp_const ("wrong\n") Perl_pp_print Perl_pp_nextstate Perl_pp_pushmark Perl_pp_const ('"') ... Perl_pp_const ('|') Perl_pp_pushmark Perl_pp_padav Perl_pp_aassign Perl_pp_leave
概ね次のようなプログラムが実行されていると考えられる。
分岐すら無いが最後に怪しい文字列が積まれている。
そこで,パスワードを聞いてきたところで止めて名前空間上にどのような名前が存在するかを調べる。
(gdb) p Perl_eval_pv(my_perl, "print qq{$_\n} for keys %main::", 1) ... _</home/vagrant/.plenv/versions/5.8.9/lib/perl5/5.8.9/i686-linux-multi/auto/B/C/C.so flag 2 ...
flagというそれらしい名前が得られた。これがどの名前空間に属するか調べるとサブルーチン名であることが分かる。
(gdb) p Perl_eval_pv(my_perl, "printf qq{%d\n}, defined &flag", 1) 1 $5 = (SV *) 0x81db078
flagを実行してみる。
(gdb) p Perl_eval_pv(my_perl, "flag()", 1) Warning: Cannot insert breakpoint 0. Cannot access memory at address 0x9214f025 0xf7d97381 in siglongjmp () from /lib/i386-linux-gnu/libc.so.6 The program being debugged stopped while in a function called from GDB. Evaluation of the expression containing the function (Perl_eval_pv) will be abandoned. When the function is done executing, GDB will silently stop. (gdb) c Continuing. ^_^ Warning: Cannot insert breakpoint 0. Cannot access memory at address 0x92f34c25 0xf7d97381 in siglongjmp () from /lib/i386-linux-gnu/libc.so.6
不穏なsiglongjmpをした上に^_^だけ表示されて停止してしまった。 flagの中で何が起きているかを調べるためにPerl_pp_entersubにブレークポイントを仕掛けた上で再実行してみる。
(gdb) b Perl_pp_entersub Breakpoint 1 at 0x810219c: file pp_hot.c, line 2561. (gdb) p Perl_eval_pv(my_perl, "flag()", 1) Breakpoint 1, Perl_pp_entersub (my_perl=0x81d0008) at pp_hot.c:2561 2561 { The program being debugged stopped while in a function called from GDB. Evaluation of the expression containing the function (Perl_eval_pv) will be abandoned. When the function is done executing, GDB will silently stop.
Perl_pp_entersubからは抜けてPerl_runops_standardをステップ実行してどのような処理が行われているかを見ていく。
(gdb) fin Run till exit from #0 Perl_pp_entersub (my_perl=0x81d0008) at pp_hot.c:2561 0x08100ce8 in Perl_runops_standard (my_perl=0x81d0008) at run.c:37 37 while ((PL_op = CALL_FPTR(PL_op->op_ppaddr)(aTHX))) { Value returned is $6 = (OP *) 0x81bdf98 <cop_list+15064> (gdb) s 38 PERL_ASYNC_CHECK(); (gdb) s 37 while ((PL_op = CALL_FPTR(PL_op->op_ppaddr)(aTHX))) { (gdb) Perl_pp_nextstate (my_perl=0x81d0008) at pp_hot.c:50 50 { (gdb) fin Run till exit from #0 Perl_pp_nextstate (my_perl=0x81d0008) at pp_hot.c:50 0x08100ce8 in Perl_runops_standard (my_perl=0x81d0008) at run.c:37 37 while ((PL_op = CALL_FPTR(PL_op->op_ppaddr)(aTHX))) { Value returned is $7 = (OP *) 0x8197828 <op_list+19848> (gdb) s 38 PERL_ASYNC_CHECK(); (gdb) 37 while ((PL_op = CALL_FPTR(PL_op->op_ppaddr)(aTHX))) { (gdb) Perl_pp_pushmark (my_perl=0x81d0008) at pp_hot.c:81 81 { (gdb) fin Run till exit from #0 Perl_pp_pushmark (my_perl=0x81d0008) at pp_hot.c:81 0x08100ce8 in Perl_runops_standard (my_perl=0x81d0008) at run.c:37 37 while ((PL_op = CALL_FPTR(PL_op->op_ppaddr)(aTHX))) { Value returned is $8 = (OP *) 0x81b9f58 <svop_list+11480> (gdb) s 38 PERL_ASYNC_CHECK(); (gdb) 37 while ((PL_op = CALL_FPTR(PL_op->op_ppaddr)(aTHX))) { (gdb) Perl_pp_const (my_perl=0x81d0008) at pp_hot.c:43 43 {
constについては中身を見ておく。
(gdb) s 44 dSP; (gdb) 45 XPUSHs(cSVOP_sv); (gdb) 46 RETURN; (gdb) printf "%s %d\n", ((char**)((**sp)->sv_any))[0], ((int*)((**sp)->sv_any))[1] ^_^ 4
実行時に表示された文字列である。
(gdb) fin Run till exit from #0 Perl_pp_const (my_perl=0x81d0008) at pp_hot.c:43 0x08100ce8 in Perl_runops_standard (my_perl=0x81d0008) at run.c:37 37 while ((PL_op = CALL_FPTR(PL_op->op_ppaddr)(aTHX))) { Value returned is $9 = (OP *) 0x81b6c40 <listop_list+6624> (gdb) s 38 PERL_ASYNC_CHECK(); (gdb) 37 while ((PL_op = CALL_FPTR(PL_op->op_ppaddr)(aTHX))) { (gdb) Perl_pp_die (my_perl=0x81d0008) at pp_sys.c:445 445 {
なんと^_^をスタックに積んだ後dieしてしまった。先ほどのsiglongjmpの正体はeval内でのdieだったのだ。
ならばと,dieしないように,次の命令をwarnに書き換えてみる。タイミングはPerl_pp_constが呼ばれた直後である。
(gdb) b Perl_pp_const (gdb) p Perl_eval_pv(my_perl, "flag()", 1) Breakpoint 2 at 0x8100df7: file pp_hot.c, line 43. (gdb) c Continuing. Breakpoint 2, Perl_pp_const (my_perl=0x81d0008) at pp_hot.c:43 43 { (gdb) fin Run till exit from #0 Perl_pp_const (my_perl=0x81d0008) at pp_hot.c:43 0x08100ce8 in Perl_runops_standard (my_perl=0x81d0008) at run.c:37 37 while ((PL_op = CALL_FPTR(PL_op->op_ppaddr)(aTHX))) { Value returned is $13 = (OP *) 0x81b6c40 <listop_list+6624> (gdb) s 38 PERL_ASYNC_CHECK(); (gdb) 37 while ((PL_op = CALL_FPTR(PL_op->op_ppaddr)(aTHX))) { (gdb) p *my_perl->Top $14 = {op_next = 0x81bdfd0 <cop_list+15120>, op_sibling = 0x81bdfd0 <cop_list+15120>, op_ppaddr = 0x81415a0 <Perl_pp_die>, op_targ = 2, op_type = 171, op_seq = 65535, op_flags = 5 '\005', op_private = 1 '\001'}
ここだ。op_ppaddrがdieを指しているのでwarnに書き換えてしまう。
(gdb) set variable my_perl->Top->op_ppaddr = Perl_pp_warn
しかしそのまま実行すると大量のuninitialized value警告が発生する。
(gdb) c ... Use of uninitialized value in bitwise and (&) at password-checker.pl line 11. Use of uninitialized value in bitwise and (&) at password-checker.pl line 11. Use of uninitialized value in concatenation (.) or string at password-checker.pl line 11. ...
プログラムの本体の処理を思い出してみると,最後に謎の値を配列に代入していた。 そこでプログラムの実行が終わった後でflagを呼び出してみる。
(gdb) b Perl_pp_leave (gdb) r
によりプログラムの実行が終わったところで止めて,先ほどの操作を再実行する。
... ADCTF_0BFusC4Ti0n_PeRl_In_bIN4Ry[Inferior 1 (process 30508) exited normally]
こうしてフラグは得られた。長い戦いであった。