amaga38のブログ

twitter: @amaga38

PlaidCTF 2017 write-up

4/22-23に開催されたPlaidCTF 2017で解けた問題のwrite-upです。
チーム名 CTFinfinitとして参加し、226pts獲得して185位でした。

sanity check (Misc/1pts)

The flag is PCTF{poop}

テスト問題なので、フラグ投稿するだけ。

zipper (Misc/50pts)

Something doesn't seem quite right with this zip file. 
Can you fix it and get the flag?

とりあえずzipinfoでは "filename too long" と出力される。

ubuntu@zipper$ zipinfo zipper.zip 
Archive:  zipper.zip
Zip file size: 236 bytes, number of entries: 1
warning:  filename too long--truncating.
-rw-rw-r--  3.0 unx      246 tx defX 17-Apr-18 19:15

バイナリエディタで眺めてみると、0x2329 となっていて確かにファイル名長さの値が大きすぎる。また、ファイル名が入りそうな領域がすべて 0x00 になっている。
f:id:amaga38:20170424234027p:plain
8文字分だったので、適当にファイル名を flag.txt として値を修正。
f:id:amaga38:20170424234406p:plain
あとは、修正したファイルを展開して中のファイルを閲覧して終わり。

Huzzah, you have captured the flag:
PCTF{f0rens1cs_yay}    

Down the Reversing Hole (Misc/50pts)

Don't forget. This is a MISC challenge. 

MISCチャレンジということで、実行ファイルに何か隠されているだろうとバイナリを眺めてみたらMZスタブが普通の実行ファイルに比べて大きい。あからさまに何かある。


f:id:amaga38:20170424230758p:plain
MZスタブはMS-DOSとの互換性のための処理らしい*1ので、IDA freeで実行ファイルをDOSプログラムとして解析させてみる。

f:id:amaga38:20170424225533p:plain

f:id:amaga38:20170424225027p:plain
解析結果は割と小さく、どうやらデータをXORしているだけらしい。

seg000:0000                         public start
seg000:0000                         start proc near
seg000:0000 B8 03 00                mov     ax, seg dseg
seg000:0003 8E D8                   mov     ds, ax
seg000:0005                         assume ds:dseg
seg000:0005 8D 16 00 00             lea     dx, unk_30
seg000:0009 B4 09                   mov     ah, 9 ; Write string to STDOUT
seg000:000B CD 21                   int     21h             ; DOS - PRINT STRING
seg000:000B                                                 ; DS:DX -> string terminated by "$"
seg000:000D B9 00 00                mov     cx, 0

seg000:0010
seg000:0010                         loc_10:
seg000:0010 83 F9 39                cmp     cx, 39h ; '9' ; 39文字分
seg000:0013 74 17                   jz      short loc_

seg000:0015 8D 1E 2B 00             lea     bx, unk_5B ; 0x25B~
seg000:0019 03 D9                   add     bx, cx
seg000:001B 8A 17                   mov     dl, [bx]
seg000:001D 8D 1E 64 00             lea     bx, unk_94 ; 0x294~
seg000:0021 03 D9                   add     bx, cx
seg000:0023 32 17                   xor     dl, [bx]

seg000:0025 B4 02                   mov     ah, 2 ; Write character to STDOUT
seg000:0027 CD 21                   int     21h             ; DOS - DISPLAY OUTPUT
seg000:0027                                                 ; DL = character to send to standard output
seg000:0029 41                      inc     cx
seg000:002A EB E4                   jmp     short loc_10

seg000:002C
seg000:002C                         loc_2C:
seg000:002C B4 4C                   mov     ah, 4Ch ; Exit program
seg000:002E CD 21                   int     21h             ; DOS - 2+ - QUIT WITH EXIT CODE (EXIT)
seg000:002E                         start endp              ; AL = exit code
seg000:002E
seg000:002E                         seg000 ends

フラグを得るプログラムは以下のようになった。

#!/usr/bin/python

f = open("reversing-hole.exe", "rb")
data = f.read()
f.close()

f = open("flag.txt", "wb")
dl = data[0x25B:0x294]
bx = data[0x294:0x2CD]

for i in range(len(dl)):
    t = ord(dl[i]) ^ ord(bx[i])
    f.write(chr(t))
f.write("\n")
f.close()
ubuntu@misc$ ./solve.py; cat flag.txt 
But here's a flag PCTF{at_l3a5t_th3r3s_d00m_p0rts_0n_d0s}

no_mo_flo (Reversing/125pts)

Can you go with the flow?

とりあえず、逆アセンブル

objdump -D -Mintel no_flo > no_flo.asm

mainのアセンブリ

 ; main
  40272e:	55                   	push   rbp
  40272f:	48 89 e5             	mov    rbp,rsp
  402732:	48 81 ec b0 00 00 00 	sub    rsp,0xb0
  402739:	48 8d 45 d0          	lea    rax,[rbp-0x30]
  40273d:	ba 20 00 00 00       	mov    edx,0x20
  402742:	48 89 c6             	mov    rsi,rax
  402745:	bf 00 00 00 00       	mov    edi,0x0
  40274a:	e8 31 de ff ff       	call   400580 <read@plt>
  40274f:	89 45 f8             	mov    DWORD PTR [rbp-0x8],eax
  402752:	83 7d f8 20          	cmp    DWORD PTR [rbp-0x8],0x20 ; 32文字
  402756:	74 14                	je     40276c <strerror@plt+0x21ac>
  ; error;
	  402758:	bf 58 2d 40 00       	mov    edi,0x402d58
	  40275d:	e8 ee dd ff ff       	call   400550 <puts@plt>
	  402762:	b8 ff ff ff ff       	mov    eax,0xffffffff
	  402767:	e9 9b 00 00 00       	jmp    402807 <strerror@plt+0x2247>
  ; for 
  40276c:	c7 45 fc 00 00 00 00 	mov    DWORD PTR [rbp-0x4],0x0
  402773:	eb 3a                	jmp    4027af <strerror@plt+0x21ef>
	  402775:	8b 45 fc             	mov    eax,DWORD PTR [rbp-0x4]
	  402778:	01 c0                	add    eax,eax
	  40277a:	48 98                	cdqe   
	  40277c:	0f b6 44 05 d0       	movzx  eax,BYTE PTR [rbp+rax*1-0x30]
	  402781:	0f be d0             	movsx  edx,al
	  402784:	8b 45 fc             	mov    eax,DWORD PTR [rbp-0x4]
	  402787:	48 98                	cdqe   
	  402789:	89 94 85 50 ff ff ff 	mov    DWORD PTR [rbp+rax*4-0xb0],edx
	  
	  402790:	8b 45 fc             	mov    eax,DWORD PTR [rbp-0x4]
	  402793:	01 c0                	add    eax,eax
	  402795:	83 c0 01             	add    eax,0x1
	  402798:	48 98                	cdqe   
	  40279a:	0f b6 44 05 d0       	movzx  eax,BYTE PTR [rbp+rax*1-0x30]
	  40279f:	0f be d0             	movsx  edx,al
	  4027a2:	8b 45 fc             	mov    eax,DWORD PTR [rbp-0x4]
	  4027a5:	48 98                	cdqe   
	  4027a7:	89 54 85 90          	mov    DWORD PTR [rbp+rax*4-0x70],edx
	  4027ab:	83 45 fc 01          	add    DWORD PTR [rbp-0x4],0x1
	  
	  4027af:	83 7d fc 0f          	cmp    DWORD PTR [rbp-0x4],0xf
	  4027b3:	7e c0                	jle    402775 <strerror@plt+0x21b5>
  ; }

  4027b5:	b8 00 00 00 00       	mov    eax,0x0
  4027ba:	e8 b8 04 00 00       	call   402c77 <strerror@plt+0x26b7>
 
  ; [rbp-0xb0]のチェック
  4027bf:	48 8d 85 50 ff ff ff 	lea    rax,[rbp-0xb0]
  4027c6:	48 89 c7             	mov    rdi,rax
  4027c9:	e8 f8 de ff ff       	call   4006c6 <strerror@plt+0x106>
  4027ce:	89 45 f4             	mov    DWORD PTR [rbp-0xc],eax
  ; [rbp-0x70]のチェック
  4027d1:	48 8d 45 90          	lea    rax,[rbp-0x70]
  4027d5:	48 89 c7             	mov    rdi,rax
  4027d8:	e8 3b e7 ff ff       	call   400f18 <strerror@plt+0x958>
  4027dd:	89 45 f0             	mov    DWORD PTR [rbp-0x10],eax

  4027e0:	83 7d f4 00          	cmp    DWORD PTR [rbp-0xc],0x0
  4027e4:	74 12                	je     4027f8 <strerror@plt+0x2238>
  4027e6:	83 7d f0 00          	cmp    DWORD PTR [rbp-0x10],0x0
  4027ea:	74 0c                	je     4027f8 <strerror@plt+0x2238>

  4027ec:	bf 65 2d 40 00       	mov    edi,0x402d65 ; "Good flow!!"
  4027f1:	e8 5a dd ff ff       	call   400550 <puts@plt>
  4027f6:	eb 0a                	jmp    402802 <strerror@plt+0x2242>

  4027f8:	bf 78 2d 40 00       	mov    edi,0x402d78 ; "You aint goin with the flow...."
  4027fd:	e8 4e dd ff ff       	call   400550 <puts@plt>
  402802:	b8 00 00 00 00       	mov    eax,0x0
  402807:	c9                   	leave  
  402808:	c3                   	ret    

main関数はなんとなくこんな感じ。
readで標準入力から読み込んだデータを2つのバッファへ交互に格納。
f_4006c6()、f_400f18()でそれぞれ入力されたデータのチェック。両関数の戻り値が0でなければ、Good flow!!が出力される流れ。なので、おそらく入力データがフラグになるだろうと予想。

int main( ) {
    int i; // rbp-0x4
    int var_0xc, var_0x10;
    char buff_0x30[33], buff_0x70[16], buff_0xb0[16];
    
    if (0x20 != read(0, buff_0x30, 0x20)) {
        return -1;
    }
    for (i=0; i<=0xf; i++) {
        buff_0xb0[i*4] = buff_0x30[2 * i];
        buff_0x70[i*4] = buff_0x30[(2 * i) + 1];
    }

    f_402c77();

    var_0xc = f_4006c6(buff_0xb0);
    var_0x10 = f_400f18(buff_0x70);

    if (var_0xc == 0 || var_0x10 == 0) {
        puts("You aint goin with the flow....");
    } else {
        puts("Good flow!!");
    }
    return 0;
}

f_4006c6の処理は、単純でcmpの結果 je されるルート(比較結果がイコール)になる入力を求める。
関数の返却値は、r8dレジスタに格納され、NGの文字が入力されていた場合、r8dに0が格納される。

  :
  4006f6:	8b 3a                	mov    edi,DWORD PTR [rdx] ; データ
  4006f8:	89 fa                	mov    edx,edi
  4006fa:	83 c2 05             	add    edx,0x5 ; データに5が加わる↓
  4006fd:	89 d7                	mov    edi,edx
  4006ff:	83 ff 55             	cmp    edi,0x55 ; 0x50 = "P" ↑で5加わっているので、入力は減算
  400702:	4c 8d 1c 25 30 07 40 	lea    r11,ds:0x400730 ; OKの場合の次のアドレス
  400709:	00 
  40070a:	0f 84 00 08 00 00    	je     400f10 <strerror@plt+0x950> ; OK
  400710:	4c 8d 1c 25 1d 07 40 	lea    r11,ds:0x40071d ; ↓でjmpして戻ってくるアドレス
  400717:	00 
  400718:	e9 f3 07 00 00       	jmp    400f10 <strerror@plt+0x950> ; 一旦jmpして戻ってくる
  40071d:	41 b8 00 00 00 00    	mov    r8d,0x0 ; NG
  400723:	4c 8d 1c 25 3d 07 40 	lea    r11,ds:0x40073d
  :

f_400f18の処理は、f_402c77でsigaction@pltで登録されたシグナル処理を利用してデータチェックをしている。
sigactionでは、SIGFPEのシグナル時の処理として、f_4005a0が登録されている。SIGFPEは浮動小数点例外(0除算処理)時のシグナル。
sigemptyset@plt に渡す前にsa_flagsにSA_SIGINFOを指定しているので、シグナルハンドラーが呼び出される際の引数として、最初の引き数としてシグナル番号を、二番目の引き数として siginfo_t へのポインターを、三番目の引き数として (void * にキャストした) ucontext_t へのポインターを受けとる。*2

  402c77:	55                   	push   rbp
  402c78:	48 89 e5             	mov    rbp,rsp
  402c7b:	48 81 ec a0 00 00 00 	sub    rsp,0xa0
  402c82:	c7 45 e8 04 00 00 00 	mov    DWORD PTR [rbp-0x18],0x4 ; SA_SIGINFO
  402c89:	48 c7 85 60 ff ff ff 	mov    QWORD PTR [rbp-0xa0],0x402b06
  402c90:	06 2b 40 00 
  402c94:	48 8d 85 60 ff ff ff 	lea    rax,[rbp-0xa0]
  402c9b:	48 83 c0 08          	add    rax,0x8
  402c9f:	48 89 c7             	mov    rdi,rax
  402ca2:	e8 f9 d8 ff ff       	call   4005a0 <sigemptyset@plt> ; 初期化
  402ca7:	48 8d 85 60 ff ff ff 	lea    rax,[rbp-0xa0]
  402cae:	ba 00 00 00 00       	mov    edx,0x0 ; struct sigaction *old
  402cb3:	48 89 c6             	mov    rsi,rax ; struct sigaction *act
  402cb6:	bf 08 00 00 00       	mov    edi,0x8 ; SIGFPE
  402cbb:	e8 a0 d8 ff ff       	call   400560 <sigaction@plt>
  402cc0:	c9                   	leave  
  402cc1:	c3                   	ret    

チェック処理は、400f84にあるようにワザと0除算をして、SIGFPEに登録したシグナルハンドラを呼び出している。シグナルハンドラーの結果がOKだったら、0x603340にjmp先のアドレスが返却されてくる。
f_400f18関数の返却値は、esiレジスタに格納され、NGの文字が入力されていた場合、esiに0が格納される。

  :
  400f37:	8b 02                	mov    eax,DWORD PTR [rdx] ; データ
  400f39:	89 c2                	mov    edx,eax
  400f3b:	83 ea 03             	sub    edx,0x3
  400f3e:	89 d0                	mov    eax,edx
  400f40:	83 f8 40             	cmp    eax,0x40 ; 0x43 = "C"
  400f43:	4c 8d 14 25 28 10 40 	lea    r10,ds:0x401028
  400f4a:	00 
  400f4b:	49 c7 c3 05 00 00 00 	mov    r11,0x5
  400f52:	c7 04 25 28 33 60 00 	mov    DWORD PTR ds:0x603328,0x1 ; 比較に利用
  400f59:	01 00 00 00 
  400f5d:	48 89 04 25 30 33 60 	mov    QWORD PTR ds:0x603330,rax ; "C"
  400f64:	00 
  400f65:	48 c7 c0 00 00 00 00 	mov    rax,0x0
  400f6c:	48 89 14 25 38 33 60 	mov    QWORD PTR ds:0x603338,rdx
  400f73:	00 
  400f74:	48 8d 15 00 00 00 00 	lea    rdx,[rip+0x0]        # 400f7b <strerror@plt+0x9bb>

  400f7b:	48 89 14 25 48 33 60 	mov    QWORD PTR ds:0x603348,rdx
  400f82:	00 
  400f83:	99                   	cdq    
  400f84:	48 f7 3c 25 20 33 60 	idiv   QWORD PTR ds:0x603320 ; 0除算でsigハンドラ呼び出し
  400f8b:	00 
  400f8c:	48 c7 04 25 20 33 60 	mov    QWORD PTR ds:0x603320,0x0
  400f93:	00 00 00 00 00 
  400f98:	48 8b 04 25 30 33 60 	mov    rax,QWORD PTR ds:0x603330
  400f9f:	00 
  400fa0:	48 8b 14 25 38 33 60 	mov    rdx,QWORD PTR ds:0x603338
  400fa7:	00 
  400fa8:	4c 8b 1c 25 40 33 60 	mov    r11,QWORD PTR ds:0x603340 ; 次のアドレス
  400faf:	00 
  400fb0:	41 ff e3             	jmp    r11 ; OKなら次の検査
  400fb3:	be 00 00 00 00       	mov    esi,0x0 ; NG
  400fb8:	4c 8d 14 25 28 10 40 	lea    r10,ds:0x401028
  :

シグナルハンドラーでは、第3引数として受け取ったコンテキスト(ucontext_t へのポインター)のなかのEFLAGSを利用してデータの比較結果を確認していた。

  40289a:	55                   	push   rbp
  40289b:	48 89 e5             	mov    rbp,rsp
  40289e:	48 89 7d d8          	mov    QWORD PTR [rbp-0x28],rdi
  4028a2:	48 89 75 d0          	mov    QWORD PTR [rbp-0x30],rsi
  4028a6:	48 8b 45 d8          	mov    rax,QWORD PTR [rbp-0x28]
  4028aa:	48 89 45 f8          	mov    QWORD PTR [rbp-0x8],rax
  4028ae:	48 8b 05 93 0a 20 00 	mov    rax,QWORD PTR [rip+0x200a93]        # 603348 <strerror@plt+0x202d88>
  4028b5:	48 83 c0 38          	add    rax,0x38
  4028b9:	48 89 45 f0          	mov    QWORD PTR [rbp-0x10],rax
  4028bd:	48 8b 45 d0          	mov    rax,QWORD PTR [rbp-0x30] ; シグナルハンドラー呼び出し時のEFLAGS
  4028c1:	83 e0 40             	and    eax,0x40 ; ZF 結果が0
  4028c4:	48 85 c0             	test   rax,rax
  4028c7:	0f 9f c0             	setg   al
  4028ca:	0f b6 c0             	movzx  eax,al
  4028cd:	89 45 ec             	mov    DWORD PTR [rbp-0x14],eax
  4028d0:	48 8b 45 d0          	mov    rax,QWORD PTR [rbp-0x30]
  4028d4:	25 00 08 00 00       	and    eax,0x800 ; OF オーバーフロー
  4028d9:	48 85 c0             	test   rax,rax
  4028dc:	0f 9f c0             	setg   al
  4028df:	0f b6 c0             	movzx  eax,al
  4028e2:	89 45 e8             	mov    DWORD PTR [rbp-0x18],eax
  4028e5:	48 8b 45 d0          	mov    rax,QWORD PTR [rbp-0x30]
  4028e9:	25 80 00 00 00       	and    eax,0x80 ; SF 符号。負の場合。
  4028ee:	48 85 c0             	test   rax,rax
  4028f1:	0f 9f c0             	setg   al
  :

比較結果の確認のパターンとしては、イコールになるか X+1 > X > X-1 となるXか?の2種類だった(気がする)。例えば、入力文字Xが0x40="@" > X > 0x3e=">"を満たすかどうかをチェックしていた。

Flag: PCTF{n0_fl0?_m0_like_ah_h3ll_n0}