切られたしっぽ

産業廃棄物の投下場所

Software Design に『はじめてのオフェンシブセキュリティ』の内容で連載を寄稿しました!


はじめに

Software Design 2026年4月号を以って『はじめてのオフェンシブセキュリティ』の連載が終了しました。2025年7月号から連載を開始したため、全10回(10か月)分寄稿したことになります。

本稿は『はじめてのオフェンシブセキュリティ』の連載内容を振り返りながら、誌面の都合上書けなかった筆者の狙いや想いなどについてまとめたものとなっております。インデックスを見て Software Design の特定のバックナンバーだけを読みたい方などの参考になれば幸いです。

連載のきっかけとテーマ

筆者は以前にも Software Design で脅威インテリジェンスをテーマに連載を経験した経験があるのですが、そのときのご縁で再び声をかけていただきました。今回のテーマは『近年注目されているオフェンシブセキュリティについて、Metasploit などを活用しながらハンズオン形式で初学者がスキルを獲得できるような内容』とのことでしたので、筆者としては『連載を通して基本的な Boot2Root チャレンジが一気通貫で攻略できるようになること』をゴールに設定して連載をデザインしました。結果として連載期間は10か月と、前回の連載期間6か月と比較してほぼ倍の期間となりました。正直めちゃくちゃ疲れましたし一瞬で10か月が過ぎ去って驚いています。

また、今回の連載では新たな試みとして、ハンズオン環境に NFLabs. さんが提供している実践型サイバーセキュリティ学習システム『Purple Flair』*1のベータテスト版を使用させていただくとともに、連載の監修を行っていただきました。
info.purple-flair.nflabs.jp


Purple Flair を活用する大きなメリットは、読者の方々がハンズオンの環境を一切用意する必要がないという点です。従来まではペネトレーションテストのハンズオンを行う場合、VMWare などの仮想化ソフトウェアを導入したうえで Kali Linux といったペネトレーションテスト用の環境を用意する必要がありました。初級者以上の方にとっては当たり前のことかもしれませんが、初学者にとってはまずこのハードルを越えることが困難です。

それに加えて、ペネトレーションテストの学習をする際はエクスプロイトコードを閲覧したり必要なツールをWebサイトからダウンロードする場面も求められます。仮想化ソフトウェアを利用していたとしても、企業や大学の研究室などでそれらのリソースにアクセスした際にネットワーク境界の機器やEDRでアラートがあがるという問題もありました。

Purple Flair はペネトレーションテストのターゲットとなる環境に加えてペネトレーションテストを実施するための Kali Linux 環境も別途用意してくれるため、これらの障害をすべて取り除けるうえに Kali Linux の操作自体はWebブラウザから行うことができます。これは初学者にとって参入障壁を下げる存在となったうえに、環境差分も生じにくいため「なんかわからないけど動かない」といった事態も回避できました。ハンズオン環境として一般では知られていないものの導入と紹介を行ったので世間からの反応が怖かったですが、インターネットからは簡単に学べることに対するポジティブな意見が見られたので、個人的にはいいアプローチだったのではないかと思います。

連載のアジェンダとキーワード

連載の概要は以下となります。ざっくり分けますと第1回が理論編、第2回から第8回までがBoot2Root基本編、第9回と第10回が応用編です。

回数 タイトル 概要
第1回 ようこそ、オフェンシブセキュリティの世界へ! なぜ『オフェンシブセキュリティ』が求められているかの説明や、本連載で扱う"オフェンシブ"のスコープについて定義しました。また、Shodan を利用して世の中の脆弱な機器を探索してみました。
第2回 ペネトレーションテストに入門してみよう! Boot2Root チャレンジについての説明と、ペネトレーションテストの基本である列挙(Reconnaissance) について nmap の出力結果を見ながら学んでいただきました。
第3回 Metasploit を使ってサーバに侵入してみよう! 第2回の列挙で発見した脆弱性に対して、著名な Post-Exploitation tool である Metasploit Framework を使用して侵入してもらう方法を紹介しました。
第4回 インターネットからエクスプロイトコードを探して使ってみよう! エクスプロイトコードを公開情報から収集して環境に合わせてカスタマイズする方法や、Boot2Root で多用する Reverse Shell の原理とその確立方法について紹介しました。
第5回 特権アカウントになってターゲットを掌握しよう!(Linux編) linPEAS を用いて権限昇格につながる脆弱性を発見し、エクスプロイトまでつなげる手法まで紹介しました。
第6回 Boot2Root に挑戦してみよう! (Linux編) 第5回までのテクニックを駆使して、Linux OS のマシンのBoot2Rootチャレンジを一気通貫で攻略してもらいました。(これができるようになるまでが一つの登竜門だと思います。)
第7回 権限昇格に挑戦してみよう! (Windows 編) Linux OS のマシンとは異なるWindows OS固有の権限昇格方法や Reverse Shell の作成方法について紹介しました。
第8回 Boot2Root に挑戦してみよう! (Windows編) MSSQL やトークンといった、Windows OS固有のテクニックが求められるマシンでの Boot2Root チャレンジに挑戦してもらいました。
第9回 内部ネットワークを掌握しよう Mimikatz, SOCKS Proxy, Metasploit Framework を駆使して、最初に侵害したマシンを起点にドメインコントローラまで横展開をする方法について紹介しました。(最も難易度が高いです)
第10回 アンチウイルスソフトを掻い潜ってみよう! Microsoft Defender の検知を回避するためのエクスプロイトコード作成方法について紹介しました。

正直なところ、テーマ段階で連載内容を「初学者向け」に設定しておきながら、最後の方に横展開やEvasionまでスコープに含めるのは少々やりすぎだったかもしれないと思っております。ただその一方で Mimikatz でクレデンシャルを窃取したり Defender 回避のためのスクリプトを修正するような体験も一度はやっていただけると Boot2Root の楽しさを実感できると思うので、内容に入れてしまいました。特にアンチウイルスソフトやEDRの回避はこの時代にペネトレーションテストをやろうとするとまず避けては通れない内容(だけど HackTheBox などではあまり取り上げられない)なので、きちんと紹介したかったという気持ちもあります。 *2 初学者にはたいへん申し訳ありませんが、後半の内容は Boot2Root に慣れてきた後にいつか思い出して読んでみてください。

執筆を通しての苦労

以前の連載終了後の後語りでも「たいへんだった(小学生並みの感想)」という内容を書いた気がするのですが、今回は今回でまた別のたいへんさを感じたので備忘録として書き残します。

8ページの限界

Software Design の1回あたりの連載のページ数は、8ページが目安です。分量に換算すると約1万字前後となります。1万字と聞くと「多くない?」と感じる方もいらっしゃるかもしれませんが、実際には画像やスクリプト、コマンドの実行結果も含まれるため実際の文章量は2/3程度にまで限定されます。これがかなり厳しかったです。「ツールの実行後に出力される結果のどこに着目するか」「成功失敗をどのようなコマンドで判断するか」「何が見えていたら成功で何が見えなかったら失敗か」など、ペネトレーションテストで用いる細かいテクニックなどを紹介していくと、ターミナルの出力結果だけですぐにページが埋まってしまいます。Metasploit Framework の使い方の回は特に顕著で、本当は使い方を説明して初期侵入まで進みたいのにオプションの設定や使い方などを丁寧に説明するとそれだけで8ページを使い切る勢いになりました。Boot2Root の回も同様に、一回の連載で一気通貫の内容を丁寧に取り上げることはほぼ不可能です。コードや出力結果まで丁寧に説明することが求められる連載では、「1回で1ツールの紹介」くらいのスコープまでに絞るのが無難だというのが学びでした。

筆者の場合、編集の方に毎回画像や内容を削って圧縮してもらったり、1ページ増やしてもらったりすることでなんとか対応していただきました。誠にありがとうございました。

AI とは違う付加価値をどこにつけるか

これが連載期間を通して本当に頭を悩ませたポイントでした。おそらく初学者向けの内容を書いたり紹介したりしている人の多くは、同じ事に頭を悩ませているのではないかなと思います。実際に本連載の内容も著名なAIサービスに対して問い合わせたり解説を求めたりしたところ、ほぼ似たような回答が返ってくるため「解説なんてなくてもAIに聞けばいいんじゃない?」と言われたらそれはほぼその通りなのかもしれません。AI エージェントに命令をすれば、連載で取り上げた Boot2Root チャレンジもほぼ自動で攻略してしまうでしょう。しかしながら、AI も万能ではありません。連載よりも難しいエクスプロイトに対して説明を促しても参照情報が少なければ全く違う解説をします。そのため、筆者はなるべく「AI では解決ができなかった課題が立ちふさがった場合」に自力で仮説検証ができるような考え方を身に着けられるよう心掛けながら執筆をしていました。具体的には以下のような点を意識してみました。

  • ペネトレーションテスト/Boot2Root の体系的な知識や考え方 (単発の解決策ではなく)
  • ツールが想定したとおりの動きをしなかった場合、何を疑ってどう確かめるかといった言語化があまりなされていない経験則
  • エクスプロイトの動作概要図といったハルシネーションが発生していた領域の説明

色々書きましたが、筆者は決して AI 否定派というわけではありません。筆者が大切だと思っていることは「自分の手で動かして、確かめて、納得するという経験」であって、これをAIのサポートを介しながら体験できていれば御の字です。

また始まる、常に締め切りに追われる生活

1か月スパンの連載は、余裕があるように見えて実はかなりカツカツです。これは『セキュリティコンサルタントの日誌から』の連載時のブログを読んでいただいても確認できるかと思います。
www.scientia-security.org

筆者もかつて連載したときはプライベートの時間はほぼ全て連載のための記事執筆と修正と技術検証に充てていて、終わった直後は『もう連載はいいかな.....』などと考えていましたが、人間は喉元を過ぎれば熱さを忘れるものです。そして2~3年では習慣は大きく変わりません。再び『技術検証 → 執筆 → レビュー → 校正 → 入稿までの修正 → 技術検証』を締め切りドリブンでループする生活が始まりました。特に今回は前回と異なり監修元の NFLabs. さんのレビューを入れていただいたので、締め切りは早めでかつ修正の回数も多くなり絵にかいたような自転車操業をしていました。

筆者は連載期間中の11月に虫垂炎となって1週間強制入院イベントが発生したのですが、このときが一番原稿が落ちるかどうかピンチの期間でした。入院中はスマホ片手にずっと文章の構成と検証項目を洗い出していて、退院と同時に昼間は仕事への復帰、夜中に病院で蓄えたコマンドを一気に打ち続けながら全体の骨組み作成といった気が気ではない作業をしていました。これでも原稿は落ちなかったので人間何とかなるものだなと思いましたが、このような間違った成功体験がギリギリの生活を作っていると考えると何とも言えません、ハイ。

おわりに

人生2回目の連載を振り返りました。やはり長期連載というのは体力的にもメンタル的にも消耗するもので、ようやく一息つける気がします。本連載の執筆にあたり編集、デザイン、校正様々な面からサポートしていただいた技術評論社様にはたいへんお世話になりました。毎回筆者が書くどうしようもない文章と図を修正していただき、心からの御礼を申し上げます。そして、技術監修および Purple Flair を提供していただいた NFLabs. 様、本当にありがとうございました。毎回とても丁寧にレビューしていただいてレビューを見た筆者本人が勉強になっていました。

最後になりますが、本連載はセキュリティエンジニアの方以外にとっては過剰すぎる知識だったかもしれません。しかしながら、オフェンシブセキュリティの知識を通して、日頃紹介されるセキュリティ関連ニュースの解像度があがったり、普段運用しているシステムに対する脆弱性への見方がクリアになっていただければ嬉しく思う次第です*3

*1:https://info.purple-flair.nflabs.jp/

*2:Evasion するための試行錯誤そのものが楽しいという筆者の個人的な感情も入っております(笑)

*3:これをきっかけにプロのペネトレーションテスターを目指したい! という人がいればそれはそれで嬉しいですが

俺の飯のタネだったマルウェア解析の仕事はそろそろAIに奪われるんだろうか

本当はどこかのアドベントカレンダーに寄稿しようと思ったのですが、エントリーしそびれたので一般的な最終日の次の日に投稿させていただきます。

はじめに

皆さんはマルウェア解析やリバースエンジニアリングはお好きでしょうか? 筆者はだいすきです......が、先日管理職になってからマルウェア解析の仕事はほぼお休み中で悲しいです。何はともあれ学生時代からマルウェア解析や CTF (Capture The Flag) の Rev. 分野に触れてきた筆者ですが、ここ1年くらいで解析のやり方そのものが大きく変わってきたなと感じています。それはなぜかというと、皆さんご察しのとおり生成AIの存在です。正直 CTF の Rev. カテゴリは当たりを付けたら後はAIにコード投げて正しい回答を引かせるプロンプトエンジニアリングゲーの領域と化しており、マルウェア解析の局面でもAIと対話する時間が大きく増加しました。AIの力によって解析やコーディングの速度があがるのはとても喜ばしいのですが、その反面「自分のやっていることはそのうち全てがAIに取って代わられるんじゃないだろうか」という考えが頭をチラついたりもします。

現在は多くのアナリストがAIの助力を受けながら解析をしていると思われる昨今、 ObfusC(AI)tion CTF という難読化されたバイナリをAIの力を使って解析するコンテストが今秋 AVTOKYO2025 にて開かれました。

本投稿では ObfusC(AI)tion CTF イベントの様子に触れながら、実際 AI を使うことでマルウェア解析の仕事というのはAIに完全に奪われるのかということを考察していきたいと思います。

ObfusC(AI)tion CTF の様子

まずは AVTOKYO2025 で開催された ObfusC(AI)tion CTF の内容について簡単に紹介したいと思います。

どんなイベントだったのか

ObfusC(AI)tion CTF は AVTOKYO2025 の現地会場のみで行われた CTF チャレンジコンテストです。AVTOKYO は『no drink, no hack.』のキャッチコピーにある通りみんなでお酒を飲みながら楽しくセキュリティについて語る場で、皆さんお酒を片手にセキュリティ関連のイベントを楽しんでいます。
www.avtokyo.org

裏を返すと AVTOKYO に参加しているメンバーはセキュリティ畑に馴染み深い方々がほとんどで、現地で「私セキュリティ全く知りません!」という人に筆者は遭遇したことがありません。そのため本コンテスト参加者は世間一般で見ればある程度コンピュータセキュリティに精通している人たちで構成されていて、公式の発表では合計25名の方が本コンテストに参加していました。競技時間は3時間で、難読化されたマルウェアライクなバイナリを解析する問題4問、検知AIエージェントをバイパスする問題2問の合計6問が用意されていました。

どんなチャレンジが出たのか(Writeup)

すべてのチャレンジを紹介することは分量の都合上難しいので、筆者が現地で解いた難読化バイナリ解読問題 Confuse について簡単に紹介します。Confuse は Windows の実行ファイルで、正解となる入力を与えると FLAG を教えてくれるいわゆる Crackme 問題です。以下、簡単にチャレンジの解法を記述します。

> confuse.exe
usage: confuse.exe <hex-str>

>confuse.exe 12345678
NOPE

Windows の実行ファイルなので Detect it Easy を使って表層解析をしてみると ConfuserEX で難読化された .NET だとわかります。

> diec.exe confuse.exe --heuristicscan
(snip.)
PE32
    Linker: Microsoft Linker
    Library: .NET Framework(v4.8, CLR v4.0.30319)
    Protector: Confuser(1.X)
    (Heur)Protection: Obfuscation
    (Heur)Protection: Anti analysis

なので、まずは難読化を解除するために著名な .NET の deobfuscator である de4dot を使って難読化を解除します。

>de4dot confuse.exe

de4dot v3.1.41592.3405 Copyright (C) 2011-2015 de4dot@gmail.com
Latest version and source code: https://github.com/0xd4d/de4dot

Detected Unknown Obfuscator
Cleaning confuse.exe
Renaming all obfuscated symbols
Saving confuse-cleaned.exe

難読化解除ができたら後は読むだけになるので、dnSpy のような .NET アセンブリエディタ/デコンパイラを使ってソースコードを解析します。

dnSpy のデコンパイル画面

ここまでAIの出番がありませんでしたが、難読化解除の下処理が終わりようやくソースコードがほぼ見えるようになりました。ここまでできればあとは入力文字列の処理部分をAIにぶん投げて解析してもらい、入力すべき文字列を逆変換するコードを書いてもらうだけで終わりですね! ということで GPT-4 に生成してもらい筆者が一部修正したコードがこちらです。

import struct


# ランダムの定数値
uint_0 = [
    0x24DBFF24, 0xAC8CA113, 0x9CB8ACAE, 0x1E04D7B3, 0x41F91C68, 0x1303E0A5,
    0x38CC75B4, 0xBBA8EFD2, 0x9C88C17F, 0x7AF131ED, 0x53A5B255, 0x0D8F6397,
    0xF81AF02F, 0x5FCCA860, 0xE275DD3F, 0x4515512C, 0x9B84B1A4, 0x215BBC53,
    0x7D23AB8D, 0x15864CAB, 0x5D5DD780, 0x14B31E96, 0x53BAD32B, 0x00F94EDF,
    0x64B1C18D, 0x43732A17
]

def rol(val, bits):
    bits &= 31
    return ((val << bits) | (val >> (32 - bits))) & 0xFFFFFFFF

def ror(val, bits):
    bits &= 31
    return ((val >> bits) | (val << (32 - bits))) & 0xFFFFFFFF

def to_uint32(b):
    return b[0] | (b[1]<<8) | (b[2]<<16) | (b[3]<<24)

def from_uint32(n):
    return bytes([n & 0xFF, (n>>8)&0xFF, (n>>16)&0xFF, (n>>24)&0xFF])

def decrypt_block(target):
    assert len(target) % 8 == 0
    out_bytes = bytearray(len(target))
    for i in range(0, len(target), 8):
        num = to_uint32(target[i:i+4])
        num2 = to_uint32(target[i+4:i+8])

        num = (num + uint_0[0]) & 0xFFFFFFFF
        num2 = (num2 + uint_0[1]) & 0xFFFFFFFF

        for j in range(1, 13):
            tmp = num
            num = num ^ num2
            num = rol(num, num2 & 31)
            num = (num + uint_0[2*j]) & 0xFFFFFFFF

            num2 = num2 ^ num
            num2 = rol(num2, num & 31)
            num2 = (num2 + uint_0[2*j+1]) & 0xFFFFFFFF

        out_bytes[i:i+4] = from_uint32(num)
        out_bytes[i+4:i+8] = from_uint32(num2)
    return out_bytes

# 暗号化後のチェック対象バイト列
byte_1 = bytes.fromhex("6f62667573636174696f6e5f636f6e6675736521213f3f21")

# 入力値(逆演算)を生成
input_bytes = decrypt_block(byte_1)

print("Input bytes (hex):", input_bytes.hex())

このsolverの実行結果を Confuse に与えるとOK表示になったため、FLAG 獲得です。CTF とは言いながらも実にマルウェア解析らしい良い問題でした。やはりAI使うと作業を楽できますね。

> python solver.py
Input bytes (hex): e5472d894122c73424c2faee024ba9df9d1998753f085826

> confuse.exe e5472d894122c73424c2faee024ba9df9d1998753f085826
OK
FLAG{e5472d894122c73424c2faee024ba9df9d1998753f085826}

結果、どれくらい解けたのか

さて、チャレンジの内容について簡単に紹介しましたが、皆さんはこれを見て25人中何人くらい解けたと思いますか? 10人でしょうか? それとも solver 自体50行程度で簡単なんで20人くらい解けたと思いますかね? 正解は.......


.........


......


...


1 solve でした! はい、つまり非公式 Writeup を書けるのは筆者だけ状態です。そもそも難読化されたマルウェアライクなバイナリを解析する問題が4問ありましたが、解かれた問題はこのうち半分の2問しかなくもう一方も 1 solve の状態でした。

ObfusC(AI)tion CTF の結果(AVTOKYO 公式 Discord から引用)

筆者はタイムアップギリギリで Confuse を解いてコンテストを終了しましたが、多くの参加者がAIの力を借りながらも1問も難読化されたバイナリを解読できなかったということになります。な、なんだって。。。

AIのパワーだけではなぜ難しいのかという考察

会場には CTFer の方々や筆者の顔見知りのマルウェアアナリスト(いわゆるプロ)もおりが本コンテストに参加している姿を見たので、繰り返しになりますが参加者のレベル自体も特段低いわけではありませんでした。みんなお酒を飲みながらコンテストに参加しているので多少注意力やパフォーマンスが落ちているというのはありますが、なぜAIをフルパワーで使えても時間内に解析が難しかったのでしょうか? 理由を簡単に考察してみようと思います。

最初に何を聞くのか? という問題

この Confuse のチャレンジについては、そもそも「ConfuserEX による難読化処理が行われている => ツールである程度難読化が解除できる」を最初にAIからうまく聞き出せないとほぼ詰みだと思っています。少なくとも、3時間以内に解くのは難しいでしょう。そのため最初の問い合わせが一番重要になるとは思いますが、筆者の見解だとゼロ知識の状態でこの問い合わせをするには Detect it Easy のような表層解析ツールの出力結果をAIに解釈させるか Triage MCP のようなバイナリのトリアージ自体をAIに自動化させておくかのどちらかになるかと考察しています。つまり、この段階からすでにツールや環境を万全に整えておく必要があり、初見でいきなり対応できるかといわれるとかなり厳しめかと思われます。

また、トリアージ自体をAIにおまかせしていても、de4dot 等を使っている場合はシンボルの解決に失敗することもあります。例えば、下図の左側は解決に成功した画面で右側は解決に失敗した場合の画面なのですが、皆さんはここからAIにさらに質問できますかね...? 経験がある人は右側の画面を見て難読化解除失敗の理由を聞けるかもしれませんが、経験がない人だと(あー少し読みにくいけどなんとか読めるか)くらいにしか思わずこのまま茨の道を進むかもしれません。「どのラインまでがツールによる難読化解除の限界なのか」をAIに聞いて評価する匙加減の領域は、解析知識のベースがないと厳しそうに思えます。

左:難読化解除成功 右: 難読化解除失敗

難読化処理の仕組みそのものを詳しく聞かないと完全な solver コードを作れない

Confuse のチャレンジの内部コードには暗号化の処理が含まれており、処理の中にはいくらか定数が使われていました。例えば、下図の uint_0 などがそれに該当します。

定数なんてその値を参照してあげれば終わりでしょ......と思うかもしれませんが、AI に dnSpy でデコンパイルされたコード全てをそのまま与えても該当の uint_0 の値は読み込めず、解析や solver コードの作成に失敗します。これは ConfuserEX の難読化によって定数がリソース領域に埋め込まれていることが起因しており、定数値そのものを取得したければ以下のような PowerShell スクリプトなどを書いて解析者が独自で抽出し、AI が作成した復号コードの適切な箇所に値を埋めてあげなければいけません。

> $bin = [System.Reflection.Assembly]::LoadFrom("confuse-cleaned.exe")
> $type = $bin.GetTypes() | Where-Object { $_.Name -eq "Class5" }
> $field = $type.GetField("uint_0", [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Static)
> $field.GetValue($null)
618397476 # この値が python の solver コード中にある uint_0 の最初の値 0x24DBFF24 に該当します
2894897427
...
(snip.)

つまり難読化解除とデコンパイルが終わった後でも、難読化手法をAIに伝えたうえで解析時の特殊な処理の部分を聞き取りしなければ解けません。これがかなり難しかったです。筆者は ConfuserEX の難読化の仕組みを知っていたのでここをAIに聞くことはなかったのですが、後日改めてAIから引き出せるか確認してみたところ、ConfuserEX の仕組みや抽出の方法論などについてかなり詳しく調べてプロンプトを作り上げないと、上記のような抽出スクリプトまで提供してくれませんでした*1。「AIくんこのコードから復号スクリプト書いて」ではなく、こちらが難読化の仕組みや抽出方法などを理解したうえで指示をするか、AIとの対話を通して短時間で指示役が成長しないと solver の完成までもっていけないというのが、初心者が解析するうえでの一つの壁になっているかと思われます。

ハルシネーションを補正する知識と能力の重要性

最後の内容も、先ほどの「仕組みそのものを知らないと難しい」というものに近いです。Confuse の暗号化処理部分は既存のアルゴリズムである RC5 をベースに uint_0 に該当するランダム定数が処理中に混入しているいわゆる カスタムRC5 といえるような実装になっています。これは ConfuserEX のソースコードを見ても、 RC5 の処理に近いけれども手が加えられている部分の処理があることが確認できるかと思います。

github.com

つまり、solver としては RC5 をベースにしてカスタム部分を逆変換できるようなコードをうまく書く必要があるのですが、モデルによっては何度も逆変換コードの作成に失敗します。お金を積めば精度がよくなるのかもしれませんが、少なくとも無料で使える範囲での ChatGPT や Gemini などでは RC5 と判別してくれるけれどもカスタム部分が反映されていなかったり、難読化処理として判定してくれたコードが実は数行分必要な処理だったり、暗号化処理から復号処理に戻す逆変換部分の処理順がめちゃくちゃになったりと、求める solver コードが出てくるにはかなり対話の積み重ねが必要でした。最終的に筆者の場合は完璧な solver を作成することは諦めて人の力で solver の微修正を行う方針にしたのですが、これはデコンパイラや難読化ツールのソースコードと照らし合わせながら矛盾する箇所を見極める力がないとできません。結局のところ、無課金勢にとっては最後の最後で己の実力で解析結果とにらめっこする力が試されるかなと思われます。

おわりに

ObfusC(AI)tion CTF のイベント結果をもとに、マルウェア解析の仕事が誰でもできるものになるのかというのを簡単に考察してみました。結論を言ってしまうと、現段階のAIを使っても知識のあるマルウェアアナリストの作業の一部を代行してくれることがメインで、経験が浅い人が使っても見違えるほど強くなれるような存在ではないように思えます。ただし AI の進化速度が速すぎるので、あと2~3年したらこれらの課題もクリアしてほぼ全自動で解析できる日が来そうな気がしています。そしたら多くの企業の CSIRT は AI で Deep なインシデントレスポンスをできるようになっていい世の中になりますね。

このようにマルウェア解析に関わる業務も様々な部分がAIによって代行され業務負荷が減った反面、筆者としては少し物足りなさを感じています。筆者が解析で好きだったことはアセンブリから少しずつRATの命令パターンや通信のフォーマット特定して、そこから復号スクリプトを作ったり解析を自動化することだったのですが、そのあたりの楽しい部分はほぼ全てAIが担うようになってしまいました。かつて好きだった数日かけて復号や解凍処理を解析をするような時間は失われ、その変わりにプロンプトエンジニアリングやAIが生成したコードの手直しといった、筆者にとっては「物足りないなぁ」と感じる作業が増えたのです(こんなことを書いている時点で老害だとは思いますが....)。筆者自身の専門性が失われたわけではなく、マルウェア解析の専門性自体は依然として高度なので仕事としては残ると思いますが、「もう少しだけ AI くんの成長速度落ちて、楽しい部分の仕事が残っていてくれないかなぁ」と思う今日この頃です。

*1:中途半端に聞くと「実行すると値を確認できます」といった動的な解決方法しか返ってこず、中途半端な場所で値を参照しても欲しい値が手に入らない場合があり運に影響されます

Fakenet-NG 単体で証明書エラー無くHTTPS通信の復号をやらせるまで

TL;DR

  • Fakenet-NG を改造して、証明書のエラーなく任意の通信先に対して HTTPS の通信を行わせるための備忘録です
  • 実質 MitM をさせる Proxy を作るための話になります

はじめに

マルウェア解析の動的解析環境(Sandbox 環境)を構築する際、TLS/SSL を用いた HTTPS 通信をどのようにして取得して分析可能とするかというのは一つの至上命題です。マルウェアがC2サーバと通信する際に TLS/SSL を使う場合はもちろんそのやりとりの中身を記録したいですし、解析環境検知のためにメジャーなWebサービスに対して HTTPS でダミー通信を発する場合もあります。現代だとほぼ必須の要望といっても過言ではありません。

おそらく最も一般的な手法として用いられるのは、Burp などの proxy サーバを立てて通信を MitM (Man-In-The-Middle) させ、それを INetSim などのインターネットサービスシュミレータにリダイレクトさせることで確立したコネクションの中身を分析する方法ではないかと思われます。このようなマルウェアを動かす端末と proxy兼擬似C2サーバの2台を用いた構成は @soji256 氏や @masaomi346 氏のブログでも紹介されているため、記事を参考にすることで円滑に環境を構築することができるでしょう。
soji256.hatenablog.jp

qiita.com


しかしながら、この手法だと Virtual Machine が2台構成になってチョットリソースを多めにとるという点と、INetSim がかれこれ4年更新がないという点もあり現代風にアップデートした環境を用意したいです。筆者は個人PCで環境を用意しているということもあり、1VM構成かつ執筆時もアクティブなインターネットサービスシュミレータ Fakenet-NG を利用した構成を目指しました。

github.com


Fakenet-NG は mandiant のFLARE teamが作成したツールです。以前筆者のブログ記事でも紹介したので、この投稿を見てくれている方の多くはすでに使ったことがあるのではないかと思います。最近の記事を確認すると一目瞭然ですが、このツールはインターネットサービスシュミレータとしての機能だけではなく、DNSサービス, ルーティング, さらには Proxy としての機能も持たせることができる非常に高機能なツールです。おそらくですが、2024年5月現在インターネットサービスシュミレータとして最も多く活用されているツールではないかと、筆者は推測しています。根拠は、身の回りで使用者が多いからという非常に Low な確証度です。
cloud.google.com

これだけ見ると「マルウェアを動かす端末で Fakenet-NG を動かせば通信はすべてフォワーディングされて localhost にリダイレクトされるし、Proxy を挟むことで HTTPS 通信も覗き見できるし万事解決」と思うかもしれませんが、これだと1つ問題があります。 それはサーバ証明書に関する問題です。

Fakenet-NG は Proxy Listener の機能によって TLS/SSL の handshake を検出した場合 MitM を試みようとはしてくれますが、Fakenet-NG 側のサーバ証明書の変更を試みようとはしてくれません。これは該当部分のコードを見てもらえるとわかりますが、証明書が保存されているパスが直接指定されています。

Fakenet-NG のサーバ証明書読み取り部分のコード

当たり前の話ですが、マルウェアがアクセスしようとしたホスト名とサーバ証明書にある SAN (Subject Alternative Name) の値が異なると証明書の検証でエラーとなるため、自作のサーバ証明書を既存の Fakenet-NG に導入しようとすると、マルウェアHTTPS で通信する宛先すべてを知っている状態で証明書を作りコンフィグに設定しなければならなくなります。特に大量にC2サーバの宛先を保有していて次々と通信先を変えるようなマルウェアにとってはこの手法だと面倒です。


この課題を解決するために、筆者はコードに手を加えた Fakenet-NG をせっせこ作っていました。前置きが長くなってしまいましたが、Fakenet-NG 単体で MitM を円滑に成功させるための変更方法を備忘録として残すのが、この投稿の趣旨となります。また、筆者はネットワーク回りが素人なので、もっとスマートな方法を思いつく方がいらっしゃればコメント願います。

環境構築

はじめに、今回目指すものの全体像だけ先に提示します。やることを率直に書くと『TLS/SSL 通信が発生するたびに自作のCA秘密鍵と証明書から通信先を SAN に詰め込んだ自己署名証明書を作成し、それを参照させる』ようにします。概要を図示ししたものが以下です。

概要図

ここからは詳細な手順を書きます。

0. 事前準備

python のコードを直接いじるので、Fakenet-NGGithub からコード一式を clone し、No installation の項目に沿って必要なモジュールをインストールします。

コードが用意できたら、今度は CAの秘密鍵と証明書を作成して、CA証明書をマルウェアを動作させる端末に『信頼されたルート証明機関』としてインポートさせます。CA秘密鍵と証明書の作成については、筆者は以下の記事を参考にさせていただきました。図も大量にあって初心者にもわかりやすかったです。
zenn.dev

以下、ブログから必要な部分のコマンドだけ抜粋します。

# CA 秘密鍵作成
$ openssl genrsa -out localCA.key 2048

# CSR (Certificate Signing Request) 作成
$ openssl req -out localCA.csr -key localCA.key -new

# CA証明書作成
$ openssl x509 -req -days 3650 -signkey localCA.key -in localCA.csr -out localCA.crt

作成したCA秘密鍵と証明書は、Fakenet-NG を clone したフォルダから見て "\fakenet\listeners\ssl_utils\localCA.key""\fakenet\listeners\ssl_utils\localCA.crt" にそれぞれ配置してください。自己署名証明書を作成する際に利用します。また、CA証明書(localCA.crt)は端末でインポートさせることを忘れないでください。やり方がわからない方は、こちらのブログ記事などを参照していただくとうまくいくのではないかと思います。これで一旦の下準備完了です。

1. 通信先情報の取得

さてここから通信先に合わせた自己署名証明書を作るわけですが、何はともあれ通信先の情報をコネクションの起点となる Proxy Listener から抽出する必要があります。抽出に必要なデータを受け取っている Proxy Listener のコードは以下あたりです。

どうやら Proxy Listener が最初に受け取ったデータが、\fakenet\listeners\ssl_utils\ssl_detector.py にある looks_like_ssl関数で識別される場合にサーバ証明書を差し替えるルートにいくようです。looks_like_ssl の実装は tls handshake を捕まえているようなので、最小限のコード追加を考えた場合、Proxy Listener 視点では handshake から通信先に関する情報を見つける必要がありそうです。

ドメイン(ホスト名)

まずはマルウェアがホスト名で通信する場合です。handshake から通信先のホスト名を特定する方法がわからなかったので、TLS Protocol v1.3 の RFC を確認してみると、ホスト名で通信を行う場合 client hello の Extensions structure に server_name (type 0x0) としてその文字列が格納されているらしいです。

datatracker.ietf.org

実際に client hello をパースするスクリプトを公開してくれている方がいたので試してみると、client hello のデータがきれいにパースされました。なので、client hello のパーススクリプトに server_name 部分もパースして抽出させるようにします。 Extensions structure は別に RFC があるようなのでこちらを参考にデータ構造を見て、パースさせます。

datatracker.ietf.org

上記の公開されたスクリプトに server_name パースを付け加えたコードが以下です。

import struct
import binascii


_int16 = struct.Struct(">H")


def int16(b):
    """
    Return first two bytes of b as an unsigned integer.
    """
    return _int16.unpack(b[:2])[0]


def take(data, count):
    prefix = data[:count]
    data = data[count:]
    return prefix, data


def parse_hello(data):
    """
    Parse TLS 1.2 ClientHello from data, return the extensions as binary data
    or None if not found.
    Likely to raise struct.error or IndexError on error.
    """
    try: 
        header, data = take(data, 7)
        messageId, major, minor, l1, l2 = struct.unpack(">BBBHH", header)

        header2, data = take(data, 4)

        # random
        random, data = take(data, 32)
        # session identifier
        slen, data = take(data, 1)
        slen = slen[0]
        session, data = take(data, slen)
        # ciphers list
        clen, data = take(data, 2)
        clen = int16(clen)
        ciphers, data = take(data, clen)
        # compression methods (should always be an array of length 1, with one 0 element)
        compression_length, data = take(data, 1)
        compression_methods, data = take(data, compression_length[0])
        # extensions
        extlen, data = take(data, 2)
        extensions, data = take(data, int16(extlen))
        
        while extensions:
            _type, extensions = take(extensions, 2)
            length, extensions = take(extensions, 2)
            body, extensions = take(extensions, int16(length))
            if not _type == b"\x00\x00":
                continue
            name_type, body = take(body, 1)
            
            if not name_type == b"\x00":
                continue
            
            server_name_len, body = take(body, 2)
            if body[0] == 0:
                server_name_len, body = take(body, 2)
            return body

        return None
    except Exception:
        return None

かなり雑なのですが、これで TLS client hello に該当するデータを Proxy Listener が受け取った場合、通信先のホスト名が識別できるようになりました。今回は、これを \fakenet\listeners\ssl_utils\parse_client_hello.py に保存します。

IPアドレス

次にマルウェアIPアドレスで直接通信する場合です。そんなもん「IP の Dst header を見れば解決では?」と思うかもしれませんが、Proxy Listener が受け取るデータは IP header が落ちているのでその手は使えません。また、Extensions structure の server_name に必ずIPアドレスも入るとも限らないのでホスト名と同様の手法も使えません。

どうしたものかと Fakenet-NG のコードを読んでいると、どうやら Proxy Listener より上流にいる diverter が IP port forwarding をする際に管理している forwarding 用のテーブルを Proxy Listener にも渡していることがわかります。(IP forwarding 回りのコードを参照)

なので、純粋にfowarding 管理テーブルである ip_fwd_table から取得することで解決させます。コードは以下のような感じです。

ip_fw_table = self.server.diverter.ip_fwd_table
for fowarded, original in ip_fw_table.items():
    if f'TCP/{self.client_address[1]}' in fowarded:
        # self.client_address[1] がフォワード元の port 番号なので、
        # 合致する key があればそれに対応する value が forwarding 前の通信先
        create_cert(original)  

なお、ホスト名アクセスの場合は Fakenet-NG 内部の DNSサーバがすべて自身のIPアドレスを返すようになっているので、 forwarding 前のIPアドレスは自身のIPアドレスになっています。なのでホスト名でのアクセスの場合、同様の手法はとれません。全く違うアプローチをしなけばならないのは面倒ですね。

2. 自己署名証明書の作成

通信先の情報が取得できたので、あとはその情報を SAN に設定したサーバ証明書を通信の度に毎回作ります。大量に証明書ができるのですが、いい方法がこれしか思いつきませんでした。しかし解析環境の場合作られる証明書はせいぜい100程度だろうと考え、たいした容量にもならないと思いこの方法で妥協します。サーバ証明書の作り方については、以下の記事を参考にコードを流用させていただきました。

zenn.dev

最終的に作成したコードは、以下のようなシンプルなつくりです。

  • \fakenet\listeners\ssl_utils\certs にすでに一度作成した通信先のための証明書がないかを確認する
  • なかった場合は、引数に与えらえた通信先を SAN に設定して、事前準備で作った CA秘密鍵で署名したサーバ証明書を作る
  • ファイルを \fakenet\listeners\ssl_utils\certs 配下に置き、サーバ証明書秘密鍵と証明書のパスを tuple で返す
import os
import logging

from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.x509 import load_pem_x509_certificate
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption, load_pem_private_key
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from datetime import datetime, timedelta, timezone

CERTS_DIR = os.path.join(os.path.dirname(__file__), 'certs')
logger = logging.getLogger("CreateCerts")
logger.setLevel(logging.DEBUG)


def create_new_cert(hostinfo, overwrite=False):
    if not os.path.exists(CERTS_DIR):
        os.makedirs(CERTS_DIR)
    
    if isinstance(hostinfo, bytes):
        hostinfo = hostinfo.decode()
    
    key_path = os.path.join(CERTS_DIR, f'privkey_{hostinfo.replace(".","-")}.pem')
    cert_path = os.path.join(CERTS_DIR, f'server_{hostinfo.replace(".","-")}.pem')


    if (os.path.exists(key_path) and os.path.exists(cert_path)) and not overwrite:
        return cert_path, key_path

    try: 
        with open(os.path.join(os.path.dirname(__file__), 'localCA.crt'), 'rb') as fc:
            ca_cert = load_pem_x509_certificate(fc.read())

        with open(os.path.join(os.path.dirname(__file__), 'localCA.key'), 'rb') as fk:
            ca_key = load_pem_private_key(fk.read(),password=None)
            
        server_private_key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=4096,
            backend=default_backend()
        )

        now = datetime.now(timezone.utc)
        csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([
            x509.NameAttribute(NameOID.COUNTRY_NAME, "JP"),
            x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Tokyo"),
            x509.NameAttribute(NameOID.LOCALITY_NAME, "Dokoka"),
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Fakenet"),
            x509.NameAttribute(NameOID.COMMON_NAME, "localhost"),
        ])).add_extension(
            x509.SubjectAlternativeName([x509.DNSName(hostinfo)]),
            critical=False
        ).sign(server_private_key, hashes.SHA256(), default_backend())

        server_cert = x509.CertificateBuilder().subject_name(
            csr.subject
        ).issuer_name(
            ca_cert.subject
        ).public_key(
            csr.public_key()
        ).serial_number(
            x509.random_serial_number()
        ).not_valid_before(
            now - timedelta(days=365)
        ).not_valid_after(
            now + timedelta(days=365)
        ).add_extension(
            x509.SubjectAlternativeName([x509.DNSName(hostinfo)]),
            critical=False
        ).add_extension(
            x509.BasicConstraints(ca=False, path_length=None), critical=True,
        ).sign(ca_key, hashes.SHA256(), default_backend())
     

        with open(key_path, 'wb') as k:
            k.write(server_private_key.private_bytes(encoding=Encoding.PEM, format=PrivateFormat.TraditionalOpenSSL, encryption_algorithm=NoEncryption()))

        with open(cert_path, 'wb') as c:
            c.write(server_cert.public_bytes(Encoding.PEM))
        
        logger.info(f'Create New certs for {hostinfo}: {key_path}')
            
        return cert_path, key_path
        
    except Exception:
        return None, None
    

これを \fakenet\listeners\ssl_utils\parse_client_hello.py に配置して、Proxy Listener から呼べるようにします。

3. Proxy Listener への組み込み

必要な機能ができたので、あとは Proxy Listener 側から作成した証明書を参照してあげます。\fakenet\listeners\ProxyListener.py から上記2つのpythonファイルを import して、ssl handshake 検出後にホスト名/IPアドレスを抽出して自己署名証明書を作成、パスを上書きするだけです。変更部分のコードは以下になります。

(snip.)
from .ssl_utils import create_certs
from .ssl_utils import parse_client_hello
(snip.)

class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
(snip.)
    def handle(self):
        (snip.)
        is_ssl_encrypted = 'No'

        if data:

            if ssl_detector.looks_like_ssl(data):
                is_ssl_encrypted = 'Yes'
                self.server.logger.debug('SSL detected')
                hostinfo = parse_client_hello.parse_hello(data)
                if hostinfo:
                    certfile_path, keyfile_path = create_certs.create_new_cert(hostinfo)

                else:
                    ip_fw_table = self.server.diverter.ip_fwd_table
                    for fowarded, original in ip_fw_table.items():
                        if f'TCP/{self.client_address[1]}' in fowarded:
                            hostinfo = original
                            certfile_path, keyfile_path = create_certs.create_new_cert(hostinfo)
                            break
(snip.)
                listener_sock.start()
                # remote_sock.setblocking(0)

                # ssl has no 'peek' option, so we need to process the first
                # packet that is already consumed from the socket
                if ssl_remote_sock:
                    ssl_remote_sock.setblocking(0)
                    remote_q.put(data)
                else:
                    remote_sock.setblocking(0)
                
                while True:
                    readable, writable, exceptional = select.select(
                            [ssl_remote_sock if ssl_remote_sock else remote_sock], [], [], .001)
(snip.)

ちなみに remote_sock.setblocking(0) 周辺のコード (元の該当コードで言うとこのあたり) を変更しているのは、TLS 検出した場合は ssl.wrap_socket で remote_sock の socket が ssl_remote_sock に wrap されてしまうため、ここでエラーが発生してしまうためでした。今回いろいろ変更を加えてしまったので平常時が動いていたのか定かではないですが、おま環起因ではない場合は HTTPS 通信を Proxy Listener が吸っただけで必ずエラーになるので、改造前の状態だと全く機能していないことになります。。。(まさかね??)

4. テスト

これで必要なパーツがそろったので、証明書エラーなしで通信ができるかテストをしてみます。HTTPS の通信が Proxy Listener を必ず通るように \fakenet\configs\default.ini に書かれた HTTPListener443 の Hidden 設定を True にして Fakenet-NG を起動します。

[HTTPListener443]
Enabled:     True
Port:        443
Protocol:    TCP
Listener:    HTTPListener
UseSSL:      Yes
Webroot:     defaultFiles/
Timeout:     10
DumpHTTPPosts: Yes
DumpHTTPPostsFilePrefix: http
Hidden:      True

Fakenet-NG の起動コマンドは以下です。

$ python -m fakenet.fakenet

起動したら、Webブラウザからホスト名ベースでのアクセスを、powershell からIPアドレスベースでのアクセスを試みてみましょう。エラーなくアクセスできたことが確認できると思います。*1

自己署名証明書の動作テスト


また、Fakenet-NG は終了時にこれまで記録していた通信を pcap ファイルにダンプして Fakenet-NG のホームディレクトリに保存してくれています。pcap を確認してみると、先ほど試行した HTTPS の通信が復号された状態で中身を確認することができました。

ダンプされたpcapデータの確認

これにて、Fakenet-NG 単体でやりたかったことができるようになったので満足です。

おわりに

ほぼほぼ MitM 攻撃の解説みたいな記事になってしました。内容も内容なので、おそらく世界で1, 2人くらいしか参考にならない投稿なような気がしています。

現状だと色々粗があるのでそのままでは PR 書けませんが、もしいろんな人に需要がありそうなら fork したプロジェクトを公開するか頑張ってリファクタして PR 出そうかなと思います。それでは。

*1: curlpython でもCAを参照させるために、環境変数 CURL_CA_BUNDLE に "\fakenet\listeners\ssl_utils\localCA.crt" へのfull pathを入れてあげてください

「2024年も始まったしそろそろマルウェアの勉強を始めるか」と思っている人向けのマルウェア解析ツール入門話

  • 追記と修正
    • 2024/01/09: FOR710 についてはプロ視点で賛否両論あったので表現を変えました
    • 2024/01/09: FLARE-VM の構築部分でも書きましたが、解析環境と普段生活する環境は分離しましょう。VMWare or VirtualBox を使ってください。普段使いの環境にここで述べた解析ツールをいきなりインストールするとAnti-Virusに検知される可能性もあります。

TL;DR

  • 将来的にベンダーレポートやカンファレンス発表レベルでの"マルウェア解析"を想定した話です
  • とりあえず FLARE-VM 環境を作ってインストールされたツールを見る・触るところから始めるといいんじゃないでしょうか

はじめに

最近は引きこもりをやめてときどき外部に顔を出すようになり、マルウェア解析やインテリジェンス関連の話をする機会も増えました。それに伴って、「これってどうやってやっているんですか?」「参考になる本やWebサイトありますか?」といったマルウェア解析に使うツールややりかたに関する質問もポツポツいただくようになりました。

「(もう令和もはじまって6年目になるし、勉強できるプラットフォームや本も大量に転がっているし、筆者への会話デッキの一つとしてみんな話題を振ってくれているんだろうなぁ)」とか筆者は思っていたのですが、先日とあるところでやったセキュリティイベントのアンケートにて「Windows バイナリの Reversing なんて初めてやるし、ツールとかどんなのがあるかわからないし、事前に教えてほしい」といった意見を目にして、意外と適切なツールをググって探してくるのも初学者にとっては難しいのかもしれないと考え方を改めました。
たしかに SEO の仕組みを考えても古い記事は検索上位に出にくくなるし、現在進行形で入門を考えている方向けの記事があってもいいのかもしれないと思い、筆者が利用するツールやそのツール選択の元となっているプールについてダンプしてみます。

内容的には「何番煎じなんだろう......」ってものですが、マルウェア解析のツール選定に関わるエントリーレベルの記事にありつけていない人がいれば何かの参考にしていただけると幸いです。

ちなみに筆者のバックグラウンドは、

程度です。知らないツールやプラットフォームもいっぱいありますし、筆者のやり方は特殊で思想が偏っている可能性もあります。もっと有用・一般的なものがあれば追記いたしますのでコメントしてください。

"マルウェア解析" のスコープと前提知識の明確化

まず"マルウェア解析"の勉強と一口に言ってもかなり主語がデカいので、今回深堀する"解析"の対象をより明確にしておきます。筆者がここで紹介するマルウェア解析のツールというのは、「マルウェアの静的解析からシグネチャを作成したり、TTP を収集したり、帰属の考察をしたり、C2サーバとの通信を復号したりする」ために使うツールを対象にしています。なので、ファイルのトリアージマルウェアEDR などに残すログ、フォレンジックといった観点の話はほとんど入っていません。また、マルウェアとして解析する対象はどうしても Windows PE が多くなるので、oletools のような Office 製品の解析なども一旦省略します。

なので、より抽象的なレイヤーでのマルウェア解析のはなしが知りたい方はマルウエアの教科書を、トリアージからフォレンジックまで幅広く知りたい人は初めてのマルウェア解析を購読してみることを推奨します。

また、解析に必要な事前知識まで話すときりがないので、本ブログでは割愛します。気になる方は pinksawtooth 先生が書いている以下の記事がめちゃくちゃ参考になるので読んでください。
github.com


ちなみに筆者のおすすめの勉強方法は、Practical Malware Analysis (宇宙人本)を一冊やり通すのと Zero2Automated というマルウェア解析における Try Harder 系のトレーニングをやるという2つがオススメです。

慣れてきたらすでにレポーティングされている簡単めな検体を Any.Run から拾ってきて解析してみるのがスキルアップにはよいとは思うのですが、危険と隣合わせなのであまりオススメはしません。個人的な腕試しのオススメは FLARE チームが主催している FLARE-On challenge です。かなり実務寄りの問題が多いのでマルウェアの勉強の実践としては十分なのではないかと思います。
flare-on.com

ツールの選択元(プール) : FLARE-VM

さて、余談が長くなってしまったのですが本題に戻ります。
まず大前提をお話しすると、「マルウェア解析においてツール選定が決定的となるような場面は少ない」というのが筆者の考えです。どちらかというとコンピュータサイエンスの基礎知識やツールに対する練度の方が重要で、いくつかのポイントを自分の使いやすいもので見れればそれで充分ではないかと思います。筆者の感覚で「何かしら自分の手に馴染むツールを抑えていたほうがいいんじゃない?」思うのは、以下の5つくらいです。(より具体的にあげるとフォレンジック関連ツール、各々の言語ごとの解析ツール、モジュールレベルのはなし.......と様々ありますがキリがなくなってしまうので、今回は一般的なマルウェア解析=>レポーティングの流れでほぼ必ず通るものに絞りました。また、pythonVSCode といったセキュリティ関係なく使う系のツールは省略しています。)

大項目を5つあげましたが、各々のツールをいちいち調べて集めてくるのも少々面倒です。なので、解析ツールを学ぶエントリーとしては分析に必要なツール一式が導入されているディストリビューションを利用して、仮想環境マシンを作成し、中に入っているツールを調べて使ってみるのが一番よいかなと思います。ペンテストで言うところの 「まずは Kali Linux 落としてきて中身見てみたら」のニュアンスですね。筆者の一番のおすすめは、mandiant がOpen-Sourceで提供している Windows OSベースのディストリビューション FLARE-VM です。

github.com

筆者も大量のマルウェアアナリストを見ているわけではありませんが、VMWare 製品上に Windows 仮想マシンを立て FLARE-VM をインストールしている方を非常によく見ます。(もしかしたら、Ghidra実践ガイド での環境構築で述べられているのが影響しているかもしれません。) ツールも大量にインストールされるので、公式の Installation instruction を参考にしながら導入してみてください。現在は GUI でインストールするツールが決められるので、とりあえず200個近くあるツールすべてインストールしてみましょう。

画面遷移後に 3時間くらい放置すれば以下のようなデスクトップに切り替わると思います。これでひとまず導入は完了です。(余談ですが、昔の FLARE-VM のデスクトップイメージは水色のMマークでしたが、いつの間にか某アンダーなテイルのメタ〇ンっぽいキャラになってました。いつの間に変わったんだろうか。)

FLARE-VM インストール後のデスクトップ

あとは、デスクトップにある "Tools" ディレクトリを見てみましょう。様々なツールが並んでいると思います。知ってる単語や興味のある単語から子階層を漁って、ツールを調べて触ってみるといいんじゃないでしょうか。


FLARE-VM に入っている中でもよく使うツール

FLARE-VM インストールして中を見てみましょうだとさすがに不親切だと思ったので、筆者自身が中からよく引っ張り出すツールも簡単にまとめてみます。

PEStudio

www.winitor.com

マルウェア解析に限らずリバースエンジニアリングで最も大切なことは「自分が今向き合っているモノは何なのか」のあたりをつけることなのではないかと筆者は考えています。.NET で作られていれば de4dotdnSpy を使って難読化解除やデコンパイルをしますし、pyinstallernexe によって実行ファイル化されたツールであればネイティブコードの抽出を目指す人が多いでしょう。つまり、対象がWindows PEファイルであれば最適な分析手法を割り出すためのワンクッションがあったほうが効率的です。そのためのツールの1種別が表層解析系のツールで、筆者は PEStudio を好んで使用します。

ただし、この項目についてはめっちゃ派閥がわかれると思います。pe-beerCFF Explorer のほうがツールとして使いやすいという人もいますし、malwoverview のようなツールでトリアージは自動化している人、Detect It Easy のような最低限の表層解析ツールで十分の人もいます。

ここまでいろいろ書きましたが、ぶっちゃけ最適な解析ツールを導出することができればなんでもよいと思うので、あとは皆さんが触ってみたフィーリングやレベル感によって使いやすいものを決めてください。筆者はなんで PEStudio 使っているかというと strings の部分が優先度順で並ぶのと全体的にグラフィカルでわかりやすいからです。


IDA Free (Pro)

hex-rays.com

最強です。純粋なマルウェア解析をやっている時間の9割以上がこのツールを触っている時間と言っても過言ではないかもしれません。チラッと目次を見てもらった方には「あれ、デバッガ書いてなくね?」と思った方もいらっしゃるかもしれませんが、現在は x64 のアーキテクチャであれば Free からデコンパイルもできるしデバッグもできるのでほぼこのツールで完結します。つよいです。

IDA Free decompiler & debugger

ただし Free だと x86 や arm まではデコンパイルできないので、そういう場合は Ghidra でディスアセンブル/デコンパイルしながら x64dbg を使うという人が多いのではないかなと思います。(ただやはり Ghidra を使うとデコンパイルの精度やショートカットの微妙さなどが気になるので、できることなら IDA 使いたいなと筆者はよく感じます。あと、バージョンアップのたびにプラグインをビルドしたり python3 がデフォルトで使えないのがあまりにも厳しい。。。)


また、最近なぜか WinDbg を使いますという声もちらほら聞きます。たしかに先日 かえるのほんだな さんが0円というまさかのバグ価格でWinDbg本を公開されていたので、こちらを参考にしながらデバッガの勉強をするのも非常にありな選択肢だと思います。
techbookfest.org

雑にまとめますが、基本 IDA を使う練度を上げて使えない部分は他のツールでカバーするというのが一番実践的だと思う、というのが筆者の主観です。

FakeNet

github.com
FLARE チームが作成したネットワーク通信のエミュレートツールです。デフォルトだとほぼすべてのネットワーク トラフィックインターセプト、リダイレクトし FakeNet 側で指定したカスタムレスポンスを返させることができます。つまり、マルウェア解析の場面ではホストオンリーの環境でもC2サーバの擬似応答を返させることでデバッグ解析を継続させることができるという強力なサポートをが得られるツールです。(リバエン力が高くない筆者にとっては、ここまでの上三つがとりあえず使うツール3強な気がしています。)

試しに FakeNet を起動した状態で googlecurl を叩いてみると、FakeNet からの fake レスポンスを返してくれることがわかります。

FakeNet sample

DNS も解決してローカルホストにリダイレクトさせてくれるので、ここからC2サーバのレスポンスを作りこみ、解析をより深く行うこともできたりします。FakeNet に届いた通信は pcap 形式でdumpしてくれるので、事後分析もしやすいです。総じて優秀なので、デバッグしながら解析している際には脇で動かさない手はないでしょう。

mandiant さんの公式ブログを見るとカスタムレスポンスのつくり方なども書いてあるので、興味がわいた方はこちらを参考にカスタムしてみてください。
www.mandiant.com

Yara

github.com

ファイルに対するシグネチャマッチングのデファクトスタンダードです。多くのベンダー・組織がマルウェアの検知ルールとして Yara rule を公開しており、さらにセキュリティ製品でもこの Yara が導入可能となっているものが多いです。なので、解析で Yara を使わなくても純粋に Yara の読み書きができるというだけでも得をする場面はあるかなと筆者は想像しています。特にマルウェア解析を仕事にしている人だと、解析結果から Yara ルールを作成して、それを組織のセキュリティ製品に組み込んで運用できる状態にしてもらうことをアウトプットの一つにしていたりするのではないかなと思います。
ルールのイメージがわかない方は JPCERT/CC さんが公開しているものなどを参考にしてください。

脇道にそれたので話を解析に戻します。主に解析後の話を中心として Yara を記述しましたが、筆者のトリアージプロセスの一つとして Yara をかけるというのは最初期段階で行います。「シグネチャマルウェアのファミリ判定されれば儲けもの」くらいのモチベーションです。たしかに標的型検体や Pack されているマルウェアなどからはいい結果が得られませんが、ものの数秒くらいのロスにしかならないのでとりあえず JPCERT/CC さんなどのGithubに公開されているルールをかけて損はないと思います。

またシグネチャマッチングとは少々異なりますが、実行ファイルがどんなことをしていそうかというのを推定してくれる capa というケーパビリティツールもあります。
github.com

筆者は自分でルールを作りこむことまではしたことなく mandiant さんが提供するルールを IDA Pro のプラグインから使ってトリアージに活用しているだけなのですが、IIJ さんのブログなどを参考にすると自身でルールを書いてもっと有用な使い方ができそうな気がしています。皆さんはぜひ有効活用してください。
eng-blog.iij.ad.jp

FLARE-VM に入ってないけどよく使うので別途入れているツール

本当は上で終わるつもりだったのですが、なんだかんだ3割くらいは別途ツールをインストールして使っている気がするのでこちらも書いてみます。静的解析で使う細かめの話が多いです。ご了承ください。

FileInsight

俗にいうバイナリエディタです。FLARE-VM にも一時期話題になった午前3時から頑張る人向けのバイナリエディタ ImHex 010 Editorなんかが入ってはいるのですが、筆者の肌感にあわずメインでは使ってません。

代わりに何を使っているかというと McAfee 製の FileInsight というのを使っています。こちらは有志の方がマルウェア解析に特化したような plugin を作ってくれたりしていることもあり、雑にデータの復号とか解凍とかができるのがうれしい点です。バイナリエディタ単体で起動することもあまり多くは無いのですが、振り返ってみると一番使っているのはこれですね。

github.com

あとは、IDA にも Hex 表示の機能がついているので、けっきょくのところそこで見て python 使ってデータいじってしまうことが多いですかね。。。IDA つおい。

Process Hacker

processhacker.sourceforge.io

プロセスが使用しているメモリやネットワークレベルのリソースまで確認できる、プロセスリソースモニターです。
似たようなツールとしてProcess Explorerもありますが、筆者の場合 Executable なメモリを見つけたりそこからメモリをダンプしたりする機能が便利すぎてずっと Process Hacker を使っています。

また、少し余談になりますが、Process Explorer などに代表される Sysinternals tool は有用なものが多く FLARE-VM にも標準でインストールされるため見てみることをオススメです。("Tools -> sysinternals" にあります)
learn.microsoft.com

前にも述べた通り筆者はほぼ IDA を使うばかりで動的解析をしなくなってしまったのであまり使ってはいないのですが、Process Monitor, Autoruns なんかはマルウェアをとりあえず動かしてサクッと挙動を掴むうえで有用なツールだと思います。

直近だと IIJ さんのブログにも Process Monitor を活用した動的解析ツール Noriben のはなしがあるので、いきなり静的ではなく動的から勉強していきたい人は活用してもらえるといいのではないかと思います。(初めてのマルウェア解析 の本を持っている方はここからでも学べたりします)
eng-blog.iij.ad.jp

bindiff

github.com

最近 Open-Source になったことで話題となったディスアセンブルコードの比較ツールです。同じところで何年も働いていると一度解析したことのあるマルウェアの亜種とは何回も戦うことになり、詳細を全部確認すると無限に時間がとられてしまいます。なので、限られた時間で読む部分にあたりをつける、さらにはコードの更新部分を第三者にわかりやすく説明するためにかなり重宝されます。(特に、非エンジニアに説明する意味だと後者はデカいと思います。)

具体的なイメージが見たい方は moly さんのブログを参考にしてください。Ghidra での解析差分をサクッとそしてビジュアブルに図示できることがわかると思います。

morimolymoly.hateblo.jp


ちなみに IDA Pro を使っている方は diaphora という便利なプラグインがあるためこちらを使っている方が多いかもしれません。こちらでも bindiff と同等のことができますが、関数をクリックするだけで IDA の該当の関数に飛んでくれるので非常に親和性が高いです。 Pro がある人はこっちも試してみることをおすすめします。
github.com

BlobRunner

github.com

かなり tips よりのツールだが、「抽出した shellcode をサクッとデバッグしたい。。。」とアナリストならば一度は思ったことがありそうな要望を叶えてくれるツール。Initial Accessでは全部が shellcode でできたようなツールがポンポン降ってくるような昨今ではすごく有用だと思います。

筆者もかつては「指定したファイルを読み込んで、Executable なメモリに配置して、3分sleepして、call する。。。」なんてツールを書いて使っていましたが、BlobRunner だとデバッガでアタッチしたあとにエンターキー押せば breakpoint まで飛んでくれるので利便性高いですね。

OALabs さんは他にも API hashing の解決を補助してくれる hashdb のようなプロジェクトや Youtube channel で解析手法なんかを公開していて非常に有用なので、時間に余裕のある方はチラ見してみてください。(筆者自身も、こういうところから便利そうなツールの情報を盗むことが多いです)
github.com

Hayabusa

github.com

大和セキュリティさんが公開している Windows のファストフォレンジックツールです。「あれ、フォレンジックツールは紹介しないんじゃなかったっけ?」と思った方もいらっしゃるかもしれませんが、筆者は Sigma と呼ばれる SIEM でのログ検出用のシグネチャルールを検証する際に使用しています。

github.com

下記は Sigma 公式 Github の README に載せられている図ですが、Sigma ルールは Sigma rule のコンバーターと何かしらの SIEM (に検証したいログがたまっている状態)がないと有効活用、つまり検証できません。

https://github.com/SigmaHQ/sigma/raw/master/images/Sigma_description_light.png

Hayabusa は Sigma を解釈したうえでローカルに溜まったエベントログをスキャンしてくれるので、コンバーターと実質 SIEM 部分の役割を担ってくれます。便利です。Sigma ルールを書いたり検証したりするジョブに就いている人にとっては、マルウェアをローカルでガチャガチャ動かして検証できるようになるツールなので入れておいて損はないと思います。

おわりに

けっこうコンパクトにまとめたつもりがかなりぐちゃった内容になってしまいました。

もっと体系的に学びたいと思った方がいれば、学生の場合はセキュリティ・キャンプの脅威解析クラス, 社会人の場合はFOR710なんかがいいんじゃないかなと思っていましたが賛否両論あるようなので、現場の解析経験ある人から知見を吸収してみましょう。

最後になりますが、一歩目の踏み出し方がわからない方の参考になったり、何かしら新しい知見が得られた方がいれば幸いです。それでは。

某のフィッシングキットから見る、調査のときに留意したいクローキングのこと

はじめに

フィッシング詐欺は企業にも個人にも迫る身近なサイバー脅威の1つであり、それが占める被害件数の割合はサイバー攻撃全体で見てもおそらく最大クラスです。
大規模な SOC や CSIRT に所属している方にとっては、対応することが多いアラート・インシデントの一つになっているのではないかと思います。
もちろんアラート・インシデントがあれば対応者がフィッシングサイトの調査をする必要が出てきますが、ここで一つ課題になるのは クローキング の存在です。

クローキングは一般的なIT用語なのですでに知っている方も多いと思いますが、いわゆるアクセス元の情報を頼りに表示するコンテンツを変える技術のことですね。
wacul-ai.com

フィッシングサイトもクローキングによってフィッシング用コンテンツと良性コンテンツの表示を切り替えているのですが、問題はこのクローキング技術は対応者・リサーチャー側からは見えないブラックボックスの存在であるという点です。
「User Agent によって動作が変わる」, 「IPアドレスによっても動作が変わる」なんて話も人から聞いたことはありますが、実際にコードを見たことがないので調査も伝聞の知見をもとにやるしかありません。

筆者もこれまではサーバ側のコードを見たことがなかったので勘で調査を行っていたのですが、今回ひょんな機会でフィッシングキットを見る機会があったので、フィッシングサイトが使うクローキング技術をコードレベルで確認してみることにしました。
せっかくの長期休暇中なのでいつもと違う分析もしてみようというモチベもあり、調べた結果を簡単ではありますがメモ書き程度に残します。筆者と同じような境遇にいてフィッシングサイトの調査をする機会があった場合参考にしていただければ幸いです。

免責事項

本投稿はフィッシングキットの使用を促す内容ではありません。よって、フィッシングサイトの構築といった部分には触れることもありません。
あくまで行うことはソースコードをベースに攻撃者が行っているクローキング手法を分析することであり、リサーチャーが効果的にフィッシングサイトを調査するための視点を学ぶことを目的としています。
本投稿内容を悪用目的では使用しないでください。

ことのあらまし

2023年9月の頭、16shop というフィッシングキットを使用していたサイバー犯罪者が海外で逮捕された事件は皆さんの記憶にも新しいと思います。
筆者も下記のTrendMicroさんの記事を見ながらその功績の詳細を追うとともに、簡単にフィッシングサイトを作成できる「16shop」というツールキットについても概要を知ることができました。
www.trendmicro.com

......が、記事の中で一枚の画像が目に留まります。それは Maltego での分析画像でしたが、VirusTotal に16shopのフィッシングキットがアップロードされているようです。

フィッシングキット16shopの分析、トレンドマイクロとインターポールのパートナーシップ(TrendMicro)』, 図5:複数のフィッシングキットが保存されているサーバ より画像引用

なぜキットがアップロードされているか理由はわかりませんが、ツールは海賊版も存在しているようなので、どこかで海賊版を入手した攻撃者がウイルスのチェックをするために VirusTotal にアップロードしたりしたのかもしれません。何はともあれ、`16shop` というキーワードのある zip だけで VirusTotal から見つけることができそうです。

試しに、以下のクエリを試してみると 47 件ほど存在しました。 TrendMicro さんの記事にある命名と同じなので、これらが海賊版(?)の16shopフィッシングキットのようです。

entity:file tag:zip name:16Shop
VirusTotal で16shopのフィッシングキットを search する

せっかく見つかったので、ツールキットを分析して筆者の中でブラックボックス化していたクローキング技術をクリアにしてみることにしました。
これで、勘でやっていたクローキング対策がより論理的に行使されるようになるでしょう。

16shop フィッシングキットの調査

基本情報

今回の分析する対象は以下のキットです。これを対象とした理由は、投稿日時が最も新しかったためです。
www.virustotal.com

ファイル名が『16Shop-Amazon.zip』なので、おそらく Amazon のフィッシングサイト構築用だと思われます。zip の Bundle info にある Latest Content Modification が 2023-05-31 なので、おそらく4か月ほど前に作られたものなのではないかと推測できます。

概要

今回入手したツールの中身を覗いてみると、どうやら php で書かれているツールのようで、index.php へのアクセスからすでに大量のクローキング処理が行われています。画像に見える blocker.php, blocklist.php などがそれに該当する処理ですね。読み込んでいる php ファイルの量が多くすべての処理を列挙していると記事の内容が非常に長くなってしまうため、いくつかにグルーピングして要点だけまとめていきたいと思います。

ディレクトリ構成


余談ですがTrendMicro さんの記事を見た際はツールキットは python で実装されているように見えたので、時代や種類によってキットの構成は一新されているのかもしれません。*1

クローキング処理部分の詳細分析

16shop の場合、フィッシングコンテンツを表示させるかどうかに使っている情報は以下のようです。

  • CLIENT-IP, X-FORWARDED-FOR ヘッダ
  • アクセス元のIPアドレス
  • アクセス元のホスト名
  • アクセス元のISP
  • User-Agent ヘッダ
  • Referer ヘッダ

そこそこあるように書きましたが、実態としては アクセス元のIPアドレス と HTTP header にある User-Agent の2つで、おまけ程度に Referer がある程度です。しかし、IPアドレス部分についてはそこから正規利用者かどうかの判定ロジックが多いのと、そもそもIPアドレスをどのようにして判定するかというロジックがあるため少し小分けにして記述してます。ここからは、各項目を細かく見ていきます。

CLIENT-IP, X-FORWARDED-FOR ヘッダ

大前提として、このフィッシングキットはアクセス元のIPアドレスを取得し、それがbotのアクセスやblacklistに登録されたIPアドレスであった場合はフィッシングコンテンツを返さずブロックするという仕様を持っています。では、そもそもアクセス元のIPアドレスをどのように取得しているかというと以下の部分のコードです。

アクセス元のIPアドレス取得部分

筆者も見て驚きましたが、ロジックがかなりザルです。このコードでは HTTP header に CLIENT-IP または X-Forwarded-For がある場合、それらを真っ先に優先してIPアドレスとして認識します。この先に様々なクローキング処理がありますが、まずは理想に見えるIPアドレスをこれらのヘッダーに入れることから試してみてもよさそうです。

アクセス元のIPアドレス

では理想的に見えるIPアドレスは何かというと、blacklist に載っていないIPアドレスを選択することになります。blacklist に載っているIPアドレスは単発のものもあれば一定のレンジのものもあるので注意が必要ですね。例えば、レンジでのblacklistを見るとTOR や AMAZONIPアドレス帯などは基本的にblockされています。とりあえず TOR からアクセスしてもクローキングに阻まれてしまう可能性は十二分にありますね。

アクセス元のホスト名

ここではIPアドレスからgethostbyaddrでホスト名を問い合わせ、blacklist にないかをチェックします。しかし、なぜか先述べたIPアドレスチェックのロジックではなく $_SERVER['REMOTE_ADDR'] で取得したIPアドレスからホスト名を取得しているので、結局のところアクセス元のIPアドレスはHeader以外でもきちんと偽装する必要がありそうです。

中をざっと見てみると VPN のホスト名などもチェックしていたので、VPN アクセスかどうかもクローキングの対象にしているようです。筆者は NordVPN や ExpressVPN を使いますがどちらもblacklistに入っていたのでVPNの契約バリエーションを増やす必要があるなと再認識しました。

アクセス元のISP

こちらはIPアドレスの取得ロジックで取得したIPv4に対して、Internet Service Provider を問い合わせてblacklistに当てはまった場合弾くロジックです。
中を見ると、Digital Ocean やら Choopa やらリサーチャー側がよく攻撃インフラとして使われているのを見る文字列は見えましたが、OCN などの日本の利用者向けのものは見えませんでした。ここらの Provider は bot ばかりで一般的な利用者がいないという判断なのでしょうかね。

User-Agent ヘッダ

このヘッダも、基本的には blacklist にマッチしたものを弾く仕組みなので、Webで検索して一般人が使用する OS, Browser のものを引っ張ってくれば弾かれることはなさそうでした。もちろん雑に curl したり、python の requests で訪れたりした場合は弾かれるので CLI から検証をする場合は必ず User-Agent を変更しましょう。


Referer ヘッダ

Referer の blacklist は .htaccess 側にその記載がありました。どうやら phishtank からのアクセスなどはここで弾けるようにしている実装のようです。Referer はヘッダに入っていた場合に弾く材料として使われているようなので基本は設定せずとも問題ないとは思いますが、Webサービス経由で調査を行う場合などは考慮する必要がありそうです。

その他、気になったこと

これはクローキングの仕組みとはずれる話となりますが、blacklist などによってbot判定された場合のIPアドレスや理由はその都度記録されていて、そこは「よくできているなー」という感想が漏れました。例えば、以下のコードは antibot[.]pw にIPアドレスを問い合わせて bot かどうかを判定する処理なのですが、bot 判定された場合はそのIPアドレスをファイルに書き出しています。

bot 判定された場合、そのIPアドレスを別途記憶する処理

書きだされたIPアドレスが即座に使われる様子は今回のコードから確認できませんでしたが、おそらくここで手に入れたIPアドレスを利用して、攻撃者が今後のblacklist拡張などに活用するものと思われます。このような処理は他の blacklist 処理のコードでも散見されるため、調査の一番最初できちんとクローキングを意識したアクセスをするかどうかが非常に大切なことがわかりますね。もし弾かれた場合、即座にVPNの切り替えなどを行った方が賢明かもしれません。

余談ですが、コードを最初に見たときは「『一度アクセスしたIPアドレスbot判定されたら blacklist 入りする』といった設定が入っていた場合、とりあえずメールに書かれているURLを全部雑にcurlすることで企業ネットワークの同じ出口にいる従業員の被害を最小限にできるかなー」とか妄想していたのですが、16shop の場合それができなそうなので残念でした。

まとめ

簡単ですが、フィッシングキットの調査をしてブラックボックスだったクローキング技術をクリアにしました。基本的にはクローキングはblacklist方式で行われているようで、フィッシングサイトにアクセスする際には

  • CLIENT-IP, X-FORWARDED-FOR ヘッダ
  • User-Agent ヘッダ
  • Referer ヘッダ
  • 出口となるVPN

を最低限工夫してから初動の調査を行われなければいけないことがわかりました。リサーチャーの場合、攻撃者の環境には分析用の仮想環境から TOR, VPN などを経由してアクセスすることになると思いますが、フィッシング関連のインシデント報告を受けてアクセスした結果それらしいものが表示されない場合、VPN を別のものに切り替えたり調査用の専用線からアクセスするようなアクションが必要となりそうです。

最後になりますが、本投稿はあくまで特定の1つのフィッシングキットを分析したサマリになります。他のフィッシングキットは同じような動作をするとは限りませんし、より入念な準備をしている攻撃者ならblacklist以外のクローキング技術を施しているかもしれません。しかしインデント対応者はその仮説が外れた場合、ブラックボックスな状態から変数を考察して次の手を打つことは必須です。この投稿がフィッシングインデントの対応者にとっての、初動対応を考えるための一助になればと思います。

*1:もしかしたら、この php のものは古い海賊版を改造しながら使いまわしている可能性もあるかもしれません。

Webミリしら人間の OSWA 合格体験記 (2023/08)

はじめに

夏休みに暇な時間ができたので、 OSWA という OffSec. の資格を取得しました。

まだ日本語でのレビュー記事や体験記がなかったので、簡単ですが備忘録を残します。

なお今回はWebセキュリティ資格関連の話をしますが、筆者はWebの専門家でもなければWebセキュリティについては全然詳しくない人間です。頓珍漢なことを書いている場合はご訂正いただけると助かります。

What is OSWA & WEB-200 ??

OSWA は Offensive Security Web Assessor の略で、Offsec.が提供するWeb アプリケーションのペネトレーションテスト資格です。
www.offsec.com

日本人の受講記録は筆者調べだと存在せず、多くの日本人にとって未知な部分が多い資格かなと思います。筆者はLearn Unlimitedを契約しているので、夏休みに様子見ということで受講してみました。

Offsec が提供するWebのペネトレーションテスト資格はWeb-300に該当する OSWE のほうが著名ですが、Web-200 は Web-300 よりもより入門者向けに作られたトレーニングとなっています。具体的に述べると、Web-300 はWebアプリケーションテストのホワイトボックステストに大きく焦点が当てられているようですが、Web-200はブラックボックステストが中心です。そのため、コードに対する深い理解などはいらず基本的なコンピュータサイエンスの知見とファジングの基礎知識があれば十分戦えるという内容になっています。公開されているアジェンダをまとめると、以下のような内容が学べます。

  • XSS (Cross-Site Scripting )
  • SQL Injection
    • RCE までを含む
  • CSRF (Cross-Site Request Forgery)
  • SSRF (Server-Side Request Forgery)
  • SSTI (Server-Side Template Injection)
  • Command Injection
  • XXE (XML External Entities)
  • Directory Traversal
  • IDOR (Insecure Direct Object Reference)

CTF やWebが得意な人なら「かなり基本的な内容しかないな」と感じたと思いますが、ご想像の通りでWeb-200の扱うスコープはかなりメジャーなWeb脆弱性のみなので少々ボリュームが少なめです。そしてCVEベースの脆弱性もスコープ外であり、基本的には構築されたWebアプリケーションの実装上の脆弱性を探すという内容になっているので、とにかく列挙列挙列挙ファジングファジングファジングが重要なゲーミングになっています。そのため、すでにWebの脆弱性診断やセキュリティに関わっている人にとっては少々退屈な話題だと思うので、そのような方は最初からWeb-300を受講することを強くお勧めします。ちなみに、筆者は「へー "/api/user/100" => "/api/user/101" にアクセス試すのってIDORって言うんだー」「XML External EntitiesってXXEって訳すんだー今までXEEって言ってたかもー」レベルだったので純粋にトレーニングコンテンツを楽しむことができました。しかしながら、用意されているラボが現状だとまだ8台しかなく、3日で遊びきってしまったのでボリューム的には不満でした。OSWA 試験が提供され始めたのも2022年(だった気がする)のでまだ発展途上な研修であることは否めず、受講者自体が現状少ないのもそのあたりが相まっているものと思われます。

試験

試験概要

OSWA の試験は OSCP と同じく1マシンからlocal.txtとproof.txtを取得するタイプの試験ですが、実態はOSCPと少々異なります。OSCP はuser shellの取得証明でlocal.txt, privilege shell の取得証明でproof.txtを提出しますが、OSWA はwebのコントロールパネルへの侵入でlocal.txtが, そこからfile system上のファイル読み出しができてproof.txtが得られるような試験になっています。(ここでいう webのコントロールパネル というのは、WordPress の admin dashboard だと考えてもらえればOKです。)

wordpress.com


つまり権限昇格のフェーズは存在せず、HackTheBox でいうところのlocal shellが取得できるまであたりがゴールという感覚です。これは"権限昇格がないので簡単です"という表現ではなく、WEB-200で取り上げているような脆弱性でもRCEにつながらないものを試験に取り上げるための都合だと思われます。具体例をあげると、単純なXSS脆弱性ではRCEにつながらないのでOSCPの試験だと取り上げにくいですが、コントロールパネルへの侵入が第一目標だとcookieをstealする問題として出しやすいという感じですね。なので、OSCP のように "whoami + cat or type proof + ip a or ipconfig" のスクリーションは必要なく、local.txt or proof.txt が映ったBrup SuiteからのスクリーンショットとWeb UIのスクリーンショット2つをレポートに張り付けることが変わりに求められていますね。

https://help.offsec.com/hc/en-us/articles/4410105650964-WEB-200-Foundational-Web-Application-Assessments-with-Kali-Linux-OSWA-Exam-Guide


もう少しイメージしやすい具体的なboxを挙げると、"soccer" というマシンのuser shell取得までをもう少し難しくしたマシンになります。
infosecwriteups.com

このようなマシンが5台分存在し、local.txt, proof.txt がそれぞれ10 pointで合計70 points overで合格です。ボーナスポイントは存在しないので、最低でも4つのマシンでコントロールパネルに侵入できないとアウトとなります。取り扱っている内容が難しくないとはいえ、8割のマシンでexploitを成功させなければならないのでスピードが重要な試験ですね。

試験本番のタイムスケジュール

Web のペネトレは列挙がすごく多いので、筆者のレベルでは時間がかなりカツカツでした。筆者の試験タイムスケジュールは以下で、合格までは15.5時間程度の時間がかかりました。

時間 score 詳細
11:00 0 試験開始.
11:31 10 1台目 local
- - 脆弱性っぽいところは見つかるがexploitまでつながらず一旦あきらめ
14:47 20 2台目 local
- - 列挙しても脆弱性見つからず一旦あきらめ。内心焦り始める。
18:03 30 3台目 local
18:38 40 3台目 proof
19:29 50 4台目 local
19:57 60 4台目 proof
- - ご飯食べて仮眠。3,4台目がめちゃ簡単だったので、"まさかね......" とベットで考えていたことを1台目で試したら通った。
02:23 70 1台目 proof
- - 合格ライン超えたのでレポート作成始め, proofと再現のチェック
08:30 70 ほぼレポートのひな形が完成し一応5台目へ
09:21 80 5台目 local
10:00 90 5台目 proof
10:45 90 2台目の proofも粘ったけど取れず
13:00 レポート提出

1, 2台目のマシンと筆者のメンタルモデルが合わず前半はけっこう焦りましたが、後半はラボやトレーニングの内容をベースにポンポン進んだので内心ホッとしました。悩んで首をひねり続けていても見えないものは見えないので、チートシートを使ってどんどん列挙を続けるのが大切な試験でしたね。ある意味、OSCP のときにやった諦めない列挙の心というのを思い出しました。

github.com

最後1台目のproof.txtだけどうしても取得できず90pointsで終了。スクショの取得漏れが一番怖いので合格点達成時点でレポートを書き、最後に5台目分の内容を追記する形で早めにレポートを提出しました。レポート提出後は別の作問作業などをしていましたが、ピッタリ24時間後に合格通知が来て特に山もなく谷もなくといった具合でWeb-200は修了です。

完走した感想

最後に、いつものように試験のことは書けないので感想を残して終わります。まず試験ですが、OSCPのときと同様に「ちょっと意地悪やなぁ」という問題が多いように感じましたね。Hy3n4 さんのレビューには以下のような文章が書かれています。
medium.com

I would consider myself as a pentester with some decent level of experience. But I have to admit that the time frame in this certification probably makes it even for more experienced pentesters not like a walk in the park.

一言でまとめると「経験者でも楽勝ではないよ」という内容になるかと思います。OffSec. の試験だとツールをただ回すだけではexploitの兆しが見えにくいというのがあり、やはり手動で微調整した列挙や、自分で簡単なコードを書いて試験する必要性が出てきます。CTFで簡単なWeb問をやっていた人なら問題ないと思いますが、bliend SQL injection の検証レベルのコードが書けたほうができることの幅が広がるので、ラボを通してコーディングへの抵抗は少なくした方が賢明です。筆者の場合は、bliend SQL injection からlocalにあるファイルを読みだすスクリプトと、gopher を使ったssrfのURLエンコーディングを二重にするコードは事前に作っておきました。

ここまでネガティブ方面に見えることを書き連ねてしまいましたが、筆者はOSWAにあまりネガティブな印象を持っているわけではありません。ラボや試験で対面したマシンはモダンな作りのWebサイトが多く、やはり実践的なトレーニングができるOffSecの研修はいいなと再認識しました。OSCPのラボやよくある日本の研修だとLAMPという平成の遺産をつかってWebの脆弱性を学ぶことが多いですが、OSWAだと node + mongo やbackendにgraphqlがいるようなモダンなつくりのwebサービスを対象にして遊べるので、「php の webshell 置いて終わり」なんてラボは一切なく非常に楽しかったです。同じような内容は OWASP Juice Shop でも遊べるのですが、やはりマシンが複数ある分Webサービスとしてのバリエーションが多く、起動やリスタートも簡単なOSWAのラボのほうが快適で楽しかったというのは心の底から思います。

owasp.org

HTBの似たようなマシンでも学べはしますが、やはりあちらもshellをとることに特化しているサービスなので、実際のWebサービスペネトレーションという意味ではOSWAのほうが実践的なのではないか、と浅薄ながら思う所存です。


おわりに

Web ミリしら人間だったので、OSWA を通してWeb脆弱性のことを少しは学ぶことができたかなとうれしく思っています。やはり「知識として持っているだけだと実際のexploitで活用できない, 応用が利かない」というのはどのOffSecトレーニングをやっても痛感することで、今回の試験でもそれを学ぶことができたことは一つの実りです。その一方でWeb-200のみだとトレーニングのボリュームは不満に感じる部分もあるので、診断経験などがある人は素直にWeb-300からやるのが無難かなと思います。しかし、セキュリティ何もわからないけれどもペンテスター目指したいような人の場合、OSCPだとスコープが広すぎて覚えることが多くたいへんな面もあると思うので、OSWAから始めてみるというのも一つの選択肢とありだと考えます。最終的なエントリーレベルとしてどちらを選択するかは、自身のキャリアプランと相談してみましょう。筆者の場合、もうちょっとWebに詳しくなるためにも今度はWeb-300を受けますかね。

そのほか、気になることがあれば Xまで気軽にリプかDMを送ってください。それでは。

SOCっぽいことをしている中の人の OSDA 合格体験記 (2023/07)

はじめに

先日 OSDA というOffsec.の資格を取得しました。

N番煎じなブログですが、体験記のリクエストをもらったので自身の記録を書き残しておきます。OSDAの取得を考えている人の参考になれば幸いです。

What is OSDA & SOC-200 ??

OSDA 合格に関わるブログは、すでにたくさんの方がまとめてくださっているためご存じの方も多いかもしれません。まだ試験が始まって1年も経っていないはずなのに早いですね。

レオンテクノロジー様『OSDA(Offsec Defense Analyst)の受験記』
www.leon-tec.co.jp

GMO CYBER SECURITY by IERAE 様『OffSec Defense Analyst (OSDA)受験記』
gmo-cybersecurity.com

nknskn さん『OSDA のケーススタディ
news-nknskn.hatenablog.com

ここでも簡単に説明すると、OSDA は Offsec Defense Analyst の略で、Try Harder で有名なOSCP資格と同じOffsec.が提供する唯一のDefense向け資格です。OSDA 試験のためのトレーニングとして SOC-200 というコースが設けられており、その名の通りSIEMを活用したログ分析の手法を学ぶことができます。具体的には、以下のようなことを学ぶことができます。

  • Windows イベントログそのものの勉強/監査設定
  • WindowsLinuxの初期侵入/横展開/権限昇格/永続化 をした際に発生するログ
  • Active Directory環境での横展開/ドメイン掌握 を行った際に発生するログ
  • Kibanaの操作、アラートルール、視覚化ダッシュボードの作成方法

一言でまとめると、Elasticsearch + Kibana を使ったログ分析ゲーミングとなります。筆者はKibanaを使ったSIEM運用やEndpointログ分析を日常からバリバリ行う職場にいるため、分析環境はとてもなじみ深いもので使いやすかったです。

また、トレーニングの内容としても『監査設定をする => どんなWindows eventログが見えるようになったか確認する => 攻撃をしてどんな特徴的なログが出るかを見る 』という基本のキに沿った内容だったので非常に親切であり、初心者入門教材としては十分だと思われます。その一方で、そこまでハイレベルなログ分析の視点までは紹介されず、一般的なドメイン環境の侵害で使うツールが残すログを雰囲気で追えるレベルの解説しかなかったのは物足りない部分だと感じました。 弊チームにいると 「event id `4662` の3回出現で何されたかも読めないの!?」と詰められますが、この研修だと「lsass.exe に Process Access されたからcredential accessされました」くらいの粒度でわかればOKなので、非常にやさしいです。より専門的なログ分析の方法については SOC-300 が出るのを待ちましょうという感じですかね。

また、この研修はOffsec研修の中でも最高に親切で、用意されている Lab (challenge) の半分以上は OffSec Academy (OSA)という動画付きの講義で詳しく解説してくれますし、Offsec の discord に行って以下のように chatbot を起動すれば、各 Lab のヒントをもらうことができます。
ぶっちゃけ Try Harder 感はほぼないので、初心者向け Offsec トレーニングともいえるでしょう。筆者の肌間的にはOSCP, OSEP, OSWPに比べてかなり快適だったので、Blue teamの人たちの温かさに感動していました。

discord chatbot

試験まで

筆者は4/10からトレーニングの受講をはじめましたが、Offsec側でVMインスタンスが安定して起動しないというトラブルがずっと発生していて、まともにLabができるようになったのは5月末からでした。別の方の合格体験記では「試験環境が不安定でログがSIEMに残らない問題があった」という記述がありましたが、同時期のLab環境でも同じような現象が発生しており「そもそも演習環境のVMが正しく立ち上がるまでが一番難しい」という状態でした。なので、VMインスタンスにちゃんと触れるようになってから約2か月Kibana触ってLabを2周くらいしあたと、「まぁいけるかな」という状態になったため7月末に試験にゴーという感じです。

試験前の対策としてはいつも使用するクエリのチートシートやKibana Dashboardの作成などをしていました。特に、SOC-200 で使用するElasticsearchのバージョンは メジャーバージョン 7 の最終系 であるため、8で運用している人とは微妙に使用感が異なります。「普段 Elasticsearch + Kibana 使っているし余裕」という人も、試験前に使用感をチューニングすることをお勧めします。(「8.8 から使える Response console スゲー」と感動して現場でそれしか使っていない人は使えないので要注意です。)
www.elastic.co

また、分析用のDashboardとosqueryのクエリチートシート(endpoint fileのハッシュ計算や通信しているプロセス列挙など)があると時短になるので、短期間で試験を受ける人もこの2つだけは準備することをおすすめです。

Kibana Dashboard のサンプル

試験

試験は10個のフェーズに分かれた攻撃を順番に分析する24時間の試験です。各フェーズ10点満点で合計75点に達すれば合格になります。体感的には10時間くらいで終わる内容で、特にトラブルもなく最終フェーズまで進んだので運がよかったかもしれません。詳細なスケジュールを書くと以下のような感じです。筆者の場合金曜日業務の終わった後の土曜日夜1時から初めて、月曜日の0時45分までにレポート提出の日程でやったので途中眠すぎて椅子の上で気絶していました。OSCPを受験した際は連勤の間に試験挟む程度余裕だったのですが、もう年のせいか徹夜試験は身体に響きますね。。。

時間 Phase 詳細
01:00 0 試験開始. まずは試験環境の視察. 基本の検索クエリ生成
01:20 1
02:05 2
03:25 3
04:40 4
06:20 5 7時くらいまでは分析していた記憶がある
- - 椅子に座ったまま気絶
10:40 6 慌てて起きて分析の続き
11:15 7
11:55 8
- - お昼食べたり買い物したり掃除したり
16:20 9
17:15 10 18:00には終わり夜食と風呂
ここからレポート作成
06:00 レポートのひな形を書き終え安心して就寝。
- - 睡眠。
10:30 起床して誤字や抜けの確認作業
15:00 提出

筆者の記憶だと PEN-200 のときは性格の悪い環境での試験だったので、今回も捻くれたものを覚悟して受けました。......が、かなり素直な環境を想定したログ分析で杞憂でした。レポートを提出した次の日の夜には合格のメールが来たので、大きな波もなく試験終了。他の人の合格体験記を見ると結果がくるまで1週間近く間が空く人もいたようなので、個人差があるようです。

試験tips

試験に関する詳しいことは書けないので、最後にちょっとした試験のコツだけ書きます。技術面というよりもマインド面でのコツです。

  • 攻撃者が一本道で目標を達成するとは限らない
    • 言い換えれば、1つの行動の追跡にこだわりすぎると全体が見えなくなります
    • 内部まで横展開が進行したあとに唐突に足取りがつかめなくなっても、落ち着いてドメイン環境を見渡して痕跡を探しましょう
  • Phase で一つの目標だけが達成されるとは限らない
    • 「あーこの端末でこれやったんだ!」で勝手に納得すると、後で足元をすくわれる可能性があります
    • 「ログが読めてる!」とノッているときでも、一度全体を見渡して他にも何かされていないかを探してみましょう
  • ログが行動を完全に追跡しているとは限らない
    • 端末によっては監査設定があまく、ネットワークログやオブジェクトへのアクセスログを完全に記録してくれていないものもあります。
    • その場合は、見えているログから攻撃者が何をやっているかを埋め合わせて記述するしかありません。細かい部分は一度脇に置いて、先に進めましょう。
  • Phase のログを実行する場合は 0時01分10秒 のように少し中途半端な秒数から始めるのをおすすめ
    • 0時5分のようにキリのいい時間にやると、cronjob などのログに攻撃のログが混じるので滅茶苦茶見にくくなります。
    • 特に試験の開始前は、焦らずにどんなログが流れている環境かを分析するところから始めましょう

おわりに

以上、OSDA のレビューでした。弊社はOSCP合格者には上限まで資格奨励金が出ますがOSDAは0円なので天と地ほどの待遇差があり、弊チームからは『nmap は神スキルだけど Kibana でログ分析はハズレスキル』なんてネタにされていますが、 Elasticsearch + Kibana を触ったことない人やWindows Event logを全く見たことのない人にとって SOC-200 はとてもいいトレーニングですし、追放された先の多くのパーティで活躍できるスキルになると思います。環境準備して攻撃してログを見てリセットして......という環境を個人のVMで用意するのは面倒なので、SOC-200の研修を受講してサッと試す分にはおすすめです。また、SIEM や EDR 導入の話をすると「セキュリティにお金を1円もかけられない人もいるんですよ!!」というお話も出ますが、Elasticsearch + Kibana の分析環境は0円からSIEM + EDRを始めることができるので、「お金かけられないおじさん」にもおすすめです。*1

逆に運用経験がある人にとっては退屈な内容なので、そういう方々は SOC-300 が出るまで待ちましょう。
そのほか、より具体的な内容でも気になることがあれば twitter (今は X か....)まで気軽にDMしてください。それでは。

*1:もちろん0円だと限界も多いですが、とりあえずおすすめされた100 ~ 1000万円規模の製品だけ買って神様に祈るよりも、ログとして見えるもの, わかること, 限界などを把握したうえでその課題をクリアする製品を選ぶのでは防御の練度がまったく異なります。