DEFCON CTF 2009 Crypto 400
pwn11.ddtek.biz
http://shallweplayaga.me/crypto/eb24fae687eaa7d4441e727cf892beac
サーバ名(pwn11.ddtek.biz)と、バイナリが配布される
配布されるバイナリ(実行ファイル)が上記サーバで動いているので
実行ファイルを解析して上記サーバへ攻撃し、パスワードを得よ、という問題
$ file eb24fae687eaa7d4441e727cf892beac eb24fae687eaa7d4441e727cf892beac: ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD), for FreeBSD 7.2, dynamically linked (uses shared libs), FreeBSD-style, stripped
strippedされているが、コードがDEFCON CTF 2009 Pwtent 300と酷似しているため
サーバ処理部分は難なく理解できる
実行するとポート7852が開く
ncでアクセス
$ ./eb24fae687eaa7d4441e727cf892beac & $ nc localhost 7852 Password: AAAA
何かの入力を得て終了する
パスワードが何かを調べるためIDAProで解析
まず文字列"Password:"の参照元を探す
.text:080497D0 mov dword ptr [esp+4], offset aPassword ; "Password: " .text:080497D8 mov eax, [ebp+arg_0]
上記の部分からのみ参照されている
下へ降りていくと以下のコードが見つかる
.text:08049816 mov dword ptr [esp+8], 0Eh .text:0804981E mov dword ptr [esp+4], offset s2 ; "chickenfingers" .text:08049826 lea eax, [ebp+s1] .text:08049829 mov [esp], eax .text:0804982C call _strncmp
"chickenfingers"がパスワード?
$ nc localhost 7852 Password: chickenfingers What is your name? oops!
少し進んだ
次に"oops!"文字列の参照元を探る
.text:08049A53 mov dword ptr [esp+4], offset aOops ; "oops!\n" .text:08049A5B mov eax, [ebp+arg_0]
呼び出し元(ジャンプ元)を辿ると以下のコードが見つかる
.text:0804993B mov dword ptr [esp+4], offset modes ; "r" .text:08049943 mov dword ptr [esp], offset filename ; "plaintext" .text:0804994A call _fopen .text:0804994F mov [ebp+stream], eax .text:08049952 cmp [ebp+stream], 0 .text:08049956 jz loc_8049A4B .text:0804995C mov edx, [ebp+ptr] .text:0804995F mov eax, [ebp+stream] .text:08049962 mov [esp+0Ch], eax .text:08049966 mov dword ptr [esp+8], 20h .text:0804996E mov dword ptr [esp+4], 1 .text:08049976 mov [esp], edx .text:08049979 call _fread .text:0804997E mov edx, eax .text:08049980 mov eax, [ebp+ptr] .text:08049983 mov [eax+20h], edx .text:08049986 mov eax, [ebp+stream] .text:08049989 mov [esp], eax .text:0804998C call _fclose
plaintextというファイルをfopenして、20hバイトfreadして、fcloseする
適当なplaintextファイルを用意して再度実行
$ cat plaintext AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH $ ./eb24fae687eaa7d4441e727cf892beac & $ nc localhost 7852 Password: chickenfingers What is your name? test Welcome test :: Your challenge is to decode the following hex encoded string: a1dd32dc471d11d42a5f15d16e297ae7c4da2ed2723d770882edade473c8242a Good luck!
いよいよ本題に突入
以下のhexデータをデコードしろ、と言われる
a1dd32dc471d11d42a5f15d16e297ae7c4da2ed2723d770882edade473c8242a
おそらく上記のhexデータをデコードするとplaintextファイル内のデータ
"AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH"が取得できると思われる
つまり最終的なパスワードは、pwn11.ddtek.bizサーバ上にあるplaintextの中身
エンコードのアルゴリズムを解析し、デコーダを作成するのが今回の問題
080497C0以降の処理を解析し、エンコード処理をコード化する
.text:080497C0 sub_80497C0 proc near .text:080497C0 .text:080497C0 var_3C = dword ptr -3Ch .text:080497C0 s1 = byte ptr -37h .text:080497C0 var_29 = byte ptr -29h .text:080497C0 var_25 = byte ptr -25h .text:080497C0 ptr = dword ptr -24h .text:080497C0 fd = dword ptr -20h .text:080497C0 var_1C = dword ptr -1Ch .text:080497C0 stream = dword ptr -18h .text:080497C0 var_14 = dword ptr -14h .text:080497C0 var_10 = dword ptr -10h .text:080497C0 var_C = dword ptr -0Ch .text:080497C0 arg_0 = dword ptr 8 .text:080497C0 .text:080497C0 push ebp .text:080497C1 mov ebp, esp .text:080497C3 push esi .text:080497C4 push ebx .text:080497C5 sub esp, 50h .text:080497C8 mov dword ptr [esp+8], 0 .text:080497D0 mov dword ptr [esp+4], offset aPassword ; "Password: " .text:080497D8 mov eax, [ebp+arg_0] .text:080497DB mov [esp], eax .text:080497DE call str_send .text:080497E3 mov dword ptr [esp+0Ch], 0Ah .text:080497EB mov dword ptr [esp+8], 11h .text:080497F3 lea eax, [ebp+s1] .text:080497F6 mov [esp+4], eax .text:080497FA mov eax, [ebp+arg_0] .text:080497FD mov [esp], eax .text:08049800 call recv_data ; recv_data(sock, s1, 0x11, 0x0a);
"Password: "という文字列をクライアントへ送信し、データをrecvする
recvするデータは0x11バイト or 0x0a(改行)が出現するまで
またrecvする際、格納先のs1(var_37)はオーバーフローしており、var_29を侵食する
0x37 - 0x11 = 0x26であり、var_29以降3バイト(var_29 var_28 var_27)を書き換える
.text:08049805 mov [ebp+var_1C], eax .text:08049808 cmp [ebp+var_1C], 0 .text:0804980C jle short loc_804986A .text:0804980E mov eax, [ebp+var_1C] .text:08049811 mov [ebp+eax+s1], 0 .text:08049816 mov dword ptr [esp+8], 0Eh .text:0804981E mov dword ptr [esp+4], offset s2 ; "chickenfingers" .text:08049826 lea eax, [ebp+s1] .text:08049829 mov [esp], eax .text:0804982C call _strncmp ; strncmp(s1, "chickenfingers", 0x0E); .text:08049831 test eax, eax .text:08049833 jz short loc_8049841 .text:08049835 mov [ebp+var_3C], 0 .text:0804983C jmp loc_8049A78 .text:08049841
s1と"chickenfingers"との比較、正しくなければプログラム終了
.text:08049841 loc_8049841: .text:08049841 movzx eax, [ebp+var_29] .text:08049845 and eax, 1 .text:08049848 mov [ebp+var_25], al .text:0804984B mov dword ptr [esp+4], 0 .text:08049853 mov dword ptr [esp], offset file ; "/dev/urandom" .text:0804985A call _open .text:0804985F mov [ebp+fd], eax .text:08049862 cmp [ebp+var_25], 0 .text:08049866 jnz short loc_8049876 .text:08049868 jmp short loc_80498D3
/dev/uramdomを開く
var_29の最下位ビットが1or0でvar_25の値が変わり、var_25の値によって処理が分岐
var_29の値はs1をオーバーフローさせることで任意の値にできるため
実質クライアント側で操作可能
最下位ビットが1ならmallocで4Chバイト確保、0なら3Chバイト確保
コードを読んでいくと分かるが、
これは1文字を2バイトとして扱うか? 1バイトとして扱うか? のフラグ
.text:0804986A .text:0804986A loc_804986A: .text:0804986A mov [ebp+var_3C], 0 .text:08049871 jmp loc_8049A78 .text:08049876 .text:08049876 loc_8049876: .text:08049876 mov dword ptr [esp], 4Ch .text:0804987D call _malloc ; eax = malloc(0x4C); .text:08049882 mov [ebp+var_14], eax .text:08049885 mov eax, [ebp+var_14] .text:08049888 add eax, 44h .text:0804988B mov dword ptr [esp+8], 5 .text:08049893 mov [esp+4], eax .text:08049897 mov eax, [ebp+fd] .text:0804989A mov [esp], eax .text:0804989D call _read ; memcpy(eax + 0x44, rand(), 5); .text:080498A2 mov eax, [ebp+var_14] .text:080498A5 add eax, 44h .text:080498A8 mov dword ptr [esp+4], 5 .text:080498B0 mov [esp], eax .text:080498B3 call sub_8049350 .text:080498B8 mov eax, [ebp+var_14] .text:080498BB mov [ebp+ptr], eax .text:080498BE mov dword ptr [esp+4], offset aWhatIsYourName ; "What is your name? " .text:080498C6 mov eax, [ebp+arg_0] .text:080498C9 mov [esp], eax .text:080498CC call unicode_send .text:080498D1 jmp short loc_8049930
4Ch確保した場合、44h番目以降に5バイトの乱数を置く
.text:080498D3 .text:080498D3 loc_80498D3: .text:080498D3 mov dword ptr [esp], 3Ch .text:080498DA call _malloc .text:080498DF mov [ebp+ptr], eax .text:080498E2 mov eax, [ebp+ptr] .text:080498E5 add eax, 34h .text:080498E8 mov dword ptr [esp+8], 5 .text:080498F0 mov [esp+4], eax .text:080498F4 mov eax, [ebp+fd] .text:080498F7 mov [esp], eax .text:080498FA call _read .text:080498FF mov eax, [ebp+ptr] .text:08049902 add eax, 34h .text:08049905 mov dword ptr [esp+4], 5 .text:0804990D mov [esp], eax .text:08049910 call sub_8049350 .text:08049915 mov dword ptr [esp+8], 0 .text:0804991D mov dword ptr [esp+4], offset aWhatIsYourName ; "What is your name? " .text:08049925 mov eax, [ebp+arg_0] .text:08049928 mov [esp], eax .text:0804992B call str_send .text:08049930
3Ch確保した場合、34h番目以降に5バイトの乱数を置く
途中に呼び出されている関数sub_8049350は乱数からテーブルを生成するコード
.text:08049350 sub_8049350 proc near .text:08049350 .text:08049350 var_8 = dword ptr -8 .text:08049350 var_1 = byte ptr -1 .text:08049350 arg_0 = dword ptr 8 .text:08049350 arg_4 = dword ptr 0Ch .text:08049350 .text:08049350 push ebp .text:08049351 mov ebp, esp .text:08049353 sub esp, 10h .text:08049356 mov ds:byte_804AEE0, 0 .text:0804935D mov [ebp+var_8], 0 .text:08049364 jmp short loc_8049376 ########################################################### ################## ds:byte_804AEE0 = 0; ################## var_8 = 0; ################## goto loc_8049376; ########################################################### .text:08049366 .text:08049366 loc_8049366: .text:08049366 mov eax, [ebp+var_8] .text:08049369 mov edx, [ebp+var_8] .text:0804936C mov ds:byte_804AF00[eax], dl .text:08049372 add [ebp+var_8], 1 ########################################################### ################## ds:byte_804AF00[var_8] = var_8; ################## var_8++; ########################################################### .text:08049376 .text:08049376 loc_8049376: .text:08049376 cmp [ebp+var_8], 0FFh .text:0804937D jbe short loc_8049366 .text:0804937F mov [ebp+var_8], 0 .text:08049386 jmp short loc_80493F6 ########################################################### ################## if(var_8 <= 0xFF) ################## goto loc_8049366; ################## var_8 = 0; ################## goto loc_80493F6; ########################################################### .text:08049388 .text:08049388 loc_8049388: .text:08049388 mov eax, [ebp+var_8] .text:0804938B movzx ecx, ds:byte_804AF00[eax] .text:08049392 mov eax, [ebp+var_8] .text:08049395 mov edx, 0 .text:0804939A div [ebp+arg_4] .text:0804939D mov eax, edx .text:0804939F add eax, [ebp+arg_0] .text:080493A2 movzx eax, byte ptr [eax] .text:080493A5 lea edx, [ecx+eax] .text:080493A8 movzx eax, ds:byte_804AEE0 .text:080493AF lea eax, [edx+eax] .text:080493B2 mov ds:byte_804AEE0, al ########################################################### ################## ecx = ds:byte_804AF00[var_8]; ################## edx = ecx + arg_0[eax % arg_4]; ################## ds:byte_804AEE0 += edx; ########################################################### .text:080493B7 movzx eax, ds:byte_804AEE0 .text:080493BE movzx eax, al .text:080493C1 movzx eax, ds:byte_804AF00[eax] .text:080493C8 mov [ebp+var_1], al ########################################################### ################## var_1 = ds:byte_804AF00[ds:byte_804AEE0]; ########################################################### .text:080493CB movzx eax, ds:byte_804AEE0 .text:080493D2 movzx edx, al .text:080493D5 mov eax, [ebp+var_8] .text:080493D8 movzx eax, ds:byte_804AF00[eax] .text:080493DF mov ds:byte_804AF00[edx], al ########################################################### ################## ds:byte_804AF00[ds:byte_804AEE0] = ################## ds:byte_804AF00[var_8]; ########################################################### .text:080493E5 mov edx, [ebp+var_8] .text:080493E8 movzx eax, [ebp+var_1] .text:080493EC mov ds:byte_804AF00[edx], al .text:080493F2 add [ebp+var_8], 1 ########################################################### ################## ds:byte_804AF00[var_8] = var_1; ################## var_8++; ########################################################### .text:080493F6 .text:080493F6 loc_80493F6: .text:080493F6 cmp [ebp+var_8], 0FFh .text:080493FD jbe short loc_8049388 .text:080493FF mov ds:byte_804AEE0, 0 .text:08049406 mov ds:byte_804AEC4, 0 ########################################################### ################## if(var_8 <= 0xFF) ################## goto loc_8049388; ################## ds:byte_804AEE0 = 0; ################## ds:byte_804AEC4 = 0; ########################################################### .text:0804940D leave .text:0804940E retn
以上が5バイトの乱数から256バイトのテーブルを作る処理
続いてplaintextを読む処理
.text:08049930 loc_8049930: .text:08049930 mov eax, [ebp+fd] .text:08049933 mov [esp], eax .text:08049936 call _close .text:0804993B mov dword ptr [esp+4], offset modes ; "r" .text:08049943 mov dword ptr [esp], offset filename ; "plaintext" .text:0804994A call _fopen .text:0804994F mov [ebp+stream], eax .text:08049952 cmp [ebp+stream], 0 .text:08049956 jz loc_8049A4B .text:0804995C mov edx, [ebp+ptr] .text:0804995F mov eax, [ebp+stream] .text:08049962 mov [esp+0Ch], eax .text:08049966 mov dword ptr [esp+8], 20h .text:0804996E mov dword ptr [esp+4], 1 .text:08049976 mov [esp], edx .text:08049979 call _fread .text:0804997E mov edx, eax .text:08049980 mov eax, [ebp+ptr] .text:08049983 mov [eax+20h], edx .text:08049986 mov eax, [ebp+stream] .text:08049989 mov [esp], eax .text:0804998C call _fclose .text:08049991 mov eax, [ebp+ptr] .text:08049994 mov eax, [eax+20h] .text:08049997 test eax, eax .text:08049999 jz loc_8049A66 .text:0804999F mov [ebp+var_10], 0 .text:080499A6 jmp short loc_80499C8
plaintextを開いて20hバイト読み、それをmallocで確保した領域にコピー
さらに20h番目以降の4バイトにそのサイズ(0x20)を格納
.text:080499A8 .text:080499A8 loc_80499A8: .text:080499A8 mov esi, [ebp+var_10] .text:080499AB mov edx, [ebp+var_10] .text:080499AE mov eax, [ebp+ptr] .text:080499B1 movzx ebx, byte ptr [eax+edx] .text:080499B5 call sub_8049410 .text:080499BA mov edx, ebx .text:080499BC xor edx, eax .text:080499BE mov eax, [ebp+ptr] .text:080499C1 mov [eax+esi], dl .text:080499C4 add [ebp+var_10], 1 .text:080499C8 .text:080499C8 loc_80499C8: .text:080499C8 mov eax, [ebp+var_10] .text:080499CB mov edx, [ebp+ptr] .text:080499CE mov edx, [edx+20h] .text:080499D1 cmp eax, edx .text:080499D3 jb short loc_80499A8
読み込んだplaintextのデータをエンコード(xor)する
途中で呼び出されているsub_8049410のコードは以下
.text:08049410 sub_8049410 proc near .text:08049410 .text:08049410 var_1 = byte ptr -1 .text:08049410 .text:08049410 push ebp .text:08049411 mov ebp, esp .text:08049413 sub esp, 10h .text:08049416 movzx eax, ds:byte_804AEC4 .text:0804941D add eax, 1 .text:08049420 mov ds:byte_804AEC4, al ########################################################### ################## byte_804AEC4++; ########################################################### .text:08049425 movzx eax, ds:byte_804AEC4 .text:0804942C movzx eax, al .text:0804942F movzx edx, ds:byte_804AF00[eax] .text:08049436 movzx eax, ds:byte_804AEE0 .text:0804943D lea eax, [edx+eax] .text:08049440 mov ds:byte_804AEE0, al ########################################################### ################## edx = ds:byte_804AF00[ds:byte_804AEC4]; ################## eax = ds:byte_804AEE0; ################## ds:byte_804AEE0 = edx + eax; ########################################################### .text:08049445 movzx eax, ds:byte_804AEE0 .text:0804944C movzx eax, al .text:0804944F movzx eax, ds:byte_804AF00[eax] .text:08049456 mov [ebp+var_1], al ########################################################### ################## var_1 = ds:byte_804AF00[ds:byte_804AEE0]; ########################################################### .text:08049459 movzx eax, ds:byte_804AEE0 .text:08049460 movzx edx, al .text:08049463 movzx eax, ds:byte_804AEC4 .text:0804946A movzx eax, al .text:0804946D movzx eax, ds:byte_804AF00[eax] .text:08049474 mov ds:byte_804AF00[edx], al ########################################################### ################## edx = ds:byte_804AEE0; ################## eax = ds:byte_804AEC4; ################## ds:byte_804AF00[edx] = ds:byte_804AF00[eax]; ########################################################### .text:0804947A movzx eax, ds:byte_804AEC4 .text:08049481 movzx edx, al .text:08049484 movzx eax, [ebp+var_1] .text:08049488 mov ds:byte_804AF00[edx], al ########################################################### ################## ds:byte_804AF00[ds:byte_804AEC4] = var_1; ########################################################### .text:0804948E movzx eax, ds:byte_804AEE0 .text:08049495 movzx eax, al .text:08049498 movzx eax, ds:byte_804AF00[eax] .text:0804949F add [ebp+var_1], al ########################################################### ################## var_1 += ds:byte_804AF00[ds:byte_804AEE0]; ########################################################### .text:080494A2 movzx eax, [ebp+var_1] .text:080494A6 movzx eax, ds:byte_804AF00[eax] .text:080494AD movzx eax, al ########################################################### ################## return ds:byte_804AF00[var_1]; ########################################################### .text:080494B0 leave .text:080494B1 retn
次にWhat is your name?の質問に対する返答をクライアントから受信
mallocで確保した領域+24hバイト目以降に格納される
var_25の値によって処理分岐(1文字2バイト? 1文字1バイト?)
.text:080499D5 cmp [ebp+var_25], 0 .text:080499D9 jz short loc_8049A16 .text:080499DB mov eax, [ebp+ptr] .text:080499DE mov [ebp+var_C], eax .text:080499E1 mov eax, [ebp+var_C] .text:080499E4 add eax, 24h .text:080499E7 mov dword ptr [esp+8], 10h .text:080499EF mov [esp+4], eax .text:080499F3 mov eax, [ebp+arg_0] .text:080499F6 mov [esp], eax .text:080499F9 call recv_data2 .text:080499FE test eax, eax .text:08049A00 jz short loc_8049A66 .text:08049A02 mov eax, [ebp+var_C] .text:08049A05 mov [esp+4], eax .text:08049A09 mov eax, [ebp+arg_0] .text:08049A0C mov [esp], eax .text:08049A0F call data_send3 .text:08049A14 jmp short loc_8049A66 .text:08049A16 .text:08049A16 loc_8049A16: .text:08049A16 mov eax, [ebp+ptr] .text:08049A19 add eax, 24h .text:08049A1C mov dword ptr [esp+8], 10h .text:08049A24 mov [esp+4], eax .text:08049A28 mov eax, [ebp+arg_0] .text:08049A2B mov [esp], eax .text:08049A2E call recv_data3 .text:08049A33 test eax, eax .text:08049A35 jz short loc_8049A66 .text:08049A37 mov eax, [ebp+ptr] .text:08049A3A mov [esp+4], eax .text:08049A3E mov eax, [ebp+arg_0] .text:08049A41 mov [esp], eax .text:08049A44 call data_send2 .text:08049A49 jmp short loc_8049A66 .text:08049A4B .text:08049A4B loc_8049A4B: .text:08049A4B mov dword ptr [esp+8], 0 .text:08049A53 mov dword ptr [esp+4], offset aOops ; "oops!\n" .text:08049A5B mov eax, [ebp+arg_0] .text:08049A5E mov [esp], eax .text:08049A61 call str_send .text:08049A66 .text:08049A66 loc_8049A66: .text:08049A66 mov eax, [ebp+ptr] .text:08049A69 mov [esp], eax .text:08049A6C call _free .text:08049A71 mov [ebp+var_3C], 0 .text:08049A78 .text:08049A78 loc_8049A78: .text:08049A78 mov eax, [ebp+var_3C] .text:08049A7B add esp, 50h .text:08049A7E pop ebx .text:08049A7F pop esi .text:08049A80 pop ebp .text:08049A81 retn
以上からmallocで確保したメモリのメモリマップは、
3Chの場合は
00h-19h -> plaintext内データ
20h-23h -> plaintext内データのサイズ
24h-33h -> クライアントが入力した文字列
34h-38h -> 5バイトの乱数
4Chの場合は
00h-19h -> plaintext内データ
20h-23h -> plaintext内データのサイズ
24h-43h -> クライアントが入力した文字列
44h-48h -> 5バイトの乱数
となる
またエンコードの処理フローは
- 5バイトの乱数から256バイトのテーブルを作成
- テーブルを使用してplaintext内の0x20バイトのデータをxorする
- エンコードされた(xorされた)データをクライアントへ送信
となる
plaintext内の0x20バイトデータはxorされているだけである
5バイトの乱数さえ分かれば、同じテーブルが作成できるため
同様にデコードデータをxorすることでplaintext内のデータが復元できる
つまり5バイトデータのブルートフォースアタックである
以上のコードをC言語にし、デコーダを作成する
// decode.c #include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> unsigned char byte_804AEC4; unsigned char byte_804AEE0; unsigned char byte_804AF00[256]; int sub_8049350(char *val, int len) { int i; unsigned char var_1; byte_804AEE0 = 0; for(i=0; i < 256; i++) byte_804AF00[i] = i; for(i=0; i < 256; i++){ byte_804AEE0 = (byte_804AEE0 + byte_804AF00[i] + val[i % len]) & 0xFF; var_1 = byte_804AF00[byte_804AEE0]; byte_804AF00[byte_804AEE0] = byte_804AF00[i]; byte_804AF00[i] = var_1; } byte_804AEE0 = 0; byte_804AEC4 = 0; return 0; } unsigned char sub_8049410(void) { unsigned char var_1; byte_804AEC4++; byte_804AEE0 = (byte_804AEE0 + byte_804AF00[byte_804AEC4]) & 0xFF; var_1 = byte_804AF00[byte_804AEE0]; byte_804AF00[byte_804AEE0] = byte_804AF00[byte_804AEC4]; byte_804AF00[byte_804AEC4] = var_1; var_1 += byte_804AF00[byte_804AEE0]; return byte_804AF00[var_1]; } int loc_80499C8(unsigned char *p, int len) { int var_10; for(var_10=0; var_10 < len; var_10++){ p[var_10] = (p[var_10] ^ sub_8049410()) & 0xFF; if(isascii((int)p[var_10]) == 0) return 1; } return 0; } int inc(unsigned char *key) { if(key[0] != 0xFF){ key[0]++; return 0; }else{ key[0] = 0; } if(key[1] != 0xFF){ key[1]++; return 0; }else{ key[1] = 0; } if(key[2] != 0xFF){ key[2]++; return 0; }else{ key[2] = 0; } if(key[3] != 0xFF){ key[3]++; return 0; }else{ key[3] = 0; } if(key[4] != 0xFF){ key[4]++; return 0; }else{ key[4] = 0; } return 1; } int main(void) { unsigned char key[5] = { 0x00, 0x00, 0x00, 0x00, 0x00, }; unsigned char temp[0x20+1]; unsigned char code[0x20] = { 0xa1, 0xdd, 0x32, 0xdc, 0x47, 0x1d, 0x11, 0xd4, 0x2a, 0x5f, 0x15, 0xd1, 0x6e, 0x29, 0x7a, 0xe7, 0xc4, 0xda, 0x2e, 0xd2, 0x72, 0x3d, 0x77, 0x08, 0x82, 0xed, 0xad, 0xe4, 0x73, 0xc8, 0x24, 0x2a, }; do{ sub_8049350(key, 5); memcpy(temp, code, 0x20); temp[0x20] = 0x00; if(loc_80499C8(temp, 0x20) == 0) printf("%s\n", (char *)temp); }while(inc(key) == 0); return 0; }
plaintext内のデータはすべて文字列だと仮定し
0x20バイトすべてがisasciiならばデコード成功と定義して
0x00 0x00 0x00 0x00 0x00 から 0xFF 0xFF 0xFF 0xFF 0xFF までを
ブルートフォースする
これを実行すれば、plaintext内のデータが得られるはずだが…
$ gcc decode.c -o decode $ ./decode (1時間待っても応答なし)
5バイトはどうやらブルートフォースするには長すぎるようだ…。
ここでもう一度実行結果を見ると…
$ nc localhost 7852 Password: chickenfingers What is your name? test Welcome test ::(←謎のデータ) Your challenge is to decode the following hex encoded string: a1dd32dc471d11d42a5f15d16e297ae7c4da2ed2723d770882edade473c8242a Good luck!
"Welcome ユーザー名"の後に謎のデータを受け取っている
この部分をIDAProで解析すると…
// malloc(3Ch)版 .text:080495FE mov dword ptr [esp+4], offset s ; "Welcome " .text:08049606 mov eax, [ebp+fd] .text:08049609 mov [esp], eax .text:0804960C call str_send .text:08049611 mov eax, [ebp+arg_4] .text:08049614 add eax, 24h .text:08049617 mov dword ptr [esp+8], 11h .text:0804961F mov [esp+4], eax .text:08049623 mov eax, [ebp+fd] .text:08049626 mov [esp], eax .text:08049629 call data_send
// malloc(4Ch)版 .text:08049706 mov dword ptr [esp+4], offset s ; "Welcome " .text:0804970E mov eax, [ebp+fd] .text:08049711 mov [esp], eax .text:08049714 call unicode_send .text:08049719 mov eax, [ebp+arg_4] .text:0804971C add eax, 24h .text:0804971F mov dword ptr [esp+8], 22h .text:08049727 mov [esp+4], eax .text:0804972B mov eax, [ebp+fd] .text:0804972E mov [esp], eax .text:08049731 call data_send
mallocで確保したメモリのメモリマップは、
3Chの場合は
00h-19h -> plaintext内データ
20h-23h -> plaintext内データのサイズ
24h-33h -> クライアントが入力した文字列
34h-38h -> 5バイトの乱数
4Chの場合は
00h-19h -> plaintext内データ
20h-23h -> plaintext内データのサイズ
24h-43h -> クライアントが入力した文字列
44h-48h -> 5バイトの乱数
と、ユーザー名用に確保するのは10h or 20hであるにも関わらず
3Chの場合は0x11バイト、4Chの場合は0x22バイトをクライアントへ送信している
つまり、クライアントが入力した文字列の次のデータ列、
つまり、5バイトの乱数の先頭1バイト or 2バイトがクライアントへ送信される
mallocするサイズ4Chか3Chかはvar_29の値で決まるため、
"chickenfingers"入力時にさらに1文字追記すれば操作可能
"A"は0x41であるため最下位ビットがON、"B"は0x42であるため最下位ビットがOFF
$ cat > test.rb #!/usr/bin/ruby require "socket" begin sock = TCPSocket.open(ARGV[0], 7852) rescue puts "TCPSocket.open failed: #$!\n" else pass = "chickenfingers" + ARGV[1] uname = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH" print sock.sysread 1024 sock.write(pass + "\x0a") print pass + "\n" sleep 1 print sock.sysread 1024 sock.syswrite(uname) print uname + "\n" sleep 1 print sock.gets print sock.gets print sock.gets sock.close() end $ ruby test.rb localhost A > 4Ch.dump $ ruby test.rb localhost B > 3Ch.dump
まずは4Chの方を調査
$ hexdump -C 4Ch.dump 00000000 50 61 73 73 77 6f 72 64 3a 20 63 68 69 63 6b 65 |Password: chicke| 00000010 6e 66 69 6e 67 65 72 73 41 0a 00 57 00 68 00 61 |nfingersA..W.h.a| 00000020 00 74 00 20 00 69 00 73 00 20 00 79 00 6f 00 75 |.t. .i.s. .y.o.u| 00000030 00 72 00 20 00 6e 00 61 00 6d 00 65 00 3f 00 20 |.r. .n.a.m.e.?. | 00000040 41 41 41 41 42 42 42 42 43 43 43 43 44 44 44 44 |AAAABBBBCCCCDDDD| 00000050 45 45 45 45 46 46 46 46 47 47 47 47 48 48 48 48 |EEEEFFFFGGGGHHHH| 00000060 0a 00 57 00 65 00 6c 00 63 00 6f 00 6d 00 65 00 |..W.e.l.c.o.m.e.| 00000070 20 41 41 41 41 42 42 42 42 43 43 43 43 44 44 44 | AAAABBBBCCCCDDD| 00000080 44 45 45 45 45 46 46 46 46 47 47 47 47 48 48 48 |DEEEEFFFFGGGGHHH| 00000090 48[a2 4b]00 3a 00 0a 00 59 00 6f 00 75 00 72 00 |H.K.:...Y.o.u.r.| 000000a0 20 00 63 00 68 00 61 00 6c 00 6c 00 65 00 6e 00 | .c.h.a.l.l.e.n.| 000000b0 67 00 65 00 20 00 69 00 73 00 20 00 74 00 6f 00 |g.e. .i.s. .t.o.| 000000c0 20 00 64 00 65 00 63 00 6f 00 64 00 65 00 20 00 | .d.e.c.o.d.e. .| 000000d0 74 00 68 00 65 00 20 00 66 00 6f 00 6c 00 6c 00 |t.h.e. .f.o.l.l.| 000000e0 6f 00 77 00 69 00 6e 00 67 00 20 00 68 00 65 00 |o.w.i.n.g. .h.e.| 000000f0 78 00 20 00 65 00 6e 00 63 00 6f 00 64 00 65 00 |x. .e.n.c.o.d.e.| 00000100 64 00 20 00 73 00 74 00 72 00 69 00 6e 00 67 00 |d. .s.t.r.i.n.g.| 00000110 3a 00 0a 00 30 00 62 00 35 00 36 00 38 00 38 00 |:...0.b.5.6.8.8.| 00000120 31 00 31 00 36 00 31 00 62 00 33 00 35 00 37 00 |1.1.6.1.b.3.5.7.| 00000130 34 00 30 00 38 00 37 00 37 00 31 00 31 00 32 00 |4.0.8.7.7.1.1.2.| 00000140 36 00 65 00 36 00 30 00 34 00 36 00 62 00 36 00 |6.e.6.0.4.6.b.6.| 00000150 34 00 65 00 64 00 35 00 66 00 64 00 37 00 37 00 |4.e.d.5.f.d.7.7.| 00000160 66 00 33 00 34 00 32 00 62 00 32 00 33 00 33 00 |f.3.4.2.b.2.3.3.| 00000170 61 00 61 00 61 00 38 00 32 00 61 00 61 00 39 00 |a.a.a.8.2.a.a.9.| 00000180 36 00 38 00 63 00 65 00 65 00 34 00 31 00 37 00 |6.8.c.e.e.4.1.7.| 00000190 63 00 65 00 0a |c.e..| 00000195 $ strings -el 4Ch.dump Welcome Your challenge is to decode the following hex encoded string: 0b56881161b357408771126e6046b64ed5fd77f342b233aaa82aa968cee417ce
続いて3Chの方を調査
$ hexdump -C 3Ch.dump
00000000 50 61 73 73 77 6f 72 64 3a 20 63 68 69 63 6b 65 |Password: chicke|
00000010 6e 66 69 6e 67 65 72 73 42 0a 57 68 61 74 20 69 |nfingersB.What i|
00000020 73 20 79 6f 75 72 20 6e 61 6d 65 3f 20 41 41 41 |s your name? AAA|
00000030 41 42 42 42 42 43 43 43 43 44 44 44 44 45 45 45 |ABBBBCCCCDDDDEEE|
00000040 45 46 46 46 46 47 47 47 47 48 48 48 48 0a 57 65 |EFFFFGGGGHHHH.We|
00000050 6c 63 6f 6d 65 20 41 41 41 41 42 42 42 42 43 43 |lcome AAAABBBBCC|
00000060 43 43 44 44 44 44[78]3a 0a 59 6f 75 72 20 63 68 |CCDDDDx:.Your ch|
00000070 61 6c 6c 65 6e 67 65 20 69 73 20 74 6f 20 64 65 |allenge is to de|
00000080 63 6f 64 65 20 74 68 65 20 66 6f 6c 6c 6f 77 69 |code the followi|
00000090 6e 67 20 68 65 78 20 65 6e 63 6f 64 65 64 20 73 |ng hex encoded s|
000000a0 74 72 69 6e 67 3a 0a 62 34 33 66 62 64 31 30 63 |tring:.b43fbd10c|
000000b0 38 35 61 35 32 62 36 34 31 31 62 37 66 62 63 35 |85a52b6411b7fbc5|
000000c0 31 31 36 37 38 38 33 30 63 37 63 63 39 64 66 66 |11678830c7cc9dff|
000000d0 31 31 30 31 36 32 39 62 31 35 66 30 65 32 38 37 |1101629b15f0e287|
000000e0 32 65 61 64 36 32 63 0a |2ead62c.|
000000e8
$ strings 3Ch.dump
Password: chickenfingersB
What is your name? AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH
Welcome AAAABBBBCCCCDDDDx:
Your challenge is to decode the following hex encoded string:
b43fbd10c85a52b6411b7fbc511678830c7cc9dff1101629b15f0e2872ead62c
それぞれ2バイト or 1バイトの乱数が出力されている
当然2バイトの方が楽なので4Ch版を使って再度ブルートフォースを行う
4Ch版の乱数の上位2バイトは0xa2, 0x4b
エンコードされたデータは
0b56881161b357408771126e6046b64ed5fd77f342b233aaa82aa968cee417ce
これを利用してdecode.cを改良する
cat > decode.c #include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> unsigned char byte_804AEC4; unsigned char byte_804AEE0; unsigned char byte_804AF00[256]; int sub_8049350(char *val, int len) { int i; unsigned char var_1; byte_804AEE0 = 0; for(i=0; i < 256; i++) byte_804AF00[i] = i; for(i=0; i < 256; i++){ byte_804AEE0 = (byte_804AEE0 + byte_804AF00[i] + val[i % len]) & 0xFF; var_1 = byte_804AF00[byte_804AEE0]; byte_804AF00[byte_804AEE0] = byte_804AF00[i]; byte_804AF00[i] = var_1; } byte_804AEE0 = 0; byte_804AEC4 = 0; return 0; } unsigned char sub_8049410(void) { unsigned char var_1; byte_804AEC4++; byte_804AEE0 = (byte_804AEE0 + byte_804AF00[byte_804AEC4]) & 0xFF; var_1 = byte_804AF00[byte_804AEE0]; byte_804AF00[byte_804AEE0] = byte_804AF00[byte_804AEC4]; byte_804AF00[byte_804AEC4] = var_1; var_1 += byte_804AF00[byte_804AEE0]; return byte_804AF00[var_1]; } int loc_80499C8(unsigned char *p, int len) { int var_10; for(var_10=0; var_10 < len; var_10++){ p[var_10] = (p[var_10] ^ sub_8049410()) & 0xFF; if(isascii((int)p[var_10]) == 0) return 1; } return 0; } int inc(unsigned char *key) { //if(key[0] != 0xFF){ key[0]++; return 0; }else{ key[0] = 0; } //if(key[1] != 0xFF){ key[1]++; return 0; }else{ key[1] = 0; } if(key[2] != 0xFF){ key[2]++; return 0; }else{ key[2] = 0; } if(key[3] != 0xFF){ key[3]++; return 0; }else{ key[3] = 0; } if(key[4] != 0xFF){ key[4]++; return 0; }else{ key[4] = 0; } return 1; } int main(void) { unsigned char key[5] = { 0xa2, 0x4b, 0x00, 0x00, 0x00, }; unsigned char temp[0x20+1]; unsigned char code[0x20] = { 0x0b, 0x56, 0x88, 0x11, 0x61, 0xb3, 0x57, 0x40, 0x87, 0x71, 0x12, 0x6e, 0x60, 0x46, 0xb6, 0x4e, 0xd5, 0xfd, 0x77, 0xf3, 0x42, 0xb2, 0x33, 0xaa, 0xa8, 0x2a, 0xa9, 0x68, 0xce, 0xe4, 0x17, 0xce, }; do{ sub_8049350(key, 5); memcpy(temp, code, 0x20); temp[0x20] = 0x00; if(loc_80499C8(temp, 0x20) == 0) printf("%s\n", (char *)temp); }while(inc(key) == 0); return 0; } $ gcc decode.c -o decode $ ./decode AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHH
無事plaintextの中が確認できた
あとはこれをpwn11.ddtek.bizに対して行うことでパスワードが得られる
答えは"Anal leakage beats key leakage a"となる