読者です 読者をやめる 読者になる 読者になる

Log.i53

Themidaのアンパックを目指すブログ改め使い物になるえんじにゃを目指すブログ

セキュキャン2015応募用紙の選択問題の一部を解いたつもりになってみるテスト

雑記

www.ipa.go.jp

 選択問題は5問選択できるので自分が選択するとしたら②⑤⑩⑪⑭で行きます。とりあえず②と⑪を素で解こうと頑張ってみました・・・・・が・・・・・駄目っ・・・・・!結局のところ、いつもどおり頑張って調べまくる作業になってしまいました・・・!

 セプキャン2009では高専時代の担任の勧めで応募して、とにかく文章で熱意をしたためたところ選考を抜けることができましたが、当時の自分が今の応募用紙に取り組んだとしたら…かなり微妙と言わざるを得ません…!

※過去のセプキャン参加者なのでセキュキャン2015の応募者ではありません。

<魔法の言葉>以下、内容の保証はできません</魔法の言葉>

選択問題2

問題

 ある機械語をobjdumpにより逆アセンブルしたところ、以下の結果が得られました。このダンプに見られる問題点を指摘してください。

00000000 <.text>:
   0:   b8 de 07 00 00       mov   $0x7de,%eax
   5:   3d df 07 00 00       cmp   $0x7df,%eax
   a:   75 06                jne   0x12
   c:   89 c0                mov   %eax,%eax
   e:   ff e3                jmp   *%ebx
  10:   75 01                jne   0x13
  12:   e9 5b ba 0e 00       jmp   0xeba72
  17:   00 00                add   %al,(%eax)
  19:   b9 00 00 00 00       mov   $0x0,%ecx
  1e:   bb 01 00 00 00       mov   $0x1,%ebx
  23:   b8 04 00 00 00       mov   $0x4,%eax
  28:   cd 80                int   $0x80
  2a:   b8 01 00 00 00       mov   $0x1,%eax
  2f:   cd 80                int   $0x80

解法 その①

 個人的にこの記法だと普段読んでいるものとsrcとdestが逆でどうしても読みづらいのでdistorm3を使っていつもの記法で出力させます。

from distorm3 import Decode, Decode32Bits

l = Decode(0, "b8de0700003ddf070000750689c0ffe37501e95bba0e000000b900000000bb01000000b804000000cd80b801000000cd80".decode('hex'), Decode32Bits)
for i in l:
    print "0x%08x (%02x) %-20s %s" % (i[0],  i[1],  i[3],  i[2])

 出力結果は以下のとおりです。

0x00000000 (05) b8de070000           MOV EAX, 0x7de
0x00000005 (05) 3ddf070000           CMP EAX, 0x7df
0x0000000a (02) 7506                 JNZ 0x12
0x0000000c (02) 89c0                 MOV EAX, EAX
0x0000000e (02) ffe3                 JMP EBX
0x00000010 (02) 7501                 JNZ 0x13
0x00000012 (05) e95bba0e00           JMP 0xeba72
0x00000017 (02) 0000                 ADD [EAX], AL
0x00000019 (05) b900000000           MOV ECX, 0x0
0x0000001e (05) bb01000000           MOV EBX, 0x1
0x00000023 (05) b804000000           MOV EAX, 0x4
0x00000028 (02) cd80                 INT 0x80
0x0000002a (05) b801000000           MOV EAX, 0x1
0x0000002f (02) cd80                 INT 0x80

 では実際に出力されたアセンブリを読み解いていきます。最初の2つの命令でEAXレジスタに0x7DEが格納された後0x7DFと比較しているためその次にあるJNZ命令は条件ジャンプでありながらも確実にジャンプすることになります。その後に続くMOV EAX,EAX命令およびJMP EBX命令は実行されることがなく、これは分析者を騙すためのアンチ逆アセンブルトリックの1つなのではないかと考えられます。

 逆アセンブラが逆アセンブルする際には一般的に線形およびフロー指向の2種類の方法があり、線形式のアセンブルは実装が容易である代わりにこのようなアンチ逆アセンブルトリックに騙されやすいといった弱点を持っています。

 さて、必ずジャンプするJNZ命令の飛び先であるアドレス0x12にジャンプしてみるとWTF!!!!!!! アドレス0xEBA72にジャンプする命令に直撃してコレ以上分析することができません…。どうしたものかと考えた時に、途中にあるJNZ 0x13命令に着目します。このダンプがある処理の途中の部分をダンプしたものであるときに0x10に遷移する何かしらの命令があったのではないかと考えます。そこでアドレス0x13から線形逆アセンブルを再実行させると以下のようになりました。

0x00000000 (05) b8de070000           MOV EAX, 0x7de
0x00000005 (05) 3ddf070000           CMP EAX, 0x7df
0x0000000a (02) 7506                 JNZ 0x12
0x0000000c (02) 89c0                 MOV EAX, EAX
0x0000000e (02) ffe3                 JMP EBX
0x00000010 (02) 7501                 JNZ 0x13
0x00000012 (01) e9                   DB 0xE9    ; ゴミ!
0x00000013 (01) 5b                   POP EBX
0x00000014 (05) ba0e000000           MOV EDX, 0xe
0x00000019 (05) b900000000           MOV ECX, 0x0
0x0000001e (05) bb01000000           MOV EBX, 0x1
0x00000023 (05) b804000000           MOV EAX, 0x4 
0x00000028 (02) cd80                 INT 0x80
0x0000002a (05) b801000000           MOV EAX, 0x1  
0x0000002f (02) cd80                 INT 0x80

 アドレス0x12にある1バイトは命令ではなくアンチ逆アセンブルトリックのために挿入されたゴミコードであると考えられます。では0x13以降の命令の分析を進めましょう。

 INT 0x80はLinuxにおけるソフトウェア割り込みに利用されるものです。この時、EAXレジスタには対応するカーネル関数の値が格納されます。そして第1引数がEBX、第2引数がECX、第3引数がEDX…となるそうです。では、最後に元のダンプを整理してみましゅ…。

0x00000000 (05) b8de070000           MOV EAX, 0x7de
0x00000005 (05) 3ddf070000           CMP EAX, 0x7df
0x0000000a (02) 7506                 JNZ 0x12
0x0000000c (02) 89c0                 MOV EAX, EAX
0x0000000e (02) ffe3                 JMP EBX
0x00000010 (02) 7501                 JNZ 0x13     ; ダンプ外のどこかからここに遷移してくる?
0x00000012 (01) e9                   DB 0xE9      ; ガベージコード
0x00000013 (01) 5b                   POP EBX
0x00000014 (05) ba0e000000           MOV EDX, 0xe ; バッファのサイズ->14バイト
0x00000019 (05) b900000000           MOV ECX, 0x0 ; バッファ->null
0x0000001e (05) bb01000000           MOV EBX, 0x1 ; ファイルディスクリプタ->標準出力
0x00000023 (05) b804000000           MOV EAX, 0x4 
0x00000028 (02) cd80                 INT 0x80     ; システムコールwriteを呼び出す
0x0000002a (05) b801000000           MOV EAX, 0x1  
0x0000002f (02) cd80                 INT 0x80     ; システムコールexitを呼び出す

 
 うーんこれで合ってるのか…?

解法 その②

IDA FreeのCode変換機能を使うためにとりあえずバイナリファイルを出力します:

import struct

buf = "b8 de 07 00 00 3d df 07 00 00 75 06 89 c0 ff e3 75 01 e9 5b ba 0e 00 00 00 b9 00 00 00 00 bb 01 00 00 00 b8 04 00 00 00 cd 80 b8 01 00 00 00 cd 80"

with open("blaaaaaaaaaa.bin", "wb") as fout:
    for i in buf.split(" "):
        fout.write(struct.pack("B", int(i, 16)))

出力された"blaaaaaaaaaa.bin"をIDA Freeで読み込みます。

seg000:00000000 ; Segment type: Pure code
seg000:00000000 seg000          segment byte public 'CODE' use32
seg000:00000000                 assume cs:seg000
seg000:00000000                 assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
seg000:00000000                 db 0B8h ; ク
seg000:00000001                 db 0DEh ; ゙
seg000:00000002                 db    7
seg000:00000003                 db    0
seg000:00000004                 db    0
seg000:00000005                 db  3Dh ; =
seg000:00000006                 db 0DFh ; ゚
seg000:00000007                 db    7
seg000:00000008                 db    0
seg000:00000009                 db    0
seg000:0000000A                 db  75h ; u
seg000:0000000B                 db    6
seg000:0000000C                 db  89h ; ・
seg000:0000000D                 db 0C0h ; タ
seg000:0000000E                 db 0FFh
seg000:0000000F                 db 0E3h ; ・
seg000:00000010                 db  75h ; u
seg000:00000011                 db    1
seg000:00000012                 db 0E9h ; ・
seg000:00000013                 db  5Bh ; [
seg000:00000014                 db 0BAh ; コ
seg000:00000015                 db  0Eh
seg000:00000016                 db    0
seg000:00000017                 db    0
seg000:00000018                 db    0
seg000:00000019                 db 0B9h ; ケ
seg000:0000001A                 db    0
seg000:0000001B                 db    0
seg000:0000001C                 db    0
seg000:0000001D                 db    0
seg000:0000001E                 db 0BBh ; サ
seg000:0000001F                 db    1
seg000:00000020                 db    0
seg000:00000021                 db    0
seg000:00000022                 db    0
seg000:00000023                 db 0B8h ; ク
seg000:00000024                 db    4
seg000:00000025                 db    0
seg000:00000026                 db    0
seg000:00000027                 db    0
seg000:00000028                 db 0CDh ; ヘ
seg000:00000029                 db  80h ; 
seg000:0000002A                 db 0B8h ; ク
seg000:0000002B                 db    1
seg000:0000002C                 db    0
seg000:0000002D                 db    0
seg000:0000002E                 db    0
seg000:0000002F                 db 0CDh ; ヘ
seg000:00000030                 db  80h ; 
seg000:00000030 seg000          ends

 任意のアドレスを選択して右クリックから"Code"を選択するか単に"C"キーを押すことでバイナリを逆アセンブルすることができ、"Undefine"を選択するか単に"U"キーを押すことで元のバイナリに戻すことができます:
f:id:i53:20150623060902p:plain

 "C"と"U(+Y)"をポチポチしながらクロスリファレンス機能も利用してアンチ逆アセンブルトリックを回避します:

seg000:00000000 ; Segment type: Pure code
seg000:00000000 seg000          segment byte public 'CODE' use32
seg000:00000000                 assume cs:seg000
seg000:00000000                 assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
seg000:00000000                 mov     eax, 7DEh
seg000:00000005                 cmp     eax, 7DFh
seg000:0000000A                 jnz     short near ptr unk_12
seg000:0000000C                 mov     eax, eax
seg000:0000000E                 jmp     ebx
seg000:00000010 ; トトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトト
seg000:00000010                 jnz     short loc_13
seg000:00000010 ; トトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトト
seg000:00000012 unk_12          db 0E9h ; ・            ; CODE XREF: seg000:0000000Aj
seg000:00000013 ; トトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトト
seg000:00000013
seg000:00000013 loc_13:                                 ; CODE XREF: seg000:00000010j
seg000:00000013                 pop     ebx
seg000:00000014                 mov     edx, 0Eh
seg000:00000019                 mov     ecx, 0
seg000:0000001E                 mov     ebx, 1
seg000:00000023                 mov     eax, 4
seg000:00000028                 int     80h             ; LINUX - sys_write
seg000:0000002A                 mov     eax, 1
seg000:0000002F                 int     80h             ; LINUX - sys_exit
seg000:0000002F seg000          ends
seg000:0000002F
seg000:0000002F
seg000:0000002F                 end

 やったぜ!

解答

 おそらく、このダンプに見られる問題点は…

  1. アンチ逆アセンブル技術によってシステムコールの一部引数が見えなくなっている
  2. ここに至るまでの他のダンプはないのんか???線形的にコードを読んだだけじゃ実行フローが分からんぞい

ということなのだと思いますがいかがでしょうか…! 間違ってたらごめんなさいごめんなさい>

選択問題10

問題

 アンチデバッグ、難読化といった単語をキーワードとする技術について、あなたが知っていること、調べたことを具体的に記述してください。基本的にPCのソフトウェアにおける技術を想定していますが、他端末、またはハードウェアに関する内容でもかまいません。

解答

i53.hatenablog.jp
 先日趣味の一環で翻訳したThe Art of Unpackingから何点かピックアップします。+αでGoogle Scholarなどでアンチデバッグ・難読化研究をピックアップしてくると選考に効いてくるのかもしれません…w 和論であれば少し古いですが、IWSEC (International Workshop on Security)の論文をいくつかピックアップすることができます。
f:id:i53:20150623023636p:plain

 個人的にあまり詳しくないので取り上げませんでしたがjavascriptの難読化などについて取り上げてみるのもよいやもしれません。某講師的な意味で。

選択問題11

問題

 下記バイナリを解析し、判明した情報を自由に記述してください

D4 C3 B2 A1 02 00 04 00 00 00 00 00 00 00 00 00
00 00 04 00 01 00 00 00 88 EB 40 54 A2 BE 09 00
52 00 00 00 52 00 00 00 22 22 22 22 22 22 11 11
11 11 11 11 08 00 45 00 00 44 1A BD 40 00 80 06
3A 24 C0 A8 92 01 C0 A8 92 80 10 26 01 BB 86 14
7E 80 08 B3 C8 21 50 18 00 FC 0D 0E 00 00 18 03
03 00 17 01 0E FB 06 F6 CD A3 69 DC CA 0B 99 FF
1D 26 09 E1 52 8F 71 77 45 FA

解答

 この手のバイナリが与えられたら大概上位数バイトがマジックナンバーになっているはず…! "D4 C3 B2 A1"で検索をかければこいつがPCAPのファイルヘッダーであることが分かります。バイナリ解析に自信ニキなら調べなくても分かるのでしょうが私は普通にググりました…!

 PCAPファイルの構造は以下のようになっています:

 Global Header | Header1 | Data1 | Header2 | Data2 | ... | HeaderN | DataN

 今回のファイルではキャプチャデータの構成は以下のとおりとなります。

グローバルヘッダ:
[D4 C3 B2 A1 02 00 04 00 00 00 00 00 00 00 00 00 00 00 04 00 01 00 00 00]
 
パケットヘッダ1
[88 EB 40 54 A2 BE 09 00 52 00 00 00 52 00 00 00]
 
パケットデータ1:
[22 22 22 22 22 22 11 11 11 11 11 11 08 00 45 00 00 44 1A BD 40 00 80 06 3A 24 C0 A8 92 01 C0 A8 92 80 10 26 01 BB 86 14 7E 80 08 B3 C8 21 50 18 00 FC 0D 0E 00 00 18 03 03 00 17 01 0E FB 06 F6 CD A3 69 DC CA 0B 99 FF 1D 26 09 E1 52 8F 71 77 45 FA]

 グローバルヘッダ(固定長24バイト)より判明した情報を示します:

  • 最初の4バイト"D4 C3 B2 A1"はPCAPファイルを識別するために使用されるマジックナンバー(フォーマット識別子)です
  • 次の4バイト"02 00 04 00"はメジャーバージョン(2バイト)とマイナーバージョン(2バイト)であり、この場合バージョンは2.4となります
  • 次の8バイト"00 00 00 00 00 00 00 00"はGMTタイムゾーンからの差分で表されるタイムゾーン(4バイト)とパケットキャプチャにおけるタイムスタンプの精度(4バイト)です
  • 次の4バイト"00 00 04 00"はキャプチャされるパケットの最大長を示しており、この場合0x40000バイトです
  • 次の4バイト"01 00 00 00"は"Link-Layer Header Type"を表しており、この場合データリンク層プロトコルEthernetであることを示しています

 パケットヘッダ(固定長16バイト)から判明した情報を示します:

  • 最初の4バイト"88 EB 40 54"はパケットキャプチャ時のタイムスタンプであり、"Unix Epoch"として知られている1970年1月1日0時0分0秒からの経過秒数で示されています。この場合"2014年10月17日19時12分24秒"となります
  • 次の4バイト"A2 BE 09 00"はパケットキャプチャ時のタイムスタンプのマイクロ秒となります
  • 次の4バイト"52 00 00 00"は実際のパケットデータのサイズで、この場合10進で82バイトとなります
  • 次の4バイト"52 00 00 00"は実際に補足されたパケットデータのサイズで、この場合10進で82バイトとなります

# 最後の2つのフィールドは同じになることが多いですが、最大パケット長を実際のデータが上回る場合に異なってくる場合があるみたいです。

 では、パケットデータ(82バイト)から判明した情報を示します:

  • 最初の6バイト"22 22 22 22 22 22"が宛先MACアドレスでこの場合"22:22:22:22:22:22"となります
  • 次の6バイト"11 11 11 11 11 11"が発信元MACアドレスでこの場合"11:11:11:11:11:11"となります

 とここまで調べていて途中で気づいたのですがPCAPファイル(破損しているわけではない)なんだから元のファイルに戻してWireSharkで開けばええやんと…!

import struct

packet = "D4 C3 B2 A1 02 00 04 00 00 00 00 00 00 00 00 00 00 00 04 00 01 00 00 00 88 EB 40 54 A2 BE 09 00 52 00 00 00 52 00 00 00 22 22 22 22 22 22 11 11 11 11 11 11 08 00 45 00 00 44 1A BD 40 00 80 06 3A 24 C0 A8 92 01 C0 A8 92 80 10 26 01 BB 86 14 7E 80 08 B3 C8 21 50 18 00 FC 0D 0E 00 00 18 03 03 00 17 01 0E FB 06 F6 CD A3 69 DC CA 0B 99 FF 1D 26 09 E1 52 8F 71 77 45 FA"

with open("blaaaaaaaaaa.pcap", "wb") as fout:
    for i in packet.split(" "):
        fout.write(struct.pack("B", int(i, 16)))

保存された"blaaaaaaaaaa.pcap"を開くといろいろ見えるぞい!
f:id:i53:20150623050205p:plain

後は以下を読んだりしていろいろ頑張る(※NWセキュリティに詳しくないのでここで力尽きた…)
TLS Protocol Version 1.2

選択問題14

問題

 以下はWebViewを使用したAndroidアプリのコードです。このコードには、不正なアプリやWebView上で開かれた悪意のあるページによる攻撃を受け、情報流出などの被害をもたらす恐れのある箇所が存在します。それはどこか、またどのような攻撃を受けた場合にどのような被害が生じるか、可能な限り述べてください。なお、コメント欄に付記したCVEの番号は、類似のコードに起因する過去の脆弱性事例です。これらの脆弱性の原因を調べることが回答の手がかりとなります。

public class MainActivity extends Activity {
 private static final String ACTION_LOAD = "jp.example.app.action.LOAD";
 private static final String ALLOW_ORIGIN = "http://www.ipa.go.jp";
 private WebView webview;
 private BroadcastReceiver receiver;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   webview = (WebView) findViewById(R.id.webView);
   webview.getSettings().setJavaScriptEnabled(true);
   webview.getSettings().setAllowUniversalAccessFromFileURLs(true); // CVE-2012-4009
   webview.addJavascriptInterface(this, "android"); // CVE-2012-6636
   webview.setWebViewClient(new WebViewClient() {
     @Override
     public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
       handler.proceed(); // CVE-2014-5991
     }
     @Override
     public boolean shouldOverrideUrlLoading(WebView view, String url) { // CVE-2014-1883
       Pattern p = Pattern.compile(ALLOW_ORIGIN); // CVE-2012-6637
       Matcher m = p.matcher(url);
       return !(m.find());
     }
   });
   webview.loadUrl(ALLOW_ORIGIN);
   IntentFilter filter = new IntentFilter(ACTION_LOAD);
   receiver = new BroadcastReceiver() {
     @Override
     public void onReceive(Context context, Intent intent) {
       webview.loadUrl(intent.getStringExtra("url")); // CVE-2014-3500
     }
   };
   registerReceiver(receiver, filter);
 }

 @Override
 protected void onDestroy() {
   unregisterReceiver(receiver);
 }
}

解答

 以下を参考に書ける分だけ書いてみます。i53.hatenablog.jp

 下記コードによりこのActivityのWebViewオブジェクトではJavaScriptの実行およびファイルURIからのユニバーサルアクセス(全てのオリジンのコンテンツへアクセス)が有効となってしまいます:

webview.getSettings().setJavaScriptEnabled(true);
webview.getSettings().setAllowUniversalAccessFromFileURLs(true); // CVE-2012-4009

 下記コードでACTION_LOADアクションを持つIntentを受け付けていますが、この時Intentの"url"追加パラメータに対して攻撃者が悪意あるWebサイトのURLを指定することでユーザの悪意あるWebサイトを参照させることができるようになってしまいます:

@Override
public void onReceive(Context context, Intent intent) {
   webview.loadUrl(intent.getStringExtra("url")); // CVE-2014-3500
}

 以下の手順でこのアプリケーションが持つユーザの機密情報を読み取ることが可能となります:

  1. ACTION_LOADアクションと悪意あるウェブサイトのURLを"url"追加パラメータに設定したインテントを発行してユーザに悪意あるウェブサイトを参照させる
  2. 悪意あるウェブサイトは被害者のSDカードに自動的にHTMLファイルをダウンロードする
  3. 悪意あるウェブサイトはWebViewオブジェクト内でダウンロードされたファイルをロードさせる.この時ファイルURIスキーム(例:"file:///sdcard/Downloads/exploit.html")を使用して先ほどダウンロードされた攻撃用のHTMLファイルを参照する
  4. ファイルURIによってロードされたJavaScriptコードはユニバーサルアクセスが許可されており,同アプリケーション下の任意のファイルに対してリードアクセス権限を持つことになる.これにより機密情報が読み取られる.

 これ以外にも読み取った情報を攻撃者に外部送信する手段を書いたり、addJavascriptInterfaceの問題はいろいろなところで取り上げられているので書きやすいかもしれません。また、攻撃用のコード(Intent発行や悪意あるコードを実行させる部分など)や攻撃の対策手法についても記載すると良いやもしれません。

参考文献

 参考・引用した資料や文献がある場合には書いた方がベター(個人的にはマスト)です。
# ただしYahoo知恵袋などに相談するのだけはお勧めしません:(
# 最悪の場合、悪意のある解答者に質問者が特定できるような文言が埋め込まれた解を投稿され講師陣にモロバレとなる危険性が有ります…w

参考書籍

Practical Malware Analysis: The Hands-On Guide to Dissecting Malicious Software

Practical Malware Analysis: The Hands-On Guide to Dissecting Malicious Software

Part 5. Anti-Reverse Engineering よりアンチ逆アセンブル技術の一部の知識を導入しています。

終わりに

 講師紹介:IPA 独立行政法人 情報処理推進機構にある「講師主査よりメッセージ」をまだ読んでない人は是非とも読んでみてください。選考に受かっても受からなくてもここにある言葉が全てだと思います。 問題の完答が求められているのではなく、その解答に至るまでの過程から努力・熱意といったものを講師陣は見ているのではないかなと思っています。たくさんの時間と熱意を注がれた皆さんに、よい知らせが届くことを心より祈っております:)