※本記事は2016年8月25日に米国で掲載されたブログ記事の抄訳を基にしています。

脅威リサーチャーのコミュニティの皆様には、Unit 42セキュリティ コンテスト、LabyREnthにご参加いただきありがとうございました。コンテストが終了いたしましたので、いよいよ各種目の解答を発表いたします。週に1種目ずつ解答を公開しますが、今回はUnix種目です。

Unix 1課題: レイヤーの上にレイヤーが、そのさらに上に...という知恵

課題作成者: Richard Wartell @wartortell

perlスクリプトが与えられています。このスクリプトは、実行されると何をしている最中なのか表示した後で、stahp(ストップ)と告げて終わるようになっています。

$perl bowie.pl
You’re doing a thing…
AAAA
stahp

このスクリプトのソースを見てみると、ネストされたif else条件文が連続しています。ネストの各レイヤーにおいて、スクリプトはユーザーに入力を要求していますが、この入力が下位のif条件で使われます。if条件が真であればbase64チャンクが新たに復号化されて$a変数の末尾に追加されます。

print “You’re doing a thing…\n”;
my $input = <STDIN>;
$input = trim($input);
if ($input eq (chr(5156 – 5035) . chr(-4615 – -4716) . chr(3162 – 3047))) {
$a = $a . MIME::Base64::decode(“…”)

これが何回も繰り返されて、ついにはbase64ブロックを評価するif条件に至ります。

eval MIME::Base64::decode(“…”)

pythonを使ってこれらの各ブロックをbase64復号化することができます。評価対象のブロックを復号化すると、このブロックには、復号化されて‘$a’変数の末尾に追加されているブロックと、それとは別に評価されるブロックがもう1つ含まれています。これを何回も繰り返します。‘$a’変数の末尾に追加される各ブロックをつかまえて復号化しファイルに書き出してみると、jpgが1つ得られますが、その画像に手がかりが含まれています。

from base64 import b64de

a = b64decode(“R0lGODlh2
a = a + b64decode(“KmZRg

a = a + b64decode(“7w3jz
f = open(“out.gif”, “w”)
f.write(a)

この課題に取り組んでいたとき、私は手操作で各ブロックを復号化しては書き出すという作業を、手がかりを得ようとして絵が十分な形になるまで続けました。


Jeff Whiteがbashのワン ライナー(1行プログラム)を使って完全な解答を得ましたが、このワン ライナーは実にみごとなものです。

cp bowie.pl bowie_1 && for i in $(seq 1 50); do grep -E “\$a|eval” bowie_$i |cut -d”\”” -f2 |base64 -D > bowie_expr $i + 1 ; done && for i in $(grep “\$a” bowie_1 |cut -d”\”” -f2); do echo $i |base64 -D >> bowie.gif; done && for i in $(find . -size +2k -ls |cut -d”/” -f2 |grep -vE “bowie_1$|bowie.pl” |sort -t _ -k 2 -n); do grep “\$a” $i |cut -d”\”” -f2 |base64 -D >> bowie.gif; done && rm bowie_* && open bowie.gif && echo “D4rkCryst4lz”

Unix 2課題: 溶けたカウボーイたちに宇宙レスリング、これって女子プロレスラー「世界の第9の驚異」じゃないよ。もしかしたら私たちはみんなちょっとサビついちゃっている(rusty)からかもね。

課題作成者: Anthony Kasza @anthonykasza

64ビットELF形式のchallengeというバイナリが与えられています。

$file challenge
challenge: ELF 64-bit LSB  shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=4570af615de39c4912c17a09f9fbf8419368572b, not stripped

まずこれを実行してみます。すると“Let’s party like it’s 1999!”(パーティーをしよう。1999年みたいだから。)というメッセージに続いてexample.comのHTMLコンテンツが表示されます。

 

このバイナリ ファイルに対してstringsコマンドを実行すると、日付、example.comおよびrustに関連する、なかなか面白いものが見つかります。


http://www.example.com
stream did not contain valid UTF-8
1/1/1999%d/%m/%Y
This system is obviously not a 90’s kid.1/1/2000Possible Y2K issue.
What year is it?!
%a, %d %b %Y %H:%M:%S GMT
That Date isn’t RFC compliant
That response was not OK
Let’s party like it’s 1999!!

rust_builtin.c
rust_begin_unwind
rust_eh_personality
rust_eh_personality_catch

次に、IDAでこのファイルを開いて中を見てみます。複雑に一意化された名前を読みやすいように処理すると、かなり大きなmain関数が目に入ります。先ほど見つけた興味深い日付文字列に関して、IDAの相互参照機能を使うことから始めてみます。すると、www.example.comに対するHTTPリクエストが作られ、日付に関してヘッダーがチェックされることが分かります。何をいじくってみればいいのか、これでかなり良いヒントが得られました。

Ubuntu仮想マシンを仮想のプライベート ネットワーク上にセットアップし、次に別のリンク クローンを作成してそれをサーバにしました。クライアント システム上のhostsファイルを変更してwww.example.comがもう一方のサーバ システムを指し示すようにしました。また、サーバ システム上にはnginxをインストールして簡単なHTTPリスナーを用意しました。

現在の日付でプログラムを実行してみたところ、nginxのレスポンスが表示されました。次にchallengeファイルが言ってきたとおり、日付を過去の1999年に変更してみたらキーが表示されました。

PAN{ThaddeusVenture}

Unix 3課題: 地上のはるか上空から眺めた星はまったく別物に見える

課題作成者: Tyler Halfpop @0xtyh

与えられたELFファイルを実行すると、猫が描画されます。

$./odd8_v1 

それをIDAで開いて、メイン関数でグラフ モードにすると、ノードで鍵が表示されます。

このバイナリは、@xoreaxeaxeaxによるREpsychフレームワークを使用して作成されました。

Unix 4課題: 奇妙な場所で大事なものを発見する場合がある…

課題作成者:: karttoon @noottrak

課題はこの美しい画像から始まります。

お気に入りの16進エディタ、010Editorで画像を開くと、jpgファイルの FF D9後の画像の終わりに大きなBLOBがあることがわかりました。

このBLOBを抽出して、タグで分けられたいくつかのセクションに分割しました。銅のトンネル、金のトンネル、銀のトンネル、水晶のトンネルです。各セクションはbase64に似ていますが、パディングが先頭にあり、=で始まります。010Editorの組み込みリバース スクリプトを使用して、テキストをリバースしてから、010Editorのデコードbase64スクリプトを使用しました。後方にbase64が再度出現するように思えたので、PK ZIPヘッダーに到達するまでこのステップを数回繰り返しました。ZIPファイルの前のファイルに到達するまで、トンネルごとにこの手順を実行してから、pythonを使用して最後のファイルを書きました。

from base64 import b64decode

ci = open(“coppertunnel.txt”, “rb”).read()
co = open(“copperout.zip”, “w”)
co.write(b64decode(ci))

si = open(“silvertunnel.txt”, “rb”).read()
so = open(“silverout.zip”, “w”)
so.write(b64decode(ci))

gi = open(“goldtunnel.txt”, “rb”).read()
go = open(“goldout.zip”, “w”)
go.write(b64decode(ci))

cri = open(“crystaltunnel.txt”, “rb”).read()
cro = open(“crystalout.zip”, “w”)
cro.write(b64decode(ci))

このファイルを解凍すると4つのパートから成るPARアーカイブを取得しました。

treasure.vol306+306 2.par2
treasure.vol306+306 3.par2
treasure.vol306+306 4.par2
treasure.vol306+306.par2

 Googleで検索すると、MultiParがこれらのファイルを処理できることがわかりました。Repair関数を使用して、 chest.zipファイルを復元できました。

chest.zipファイルは暗号化されているzipファイルであるため、元の画像でパスワードを探しました。steghideを試しに使用すると、bards_songが出力されました。これはテキスト ファイルで、より早い段階から役に立ったと思われる適切な指示と、zipファイルのパスワードが含まれていました。

steghide.exe extract -sf labyrinth_entrance.jpg
Enter passphrase:
wrote extracted data to “bards_song”.

Over the hills and through the grass
By dawn of light in the mountain pass
The goblins treasure awaits the steadfast
Walking in REVerse, the eye opens as you go past
A smell leads you onward, luring you to follow
At the end of each tunnel, a PARt of treasure in the hollow
Combine them to find a door hidden by rhyme
Opened once with the words “aintnobodygottime”

これによりchest.zipを解凍できるようになり、その結果、jareths_mazeという名前のMachOファイルを取得しました。このファイルをVMで実行すると、以下の恐ろしいアスキー アートが表示されました。

からかわれたままにしておくことはできませんので、このファイルをIDAで開く必要がありました。メインをデコンパイルすると、よりふざけた内容になりましたが、メッセージが変わりました。

そして表示された以下の関数名の文字列に圧倒され、少し驚きました。

このプログラムは、これらの関数実行後にアスキー アートを印刷しました。最初のメッセージが正しくないこと、また最後のメッセージが正しくないことがわかりました。各関数はメッセージ内の1文字を上書きしており、関数のグループは関数のbグループが書き込んだものによって上書きされていました。

IDAを使用して各関数にジャンプし、a関数を使用した場合にメッセージで想定される文字の序数を取得してから、pythonを使用して、その序数を文字に変換し鍵を取得しました。

PAN{D4nKkry5t4l}

Unix 5課題: あなたのファイルはすべて私たちに帰属している

課題作成者: Tyler Halfpop @0xtyh

krypto.dangerと呼ばれるバイナリが与えられています。ファイルを起動すると、それが64ビットMach-Oであることがわかります。

file krypto.danger
krypto.danger: Mach-O 64-bit executable x86_64

Mach-Oを調べる場合の適切な第1ステップは、MachOViewのosxreverserのブランチをチェックして、PEファイルの場合と同様に、さまざまなMach-Oヘッダー情報と文字列を調べることです。

次のステップでは、バイナリを実行して、それが何をするのかをさらに詳しく把握します。信頼されていないバイナリを仮想マシンで実行する必要がありますが、忙し過ぎるほどMr. Bigglesworthの写真を目にすることに悩まされます。それを実行すると、Mr. Bigglesworthの写真は暗号化され、.laby拡張子が付加されます。脅威を与えるイッカクもポップアップされ、皮肉にも「Congratulations! All your pngs are belong to us Pay us all the moneyz – PANW GSRT (おめでとうございます。あなたのpngはすべて私たちに帰属しています。すべての金額(moneyz)をお支払いください – PANW GSRT)」と表示されます。Mr. Bigglesworthではありません。これは戦いです。

IDAでバイナリを開くと、見づらくマングルされたSwift名がすべて表示されます。

SwiftDemangというIDAスクリプトを使用すると、Swift名をデマングルして、それを読み取りやすくすることができます。スクリプトの実行後は、多少容易に関数名を見ることができます。

関数をデマングルすると、krypto_dkryptやkrypto_ekryptなどの興味深い関数がいくつか見つかります。krypto_dkryptは、dekrypt引数がある場合に呼び出されます。それを使用して、Mr. Bigglesworthを取り戻すことができるかどうか確認してみましょう。

これで、Mr. Bigglesworthが再び表示され、私たちは課題の解決に戻ることができます。dkrypt関数とekrypt関数はどちらも、get_pwという興味深い検索関数を呼び出します。この関数は、SwiftDemangによって追加されたコメントで確認できる文字列を返します。

get_pwを見ると、その下にRNCryptorという興味深い検索文字列が強調表示されています。RNCryptorをGoogleで検索すると、AES-256のオープン ソースSwiftバージョンを見つけることができます。これは、その後、ファイルを暗号化または復号化するために使用されるパスワードを暗号化するために使用されています。

IDAでリモート デバッグを設定して、get_pwからの戻り値を解読すると、0x100608101のRAX内の戻り値によってポイントされた文字列に課題の鍵があることがわかります。

Unix 6課題: 適切な言語を発行する

課題作成者: Richard Wartell (@wartortell)

この課題では、サーバとクライアントのバイナリが与えられており、どちらもMach.Oファイルです。クライアント バイナリを実行すると、「賞品」を得るための、4トークン キーの入力を求められることがわかります。

幸いなことに、これらのバイナリは記号が取り除かれていないため、バイナリ内で簡単に興味深い関数を見つけることができます。クライアント バイナリを一見すると、上に示された文字列と、入力した4トークンをサーバに送信しているクライアント ライブラリが示されています。

そこで、サーバが送信されたトークンをどのように確認するか調べる必要があります。IDA Proでサーバを開くと、関数main_check_key1、main_check_key2、main_check_key3、およびmain_check_key4が連続して呼び出されていることが簡単にわかります。それらを辿っていくと、これが実際のサーバだった場合に実際のキーが存在する場所に誘導するチェックがあります。

ここから、やるべきことは各トークンが何である必要があるかを解くことだけです。4つの関数それぞれを調査すると、キーに導くトークンを見つけることができます。

  1. 最初のメッセージを文字ごとにチェックします。トークンは「3at」です。
  2. 2番目のメッセージをチェックします。その長さは11である必要があり、各文字はそれぞれ比較するとペアになっていて、その相違点を使用して文字を決定できます。トークンは「chInch1ll@z」です。
  3. 別の4バイト文字列に対してこのトークンの特別チェックを実行します。トークンは「H1gh」です。
  4. トークンの文字を文字列「Fromunda」および数値183と比較します。トークンは「F183r」です。

サーバおよびクライアントをローカルで実行できることから正しいトークンを取得したことがわかります。以下が表示されます。

代わりに指示ファイルに格納されているIPに接続し、4つのトークンを入力すると、次のキーを取得できます。

PAN{th@Nk5_m4r1ha_U_s0_n1c3}

Unix 7課題: コードを解いてリンゴを入手する

課題作成者: Tyler Halfpop (@0xtyh)

今回、RedDeliciousというOSXコンパイル済みアプリケーションを渡されました。まず、単純にこれを実行しました(賢くありませんが、自分が賢いと言ったことはありません)。次のウィンドウ ポップアップが表示されました。

ウィンドウにテキストを入力し、「Submit」(送信)をクリックしました。すると「Try harder…」(もっと頑張れ   )というメッセージが表示されました。このことから、成功メッセージを得るためには正しいキー、できればこの課題のキーを送信する必要があると想定しました。ここで、バイナリを逆アセンブルし、「Submit」(送信)をクリックしたときに何が起こるのかを確認しようとしました。

IDAで、最初に気付いたことは、ほとんどの関数が「__T」で始まっているということです。このことから、Swiftのコンパイル済みバイナリを相手にしているとわかりました。「__T」はSwiftのすべての記号の接頭辞だからです。Swiftは、醜く、非常に長い関数名を作ることに優れているため、これらひどく醜いものに労力を割いて無駄な時間を過ごすのではなく、間違いのキーを入力したときの「Try harder…」(もっと頑張れ   )という文字列の手掛かりに注目しました。文字列のウィンドウを観察し、その関数への参照を発見し、そこから戻りました。

ここで、文字列「Try harder…」と「C0ngr4tz!」の両方を参照する同じ関数を見つけました。ここで、自分は正しい道を進んでいるとわかりました。これらを参照する関数は以下のようなものです。

コードフローが2つに分かれる下部で、各パスには異なる文字列参照を含む同じコードがあります。ここで、キーチェック関数に到達したことがわかりました。さあ、これをたとえば「C0ngr4tz!」にする方法を解きましょう。

Swiftのコンパイラは、C++と非常によく似た方法で変数を処理します。つまり、「this」への参照を引数として関数に渡し、これを使用して他のクラスリファレンスを検索します。このクラスのクラス ジャンプ テーブルはNSViewControllerで0x100008090にあります。関数で使用される呼び出しを修正するためにこれが必要になります。Swiftのオブジェクト指向コードおよびARC(Automatic Reference Counting)をすべてクリーンアップした後、この関数をより簡潔な疑似コードにできます。

こうすることによって、何が進行しているかを非常に明確に理解できますが、これらの関数それぞれを確認し、何をするのかを理解する必要があります。分析後、次の異なる関数を実行していることがわかりました。

  • bb64 – 渡された引数をBase64エンコード
  • xxor – 入力したキーを使用して渡された文字列をXOR

よって、簡単なpythonスクリプトを書き込むとキーを取得できます。

 

キーは次のとおりです。PAN{My_m0th3r_told_m3_2b3_w4ry_of_F@uns}

以下にコメントを入力してUnixトラッキング課題について感じたことを共有しましょう。他の脅威リサーチャーがこれらの課題をどのように解決したかも確認してください。

Unix課題1

Unix課題3

Unix課題5

Unix課題6

Unix課題7