Posts セキュリティキャンプ 応募課題晒し
Post
Cancel

セキュリティキャンプ 応募課題晒し

セキュリティキャンプ全国大会2021のCトラックに応募したら、たまたま通りました!!!! なので、課題を晒していきたと思います。

問6は他の人がやっていなさそうなことをやっているので自信作です。 ぜひ参考にしてもらえたらうれしいです。 それ以外は他の人のやつを参考にしたらいいと思います。

今年度のCトラックの方々の応募課題晒し

問1

あなたがセキュリティ・キャンプ全国大会に応募する理由を教えてください。受講生や講師とのコミュニケーション、受講したい講義、なりたい自分など、何でも構いません。


受講したい講義

C3: Zero to Malware Analyst Part1/Part2

私はブログなどを参考にしながらマルウェアの解析を行っています。 pink氏が出版したGhidra本を読み、多くの知見を得ることができました。 特に勉強になったのがGoバイナリの解析です。 私はそれまでGoバイナリの検体を解析したことがありませんでした。 そのため、良い経験になりました。 そのpink氏の講義はとても興味深いです。 また、Ghidra本にはなかったYaraルールの作成まであるのでとても興味深いです。

C4: UEFI BIOSセキュリティ

これまでC3のようなものは多く解析してきました。 しかし、私がやっているのはユーザランドのマルウェアだけでUEFIのマルウェア解析を行ったことがありません。 それを解析するにはUEFIの知識が必要です。 過去に丹田さんの解析記事を読みながらUEFIの解析を行ってみました。 ブログ通りに進めることができたのですが、それについての理解を得ることができませんでした。 他の記事がないか調べたのですが、UEFIについての記事が多くなく諦めてしまいました。 この講義ではUEFIの基礎的な理解を深められるのではないかと思い興味があります。

C6: ファジング+トリアージ技術を用いた脆弱性解析の自動化

今回の課題8がファジングについてでした。 ファジングという技術はこの先脆弱性を見つける方法としてとても大切な技術の1つになるので、とても興味がありました。 しかし課題8をやってみると一番時間をかけたのに全く理解することができなく悔しかったです。 私はこの技術について期限内に理解できませんでしたが、必ず理解したいと考えています。

この講義を受けることで私はファジング+トリアージ技術の基礎を理解することができるのではないかと考えています。 基礎を理解することで、この先その環境に応じて様々なファジングツール作成することができると考えています。 そのため、私はこの講義にとても興味があります。

なりたい自分

私は多くの分野の技術を身に付けたいと考えています。 多くの分野を理解することである分野での技術がが他の分野で応用することができます。 また、自分の専門分野を深く理解するためには他の分野もある程度理解していないといけません。 今まではWindows(PE)のバイナリばかりをを読んでいましたが、最近はPwnなどにも手を出し始めてLinux(ELF)を読むようになりました。 さらに、Pwnは脆弱性を探す力を付けることができます。 まだheap問が出るとお手上げ状態ですが、これからheap問も理解し新たな分野にも手を出していきたいと考えています。

問2

今までに解析したことのあるソフトウェアやハードウェアにはどのようなものがありますか? 解析の目的や解析方法、結果として得られた知見などを含めて教えてください。


私は1年程前から解析ブログを参考にマルウェア解析をしてきました。 1つのファミリーに囚われずに色々な検体を解析してきました。

解析方法

解析の流れは、解析対象のファイルによって異なります。

マクロファイル

マクロファイル(xls, doc)の場合は、oletoolsを使って解析します。olevbaを使って、vbaコードを取得します。 取得したコードを解析すると、dropperであることがほとんどなのでpowershellファイルか実行可能ファイルをダウンロードしてきます。

powershellファイル

powershellファイル(ps1)の場合は気合でコードを読んでいきます。powershellコードは難読化の方法が多いです。 例えば、文字列を分割して扱ったり、base64で難読化されているコードがあったりします。 このように難読化されているコードを気合で読んでいきます。

実行可能ファイル

実行可能ファイル(exe, dll)の場合は、まず表層解析でx32かx64のどちらで動くのか、import, exportなどの基本的な情報を確認します。 次に静的解析で、解析対象のファイルがどのような挙動をするのかを解析します。 ここではどのコードがマリシャスで、どこがマリシャスでないのかを確認していきます。 例えば、ネットワークコネクトやプロセスインジェクションをしている部分はマリシャスだと考えられますが、難読化の部分などはマリシャスではないと考えられます。 最後に動的解析で、実際にコード動かしてみます。 この動的解析では主に静的解析でsolverを作成できなかった難読化の部分などを実際に動かして解析します。 具体的には、マリシャスなコードを動かさないようにjmp命令の書き換えや、フラグの書き換えなどをします。 そうすることで自分が動かしたい部分を実行させることができます。

知見

powershell

マルウェアを解析していると自分の知らない関数や書き方が出てきます。 例えば、Emotetのドロッパー(powershell)を解析した時です。 私はそれまでpowershellを書いたり、読んだりしたことがほとんどありませんでした。 そのため、1つずつ調べながら行っていました。 その中で驚いた文法が下記のような書き方です。

1
"{1}{0}"-f("{1}{0}"-f'o','ll'),'he'

これは、-fの後にある0番目の文字列を”{0}”に渡され、-fの後にある1番目の文字列を”{1}”に渡すような感じです。 これを実行するとhelloと出力されます。 これを見たときに私はpowershellの文法の独特さを感じました。

このようにマルウェアを解析していると、自分が普段使わないような言語に触れることもあります。 そのため、言語それぞれの文法、難読化方法を知ることができます。

実行可能ファイル

ryukを解析した時、Reflective DLL Injectionのshellcodeがリソース内に埋め込まれておりそれを実行するような検体でした。 私はそれを解析するためにデコンパイルして解析をしました。 IATを解決するコードの部分を読んでいると、Exceptionセクションの部分を参照してるところがありました。 IATを解決するには最初にLoadLibrary、GetProcAddress関数のアドレスを見つけます。 私はExceptionからIATを解決する方法があるのかと思い、GetProcAddressを見つける方法があるか調べました。 すると、このようなブログが出てきました。 私は”Exceptionからも見つけられるんや~”と思いました。 それで再度ディスアセンブリ結果を確認してみると、デコンパイルが間違えていることに気が付きました。

このように自分のミスで新たな知見を得ることもできるのではないかと思います。

問3

今までに作成したソフトウェアやハードウェアにはどのようなものがありますか? どんな言語やライブラリ、パーツを使って作ったのか、どこにこだわって作ったのか、などたくさん自慢してください。


readpe

私がマルウェア解析に興味を持ち始めた頃に作成したツールです。 このツールはPEのヘッダー情報を出力してくれます。 使用言語は最近注目を浴びているRust製です。

当時PEという実行ファイルがあることすら知りませんでした。 私はそこでPEのヘッダーにはどのような情報があるのか知りたいと思い、作成したツールです。 構造体や列挙型を一から定義しました。 また、パーサーも自分で書きました。

ResourceHack

応募期限が延長されたので作成することができました。 これは今回の課題6が出されて作成したツールです。 課題6では前後の文字列の差が少なかったため元々保存されていたところのたまたま書き込むことができました。 しかし、このツールでは文字列の長さが関係なく書き込むことを可能にしてあります。

問4

ここ数年に発表された、以下のキーワードに関連するニュースや記事や学術論文から1つ選び、それに関して調べた内容を記述してください。内容には、1.選んだ理由、2.技術的詳細、3.被害規模または影響範囲、4.対策、の4点を必ず含めてください。なお、対策は今ある技術のみに捕われず、将来的な技術や法律など、自由な発想で書いてください。

キーワード:

  • サイバーセキュリティインシデント
  • マルウェア
  • 攻撃キャンペーン
  • 脆弱性
  • 新たな攻撃手法
  • 未知の脅威
  • 組み込みシステム

選んだ理由

私が選んだAtomBombingというテクニックにはパッチが存在していません。 そのため現在でもこのテクニックを使用することができます そのためとても危険です。 これはマリシャスではないプロセスにインジェクションして、マリシャスではないコードとして実行させることが可能になります。 そのためこのテクニックを理解し、対策することが可能なのかを考えられる機会だと思い、これを選びました。

技術的詳細

WindowsにはAtomTableをいうテーブルが存在します。 1つプロセスに2種類のAtomTableが必ずあります。

  • Local Atom Table
  • Global Atom Table 1つ目はそれぞれのプロセスに1つずつ存在するため、他のプロセスからアクセスすることができません。 2つ目はカーネル領域にあるため、全てのプロセスからアクセスすることができます。 今回のテクニックで使われるAtomTableはGlobal Atom Tableです。 AtomBombingはWindowsのAtomTableを経由して任意のプロセスへコードを挿入する技術です。 主な工程としては2つあります。

AtomTable経由でコードを挿入する

ソースコードはここにあります。

OpenProcess

OpenProcessを呼び出すことで、ターゲットのプロセスハンドルを取得します。

VirtualAllocEx

VirtualAllocExを呼び出して、ターゲットのメモリを確保します。

メモリへの書き込み

ターゲットのメモリへ書き込む方法は、AtomTableへシェルコードを書き込んでターゲットがATOMを使って文字列をメモリ上に書き込みます。 GlobalAddAtomWを使ってAtomTableへシェルコードを書き込みます。 ※ GlobalAddAtomWを使用しているのはGlobalAddAtomAの場合だと文字列がASCiiからUTF-16に変換されてしまうため、自分が作成したコードがおかしくなってしまうからです。 戻り値としてATOMを取得できます。 この値を使うことでどのプロセスからも文字列を取得することができます。 他のプロセスでGlobalGetAtomNameWを呼び出したいので、NtQueueApcThreadを使います。 NtQueueApcThreadは第一引数にターゲットスレッドのハンドル。 第二引数に実行したい位置へのアドレスなのでこの場合はGlobalGetAtomNameWのアドレスです。 第三、第四、第五引数は第二引数で呼び出した関数への引数です。 今回の場合だと、第三引数にシェルコードへのATOMが渡され、第四引数にメモリのベースアドレス、第五引数にバッファサイズが渡されます。 これでAtomTableへの書き込み、読み込みが可能です。 次はGlobalAddAtomWでは文字列が0xFF文字しか書き込めないので書き込みを分けてGlobalGetAtomNameWのベースアドレスもそれに合われせて少しずつ動かします。 また、GlobalAddAtomWではNULL文字までしか書き込むことができません。 なのでその部分でも分けて書き込みます。 また、NULLが連続する場合はAtomTableにNULLを書き込み、ターゲットにNULLを読み込ませます。

CreateRemoteThread

CreateRemoteThreadで確保したメモリの先頭アドレスからスレッドを作成させて実行させます。

NXビットをを回避するためにROPコードを作成、実行する

ROPで作成しているコードをここに置いておきます。 また、ここに私が作成したROPコード(x64)を置いておきます。

ROPコードの作成

AtomBombingで使われているROPは最初に、ZwAllocateVirtualMemoryを呼び出しRWXのメモリを確保する。 その後に、memcpyで実行したいコードをZwAllocateVirtualMemoryで確保したメモリに書き込む流れです。

x86の引数とretは下記のようになります。

1
2
3
4
5
6
7
8
[ stack ]
| ret   |
| arg1  |
| arg2  |
| arg3  |
| arg4  |
| arg5  |
 ...

上記で書いたように引数をスタックに保持して関数へ飛びます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
NTSTATUS ZwAllocateVirtualMemory(
  _In_    HANDLE    ProcessHandle,
  _Inout_ PVOID     *BaseAddress,
  _In_    ULONG_PTR ZeroBits,
  _Inout_ PSIZE_T   RegionSize,
  _In_    ULONG     AllocationType,
  _In_    ULONG     Protect
);

void *memcpy(
  void *dest,
  const void *src,
  size_t count
);

それぞれの関数の引数は上記のようになるのでこれと引数とスタックの関係を元にROPを作成したのが下記のようになります。

1
2
3
4
5
6
7
8
9
10
11
12
13
https://github.com/BreakingMalwareResearch/atom-bombing/blob/master/AtomBombing/main.cpp#L1169
[ stack ]
| memcpy address |
| ProcessHandle |
| BaseAddress |
| ZeroBits |
| RegionSize |
| AllocationType |
| Protect |
| ret address |
| dest |
| src |
| count|

ROPの書き込み、実行

前述でROPの作成について説明しました。 次に、そのコードの書き込み先と実行について説明します。

まずターゲットのプロセスにCreateRemoteThreadを用いてスレッドをサスペンド状態で作成します。 次に、GetThreadContextで作成したスレッドのレジスターに格納された値を取得します。 espよりスタックの位置を確認し、そこに先ほど作成したROPを書き込みます。 eipをZwAllocateVirtualMemoryのアドレスに書き換えます。 SetThreadContextで作成したスレッドのレジスターを書き換えます。 最後にResumeThreadで実行することでROPが実行されます。

まとめ

上記で説明した2つのテクニックを組み合わせることでAtomBombingになります。 実行したいコードは実行フラグがない場所に書き込みます。 今回参考にしたコードでは、kernel32.dllのdata領域の下の方に書き込んでいました。

被害規模または影響範囲

このテクニックはProcess Hollowingと一緒にDridexというトロイの木馬で用いられています。 そのため被害範囲はとても多いと考えられます。 少なくとも1億ドル以上ではないかと思っています。

対策

ROPコードの部分はShadow stackによりブロックすることが可能になります。 なので、問題はAtomTableの部分です。 前提として、AtomTableは悪意のないプログラムでも使われているのでそこに影響が出ないようにしなければなりません。

考えとしては、NtQueueApcThreadの引数にGlobalGetAtomNameのアドレスが入っている場合は例外が発生するようにすると良い考えました。 これだと、他のプロセスでGlobalGetAtomNameを実行させることができません。 しかし、悪意のないプログラムでもGlobalGetAtomNameを使う時にこのような方法を取っている場合悪意のないプログラムに支障が出てしまうため最前の策ではないのかと思います。

AtomTableに書き込んだプロセスの子プロセス(と親プロセス)からしか読み込むことができないようにしればいいのではないかと考えます。 そうすることで事前に実行されているプロセスに対してインジェクションすることができなくなります。 もし別のプロセスに対してAtomTable経由で文字列などを渡したい場合は子プロセスとして実行させればいいと考えます。 また、前提としていたことも少しのコード変更で対処することができるのではないかと考えます。

https://github.com/BreakingMalwareResearch/atom-bombing https://www.enisa.europa.eu/publications/info-notes/atombombing-2013-a-new-code-injection-attack https://japan.zdnet.com/article/35097430/?tag=mcol;relArticles https://msrc-blog.microsoft.com/2017/08/16/detecting-stealthier-cross-process-injection-techniques-with-windows-defender-atp-process-hollowing-and-atom-bombing/

問5

ブログや GitHub など、技術情報を公開している URL があれば教えてください。またその内容についてアピールすべきポイントがあれば記載してください。


GitHub: https://github.com/3ts75 Blog: https://3ts75.github.io

問6

配布した6_decode.binは PE形式のプログラムであり、実行すると内部に保持したエンコードされたデータをデコードして表示します。6_decode.binを解析し、挙動などわかったことをまとめて解析レポートを作成してください。また、エンコードアルゴリズムを特定して、デコードスクリプトを作成し、次のデータを復号して回答してください。

“\x8d\x93\x13\x8a\x43\xb6\x59\x4d\x41\x80\x1b\x53\x02\x86\xf2\xed\x55\x55\x78\x59\x8b\x77\x35\x17\x56”

解析が最後までできたとしても、できなかったとしても、解析の手順、解析にあたり学習したこと、解析の過程で判明したことを文字数制限の範囲で自由に記述してください。


それぞれを下記のように定義します。

1
2
9D CB 1E A7 65 ED 5F 4D 01 D6 49 4A 55 BD D7 83 52 07 30 40 --- ①
8d 93 13 8a 43 b6 59 4d 41 80 1b 53 02 86 f2 ed 55 55 78 59 8b 77 35 17 56 --- ②

表層解析

表層解析ではPE-bear.exeを使って、ファイルのヘッダー情報を確認します。 私が、表層解析で気になったのは主に、

File_Header.Characteristics

このフラグはファイルの属性を示します。 各bitがそれぞれの意味を持っています。 今回のファイルではIMAGE_FILE_EXECUTABLE_IMAGEのフラグが立っているので実行ファイルだということがわかります。 また、IMAGE_FILE_32BIT_MACHINEのフラグが立っていないのでx64の実行ファイルだということがわかります。

Optional_Header.Subsystem

この値では実行ファイルのSubsystemを確認することができます。

PE-bearで見ると、Windows GUIと表記されています。 なので、これはWinMainから始まることが予想できます。

Section_Header

セクションにはそれぞれの役割があります。 例えば、.textだと実行可能コードが格納されています。.rdataだと読み込み専用のグローバル領域、.dataは初期化を持つグローバル変数、.pdataは例外情報、.rsrcはリソース、.relocは再配置です。 このようにそれぞれのセクションにはそれぞれの役割があります。 この中で私が気になったのは、.rsrcです。 他のセクションは、コードを書いてコンパイルすると自然とくっつくセクションなのですが、リソースセクションはリソースに情報を付加しないと付きません。

Resource

リソースに何かしらのデータを保持していることがわかったので、Resource Hackerを使ってリソース内を見ていきます。 リソースには4つのデータが保持されています。 どのリソースを読み込んでいるか、リソースを使っているのかもわかりません。 なので、このファイルを静的解析していきたいと思います。

静的解析

静的解析ではidaを使って、解析していきます。 表層解析の時にSubsystemがGUIということがわっているのでWinMainがあるか探します。 Function windowでWinMainを検索すると、見つかるのでそこから調査していきます。

WinMain

WinMainでは7つの関数が呼ばれています。

Resource Functions

WinMainではリソース関数が3つ呼ばれています。 FindResourceWの引数を見てみると、リソース名(第二引数)に129でリソースタイプ(第三引数)が”DATA”だということがわかります。 (FindResourceWの戻り値はIMAGE_RESOURCE_DATA_ENTRYの先頭アドレスを取得します。)

Resource Hackerを使ってDATA-129のリソース情報を見てみます。 ①のデータを保持していることがわかりました。

SizeofResourceは、戻り値で①のサイズを取得する関数です。(IMAGE_RESOURCE_DATA_ENTRYのSizeの値を返します。) LoadResourceは、戻り値で①の先頭アドレスを取得する関数です。 LockResourceは、正直何をしたいのかわからないのですが、型変換をしているだけだと思います。

Allocate

VirtualAllocでリソースのサイズを確保しています。(permissionはrw-です。) 確保した領域には①のデータをmemcpyを使って書き込みます。

DecodeRoutine

この関数では①のデータをデコードする関数です。 引数としては5つ受け取っています。

  • 第一引数に確保した先頭のアドレス
  • 第二引数にSizeofResourceの返り値
  • 第三引数に0x89192712の値
  • 第四引数に”SECCAMP2021”の先頭アドレス
  • 第五引数に11の値(“SECCAMP2021”の文字の数)

詳細は後述します。

MessageBoxA

ダイアログのタイトルとして、”Decode!!”を出力します。 テキストにデコードされた文字列を出力します。

DecodeRoutine

前述したのですが、この関数ではリソースに格納されていた文字列①をデコードしている関数です。 この関数のルーチンを見ながらsolverを書いていきます。 solverと言えばPythonで書くイメージがあるのでPythonで書こうと思ったのですが、思い通りに書くことができませんでした。 普段からPythonを書いていないこともありますが、Pythonはどのような型なのか、文字列や値をどのように格納しているのかイメージがしにくいからです。 なので、まず普段書いているC++で書いてからPythonで書こうと思います。

まずはアセンブリを一行ずつ読みながらC++に変換していきました。

流れは下記のようになります。

  1. keyに5を掛けて、keyに格納
  2. keyに0x2365F703を足す
  3. もし(allocate[i]-1)が0xA0より大きく0xFFより以下であればallocate[i]の値から1引く
  4. allocate[i]をkeyでxorする
  5. keyを2だけ右シフトする
  6. keyを0x1CA9引く
  7. seccamp[i%11]に2を掛ける
  8. allocate[i]をkeyとaddを足した値でxorする

アセンブリはたまに読んでいたため、私にとってデコードのルーチンを読むことは困難ではありませんでした。 なので、デコードのC++バージョンはすぐに難なく書くことができました。 c++のコードはここにあります。

実行結果は下記のようになりまた

1
2
> solver.exe
This_is_decoded_text

上記よりデコードのフローがわかりました。 なので、今度こそpythonでsolverを書きます。 しかし、すぐには書けませんでした。 文字列をb”\x9D..\x40”と扱っていたのですが'str' object does not support item assignmentというエラーが出てしまい、 中々先に進むことができませんでした。 私は文字列で扱うのではなく、リストとして扱うことにしました。 すると、エラー無く実行できました。 pythonのコードはここにあります。

コードを実行してみると、下記のような結果が得られました。

1
2
> python solve.py
This_is_decoded_text

動的解析

最後に実際に動かしてみて、solverによってデコードされた文字列と同じかを確認したいと思います。 すると、solverによってデコードされた文字列と同じ文字列がポップアップされました。 これでsolverが間違えていないことがわかります。

デコード

これでsolverが間違えていないことが確認できました。 ①の値を②の値に変えて実行してみます。

1
2
> python solve.py
D1d_y0u_$01v3_C0s70m_X0r?

上記のような文字列を取得しました。

デバッグ

取得した文字列が本当に正しいのか、確認したいと思います。 その際に用いるのがx64dbgです。

ブレークポイントをWinMainの中でDecodeRoutineを呼び出す前に当てます。 そして、ブレークポイントを当てたところまで実行します。

VirtualAllocで確保したメモリにはリソースから取ってきた文字列が格納されているので、②に置き換えます。 また、DecodeRoutineの第二引数で文字列の長さを渡しています。第二引数を渡すときに使われるレジスタはRDXです。 なのでRDXの値を0x14から0x19に書き換えます。 それで実行すると、solverから取得した文字列と同じ文字列がポップアップしました。

おまけ

ファイルにパッチを当てるバージョンを説明します。 この方法をsolverを作成することなく、実行するだけで目的の文字列を取得することができます。

概要

これはいっその事リソースに格納されている値そのものを書き換えてしまえばいいのではという考えです。 リソースの値もファイルのどこかしらに格納されているのでそれを書き換えれば実行するだけで結果が得られるのです。

Let’s go

やっていきます。 まずはHxDの目的の値がどこにあるのか探します。 0x293Cの位置からあることがわかりました。 次にリソースに使われている構造体があると思ったのでググります。 すると3つの構造体があることを知りました。

  • IMAGE_RESOURCE_DIRECTORYではリソースの数の情報などを扱っています。
  • IMAGE_RESOURCE_DIRECTORY_ENTRYではリソース名とエントリーがディレクトリなのかなどを扱っています。
  • IMAGE_RESOURCE_DATA_ENTRYではリソースの先頭アドレスとサイズなどを扱っています。

始めは.rsrcの後ろに②の値を付加して、先頭アドレスとサイズを書き換えます。 そして、.rsrc以降にあるセクションテーブルの書き換えと、DataDirectoryの書き換えだけだと始めは考えていました。 しかし、.rsrcより後ろにあるセクションでRVAがあったら大変だと気が付きました。

他の方法がないか①を眺めていると、この値の後ろに0x00がいくつもあることがわかりました。 その数は(0x19-0x14)以上は確実にあるので、そのまま書き換えても大丈夫だと思いました。 なので、①を与えられた文字列に書き換え、サイズも0x14から0x19に書き換えました。 それで実行するとD1d_y0u_$01v3_C0s70m_X0r?の文字列を取得できました。

また、ファイルを書き換えてくれるコードを書いたので参照ください。

問7

配布ファイル内の7_sc_spreadsheets.001は、NTFSボリュームのddイメージファイルです。イメージファイル内には複数のExcel形式のファイルが存在します。以下の問題について、必要であれば解析結果から得られる見解も含めて回答してください。

問7-1

これらはあるファイルが起点となり、変更が加えられたファイルです。起点となったファイルの入手元を回答してください。


文章中にNTFSのddイメージと書かれていたので私は最初にAutopsyへインポートさせました。 すると、Book10m.xls_Zone.Identifierのファイルが存在していました。 このファイル内を確認するとChromeからインストールされていることがわかりました。 ダウンロード元も確認することができました。 よって入手元はhttps://www.kazamiya.net/files/ccba209a4d0c139e1437781932409ccf/Book10.xlsになります。

https://soji256.hatenablog.jp/entry/2018/09/07/071500

問7-2

復元可能なファイルを含めて、全てのExcelファイルのSHA-256ハッシュ値を回答してください。


Book10.xls : 15702C89D64E3F7DA51D2BD7376B05BF5ECC8E477A2D14221FDE6DA67C714140 Book10m.xls : 12094EA2678D9750E482CD9B3BD2FA924647C4101F7B280A3F9AC0E9A38D7B47 Book10p.xls : 921E84D44AC6AC57F49C9A25B9E6AD60ACF6698643E7167A29970B290F809427 f0000000.xls : 020B9E009135573EF32433135457292F5C9C706CCA22B3785AC73A20707AF473 f0000048.xls : 12094EA2678D9750E482CD9B3BD2FA924647C4101F7B280A3F9AC0E9A38D7B47

問7-3

これらのファイルはどのような順番で、どのような変更が加えられたかを、できるだけ具体的に示してください。


Autopsyで作成日時と修正日時を確認することができるので確認した結果が下記になります。

1
2
3
4
5
6
ファイル名 : 作成日時/修正日時
Book10.xls :   2021-03-21 11:44:12/2021-03-21 11:48:01
Book10m.xls :  ‎2021-03-21 11:44:12/2021-03-26 16:06:17
Book10p.xls :  ‎2021-03-26 16:07:09/2021-03-26 16:07:36
‎f0000000.xls : 2021-03-21 11:44:12/2021-03-26 16:06:08
f0000048.xls : 2021-03-21 11:44:12/‎2021-03-26 16:06:17

これより、時刻が早い順に変更が加えられたと想定することができます。 よって、作成された順を下記に示します。

  1. Book10.xls
  2. ‎f0000000.xls
  3. Book10m.xls、‎f0000048.xls
  4. Book10p.xls

Autopsyから5つのファイルをexportして見ていきたいと思います。 よくマルウェアのxlsを解析する時はoletoolsを使って解析するのでこれを使って何か情報を得られないか調べました。 しかし、これとしたいい情報が見つかりませんでした。 唯一気になったのがBook10p.xlsにMicrosoft Enhanced Cryptographic Providerという文字列が含まれていました。 このことからBook10p.xlsは難読化されたのではないかと考えられます。

次に試したことが、xlsファイルをzipで解凍するとxmlが出てきてそれの書き込み順でどれが変更されたのか予想できると思いました。 しかしこれも何も出ず、手詰まりになりました。

Book10m.xlsと‎f0000000.xls、‎f0000048.xlsは作成者がtestユーザだが最終変更者はforensicsユーザになっています。

問8

ファジングは大量の入力を生成してプログラムを実行、結果を観測しプログラムがクラッシュしたり正常終了しなくなるような入力を発見する手法です。

しかしプログラムが異常な動作をする度に全ての入力を保存しているとキリがありません。例えば「開き括弧”(“に対応する閉じ括弧”)”が無い場合にクラッシュする」ようなバグの場合、ファザーは”(“, “((“, “(()”…. 等の膨大な数の入力をクラッシュ入力として保存してしまいます。発見された入力が数万個もあるのに蓋を開ければ全て同じ単一のバグを引いていたようでは解析も徒労に終わってしまいます。

そこでAmerican Fuzzy Lop (AFL)というファザーは独自のアルゴリズムを用いて、同一のバグに対するクラッシュ入力は1つしか保存しないようにしています。

https://github.com/google/AFL/blob/fab1ca5ed7e3552833a18fc2116d33a9241699bc/afl-fuzz.c#L3302

AFLのソースコードを読解し、当該アルゴリズムの概要を「afl-gcc」「エッジカバレッジ」「ビットマップ」という用語を用いて説明してください。


afl-gcc: gccのラッパーでaflをコンパイルする際に使われる専用のコンパイラ エッジカバレッジ: 分岐条件の真と偽の両方を通っているかを判断する ビットマップ: 画像の保存などで使われるフォーマット

afl-gcc はgccのラッパーでaflをコンパイルする時に使われる専用のコンパイラーです。 afl-asでjmp命令以外の時にその前にtrampoline_fmt_64アセンブリかtrampoline_fmt_32アセンブリを挿入することでエッジカバレッジの計算をできるようにしています。

今回、URLによりif (!has_new_bits(virgin_crash)) return keeping;の行が指定されていたのでまずはhas_new_bits関数から読んでいきます。 第一引数でvirgin_crashという変数を渡しているのでそれが何かを調べると、setup時に0xffでサイズ分を埋めていることがわかりました。 おそらくこれはクラッシュの起きたpayloadを格納する変数だと思います。 has_new_bits関数の最初にtrace_bits変数が使われていたのでhas_new_bitsを呼び出す前にsimplify_trace関数にtrace_bitsを渡していたのでそこを調べると、trace_bitsに実行したカバレッジがビットマップで格納されていました。 while文でMAP_SIZE»2だけループしています。 その中で(\*current == 0 && (\*current & \*virgin) == 0)でなければcurrentとvirginをインクリメントして次へいき、真であればさらに(ret < 2)であるか比較しています。 真であれば((cur[0] && vir[0] == 0xff) || (cur[1] && vir[1] == 0xff) || (cur[2] && vir[2] == 0xff) || (cur[3] && vir[3] == 0xff))が真であればretに2(新しい情報が含まれている)の値を入れ、偽であればretに1(カウントを更新)の値を入れています。 さらに、virginにcurrentを反転した値とvirginの値をandした値を代入しています。 最後にvirginとcurrentをインクリメントしてwhile文の条件に戻っています。 has_new_bitsは最後に(ret && virgin_map == virgin_bits)を比較し、真であればbitmap_changedに1を代入します。 これはファイルと同じか確認しているのだと思います。 それで、retを返します。 つまり、virginとcurrentを比較し、既に保持してる情報内にcurrentが存在した場合はvirginを更新しないようになっています。

まとめ

私が得意としているPEファイルの解析が問6で出たので苦なく解くことができました。 問7と問8は全然わからなかったのでとりあえず書いたって感じです。 問7なんか途中で諦めているので、これでセキュリティキャンプに受かったのはラッキーだったと思います。

This post is licensed under CC BY 4.0 by the author.