セキュリティ・キャンプ2015応募用紙晒す
受かると思ってなかったけど受かった、そんなジャンクさんです。
セキュリティ・キャンプ参加者向けに、ちょっと公開してみます。ほんと初心者だから、突っ込みどころは多いはず!
共通問題も晒しますよー。あと見づらいけどすみません。
ほぼ丸々持ってきてるので、問題あれば消します。
共通問題1
セキュリティ・キャンプに応募した自分なりの理由とセキュリティ・キャンプで学んだことを何に役立てたいかを教えてください。
【以下に回答してください(行は適宜追加してください)】
・セキュリティ・キャンプに応募した理由
私がセキュリティ分野に興味を持ったのはつい数か月前のことです。きっかけは平成27年春に情報処理技術者試験の情報セキュリティスペシャリスト試験を受験したことでした。
私は最初、ネットワーク関係に興味があって、平成26年秋にネットワークスペシャリスト試験を受験しました。しかし残念ながら不合格となり、春はネットワークと関係の深いセキュリティの勉強をして秋の試験に備えようと考えました。暗号技術の本を読んだり、次々と新しく現れる攻撃手法などを調べたりするうちに、とても勉強のしがいがあり面白い分野だとわかりました。
私は父が警察官で、その影響か昔から悪いことは絶対に許せない性格です。攻撃されることで情報漏洩が起きたときの被害の深刻さを知り、いつか私が技術者の立場から人々を守りたいと思うようになりました。そのため私は将来セキュリティ関係の仕事に就きたいと考えています。
まだセキュリティを勉強し始めて数か月の初心者ですが、いつから勉強を始めても遅くないと思いますし、勉強の機会があるのならなるべく逃したくないと思っています。セキュリティ・キャンプでは、受講生の方々は私よりもきっとセキュリティの知識が深いでしょう。しかし、講師の方々や受講生の方々に教えていただいたり、いろいろな話をしたりして知識の共有を図るなど、刺激を受け自分をさらに高められるとても良い機会になるはずだと確信しています。ぜひ今回参加したい、そう思い応募させていただきました。
・セキュリティ・キャンプで学んだことを何に役立てたいか
社会はまだまだセキュリティの重要さについての認識が甘いと感じています。セキュリティ事故は、その認識の甘さから起こることも多くあります。例えば最近起こった日本年金機構の情報漏洩事件では、安易に職員がメールに添付された実行ファイルをクリックしてしまうなど、業務で情報を扱う人間のセキュリティへの関心が低いために情報漏洩が起こってしまいました。セキュリティの専門家がセキュリティポリシを作っても、それを守るのはセキュリティへの関心が低い、一般の業務のために情報を扱う人々です。そのため、これからはセキュリティへの関心が低い人向けに情報の大切さや情報の守り方をもっと教えていくべきだと思います。
私はセキュリティ・キャンプでいろいろなことを学び、それをきっかけとして勉強の範囲を広げたり、深くしたりして知識を多くつけ、人に教えられるようになりたいです。誰にでもわかるようにセキュリティの知識を教え、少しでもセキュリティに関することの認知度を上げたいです。そうすることで、情報を扱う人々がセキュリティへの関心が低いことを攻撃者に付け込まれ、情報漏洩させられてしまうなどといったことを少しでも防ぎたいと考えています。
【回答ここまで】
共通問題2
セキュリティに関することで、過去に自分が経験したことや、ニュースなどで知ったことの中から、最も印象に残っていることを教えてください。また、その印象に残った理由も教えてください。
【以下に回答してください(行は適宜追加してください)】
私が最も印象に残っているのは、2014年に発覚したベネッセの個人情報流出事件です。なぜなら、2070万人もの子供の情報が、サイバー攻撃ではなく従業員が故意に流出させたという事実が、とても衝撃的だったからです。私はこの事件を知り、組織内部に対するセキュリティについて関心を持ち始めました。
組織内部の悪意を持った人間への対策は、外部からのサイバー攻撃に比べて対策が限られていて難しいと思います。入退室管理や、防犯カメラの設置、ログを取得するなど、組織内部の悪意を持った人間に対する牽制を行っても、どこかに穴があります。例えば、ログ取得に関するアラートを設定していても、しきい値に触れないように操作することで、検知されずに悪意のある操作ができます。
私は、情報セキュリティスペシャリスト試験の勉強をしているときに、組織内部のセキュリティの問題が出題されることがあり、その問題を解くことである程度の知識を得ました。これからもさまざまな事例を調べ、組織内部のセキュリティについて知っていきたいと思っています。そして、組織に入ったら組織内部に対するセキュリティの穴をなるべく小さくできるようになりたいと考えています。
【回答ここまで】
共通問題3
その他に自己アピールしたいことがあれば自由に書いてください。(たとえば、あなたが希望する講座を受講する上で、どのような技術力を持っているか、部活動、技術ブログ、GitHub、ソフトウェア開発、プログラミングコンテスト、勉強会での発表・運営などの実績や熱意があれば、あるだけ書いてください。)
【以下に回答してください(行は適宜追加してください)】
私は約2か月前からRaspberry Piでハニーポットを構築し運用しています。使っているハニーポットはDionaeaというもので、HTTPやFTP、SIPなど攻撃されやすいサービスを集めたものです。私はこれで検知した攻撃を調べ、ブログで解説しています。まだ始めたばかりでブログの記事は少ないですが、これからも続けて、私が調べた脆弱性に関する知識を多くの人に共有したいと思っています。さらに、もっと知識をつけたら自分でオリジナルのハニーポットを構築してみたいと考えています。
また、私が一番得意なプログラミング言語はC言語です。C言語でどんなプログラムを作ったかというと、例えばMP3のヘッダを解析して内容を抽出するプログラムを作成しました。このプログラムは専門学校1年生の時に授業で自由にプログラミングをするという課題が出たときに、数人で集まってMP3プレーヤを作ることになり、モジュールの一つとして作成しました。MP3プレーヤはその年の学外での成果発表会の発表成果物に選出され、発表することができました。また、私はベンダ試験のC言語プログラミング能力検定試験の1級に合格しています。
次に、私がセキュリティ・キャンプで希望する講義は解析トラックです。私はハニーポットでいろいろな攻撃手法を知ろうと思っています。しかし、HTTPやSIPなどの攻撃手法がわかってもマルウェアの感染方法などは、まだハニーポットでマルウェアを入手できていないこともありわかっていません。マルウェアの解析ができると、今よりもっと攻撃手法を知れて、対策が提案できるようになると思います。そして、それをさまざまな人に教えて注意喚起をしたいと思っています。
私は情報セキュリティスペシャリスト試験に合格していることもあり、初歩的な知識は持っています。また、ネットワークスペシャリスト試験を受験したこともあるので基本的なネットワークの知識もあります。そして、知識を少しでも多くするため、普段からネットで調べたり、図書館で本を借りたりし、実践をしています。
最後に、私は共通問題に回答する前に選択問題を解いたのですが、はじめに見たときは知らないことばかりでとても難しく感じました。しかし、毎日調べていると少しずつ解ってきて、この応募用紙を記入するだけでもかなり知識がついたと思っています。苦労しましたが、とても面白かったです。
【回答ここまで】
感想:初心者アピ激しい!
選択問題2
ある機械語をobjdumpにより逆アセンブルしたところ、以下の結果が得られました。このダンプに見られる問題点を指摘してください。
[略]
【以下に回答してください(行は適宜追加してください)】
この内容は、実行時にエラーが出ると想定されます。まず、アドレス0,5,a番地までの命令が実行されます。次にアドレス12番地のjmp命令を実行しようとしますが、実行できません。アドレス12番地のオペコードがE9であるjmp命令は、ニアジャンプ(現在のセグメント内でのジャンプ)です。eba72番地は.textセグメントに存在しないため、エラーになります。
さらに、アドレス5番地のcmp命令の結果を変えて別の分岐をたどった場合、危険な命令があります。それはアドレスe番地のjmp命令です。この時点でオペランドのebxレジスタが不定の値であり、さらにオペコードがFFでありファージャンプ(別のセグメントへのジャンプ)となるため、エラーで中断されずに予期せぬ命令が実行されてしまう危険性があります。
このダンプでは無駄な処理も含まれます。アドレスc番地のmov命令です。eaxレジスタ同士をコピーしています。フラグが変わるということもありません。ほかにも、アドレス10番地のjne命令がアドレス5番地のcmp命令の結果に依存する上に、アドレスe番地にjmp命令があるため、アドレス5番地のcmp命令の結果を変えても実行されることはありません。ただし、別のセグメントがある場合、そこからアドレス10番地にジャンプすることができれば実行できます。
このダンプで特筆すべきところは、アドレス10番地でのjne命令でアドレス13番地にプログラムカウンタが設定された場合、上記のダンプとは違うダンプになります。以下がそのダンプです。
13: 5b pop %ebx 14: ba 0e 00 00 00 mov $0xe,%edx 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
このダンプではwriteを呼び出すアドレス28番地のint命令のシステムコールは正常になります。実行されたときに標準出力に出力される文字列は、アドレス0番地からd番地までの値となります。このダンプでは新たに、アドレス13番地で今までpushを行っていないのにpopをするという、スタックアンダーフローを起こしかねない問題が発生しています。
問題点のまとめとして、このダンプだと実行することができないこと、無駄な処理が存在すること、実行されない可能性のある命令が含まれていること、スタックアンダーフローを引き起こす可能性があることが挙げられます。
【回答ここまで】
感想:すっごく自信ないけど、jmpすることでダンプの見え方が変わるってのは面白かったです。
選択問題5
以下のようなC言語の関数functionがあるとします。
[略]
上記プログラムをコンパイルした結果の一例 (i386)は以下となりました。
[略]
このとき以下の(1)~(5)の設問について、回答と好きなだけ深い考察を記述してください。知らない点は、調査したり自分で想像して書いてもらっても結構です。どうしてもわからない部分は、具体的にここがわかりませんと記述しても良いです。(1)~(2)の回答は必ず答えてください。(3)~(5)の回答は任意です。わかることを書いてください。CPU やコンパイラは特定の実装を例に説明しても良いですし、理想を自由に考えても良いです。
(1)【必須】上記の C 言語のプログラムはどのような動作をしますか。また、この関数を呼び出して利用する main 関数の例を作成してください。
(2)【必須】上記のアセンブリコードを、いくつかのブロックに分割して、おおまかに何をしている部分かを説明してください。もし、上記のアセンブリが気に入らないのであれば、好きなアーキテクチャやコンパイラのアセンブル結果を載せて説明しても良いです。
(3)【任意】 コンパイラがソースコードの関数を解釈して、ターゲットのアーキテクチャのバイナリを生成するまで、どのように内部で処理を行っていると思いますか。(キーワード: 構文解析、変数、引数、呼出規約、レジスタ、スタック、アセンブラ、命令セット)
(4)【任意】CPU の内部では、プログラムのバイナリはどのように解釈され実行されていると思いますか。(キーワード: フェッチ、デコード、オペコード、オペランド、命令パイプライン、回路)
(5)【任意】現在の CPU やコンパイラの不満点があれば自由に記述してください。
【以下に回答してください(行は適宜追加してください)】
(1)
配列と配列の長さを受け取り、配列の位置番号と配列の長さで掛け算をしてその配列に格納、配列の長さ分だけ繰返したら呼出し元に戻ります。配列0番目は必ず0の値が格納されます。
・main関数の例
/* このプログラムはシーザー暗号のように文字列をずらして暗号化するプログラムです。実用性は全くありません。 プログラムの第一引数に暗号化したい半角英小文字列を指定すると暗号化されます。 復号する場合第二引数に2を指定することで復号されます。 */ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> void main(int argc, char *argv[]) { int length, i; int *array; if(argc == 1){ printf("英小文字列を指定してください\n"); return; } length = (int)strlen(argv[1]); for(i = 0; i < length; i++){ if(!islower(argv[1][i])){ printf("英小文字列を指定してください\n"); return; } } printf("渡された文字列:%s\n", argv[1]); array = (int*)malloc(sizeof(int) * length);//入力された文字数分だけarrayにメモリを確保する。 function(array, length); //配列と配列の個数を関数functionに渡す。 //以下array[i]の中身を鍵としたシーザー暗号を行う。 if(argc == 2 || strcmp(argv[2],"2")){ for(i = 0; i < length; i++){ argv[1][i] = 'a' + (argv[1][i] - 'a' + array[i]) % 26; //暗号化処理 } printf("暗号化した文字列:%s\n", argv[1]); }else{ for(i = 0; i < length; i++){ argv[1][i] = 'z' + (argv[1][i] - 'z' - array[i]) % 26; //復号処理 } printf("復号した文字列:%s\n", argv[1]); } free(array); }
(2)
00000000 <function>: 0: 56 push %esi 引数 array,n のアドレスを<br> 1: 53 push %ebx スタックに退避する。<br> ──────────────────────────────────────────────────── 2: 8b 5c 24 0c mov 0xc(%esp),%ebx 引数arrayのアドレス,引数nのアドレスを 6: 8b 4c 24 10 mov 0x10(%esp),%ecx それぞれebx,ecxにセットする。 ──────────────────────────────────────────────────── a: 85 c9 test %ecx,%ecx nが0以下ならばアドレス26番地までの c: 7e 18 jle 26 <function+0x26> 処理を行わない。 ──────────────────────────────────────────────────── e: 89 ce mov %ecx,%esi 10: ba 00 00 00 00 mov $0x0,%edx i と計算レジスタ(eax)を0クリアする。 15: b8 00 00 00 00 mov $0x0,%eax ──────────────────────────────────────────────────── 1a: 89 14 83 mov %edx,(%ebx,%eax,4) for文の処理内容。 1d: 83 c0 01 add $0x1,%eax 配列arrayのi番目に現在の計算レジスタ 20: 01 f2 add %esi,%edx (eax)の値を格納、nとiの値を比較し、 22: 39 c8 cmp %ecx,%eax 同一でない間この部分を繰返す。 24: 75 f4 jne 1a <function+0x1a> ──────────────────────────────────────────────────── 26: 5b pop %ebx 退避していた引数n,arrayのアドレスを 27: 5e pop %esi ebx,esiにそれぞれ格納しなおす。 28: c3 ret そして呼出し元プログラムに戻る。
(3)
C言語のコンパイラを例に説明します。まずコンパイラは渡されたソースコードを字句解析し、トークンごとに解釈します。例えば、次のようなソースコードが渡されたとします。
void main(void) { int a; a = 5 + 2 * 3; }
これを字句解析すると次のようになります(わかりやすくするため1行1トークンで記述しています)。
void main ( void ) { int a ; a = 5 + 2 * 3 ; }
次に、構文解析を行います。トークンの並びが文法に倣っているかどうかを判定し、構文木を生成します。構文解析はBNF記法で表されることが多いです。先ほどのソースコードをBNF記法で表してみます。(簡略化のため実際のC言語parserとはかけ離れていますがご了承ください。)
<main> ::= <ARG> <FUNCTION> "(" <ARG> ")" "{" <program> "}" <program> ::= { <type> | <assign> } <type> ::= <TYPE> ( <IDENTIFIER> | <IDENTIFIER> "=" ope3 ) ";" <ope3> ::= <ope2> { "+" <ope2> | "-" <ope2> } <ope2> ::= <ope1> { "*" <ope1> | "/" <ope1> } <ope1> ::= ( <NUMBER> | <IDENTIFIER> ) | "(" <ope3> ")" <assign> ::= <IDENTIFIER> "=" <ope3>";" <TYPE> ::= "int" | "double" <ARG> ::= "void" | <TYPE> <FUNCTION> ::= "main" <IDENTIFIER> ::= ( "a".."z" | "A".."Z" ) { "a".."z" | "A".."Z" | "0".."9" } <NUMBER> ::= 1*( "0".."9" )
生成される構文木はこのようになります。(antlr4で作成)
次は意味解析です。構文には従っていても、未定義の変数を使っていないか、引数の個数が多すぎないかなどのチェックを行います。ほかにも、ブロック表と記号表を使って変数を管理します。ブロック表はグローバル変数やローカル変数のスコープを管理します。記号表はスタックのようにスコープ内での変数の内容を保持します。意味解析が行われると中間表現と呼ばれるそのプログラムのデータ構造が生成されます(C言語の場合はオブジェクトファイル)。中間表現を利用して、CPUで解釈できる機械語(目的コード)を生成します。
機械語を生成するためには、呼出規約に従ってアセンブラ言語化し、それを命令セットに従って数値化する必要があります。呼出規約とは、関数の呼出しに関する規則です。C言語は実行するべきプログラムはすべて関数です。関数の呼出しでは、呼出規約に従って引数が複数ある場合にスタックにどの順番で格納するか、どのレジスタに値を格納するかが決定されます。例えば、インテルx86ベースで使われる規約cdeclでは、関数の引数は右から左へ順に格納され、戻り値はEAXレジスタに格納されます。呼出された関数ではEAX,ECX,EDXレジスタは元の値をスタックに格納することなく自由に使えます(必要に応じて呼出し側で値をスタックに格納する)。この規則に従い、アセンブラ言語化します。
アセンブラ言語を機械語化するためには、CPUの命令セットに従います。例えば、ADD命令でECXレジスタの内容をEAXレジスタに加算する場合だとオペコード0x03,アドレッシングモード0xC1をつなげて03 C1というバイナリ表現になります。命令セットはよく使われる命令ほどオペコードが短く設定されています。このように命令セットに従って変換していくことで、バイナリが生成されます。
(4)
CPUがバイナリを実行するにはまず、フェッチ(命令の読み込み)を行います。命令をどこから読みだせばいいのかは、プログラムカウンタというレジスタで指定されます。次に、読み出した命令のデコード(解読)を行います。デコーダという回路で、命令セットに従ってオペコード(命令コード)とオペランド(レジスタ、アドレスや即値の情報)に分割します。そして、読みだした命令を一つずつ実行していきます。その結果を、メモリアクセス(メモリへの読み書き),ライトバック(レジスタへの書き込み)をします。
命令をフェッチ、デコード、実行、メモリアクセス、ライトバック、また次の命令をフェッチ、デコード、実行、メモリアクセス、ライトバック、としていると時間がかかるため、命令パイプラインという、複数の命令を同時に処理してスループットを向上させる方法が使われます。例えば、1つの命令がフェッチされている間に別の命令がデコードされ、また別の命令が実行され、更に別の命令がメモリアクセスされ、また更に別の命令がライトバックされます。同時に実行できる数(ステージ数)が多いほど、スループットを向上できます。
しかし、命令の依存関係によるパイプラインハザードが発生することがあります。例えば、CMP命令で比較した結果で処理を実行するかが変わるJGE命令などは、条件に合致するとジャンプによって処理が飛ばされます。条件に合致する場合ジャンプが実行される前までにフェッチ、デコードされていた命令は無駄になってしまいます。そのため、ステージ数を多くしていても、プログラムによっては一定以上速くならない場合があります。
このようにしてCPUで処理が行われます。
(5)
これはC言語コンパイラの話ですが、C言語コンパイラではstrcpy関数やstrcat関数など、バッファオーバーフローを引き起こす可能性のある関数が使われていても、コンパイルを通してしまうのは問題だと思います。せっかくstrncpy関数やstrncat関数など、より安全な代替関数があるので、エラーにしてでもバッファオーバーフローを引き起こす可能性のある関数を使わせないようにしたほうがいいと思います。最低限警告は出すべきです。コンパイラの機能でバッファオーバーフローを検知するのではなく、発生させないようにプログラマに強制することも必要だと思います。
【回答ここまで】
感想:コピペの関係でコード部分はぐちゃってます。評価されていると思うのは多分ここ。wordで出せるのをいいことに、画像貼っつけてみました。コンパイラのところはすごく調べながら書きました。
選択問題8
gccが持つ-fno-stack-protectorは、どのようなセキュリティ機能を無効にするオプションであるのか、またこの機能により、どういった脆弱性からソフトウェアを守れるのかをそれぞれ記述してください。
【以下に回答してください(行は適宜追加してください)】
-fno-stack-protectorとは、GCCに搭載されているSSP(Stack Smashing Protector)を明示的に無効化してコンパイルするオプションです。SSPとは、GCCにより自動生成されるスタックバッファオーバーフローを検知するものです。SSPは、スタックのローカル変数領域の前に、カナリアコードと呼ばれる4バイトの値を挿入します。実行時に、関数からリターンする直前にカナリアコードの値が書き換わっていないか確認し、書き換わっている(スタックバッファオーバーフローが発生している)ことを検知すると、処理を中断します。対応するのはスタックバッファオーバーフローだけであり、ヒープバッファオーバーフローには対応しません。
スタックバッファオーバーフローとは、スタックに確保されたバッファより大きなデータが挿入されてしまった場合に別のローカル変数や関数から戻るためのリターンアドレスが書き換えられてしまうなどする脆弱性です。例えば攻撃者がリターンアドレスの部分に悪意のあるコードが来るように調整してオーバーフローさせれば、リターンの代わりに悪意のあるコードが実行されてしまいます。
スタックバッファオーバーフローは、入力データをチェックしないために起こります。例えば、strcpyを使ったときに、コピー先の領域よりコピー元の領域が大きい場合、スタックバッファオーバーフローが発生します。SSPは、その脆弱性による被害を実行時に防ぐために使われます。
-fno-stack-protector でSSPを強制的に無効化してしまうのは、スタックバッファオーバーフローが絶対に発生せず、実行時に消費するメモリサイズをとにかく小さくしたい場合に限るべきだと思います。
【回答ここまで】
感想:別にカナリアは殺してないです。
選択問題10
アンチデバッグ、難読化といった単語をキーワードとする技術について、あなたが知っていること、調べたことを具体的に記述してください。基本的にPCのソフトウェアにおける技術を想定していますが、他端末、またはハードウェアに関する内容でもかまいません。
【以下に回答してください(行は適宜追加してください)】
アンチデバッグ、難読化は主にマルウェアの解析を妨げるために使われる手法です。マルウェアの解析では、主に動的解析と静的解析を用いてマルウェアの特徴を特定します。アンチデバッグは動的解析、難読化は静的解析を妨げます。
・アンチデバッグについて
アンチデバッグとは、動的解析されていることを検知した場合に、感染活動をやめるものです。動的解析は、解析環境で実際にマルウェアを動作させてどのような動きをしたかを記録して感染活動を明らかにします。アンチデバッグには、次のような方法があります。
①仮想環境の検知
②デバッグに関するツールの検知
③デバッガによる実行の検知
①仮想環境の検知
動的解析には仮想環境が用いられる場合が多く、アンチデバッグでは仮想環境を検知して活動をやめるといったことができます。例えば仮想環境構築ソフトの一つであるVMwareを検知するためには、バックドアI/Oポートに対し通信を試み、応答があれば活動を停止します。検知を回避するには、バックドアI/Oポートを設定で閉じておく方法が有効です。ほかの仮想環境でも、仮想環境の補助ツールなどをインストールしている場合にそれを検知されてしまう場合があります。補助ツールなどはなるべく使わないなどの対策が必要です。
②デバッグに関するツールの検知
マルウェアは、動的解析によく使われるデバッガ、システムリソース監視ツールなどのツールが起動されているか検知することがあります。プロセス一覧を取得するAPIを使い、プロセス一覧とよく使われるツールの実行ファイル名一覧を突き合わせ、一致したら活動をやめます。検知を回避するには、実行ファイル名を変更します。しかしこれは、ウィンドウ検知をするものには効果がありません。ウィンドウ検知では、ウィンドウ名を列挙するAPIでウィンドウ名を取得し、ツールのウィンドウ名一覧と突き合わせることでツールを検知します。検知を回避するには、デバッガでAPIの戻り値を操作します。
また、ツールが利用するデバイスファイルを検出することもあります。これは、ツール起動時に自動生成されるデバイスファイルをAPIで定期的に確認するものです。検知を回避するには、これもデバッガでAPIの戻り値を操作する必要があります。
③デバッガによる実行の検知
マルウェアのデバッガ対策として、実行時にデバッガによるものかどうかを検知する方法もあります。これには、次のような方法があります。
(1)APIによる検知
(2)意図的に例外を発生させて検知
(3)タイマーによる検知
(4)ブレークポイントの検知
(1)APIによる検知
Windowsにはデバッグされているか検知するIsDebuggerPresentというAPIがあります。このAPIはデバッガで実行されている場合に0以外の数値を返すものです。このようなAPIから検知を回避するには、デバッガでAPIの戻り値の操作を行います。
(2)意図的に例外を発生させて検知
プログラムの実行中に意図的に例外を発生させ、デバッガによる実行かどうか判定するものがあります。通常の実行では例外が発生すると例外を捕捉するコードに処理が移りますが、デバッガによる実行だと例外を捕捉するコードに処理を移すことができません。そこで例外が発生しないようにメモリに変更を加えてしまうと、検知されてしまいます。検知を回避するには、デバッガの設定で例外を無視するよう設定します。
(3)タイマーによる検知
デバッガで解析を行うとき、ステップ実行を行うためどうしても実行時間が長くなってしまいます。その特徴を利用して、タイマーで解析を検知しようとするものがあります。タイマーには、APIや機械語であるRDTSC命令が使われます。検知を回避するにはデバッガで、APIの場合は戻り値を操作、RDTSC命令の場合はレジスタの操作が必要です。
(4)ブレークポイントの検知
デバッガではブレークポイントを設定することができますが、そのブレークポイントの呼出しで使われる機械語を検知しようとするものがあります。デバッガで解析する際、APIにブレークポイントを設定するであろうと考えた攻撃者が、そのAPIを呼び出す前にデバッグ例外ハンドラであるINT3命令が呼び出されていないかチェックします。検知を回避するには、機械語レベルでINT3命令を呼び出す場所を変える必要があります。
・難読化について
難読化とは、コードを人間に読まれないように解りにくくすることです。マルウェアだけでなく普通のプログラムでもリバースエンジニアリングを防止するために用いられることがあります。プログラムの難読化は大抵、ランタイムパッカーという実行可能な形式のまま圧縮するプログラムを使ってパック処理(難読化)が行われます。これにより、逆アセンブラを妨害することができます。
マルウェアが最も多くねらうOSであるWindowsでは、実行ファイルはすべてPEファイルフォーマットと呼ばれる形式になっています。逆アセンブラは、この形式に則り逆アセンブルを行おうとします。しかし、パック処理されたプログラムは正常な形のフォーマットにはならないので逆アセンブルに失敗します。
パック処理が行われているかどうかを判断するには、バイナリエディタに読み込ませたときに可読性のある文字列が存在するか、逆アセンブルしたときにインポートアドレステーブルの数が少なすぎないか、.textセクションが存在するかを見ることや、(パッカーが既知のものであれば)パッカーのシグネチャを登録し検知するツールを使って判断することができます。
パックされたプログラムは、大きく分けて展開ルーチンと圧縮されたオリジナルコードが存在します。展開ルーチンでは、オリジナルコードの展開、オリジナルコードのインポートアドレステーブルの構築を行います。展開ルーチンの処理が終了するとオリジナルコードのエントリポイント(オリジナルエントリポイント)に処理が移行します。
マルウェアのアンパックは、次の順序で行われます。
①オリジナルエントリポイントを発見、そのポイントまで実行
②メモリダンプをファイルに保存
③インポートアドレステーブルの再構築
①オリジナルエントリポイントの発見、そのポイントまで実行
オリジナルエントリポイントを発見することで、どこまでが展開ルーチンで、どこからが圧縮されたオリジナルコードであるかがわかります。オリジナルエントリポイントを発見するには、コード中のPUSHAD命令やPOPAD命令の対応や、メモリ上に動的に生成されたコードに制御が移ったとき、JMP命令やCALL命令でジャンプするときなどを見てオリジナルエントリポイントを探します。そして見つけたオリジナルエントリポイントにブレークポイントを設定しておき、実行することでメモリにオリジナルコードが展開されます。
②メモリダンプをファイルに保存
オリジナルエントリポイントまで実行したら、その時点のメモリダンプをファイルに出力します。このファイルの内容が、展開されたオリジナルコードです。
③インポートアドレステーブルの再構築
次に、インポートアドレステーブルを再構築します。インポートアドレステーブルはプログラムがロードされる前後でローダによって実際にインポートされる各関数のアドレスに書き換えが行われます。オリジナルエントリポイントでメモリがダンプされた状態では既にローダにより書き換えられているため、インポートアドレステーブルをロード前の状態に修復する必要があります。メモリダンプを出力したファイルにインポートセクションを追加し、それを認識できるようにPEヘッダのオプショナルヘッダ内のIMAGE_DATA_DIRECTORY構造体のDataDirectoryという配列でインポートアドレステーブルの相対仮想アドレスとサイズを指定し直すことが必要です。
これで、パックされたプログラムを、逆アセンブラを使って解析できるようになります。
既知のパッカーでパックされたマルウェアはアンパックツールを使うことで簡単にアンパックできますが、攻撃者が新しいパッカーでパックすることも多くあります。その場合は、アンパックをサポートするツールを使いながら上記の方法で手作業で行っていき、マルウェアの目的を導き出していきます。
【回答ここまで】
感想:ほぼすべて本で読んだ知識。自由に書いていいみたいだからめちゃめちゃに書きました。
選択問題11
下記バイナリを解析し、判明した情報を自由に記述してください
[略]
【以下に回答してください(行は適宜追加してください)】
これは、tcpdumpファイルです。解析すると、192.168.146.1のIPアドレスから192.168.146.128のIPアドレスに送られたHeartbeatリクエストでした。しかし、このHeartbeatリクエストには問題があります。
まず、dump内のHeartbeatリクエストは、Payloadが4バイトとなっています。しかし、実際のPayload長が3835バイトになっています。したがって、このdumpは、CVE-2014-0160(いわゆるHeartbleed)の脆弱性を狙う攻撃であると考えられます。このリクエストを脆弱性のあるバージョンのOpenSSLを使うサーバで処理すると、3831バイトものメモリ内容(他ユーザのセッション情報など、重要な情報)が漏洩してしまいます。
また、このパケットからは宛先MACアドレスが22:22:22:22:22:22、送信元MACアドレスが11:11:11:11:11:11であることもわかります。宛先MACアドレス22:22:22:22:22:22はローカルアドレスで、ユニキャストアドレスです。それに対して送信元MACアドレス11:11:11:11:11:11はグローバルアドレスでマルチキャストアドレスです。しかし送信元MACアドレス11:11:11:11:11:11についてはIEEEの規格に従っていません。なぜならグローバルアドレスに必須なOUIの割り当てがされておらず、マルチキャストアドレスには決められたプレフィックスが存在しますが、それに適合しません。
次に、IPアドレスについて、クラスCのプライベートアドレスで、第4オクテットが1であるアドレスはデフォルトゲートウェイになることが多いと思います。そのため、送信元のIPアドレスは偽装されている可能性があると考えます。
このパケットから私が推測するのは、攻撃者が同一のネットワーク内のMACアドレスを偽装した機器を使い、何らかの手段(例えばIPスプーフィング、または正当に割り当てるなど)で192.168.146.1のIPアドレスを利用して攻撃を行うことで攻撃元をわからなくしているのだと思います。
【回答ここまで】
感想:fileコマンドで調べてwiresharkに投げました。heartbleedよりMACアドレスが気になりました。適当かとおもえば、ちゃんと考えられていたのですね。
以上です。