
Nim (https://nim-lang.org/) を武器化する私の実験
インプラント開発および一般的な攻撃運用において、Nim を武器化するための実験です。
DllMain をエクスポートする Windows DLL の作成
mingw ツールチェーンをインストールし、nim コンパイラに 1 つのフラグを渡すだけで完了します。| ファイル | 説明 |
|---|---|
| amsi_patch_2_bin.nim | 別の方法を使用して現在のプロセスから AMSI をパッチする(開発中、助けてください!) |
| excel_4_com_bin.nim | Excel COM オブジェクトと Excel 4 マクロを使用してシェルコードを注入する(開発中) |
このリポジトリはバイナリを提供しません。自分でコンパイルする必要があります。このリポジトリは、Linux または MacOS から Windows 向けに Nim ソースファイルをクロスコンパイルするように設定されています。
VSCode Devcontainers を使用して、自動的に開発環境をセットアップします(開発環境のセットアップ セクションを参照)。それが完了したら、make を実行するだけです。
システムのパッケージマネージャーを使用して Nim をインストールします(Windows の場合は公式サイトのインストーラーを使用します)
brew install nimapt install nimchoco install nim(Nim は Dockerhub に Docker イメージも提供しています)
これで nim と nimble コマンドが使用可能になるはずです。前者は Nim コンパイラ、後者は Nim のパッケージマネージャです。
Windows へのクロスコンパイルに必要な Mingw ツールチェーンをインストールします(Windows でコンパイルする場合は不要):
apt-get install mingw-w64brew install mingw-w64最後に、素晴らしい Winim ライブラリと、zippy および nimcrypto をインストールします。
nimble install winim zippy nimcrypto次に、このリポジトリのルートに cd して make を実行します。
バイナリと DLL は bin/ ディレクトリに生成されます。
詳細については、Nim コンパイラ使用ガイドのクロスコンパイルセクションを参照してください。
MacOS/*nix から Windows へのクロスコンパイルには mingw ツールチェーンが必要です。通常は brew install mingw-w64 または apt install mingw-w64 を実行するだけです。
そして、nim コンパイラに -d=mingw フラグを渡すだけで済みます。
例:nim c -d=mingw --app=console --cpu=amd64 source.nim
Nim マニュアルの素晴らしい FFI セクションを参照してください。
C# の P/Invoke に慣れている方なら、基本的には同じ概念ですが、見た目は少し不格好です:
MessageBox を呼び出す例```nim
type
HANDLE* = int
HWND* = HANDLE
UINT* = int32
LPCSTR* = cstring
proc MessageBox*(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT): int32 {.discardable, stdcall, dynlib: "user32", importc: "MessageBoxA".}
MessageBox(0, "Hello, world !", "Nim is Powerful", 0)
複雑なWindows API呼び出しには[Winimライブラリ](https://github.com/khchen/winim)を使用してください。インポートの方法によっては、時間を大幅に節約でき、実行可能ファイルのサイズもあまり増加しません(下記参照)。
COMサポートもあります!!!
## エクスポートされた`DllMain`を使用したWindows DLLの作成
Nimフォーラムに[これ](https://forum.nim-lang.org/t/1973)を投稿してくれた方に大感謝します。
Nimコンパイラは、Windows DLLを作成するように指示すると、コンパイル時に自動的に`DllMain`関数を作成しようとしますが、なぜか実際にはエクスポートしません。エクスポートされた`DllMain`を持つためには、`--nomain`を渡し、適切なプラグマ(`stdcall, exportc, dynlib`)を付けて自分で`DllMain`関数を定義する必要があります。
また、Nimのガベージコレクタを初期化するために、`DllMain`から`NimMain`を呼び出す必要があります。(非常に重要です。そうしないと、あなたのコンピュータが文字通り爆発します)。
例:```nim
import winim/lean
proc NimMain() {.cdecl, importc.}
proc DllMain(hinstDLL: HINSTANCE, fdwReason: DWORD, lpvReserved: LPVOID) : BOOL {.stdcall, exportc, dynlib.} =
NimMain()
if fdwReason == DLL_PROCESS_ATTACH:
MessageBox(0, "Hello, world !", "Nim is Powerful", 0)
return true
コンパイルするには:``` nim c -d=mingw --app=lib --nomain --cpu=amd64 mynim.dll
### XLLの作成
XLL(Excel DLL、想像してみてください)を自動オープン関数付きで作成でき、ペイロード配信に使用できます。以下のコードは、自動オープン関数と、リンクライブラリとしてコンパイルするために必要なその他の定型コードを備えたXLL用のシンプルなコードを作成します。PoCはDLLとしてコンパイルされ、拡張子を.xllに変更すると、ダブルクリック時にExcelが開き、ペイロードが実行されます:```nim
#[
Compile:
nim c -d=mingw --app=lib --nomain --cpu=amd64 nim_xll.nim
Will compile as a DLL, you can then just change the extension to .xll
]#
import winim/lean
proc xlAutoOpen() {.stdcall, exportc, dynlib.} =
MessageBox(0, "Hello, world !", "Nim is Powerful", 0)
proc NimMain() {.cdecl, importc.}
proc DllMain(hinstDLL: HINSTANCE, fdwReason: DWORD, lpvReserved: LPVOID) : BOOL {.stdcall, exportc, dynlib.} =
NimMain()
return true
XLLでは他にも多くの巧妙なことができます。XLLのさらに多くの例はこちらをご覧ください。
NimのFAQページより引用
最大のサイズ削減には、以下のフラグを使用します: -d:danger -d:strip --opt:size
さらに、--passc=-flto --passl=-flto をコンパイラに渡すことで、さらに数バイト削減できることがわかりました。このリポジトリの Makefile も参照してください。
これらのフラグはサイズを劇的に削減します。シェルコードインジェクションの例では、MacOSXからクロスコンパイルした場合、484.3 KB から 46.5 KB になります!
これを解明してくれた @Shitsecure に多大なる感謝を!
デフォルトでは、Nimはリロケーションテーブルを持つPEを生成しません。これは、リフレクティブにEXEをロードするほとんどのツールに必要です。
リロケーションセクション あり のNim実行ファイルを生成するには、リンカにいくつかの追加フラグを渡す必要があります。
具体的には: --passL:-Wl,--dynamicbase
完全なコマンド例:``` nim c --passL:-Wl,--dynamicbase my_awesome_malwarez.nim
## Winimライブラリを使用した場合と使用しない場合の実行ファイルサイズの違い
驚くべきことに、サイズの違いはごくわずかです。特に、上記で概説したサイズ最適化を適用した場合は顕著です。
この目的のために、2つのサンプル `pop_bin.nim` と `pop_winim_bin.nim` が作成されました。
前者は手動で `MessageBox` WinAPI 呼び出しを定義し、後者は Winim ライブラリ(具体的にはコアSDKのみの `winim/lean`、[こちら](https://github.com/khchen/winim#usage)を参照)を使用しています。結果:```
byt3bl33d3r@ecl1ps3 OffensiveNim % ls -lah bin
-rwxr-xr-x 1 byt3bl33d3r 25K Nov 20 18:32 pop_bin_32.exe
-rwxr-xr-x 1 byt3bl33d3r 32K Nov 20 18:32 pop_bin_64.exe
-rwxr-xr-x 1 byt3bl33d3r 26K Nov 20 18:33 pop_winim_bin_32.exe
-rwxr-xr-x 1 byt3bl33d3r 34K Nov 20 18:32 pop_winim_bin_64.exe
Winimライブラリ全体を import winim/com でインポートしても、追加されるサイズは約20KB程度であり、抽象化される機能の量を考慮すると、その追加サイズは100%価値があります:```
byt3bl33d3r@ecl1ps3 OffensiveNim % ls -lah bin
-rwxr-xr-x 1 byt3bl33d3r 42K Nov 20 19:20 pop_winim_bin_32.exe
-rwxr-xr-x 1 byt3bl33d3r 53K Nov 20 19:20 pop_winim_bin_64.exe
## オペセクに関する考慮事項
Nimが`LoadLibrary`を使用してDLLを動的に解決する仕組みにより、そのFFIを介して外部からインポートされた関数は実行ファイルの静的インポートには一切表示されません(詳細は[こちらのブログ記事](https://web.archive.org/web/20210117002945/https://secbytes.net/implant-roulette-part-1:-nimplant/)を参照):

NimのソースをDLLにコンパイルすると、独自の`DllMain`を指定したかどうかに関わらず、常にエクスポートされた`NimMain`が存在するようです(?)。これはシグネチャとして使用される可能性がありますが、開発スタックで実際にNimを使用している組織がどれほどあるかは不明です。間違いなく目立ちます。

## Nimランタイムを使わないNimの記述
Nimはアンチウイルスソリューションに厳しくフラグが立てられるため、その回避方法の一つはNimランタイムを使わずにNimプログラムを記述することです。[Writing Nim-less Nim](https://www.youtube.com/watch?v=EXX3HmCG3pw) は、NimランタイムとCランタイムを使わずにNimコードを記述する手順を概説した講演で、そのソースコードは[こちら](https://github.com/m4ul3r/writing_nimless)から入手できます。この講演は[zimawhit3](https://github.com/zimawhit3)氏の[Bitmancer](https://github.com/zimawhit3/Bitmancer/tree/main)の研究を基に拡張しています。
前提として、`winim`の型定義に依存し、Nimのランタイムが使用されないようなコード記述を活用することです。
## CコードからNimへの変換
https://github.com/nim-lang/c2nim
小さなCスニペットの変換に使用しましたが、大規模なものは試していません。
## 言語ブリッジ
- Python統合 https://github.com/yglukhov/nimpy
* これは非常に興味深いもので、特に[この部分](https://github.com/yglukhov/nimpy/blob/master/nimpy/py_lib.nim#L330)がそうです。多少の修正で、このPythonxXX.dllをメモリから読み込めるようになるのでしょうか?
- Java VM統合:https://github.com/yglukhov/jnim
## デバッグ
`echo`と組み合わせて`repr()`関数を使用します。ほぼすべて(?)のデータ型(構造体も含む)をサポートしています。
詳細は[こちらのブログ記事](https://nim-lang.org/blog/2017/10/02/documenting-profiling-and-debugging-nim-code.html)を参照
## 開発環境のセットアップ
このリポジトリは[VSCode Devcontainers](https://code.visualstudio.com/docs/remote/create-dev-container)をサポートしており、Dockerコンテナ内で開発を行うことができます。これにより、開発環境のセットアップが自動化されます。
1. VSCodeとDocker Desktopをインストール
2. このリポジトリをクローンしてVSCodeで開く
3. `Visual Studio Code Remote - Containers`拡張機能をインストール
4. コマンドパレットを開き、`Remote-Containers: Reopen in Container`コマンドを選択
VSCodeがDockerイメージをビルドし(少し時間がかかります)、あらかじめ構築されたNim開発環境に直接移動します!
## 私が陥りがちだった落とし穴
- WinimでWinAPIを呼び出し、null値を渡そうとするときは、Nim組み込みの`nil`値ではなく、Winimライブラリ内で定義されている`NULL`値を渡すようにしてください。(うっ)
- Windowsで`open()`を呼び出して作成されたファイルのOSハンドルを取得するには、`f.getOsFileHandle()`を呼び出す必要があります。`f.getFileHandle()`ではダメです。理由はいろいろあります。
- Nimコンパイラは、使用法を見ると`-a:value`または`--arg:value`の形式でしか引数が渡されないように見えますが、実際には`-a=value`や`--arg=value`の形式も受け付けます。(Makefileにとって重要)
- バイト配列を定義するときは、少なくとも最初の値でそれがバイト配列であることを示す必要があります。少し変ですが、仕方ありません。(https://forum.nim-lang.org/t/4322)
C#でのバイト配列:```csharp
byte[] buf = new byte[5] {0xfc,0x48,0x81,0xe4,0xf0,0xff}
Nimにおけるバイト配列:```nim var buf: array[5, byte] = [byte 0xfc,0x48,0x81,0xe4,0xf0,0xff]
## おもしろいNimライブラリ
- https://github.com/dom96/jester
- https://github.com/pragmagic/karax
- https://github.com/Niminem/Neel
- https://github.com/status-im/nim-libp2p
- https://github.com/PMunch/libkeepass
- https://github.com/def-/nim-syscall
- https://github.com/tulayang/asyncdocker
- https://github.com/treeform/ws
- https://github.com/guzba/zippy
- https://github.com/rockcavera/nim-iputils
- https://github.com/FedericoCeratto/nim-socks5
- https://github.com/CORDEA/backoff
- https://github.com/treeform/steganography
- https://github.com/miere43/nim-registry
- https://github.com/status-im/nim-daemon
## インプラント開発向けNimリンク
- https://web.archive.org/web/20210117002945/https://secbytes.net/implant-roulette-part-1:-nimplant/
- https://securelist.com/zebrocys-multilanguage-malware-salad/90680/
- https://github.com/MythicAgents/Nimplant
- https://github.com/elddy/Nim-SMBExec
- https://github.com/elddy/NimScan
## コントリビューター
貢献してくださった皆様にバーチャルハグ❤️
<a href="https://github.com/byt3bl33d3r/OffensiveNim/graphs/contributors">
<img src="https://contrib.rocks/image?repo=byt3bl33d3r/OffensiveNim" />
</a>
| ファイル | 説明 |
|---|
| pop_bin.nim | Winim ライブラリを使わずに MessageBox WinAPI を呼び出す |
| pop_winim_bin.nim | Winim ライブラリを使って MessageBox を呼び出す |
| pop_winim_lib.nim | DllMain をエクスポートする Windows DLL の作成例 |
| execute_assembly_bin.nim | CLR をホストし、メモリから .NET アセンブリをリフレクティブに実行する |
| clr_host_cpp_embed_bin.nim | C++ コードを直接埋め込んで CLR をホストし、ディスク上の .NET アセンブリを実行する |
| scshell_c_embed_bin.nim | 既存の C コードを SCShell で素早く武器化する方法を示す(C コードを Nim 内に直接埋め込む) |
| fltmc_bin.nim | すべてのミニフィルタードライバーを列挙する |
| blockdlls_acg_ppid_spoof_bin.nim | PPID を explorer.exe に偽装した中断プロセスを作成し、BlockDLLs と ACG も有効にする |
| named_pipe_client_bin.nim | 名前付きパイプ クライアント |
| named_pipe_server_bin.nim | 名前付きパイプ サーバー |
| embed_rsrc_bin.nim | コンパイル時にリソース(zip ファイル)を埋め込み、実行時に内容を抽出する |
| self_delete_bin.nim | ディスク上でロックされている、または実行中の実行ファイルを削除する方法。@jonasLyk 氏によって発見された手法 |
| encrypt_decrypt_bin.nim | Nimcrypto ライブラリを使用した AES256(CTR モード)による暗号化/復号化 |
| amsi_patch_bin.nim | 現在のプロセスから AMSI をパッチする |
| amsi_providerpatch_bin.nim | AMSI プロバイダー DLL(この例では MpOav.dll)にパッチを当てて AMSI をバイパスする。こちらで公開 |
| etw_patch_bin.nim | 現在のプロセスから ETW をパッチする |
| wmiquery_bin.nim | WMI を使用して実行中のプロセスとインストールされている AV をクエリする |
| out_compressed_dll_bin.nim | 圧縮および Base-64 エンコードを行い、マネージド DLL をメモリにロードする PowerShell コードを出力する。元の PowerSploit スクリプトの Nim への移植版 |
| dynamic_shellcode_local_inject_bin.nim | ハードコードせずに動的に取得したシェルコードをローカルに注入する PoC |
| shellcode_callback_bin.nim | コールバック関数を使用してシェルコードを実行する |
| shellcode_bin.nim | 中断プロセスを作成し、VirtualAllocEx/CreateRemoteThread でシェルコードを注入する。さらにコンパイル時定義を使用してアーキテクチャや OS などを検出する方法を示す |
| shellcode_fiber.nim | ファイバーを介したシェルコード実行 |
| shellcode_inline_asm_bin.nim | インラインアセンブリを使用したシェルコード実行 |
| ssdt_dump.nim | 例外ディレクトリのランタイム関数テーブルを使用したシンプルな SSDT 取得。MDSEC の記事に着想を得た手法 |
| syscalls_bin.nim | 直接システムコールを行う方法を示す |
| execute_powershell_bin.nim | CLR をホストし、アンマネージド実行空間を介して PowerShell を実行する |
| passfilter_lib.nim | パスワード複雑性フィルターを(悪用して)パスワード変更をファイルに記録する |
| minidump_bin.nim | MiniDumpWriteDump を使用して lsass のメモリダンプを作成する |
| http_request_bin.nim | HTTP リクエストを行ういくつかの方法を示す |
| execute_sct_bin.nim | .sct ファイルの実行:GetObject() 経由 |
| scriptcontrol_bin.nim | MSScriptControl COM オブジェクトを使用して VBScript と JScript を動的に実行する |
| excel_com_bin.nim | Excel COM オブジェクトとマクロを使用してシェルコードを注入する |
| keylogger_bin.nim | SetWindowsHookEx を使用したキーロガー |
| memfd_python_interpreter_bin.nim | memfd_create システムコールを使用してバイナリを匿名ファイルにロードし、execve システムコールで実行する |
| uuid_exec_bin.nim | UUID 配列からヒープ領域にシェルコードを配置し、EnumSystemLocalesA コールバックを使用してシェルコードを実行する |
| unhookc.nim | ntdll.dll のフックを解除して EDR/AV のフックを回避する(ired.team の C コードテンプレートを埋め込む) |
| unhook.nim | ntdll.dll のフックを解除して EDR/AV のフックを回避する(純粋な Nim 実装) |
| taskbar_ewmi_bin.nim | タスクバーの実行中アプリケーションプロパティを介した Extra Window Memory インジェクションを使用してシェルコードを実行する |
| fork_dump_bin.nim | Windows の fork() 実装を(悪用して)、PROCESS_CREATE_PROCESS アクセス権を持つリモートプロセスのハンドルを取得する。その後、MiniDumpWriteDump() を使用してフォークしたプロセスのメモリをダンプしようとする |
| ldap_query_bin.nim | ADO の ADSI プロバイダーを使用して COM 経由で LDAP クエリを実行する |
| sandbox_process_bin.nim | プロセスの整合性レベルを Untrusted に設定し、重要なトークンを削除してサンドボックス化する。これを使用して PPL プロセス(例:AV/EDR)を「サイレントに無効化」できる |
| list_remote_shares.nim | NetShareEnum を使用して現在のユーザーがアクセス可能な共有を一覧表示する |
| chrome_dump_bin.nim | Chrome の sqlite データベースから Cookie を読み取り、復号化する |
| suspended_thread_injection.nim | 中断スレッドインジェクションによるシェルコード実行 |
| dns_exfiltrate.nim | TXT レコードクエリを介したシンプルな DNS 流出 |
| rsrc_section_shellcode.nim | バイナリの .rsrc セクションに埋め込まれたシェルコードを実行する |
| token_steal_cmd.nim | トークンを盗んで偽装し、コマンドを実行する |
| anti_analysis_isdebuggerpresent.nim | デバッガの存在をチェックするシンプルなアンチ解析 |
| sandbox_domain_check.nim | コンピュータがドメインに接続されているかどうかをチェックするシンプルなサンドボックス回避手法 |
| Hook.nim | MessageBoxA の攻撃的フッキングの例 |
| anti_debug.nim | 2 つのアンチデバッグ手法の紹介 |
| anti_debug_via_tls.nim | TLS によるアンチデバッグ |
| local_pe_execution.nim | メモリ内で exe および dll ファイルを実行する |
| stack_string_allocation.nim | 配列を使用してスタック上に C およびワイド文字列を割り当てる |
| hardware_breakpoints.nim | ハードウェアブレークポイントを使用した関数のフック |