SENTO NO OBOEGAKI

技術よりのメモやポエムを書いていきます。

MENU

セキュリティ・キャンプ全国大会2015に参加することになりました

セキュリティ・キャンプ全国大会2015に参加させていただくこととなりました。

年齢制限の関係で今年が最初で最後の応募だったわけですがまさか参加できるとは!!

セキュリティ・キャンプは去年たまたまIPAのホームページで見かけて存在を知り、なんか面白そうだなー、なんか行ってみたいなーと思っていました。

当時は応募用紙を見ただけで「これは無理だ...」となり、夢のまた夢という気持ちでしたが、これがきっかけでセキュリティに興味を持って勉強し始めたような気がしています。

ということで、いろいろ経験不足、技術力不足で苦労しそうですが、このような機会を与えていただけることに感謝しつつ精一杯頑張りたいと思います。

 

応募用紙

 一部公開します。難しくて本などいろいろ参考になんとか書いた問題もあるので間違っているところがあると思います。

共通問題1

セキュリティ・キャンプに応募した自分なりの理由とセキュリティ・キャンプで学んだことを何に役立てたいかを教えてください。

[理由]

いろんな技術を学べること、講師・卒業生・参加者などと繋がりが持てること、無料であることを書きました。

[何に役立てたいか]

セキュリティの専門家あるいはセキュリティが分かるITエンジニアとして社会のために貢献したい!!みたいなことを書きました。

共通問題2

セキュリティに関することで、過去に自分が経験したことや、ニュースなどで知ったことの中から、最も印象に残っていることを教えてください。また、その印象に残った理由も教えてください。

日本年金機構の個人情報流出事件について書きました。

共通問題3

その他に自己アピールしたいことがあれば自由に書いてください。(たとえば、あなたが希望する講座を受講する上で、どのような技術力を持っているか、部活動、技術ブログ、GitHub、ソフトウェア開発、プログラミングコンテスト、勉強会での発表・運営などの実績や熱意があれば、あるだけ書いてください。)

勉強会やCTF経験が無いので、この1年独学で学んだことや逆アセンブルしたバイナリを読むのが好きだということ、また、これから勉強したいことを書きました。 

選択問題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
---

 まず、最初から命令を辿っていくと、0x7deと0x7dfを比較して等しくならないため必ずjne 0x12でアドレス0x12にジャンプしてjmp 0xeba72という謎のコードに到達してしまう。そこでコードの後半を見てみると、int $0x80が二つあり、eaxのシステムコール番号からwriteシステムコールとexitシステムコールを呼ぶのが確認できる。しかし、writeシステムコールの引数は3つ必要でひとつ足りない。また、アドレス0x12、0x17辺りがなにやらおかしなことになっている。アドレス0x10でjne 0x13という命令がある。ということで怪しいアドレス0x13から次のように逆アセンブルしてみると、

%echo -en "\x5b\xba\x0e\x00\x00\x00\xb9\x00\x00\x00\x00\xbb\x01\x00\x00\x00\xb8\x04\x00\x00\x00\xcd\x80\xb8\x01\x00\x00\x00\xcd\x80" | ndisasm -u -
00000000  5B             pop ebx
00000001  BA0E000000       mov edx,0xe
00000006  B900000000       mov ecx,0x0
0000000B  BB01000000       mov ebx,0x1
00000010  B804000000       mov eax,0x4
00000015  CD80             int 0x80
00000017  B801000000       mov eax,0x1
0000001C  CD80             int 0x80

 と表示されwriteシステムコールを呼び出す部分の全貌が現れた。writeシステムコールは次のような引数を受け取り、ebxは一つ目の引数、ecxは二つ目の引数、edxは三つ目の引数に対応する。
write(int fd,const void* buf,size_t count);
ebx:0x1  ecx:0x0  edx:0xe
ecxに格納されるbufのアドレスはリンク時に書き換わるものだと思われる。これをまとめると、writeシステムコールによって標準出力に文字列を14バイト書き込み、その後、exitシステムコールによってプログラムを終了する。
 ここで、始めのアセンブリコードから私が分かったことを以下にまとめる。
・先頭から命令を実行してもうまく動かない。
・アドレス0x12の位置にe9というジャンプコードが挿入されることでobjdumpに誤った結果を出力させている。
・正しいと思われる処理(writeシステムコールを呼ぶ部分)を実行するためには、アドレス0x10のjne 0x13に制御を移す必要があるがどういう流れで制御が移るのかは分からない。
[結論]
このダンプの問題点はe9というジャンプコードを挿入されることで誤った逆アセンブル結果を表示してしまっていること。

選択問題5

以下のようなC言語の関数functionがあるとします。
void function(int *array, int n) {
  int i;
  for(i = 0; i < n; i++) {
    array[i] = i * n;
  }
}
上記プログラムをコンパイルした結果の一例 (i386)は以下となりました。
00000000 <function>:
   0:   56                   push   %esi
   1:   53                   push   %ebx
   2:   8b 5c 24 0c          mov    0xc(%esp),%ebx
   6:   8b 4c 24 10          mov    0x10(%esp),%ecx
   a:   85 c9                test   %ecx,%ecx
   c:   7e 18                jle    26 <function+0x26>
   e:   89 ce                mov    %ecx,%esi
  10:   ba 00 00 00 00       mov    $0x0,%edx
  15:   b8 00 00 00 00       mov    $0x0,%eax
  1a:   89 14 83             mov    %edx,(%ebx,%eax,4)
  1d:   83 c0 01             add    $0x1,%eax
  20:   01 f2                add    %esi,%edx
  22:   39 c8                cmp    %ecx,%eax
  24:   75 f4                jne    1a <function+0x1a>
  26:   5b                   pop    %ebx
  27:   5e                   pop    %esi
  28:   c3                   ret  
このとき以下の(1)~(5)の設問について、回答と好きなだけ深い考察を記述してください。知らない点は、調査したり自分で想像して書いても らっても結構です。どうしてもわからない部分は、具体的にここがわかりませんと記述しても良いです。(1)~(2)の回答は必ず答えてください。(3)~ (5)の回答は任意です。わかることを書いてください。CPU やコンパイラは特定の実装を例に説明しても良いですし、理想を自由に考えても良いです。
(1)【必須】上記の C 言語のプログラムはどのような動作をしますか。また、この関数を呼び出して利用する main 関数の例を作成してください。
(2)【必須】上記のアセンブリコードを、いくつかのブロックに分割して、おおまかに何をしている部分かを説明してください。もし、上記のアセンブリが気に入らないのであれば、好きなアーキテクチャやコンパイラのアセンブル結果を載せて説明しても良いです。
(3)【任意】 コンパイラがソースコードの関数を解釈して、ターゲットのアーキテクチャのバイナリを生成するまで、どのように内部で処理を行っていると思いますか。(キーワード: 構文解析、変数、引数、呼出規約、レジスタ、スタック、アセンブラ、命令セット)
(4)【任意】CPU の内部では、プログラムのバイナリはどのように解釈され実行されていると思いますか。(キーワード: フェッチ、デコード、オペコード、オペランド、命令パイプライン、回路)
(5)【任意】現在の CPU やコンパイラの不満点があれば自由に記述してください。

(1)

void main(void){
    int i,array[20],n=20;

    function(array,n);
    for(i=0;i<n;i++)
        printf("array[%d]:%d\n",i,array[i]);
}

(2)

00000000 <function>:
① 0: 56 push %esi 1: 53 push %ebx
② 2: 8b 5c 24 0c mov 0xc(%esp),%ebx 6: 8b 4c 24 10 mov 0x10(%esp),%ecx
③ a: 85 c9 test %ecx,%ecx c: 7e 18 jle 26 <function+0x26> e: 89 ce mov %ecx,%esi 10: ba 00 00 00 00 mov $0x0,%edx 15: b8 00 00 00 00 mov $0x0,%eax
④ 1a: 89 14 83 mov %edx,(%ebx,%eax,4) 1d: 83 c0 01 add $0x1,%eax 20: 01 f2 add %esi,%edx 22: 39 c8 cmp %ecx,%eax 24: 75 f4 jne 1a <function+0x1a>
⑤ 26: 5b pop %ebx 27: 5e pop %esi 28: c3 ret

[説明]
①レジスタをスタックに退避
②引数として受け取ったarrayの先頭アドレスとnをそれぞれebx,ecxレジスタに格納
③n(ecx)が0以下の場合、for文を実行しないので⑤にジャンプ、esiにecx(n)の値を格納、edx,eaxレジスタに0を格納
④for文に対応した繰り返し部分の処理(edxはarray[]に格納する計算結果に対応し、eaxは変数iに対応している)
⑤退避していたレジスタを復元してリターン

このアセンブリコードでは、ローカル変数iの領域をスタックに確保するのではなくeaxレジスタを変数iに対応させて直接使用しているため、コンパイラにより最適化されていると考えて、gccで最適化させずにアセンブリコードを(好みのintel形式で)生成してみた。その結果は以下のようなローカル変数iをスタックに確保する見慣れたコードが生成された。

00000000 <function>:

   0:    55                       push   ebp
   1:    89 e5                    mov    ebp,esp
   3:    83 ec 10                 sub    esp,0x10


   6:    c7 45 fc 00 00 00 00     mov    DWORD PTR [ebp-0x4],0x0
   d:    eb 1c                    jmp    2b <function+0x2b>
   f:    8b 45 fc                 mov    eax,DWORD PTR [ebp-0x4]
  12:    8d 14 85 00 00 00 00     lea    edx,[eax*4+0x0]
  19:    8b 45 08                 mov    eax,DWORD PTR [ebp+0x8]
  1c:    01 c2                    add    edx,eax
  1e:    8b 45 fc                 mov    eax,DWORD PTR [ebp-0x4]
  21:    0f af 45 0c              imul   eax,DWORD PTR [ebp+0xc]
  25:    89 02                    mov    DWORD PTR [edx],eax
  27:    83 45 fc 01              add    DWORD PTR [ebp-0x4],0x1
  2b:    8b 45 fc                 mov    eax,DWORD PTR [ebp-0x4]
  2e:    3b 45 0c                 cmp    eax,DWORD PTR [ebp+0xc]
  31:    7c dc                    jl     f <function+0xf>


  33:    c9                       leave  
  34:    c3                       ret  

 [説明]
①,③最初のアセンブリコードと違ってスタックを調整するための典型のコードが追加されている
②[ebp-4]に変数iに対応する値を入れている
因みにgccの最適化オプション-01でコンパイルすると最初のアセンブリコードと全く同じアセンブリコードが生成されるのを確認できた。

(3)省略

(4)省略

(5)未記入

選択問題8

gccが持つ-fno-stack-protectorは、どのようなセキュリティ機能を無効にするオプションであるのか、またこの機能により、どういった脆弱性からソフトウェアを守れるのかをそれぞれ記述してください。

-fno-stack-protectorはstack-smashing protector(SSP)というセキュリティ機能を無効にするオプションである(有効にするオプションは-fstack-protector)。SSPはスタックバッファオーバーフローを検出することができ、スタックバッファオーバーフローの脆弱性からソフトウェアを守ることができる。実際にどのようにスタックバッファオーバーフローを検出できるかを調べるために、以下のようなCプログラムを作成して、SSPを無効にしてコンパイルしたものとSSPを有効にしてコンパイルしたものを以下に示す。SSPを有効にすると最初と最後に新たなコード(①,②)が追加されているのを確認できる。

(コード一部以外省略)


 80484af:    65 a1 14 00 00 00        mov    eax,gs:0x14
 80484b5:    89 84 24 2c 01 00 00     mov    DWORD PTR [esp+0x12c],eax

 80484eb:    8b 84 24 2c 01 00 00     mov    eax,DWORD PTR [esp+0x12c]
 80484f2:    65 33 05 14 00 00 00     xor    eax,DWORD PTR gs:0x14
 80484f9:    74 05                    je     8048500 <main+0x64>
 80484fb:    e8 70 fe ff ff           call   8048370 <__stack_chk_fail@plt>

①のコードでは、[esp+0x12c]というスタック領域にgs:0x14の値を格納しており、②のコードでは、この領域に①で格納した値と同じ値が入っているかを調べて、値が変わっていたら<__stack_chk_fail@plt>をコールしている。これによってバッファオーバーフローを検知している。実際に、バッファオーバーフローさせてreturn addressの書き換えを試みた結果、
*** stack smashing detected ***
と表示されて、検知されているのが確認できた。

選択問題10

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

主に『リバースエンジニアリングバイブル ~コード再創造の美学~』を参考にしていろいろ書きました。

選択問題13

これまでに起こったこと(データ)から、これまでにまだ起こっていないことを事前に予想する場合、どのような点に注意し、どのようなことを考慮すべきか熱烈にアピールしてください。

 予測の精度を上げるためには、これまでに起こったデータからの特徴量の選択に慎重になる必要があるという点に注意すべきである。例えば、誰かの年収を予測する場合、年齢・所有資格・住所などを特徴量とした場合、年収に直接関係がありそうだが、身長・体重・好きな食べ物などは年収に直接関係がなさそうであり、どの特徴量に注目するかが予測の精度に大きく影響することが容易に想像できる。別な例としてマルウェアの検知を考えてみると、インポート関数やアクセスするレジストリ、通信に使用されるIPアドレスなどを特徴量とする方法が考えられる。マルウェアに頻繁に使われる関数を特徴量とすることでそれらの関数が多くインポートされている場合マルウェアであると判断するなどである。このように、精度を上げるためにはどの特徴量に注目するかを(現在の技術ではコンピュータで判断するのは難しいため)人間が正しく選択することが重要である。
 どのような方法で予測する場合でも精度を100%にすることはほぼ不可能だということも考慮するべきである。間違いには偽陰性(False negative)と偽陽性(False positive)があり、偽陰性は正しくないデータを正しいデータと判断してしまう間違いで、偽陽性は正しいデータを正しくないデータと判断してしまう間違いである。偽陰性と偽陽性はトレードオフの関係にあるため用途に合わせた対策を考える必要がある。例えばスパムメールの判定を考えると、正常なメールをスパムメールと判定されてはいけないがスパムメールを正常なメールと判定されるのはまだ許容できる。つまり、スパムメールを判定するシステムの場合は偽陽性にならないように考慮した設計が必要となる。別な例としてマルウェアの検知も考えてみると、正常なソフトウェアをマルウェアと判定されるのは許容できるがマルウェアを正常なソフトウェアと判定されてはいけない。つまり、マルウェアの検知システムの場合は偽陰性にならないように考慮した設計が必要となる。このように、予測結果を間違ってしまう時に備えてシステムの利用形態に応じた設計が重要である。