Challenge 1: Magic Methods via Internal Classes
The regex validation requires both parameters to contain alphabetic characters. The eval function executes the string as PHP code, where new $v1 instantiates a class named by the value of $v1, and ($v2()) invokes the function specified by $v2, passing its return value to the constructor.
When an object is passed to echo, PHP triggers the __toString() magic method if defined. Several built-in classes implement this method, including Exception, CachingIterator, and ReflectionClass.
By leveraging these internal classes, arbitrary code execution can be achieved:
?v1=Exception&v2=system('ls')
?v1=ReflectionClass&v2=system('cat fl36dg.txt')
This evaluates as:
eval("echo new Exception(system('cat fl36dg.txt'));");
The Exception constructor accepts the command output as a string parameter, and its __toString() method returns that string, revealing the flag.
An alternative approach uses anonymous classes:
?v1=class{public function __construct(){system('cat f*');}}&v2=undefinedFunc
The anonymous class executes system() in its constructor, and the subsequent undefined function call error does not prevent command execution.
Challenge 2: Directory Listing Without Parentheses
Parentheses are now filtered, preventing direct function calls with arguments. However, getcwd() requires no parameters and returns the current working directory path.
The FilesystemIterator class can iterate over a directory:
?v1=FilesystemIterator&v2=getcwd
This executes:
eval("echo new FilesystemIterator(getcwd());");
The iterator outputs the first file path in the current directory, allowing access to the hidden flag file.
Challenge 3: Super Globals and Variable Variables
The filter requires $v1 to contain the string "ctfshow" before calling the flag retrieval function. The code performs:
eval("$$v1 = &$$v2;");
var_dump($$v1);
Initially, setting v1=ctfshow&v2=flag returns NULL because $flag is defined in the global scope, not within the function's scope.
The solution uses $GLOBALS, a superglobal array containing all global variables:
?v1=ctfshow&v2=GLOBALS
This dumps all global variables, including the flag.
Challenge 4: Pseudo-protocol File Access
The is_file() check must be bypassed while still allowing highlight_file() to process the target. PHP pseudo-protocols serve this purpose:
?file=php://filter/resource=flag.php
Alternative encodings work as well:
?file=php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
?file=php://filter/read=convert.quoted-printable-encode/resource=flag.php
?file=compress.zlib://flag.php
Challenge 5: Filter Bypass via Compression Wrapper
With "filter" now blocked, the compression wrapper provides an alternative:
?file=compress.zlib://flag.php
A more complex bypass involves directory traversal overflow:
?file=/proc/self/root/proc/self/root/.../var/www/html/flag.php
The path /proc/self/root is a symlink to the process root directory. Excessive nesting causes is_file() to fail while still allowing file access.
Challenge 6: Standard Filter Access
With filter restrictions removed, direct pseudo-protocol access succeeds:
?file=php://filter/resource=flag.php
Challenge 7: Numeric Validation Bypass
The challenge requires the input to not equal 36 before filtering, but equal 36 after filtering. The trim() function removes whitespace, tabs, newlines, carriage returns, null bytes, and vertical tabs, but does not remove the form feed character (\x0c or %0c).
Additionally, is_numeric() accepts leading whitespace characters:
?num=%0c36
This passes all checks:
is_numeric()returns true because %0c is treated as leading whitespace- Strict comparison
$num !== '36'is true since the string contains %0c trim($num) !== '36'is true because trim does not remove %0c- Weak comparison
filter($num) == '36'is true after type conversion strips the character
PHP's weak typing comparison automatically converts strings to numbers, allowing numeric-like strings with invisible characters to pass validation checks.