ox0xo infosec tutorial

CCTF2018 writeup

2018-12-28
CTF

Forensic

Admission

Question

このファイルの種類を判別できる4バイトのシグネチャを特定してフラグとして提出してください。

Answer

入力テスト用の問題です。 シグネチャという言葉の意味は文脈によって様々ですが、今回は「ファイルの種別を判別できる」「4バイト」とあるのでマジックナンバーの事だと推察できます。

フラグはPDFファイルのマジックナンバーであるFLAG{25504446}です。

Broken header

Question

このファイルのヘッダは破損しています。 ファイルの種類を判別できる8バイトのシグネチャを特定してフラグとして提出してください。

Answer

先頭8バイトが00で上書きされていますが、IHDR、RGB、IDAT等の文字列が読み取れます。 これらは画像ファイルフォーマットの一つであるPNGの特徴です。

フラグはPNGのマジックナンバーであるFLAG{89504E470D0A1A0A}です。

Crypted header

Question

このファイルは暗号化されています。 ファイルの種類を判別できる2バイトのシグネチャを特定してフラグとして提出してください。

Answer

暗号化の詳細が明示されていませんが、CCTF2018では特別なツールをインストールする必要が無いとアナウンスされていますので、複雑な計算を要する暗号ではないと推察できます。

このバイナリから読み取れる特徴は以下の通りです。

  • 先頭2バイトは 0xDD 0xCA
  • 画像の先頭に 0x90 が多く、その他の部分にも散見される
  • 画像の中央すこし上に複雑なパターンのバイナリがある
  • 画像の中央以下に 0x7F 0x91 というパターンのバイナリが散見される

これらの特徴から 0x90 0x90 もしくは 0x7F 0x91 を利用して 0xDD 0xCA に何らかの操作を加えればマジックナンバーが復号されると仮説を立てて結果を確かめます。

操作 0x90 0x90 0x7F 0x91
AND 90 80 5D 80
OR DD DA FF DB
XOR 4D 5A A2 5B

4D 5A以外は少なくとも代表的なファイルフォーマットのマジックナンバーではありません。

フラグはexeのマジックナンバーであるFLAG{4D5A}です。

Reversing

Debug

Question

このプログラムが出力する数値を特定してフラグとして提出してください。

000000000040051d <main>:
40051d:    push   rbp
40051e:    mov    rbp,rsp
400521:    sub    rsp,0x10
400525:    mov    DWORD PTR [rbp-0x4],0x1
40052c:    mov    DWORD PTR [rbp-0x8],0xa
400533:    mov    DWORD PTR [rbp-0xc],0x64
40053a:    mov    eax,DWORD PTR [rbp-0x8]
40053d:    mov    edx,DWORD PTR [rbp-0x4]
400540:    add    edx,eax
400542:    mov    eax,DWORD PTR [rbp-0xc]
400545:    add    eax,edx
400547:    mov    esi,eax
400549:    mov    edi,0x4005f0 ;"%d\n"
40054e:    mov    eax,0x0
400553:    call   400400 <printf@plt>
400558:    leave  
400559:    ret

Answer

アセンブリの読解問題です。 アセンブリはオペコード(命令)とオペランド(操作対象)の組み合わせから構成される単純なプログラミング言語です。

まずはここここなどを読んでオペコードの意味を調べます。

オペコード 操作
mov 右辺の値を左辺に代入する
add 右辺の値を左辺に加算する
call 指定したアドレスの命令を呼び出す

これによるとアドレス400553のcall 400400 printf@pltが値を出力する役目を持っていそうです。

printfは第1引数に与えられた文字列を出力しますが、第1引数の文字列のなかで書式を指定することで、第2引数以降の任意の数の引数を出力することが出来ます。 アドレス400549で書式指定子”%d\n”が渡されているので、実際に出力される値はアドレス400547で渡されているeaxだと読み取れます。

これを念頭に置いてeaxに関する操作を辿っていきます。

000000000040051d <main>:
40051d:    push   rbp
40051e:    mov    rbp,rsp
400521:    sub    rsp,0x10
400525:    mov    DWORD PTR [rbp-0x4],0x1   ; [rbp-0x4]に1を代入する
40052c:    mov    DWORD PTR [rbp-0x8],0xa   ; [rbp-0x8]に10を代入する
400533:    mov    DWORD PTR [rbp-0xc],0x64  ; [rbp-0xc]に100を代入する
40053a:    mov    eax,DWORD PTR [rbp-0x8]   ; eaxに[rbp-0x8]の10を代入する
40053d:    mov    edx,DWORD PTR [rbp-0x4]   ; edxに[rbp-0x4]の1を代入する
400540:    add    edx,eax                   ; edxにeaxを加算して11にする
400542:    mov    eax,DWORD PTR [rbp-0xc]   ; eaxに[rbp-0xc]の100を代入する
400545:    add    eax,edx                   ; eaxにedxを加算して111にする
400547:    mov    esi,eax                   ; esi(第2引数)にeaxの111を代入する
400549:    mov    edi,0x4005f0 ;"%d\n"
40054e:    mov    eax,0x0
400553:    call   400400 <printf@plt>
400558:    leave  
400559:    ret

したがってフラグはFLAG{111}です。

Enlarging

Question

このプログラムが出力する数値を特定してフラグとして提出してください。

000000000040051d <main>:
40051d:    push   rbp
40051e:    mov    rbp,rsp
400521:    sub    rsp,0x10
400525:    mov    DWORD PTR [rbp-0x4],0x0
40052c:    mov    DWORD PTR [rbp-0x8],0x1
400533:    jmp    40053f <main+0x22>
400535:    mov    eax,DWORD PTR [rbp-0x8]
400538:    add    DWORD PTR [rbp-0x4],eax
40053b:    add    DWORD PTR [rbp-0x8],0x1
40053f:    cmp    DWORD PTR [rbp-0x8],0xa
400543:    jle    400535 <main+0x18>
400545:    mov    eax,DWORD PTR [rbp-0x4]
400548:    mov    esi,eax
40054a:    mov    edi,0x4005f0 ;"%d\n"
40054f:    mov    eax,0x0
400554:    call   400400 <printf@plt>
400559:    leave  
40055a:    ret

Answer

基本はDebugと同じですが新しいオペコードが含まれているので調べます。

オペコード 操作
jmp 指定したアドレスにジャンプする
cmp 引数として渡された左辺と右辺の値を比較する
jle 直前のcmpの結果、左辺の値が右辺の値以下なら指定したアドレスにジャンプする

これを念頭に置いてeaxに関する操作を辿っていきます。

000000000040051d <main>:
40051d:    push   rbp
40051e:    mov    rbp,rsp
400521:    sub    rsp,0x10
400525:    mov    DWORD PTR [rbp-0x4],0x0 ; [rbp-0x4]に0を代入する
40052c:    mov    DWORD PTR [rbp-0x8],0x1 ; [rbp-0x8]に1を代入する
400533:    jmp    40053f <main+0x22>      ; アドレス40053fにジャンプする
400535:    mov    eax,DWORD PTR [rbp-0x8] ; eaxに[rbp-0x8]を代入する
400538:    add    DWORD PTR [rbp-0x4],eax ; [rbp-0x4]にeaxを加算する
40053b:    add    DWORD PTR [rbp-0x8],0x1 ; [rbp-0x8]に1を加算する
40053f:    cmp    DWORD PTR [rbp-0x8],0xa ; [rbp-0x8]と10を比較する
400543:    jle    400535 <main+0x18>      ; [rbp-0x8]が10以下ならアドレス400535にジャンプする
400545:    mov    eax,DWORD PTR [rbp-0x4] ; eaxに[rbp-0x4]を代入する
400548:    mov    esi,eax                 ; esi(第2引数)にeaxを代入する
40054a:    mov    edi,0x4005f0 ;"%d\n"
40054f:    mov    eax,0x0
400554:    call   400400 <printf@plt>
400559:    leave  
40055a:    ret
  • [rbp-0x4]は0で初期化されています。
  • [rbp-0x8]は1で初期化されています。
  • [rbp-0x8]が11になるまでアドレス400535からアドレス400543の処理をループしています。
  • ループの中で[rbp-0x4]に[rbp-0x8]が加算されています。
  • [rbp-0x4]に加算された後で[rbp-0x8]に1が加算されています。
  • ループを抜けた時点の[rbp-0x4]がprintfで出力されています。

したがってこのアセンブリは1から10までの数の和を出力するものです。

フラグはFLAG{55}です。

FILO

Question

このx86プログラムは無限ループするように作られていますがscanf関数にある文字列を渡すとループを抜けることが出来ます。 その文字列の16進数表現を特定してフラグとして提出してください。

000000000040057d <main>:
40057d:    push   rbp
40057e:    mov    rbp,rsp
400581:    sub    rsp,0x10
400585:    mov    QWORD PTR [rbp-0x10],0x0
40058c:    
40058d:    mov    DWORD PTR [rbp-0x8],0x0
400594:    mov    DWORD PTR [rbp-0x4],0x0
40059b:    lea    rax,[rbp-0x10]
40059f:    mov    rsi,rax
4005a2:    mov    edi,0x400660 ;"%s"
4005a7:    mov    eax,0x0
4005ac:    call   400470 <__isoc99_scanf@plt>
4005b1:    cmp    DWORD PTR [rbp-0x4],0xdeadbeef
4005b8:    jne    4005bc <main+0x3f>
4005ba:    jmp    4005be <main+0x41>
4005bc:    jmp    400594 <main+0x17>
4005be:    mov    edi,0x400663 ;"Congratulations!\n"
4005c3:    call   400450 <puts@plt>
4005c8:    leave  
4005c9:    ret  

Answer

まずは新しいオペコードの意味を調べます。

オペコード 操作
lea 右辺のアドレスを左辺に代入する
jne 直前のcmpの結果、右辺と左辺の値が等しくなければ指定したアドレスにジャンプする

leaが使われているのはscanf関数がアドレスを引数にしているからです。 scanf関数は標準入力から受け取った文字列を引数で指定されたアドレスに書き込みます。

これを念頭に置いてアセンブリにコメントを追記します。

000000000040057d <main>:
40057d:    push   rbp
40057e:    mov    rbp,rsp
400581:    sub    rsp,0x10
400585:    mov    QWORD PTR [rbp-0x10],0x0  ; [rbp-0x10]に0を代入する
40058c:    
40058d:    mov    DWORD PTR [rbp-0x8],0x0   ; [rbp-0x8]に0を代入する
400594:    mov    DWORD PTR [rbp-0x4],0x0   ; [rbp-0x4]に0を代入する
40059b:    lea    rax,[rbp-0x10]            ; raxに[rbp-0x10]のアドレスを代入する
40059f:    mov    rsi,rax                   ; rsi(第2引数)にraxを代入する
4005a2:    mov    edi,0x400660 ;"%s"
4005a7:    mov    eax,0x0
4005ac:    call   400470 <__isoc99_scanf@plt>     ; rsiで指定したアドレスに文字列を書き込む
4005b1:    cmp    DWORD PTR [rbp-0x4],0xdeadbeef  ; [rbp-0x4]と0xDEADBEEFを比較する
4005b8:    jne    4005bc <main+0x3f>              ; [rbp-0x4]が0xDEADBEEFでなければアドレス4005bcにジャンプする
4005ba:    jmp    4005be <main+0x41>              ; アドレス4005beにジャンプする
4005bc:    jmp    400594 <main+0x17>              ; アドレス400594にジャンプする
4005be:    mov    edi,0x400663 ;"Congratulations!\n"
4005c3:    call   400450 <puts@plt>
4005c8:    leave  
4005c9:    ret  
  • [rbp-0x10] [rbp-0x8] [rbp-0x4] はすべて0で初期化されています。
  • scanf関数で標準入力から受け取った文字列は[rbp-0x10]に書き込まれます。
  • [rbp-0x4]に0xdeadbeefが書き込まれたらループを抜けます。

[rbp-0x4]に値を書き込む手段が用意されていないので正規の手続きでは解決できません。 scanf関数に抜け道になりそうな脆弱性が無いか確認したところバッファオーバーフローを利用すれば想定外のアドレスに値を書き込めそうです。

バッファオーバーフローを試す前に現在のスタックの状態を確認します。

スタック上の位置 値(byte)  
rbp-0x10 QWORD (8byte) 0x00 低位アドレス
rbp-0x0f   0x00
rbp-0x0e   0x00  
rbp-0x0d   0x00  
rbp-0x0c   0x00  
rbp-0x0b   0x00  
rbp-0x0a   0x00  
rbp-0x09   0x00  
rbp-0x08 DWORD (4byte) 0x00  
rbp-0x07   0x00  
rbp-0x06   0x00  
rbp-0x05   0x00  
rbp-0x04 DWORD (4byte) 0x00  
rbp-0x03   0x00  
rbp-0x02   0x00
rbp-0x01   0x00 高位アドレス

[rbp-0x4]の位置に0xDEADBEEFを書き込むには[rbp-0x10]の位置から12byteのパディングが必要です。 また0xDEADBEEFはリトルエンディアンを考慮して書き込む必要があります。

最終的に作り出したいスタックの状態は以下の通りです。 パディングは0x00以外の値でも構いません。

スタック上の位置 値(byte)  
rbp-0x10 QWORD (8byte) 0x00 低位アドレス
rbp-0x0f   0x00
rbp-0x0e   0x00  
rbp-0x0d   0x00  
rbp-0x0c   0x00  
rbp-0x0b   0x00  
rbp-0x0a   0x00  
rbp-0x09   0x00  
rbp-0x08 DWORD (4byte) 0x00  
rbp-0x07   0x00  
rbp-0x06   0x00  
rbp-0x05   0x00  
rbp-0x04 DWORD (4byte) 0xEF  
rbp-0x03   0xBE  
rbp-0x02   0xAD
rbp-0x01   0xDE 高位アドレス

したがってフラグはFLAG{000000000000000000000000EFBEADDE}です。

OSINT

Gotcha

Question

2018年4月下旬に複数の自治外が運用しているネットワークカメラがクラッキングされる事件がありました。 この時クラッキングされたネットワークカメラの管理画面のバージョンはrelease-14です。 このバージョンがリリースされた日時を特定しフラグとして提出してください。

リリース日時はネットワークカメラの管理画面のHTMLソースに記載されています

Answer

手掛かりは限られていますが、問題の事件について調べるとクラッキングされたネットワークカメラのメーカー名と型番が分かります。 そのネットワークカメラのメーカー名か型番が管理画面に含まれる可能性があるため、それらを指定して検索エンジンでHTMLを探します。

IoTに強い検索エンジンであるshodanで検索した結果は以下の通りです。

フラグはFLAG{20090318 04:53:59}です。

Helpful framework

Question

2018年12月11日に楽天市場を騙るスパムメールがバラまかれました。 メールに記載されたリンクをクリックするとマルウェアがインストールされます。 マルウェアのSHA256ハッシュは0207c06879fb4a2ddaffecc3a6713f2605cbdd90fc238da9845e88ff6aef3f85です。 このマルウェアが権限昇格と防衛回避に用いている一つの戦術を特定し、そのMITRE ATT&CK IDをフラグとして提出してください。

Answer

MITRE ATT&CKは攻撃者が利用するテクニックを分類・識別するためのフレームワークです。 様々なセキュリティ製品が採用しています。 オープンソースで自由に利用できる製品としてはHybrid-AnalysisAnyRunが有名です。

Hybrid-Analysisで対象のハッシュを検索して得られる情報は以下の通りです。

権限昇格と防衛回避に等しく用いられている戦術はProcess Injectionです。

したがってフラグはProcess InjectionのATT&CK IDであるFLAG{T1055}です。

Identity

Question

クリプトジャッキングと呼ばれる攻撃が世界中で行われています。 2018年12月13日時点でクリプトジャッキングの対象にされているサイトの例は次の通りです。 直接アクセスするとマイニングが始まる可能性があるので注意してください

  • www.sysalmon[.]com
  • www.sbzlbq[.]com
  • xztsjf[.]com

この攻撃者を識別できるサイトキーを特定してフラグとして提出してください。

Answer

ESETのWebサイトではクリプトジャッキングについて以下の通り述べられています。

その典型例が「コインハイブ」(CoinHive)というツールである。
コインハイブは、仮想通貨「モネロ」(Monero)の採掘サービスであり、ブラウザーを通じて作動する。
JavaScriptコードを挿入することで、訪問者のコンピューターリソースをそのWebサイトのために利用している。

またCoinHiveの公式サイトによると設置するJavaScriptコードにはSite-Keyが必要であるとされています。 CoinHiveはSite-Keyを参照して、採掘されたMoneroを誰に支払うべきなのか特定します。

以上の事を踏まえると、クリプトジャッキングの対象にされているサイトにはSite-Keyを含むJavaScriptコードが埋め込まれているはずです。 urlscan.ioから対象のサイトをスキャンしてHTMLコードを精査します。

フラグはFLAG{I8rYivhV3ph1iNrKfUjvdqNGfc7iXOEw}です。

MISC

Jail evasion

Question

フラグはwww.cctf2018.cf:10001に隠されていますがアクセス制限が掛けられています。 手がかりとして当該サイトにアクセスしたユーザーのパケットを入手しています。 HTTP Authorizationヘッダに以下の値が書かれているようです。

Basic YmFzaWNfYXV0aF91c2VyOnBhY2tldGNhcHR1cmVfaXNfcG93ZXJmdWxs

Answer

Authorizationヘッダの仕組みについて調べると、ユーザー名とパスワードをコロンで繋いでBase64でエンコードしただけである事が分かります。 AuthorizationリクエストヘッダをBase64デコードして得られるユーザー情報は以下の通りです。

  • ユーザー名:basic_auth_user
  • パスワード:packetcapture_is_powerfull

これを使ってアクセス制限されたディレクトリにアクセスしフラグを入手します。

フラグはFLAG{BASIC_AUTH_IS_WEAKNESS}です。

Keep the ID

Question

エビフライやな (@ebifryyana) がフラグを持っています。 Twitterに姿を隠した彼女を見つけてください。

Answer

Twitterで @ebifryyana を検索しても有益な情報は得られません。

@から始まるユーザー名はスクリーンネームと呼ばれユーザーが任意に変更することが出来ます。 問題文にある通りターゲットは既に別のスクリーンネームを名乗っているのだと推察できます。

Twitterのスクリーンネーム変更履歴を追跡するサービスとして idtwi があります。 これを利用して @ebifryyana のスクリーンネームの変遷を辿ると、最新のスクリーンネームが @Y0U_R34CH3D_M3 であることを突き止められます。

フラグは @Y0U_R34CH3D_M3 のBIOに記載されているFLAG{TWITTER_USER-ID_IS_PERMANENT}です。

Leak

Question

www.cctf2018.cf:10003は代理ブラウザです。 テキストボックスにURLを入力してlookupをクリックすると該当のWebサイトを表示します。

このWebアプリケーションを動かすには/secret/flagファイルが必要です。 フラグはそのファイルに書かれています。

Answer

まずは代理ブラウザの動作を確認します。 説明にある通りテキストボックスにURLを入力してlookupをクリックするとgoogleのWebサイトが取得できました。

問題に添付されていたソースコードを確認すると、POSTメソッドで受け取ったurlパラメタを文字列”curl -L “に連結してshell_exec関数で実行しているようです。 urlパラメタに任意の値を指定して/secret/flagファイルを読み込ませるのが問題の趣旨だと判断できます。 ただし、コマンドがescapeshellcmd関数を通して実行されており、安易なコマンドインジェクションは成立しないと推察されます。

※徳丸先生のブログにescapeshellcmdの危険性に関する記事がありますが今回の問題で利用することは難しいはずです。

次にcurlコマンド自体の挙動について確認するとWikipediaに以下の記述があります。

cURLはURLシンタックスを用いてファイルを送信または受信するコマンドラインツールである。 cURLはlibcurlを使うため、幅広いインターネットプロトコルをサポートする。 libcurlとはフリーで使いやすいクライアントサイドURL転送ライブラリであり、2013年10月現在、DICT、FILE、FTP、FTPS、GOPHER、HTTP、HTTPS、LDAP、LDAPS、SCP、SFTP、TELNET、TFTPのスキームをサポートしている。

この説明の中でFILEスキームに言及されている事に着目します。 WikipediaによるとFile URL スキームを使ってローカルホスト上の/etc/fstabを参照する場合は次のように指定すれば良いとされています。

file:///etc/fstab

この手法を使って/secret/flagにアクセスしフラグを確認します。

フラグはFLAG{curl_supports_many_schemas}です。


Content