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"となる