SQL Injection with Advanced Bypass Techniques
This section details a classic SQL injection vulnerability, requiring a series of discovery steps and an advanced bypass method to retrieve sensitive information.
Vulnerability Identification
Initial reconnaissance revealed the presence of a SQL injection vulnerability. Inputting a single quote (1') resulted in a database error, confirming that single quotes were not properly sanitized. Further tests demonstrated the injection point: 1'# executed successfully, indicating a comment bypass. Boolean-based checks like 1' AND 1=1# (true) and 1' AND 1=2# (false) firmly established the vulnerability, allowing for conditional logic to be injected into the query.
Information Gathering
With an identified injection point, the next crucial step was to enumerate the database structure:
- Database Enumeration: The payload
1'; SHOW DATABASES; #was utilized to list all available databases accessible to the current user. - Table Enumeration: Once the target database was identified,
1'; SHOW TABLES; #was employed to enumerate tables within that database. - Column Enumeration: Several tables were discovered. To identify relevant columns, specific queries like
1'; SHOW COLUMNS FROM words; #and1'; SHOW COLUMNS FROM1919810931114514; #were executed. It's important to note the use of backticks for table names that are purely numeric or contain special characters, as standard quoting might not apply. Analysis indicated that the flag resided within the table named1919810931114514.
Data Retrieval with CONCAT and Prepared Statements
Directly querying the target table using conventional SQL statements might be blocked by input filtering, which often targets common keywords like SELECT. To bypass such restrictions, a technique leveraging CONCAT with SQL prepared statements was employed. This method allows for the dynamic construction of SQL queries, which are then prepared and executed, circumventing static filtering mechanisms. The process involved:
- Specifying the target database:
USE supersqli; - Constructing the query string dynamically using
CONCAT. The querySELECT * FROM 1919810931114514was fragmented and reassembled. - Preparing the dynamically constructed statement using
PREPARE. - Executing the prepared statement using
EXECUTE.
The final payload used to retrieve the flag was:
1'; USE supersqli; SET @dynamic_sql = CONCAT('SEL','ECT * FROM `1919810931114514`'); PREPARE statement_handle FROM @dynamic_sql; EXECUTE statement_handle; --+
This approach successfully bypassed the filters and retrieved the flag from the specified table.
ID Parameter Enumeration and Length-Based Detection
This challenge focused on exploring web application parameters to discover hidden or less obvious content, typical in enumeration scenarios.
Parameter Discovery and Enumeration
Navigating through the application's various features led to a "Report Center" link. Clicking this link opened a new page, and the URL structure clearly indicated the use of an id parameter (e.g., index.php?id=value), which typically fetches specific content based on the provided identifier.
Manually modifying the id parameter to sequential or common values did not immediately reveal alternative content. This suggested that content might be identified by non-sequential IDs, or that there was a large range of possible identifiers to check.
Brute-Force and Anomaly Detection
A systematic brute-force strategy was implemented to test a wide range of potential id values. During this automated testing, a notable anomaly was observed: when id=2333 was submitted, the HTTP response body's content length significantly differed from the typical responses received for other IDs. This change in length served as a strong indicator that a unique piece of content or a different application state had been triggered. Upon further inspection, this unique response contained the flag.
Local File Inclusion (LFI) with URL Encoding and Path Traversal Bypass
This challenge presented a web application that attempted to restrict file access through a custom PHP filtering mechanism, ultimately proving vulnerable to Local File Inclusion (LFI).
Initial Discovery and Source Code Review
The challenge began with a simple web page. Standard web enumeration techniques (such as checking common filenames or using developer tools) quickly revealed the existence of source.php. Accessing this file displayed the application's PHP source code, which is often a critical step in identifying vulnerabilities.
The core logic for handling file requests involved processing a file parameter from $_REQUEST. This processing was guarded by three conditions:
- The
filevariable must not be empty (!empty($_REQUEST['file'])). - The
filevariable must be a string (is_string($_REQUEST['file'])). - A custom static method,
emmm::checkFile($_REQUEST['file']), must returntrue.
Analysis of emmm::checkFile Function
The checkFile method was designed with several layers of checks to enforce a whitelist policy:
$whitelist = ["source"=>"source.php", "hint"=>"hint.php"];
// First check: direct string comparison against whitelist
if (in_array($page_param, $whitelist)) {
return true;
}
// Second check: truncates input at the first '?' and compares
$processed_page = mb_substr(
$page_param,
0,
mb_strpos($page_param . '?', '?')
);
if (in_array($processed_page, $whitelist)) {
return true;
}
// Third check: URL decodes input, then truncates at '?' and compares
$decoded_page = urldecode($page_param);
$processed_decoded_page = mb_substr(
$decoded_page,
0,
mb_strpos($decoded_page . '?', '?')
);
if (in_array($processed_decoded_page, $whitelist)) {
return true;
}
echo "Access denied!";
return false;
The function aims to restrict included files to only source.php or hint.php. The critical ensight for bypassing this mechanism lies in the second and third checks: the use of mb_strpos($page . '?', '?') effectively truncates the input string at the first occurrence of a question mark. This allows for appending arbitrary path traversal sequences after a ?.
Crafting the LFI Payload
The vulnerability arises from a disparity between how the checkFile method processes the filename and how PHP's native include function interprets file paths. When PHP's include function encounters a ? in a file path, it typically ignores everything that follows, treating it as query parameters. This behavior can be exploited to satisfy the whitelist check while simultaneously injecting path traversal directives.
A payload such as source.php? would successfully pass the second whitelist check (as mb_substr would truncate it to source.php). Following this, path traversal characters (../) can be appended. Since $_REQUEST aggregates GET, POST, and COOKIE variables, a GET request was a straightforward method to deliver the payload.
The final payload successfully exploited this behavior to access a file outside the intended directory, likely a flag file located in a parent directory (e.g., /ffffllllaaaagggg):
http://example.com/source.php?file=source.php?/../../../../../ffffllllaaaagggg
This request works because emmm::checkFile validates the source.php? portion. Subsequently, the PHP include function processes the full string source.php?/../../../../../ffffllllaaaagggg. It effectively includes source.php, but critically, it also resolves the path traversal sequence ../../../../../ffffllllaaaagggg relative to the current working directory or the included file's context, leading to the desired file.
Client-Side JavaScript Obfuscation and Code Analysis
This challenge involved analyzing heavily obfuscated JavaScript code embedded within an HTML file to deduce a specific input string and then to reconstruct a hidden flag.
Initial Analysis and Debugging
The challenge file was initially downloaded and appeared as garbled binary data. Opening the file with a hexadecimal editor (such as WinHex) revealed that it was, in fact, an HTML document. The file was then saved with an .html extension and opened in a web browser.
Upon interacting with the rendered page, an input field and an "Ok" button were visible. Providing arbitrary input yielded no apparent effect. To debug the client-side script, the obfuscated JavaScript was inspected. The critical step was to modify the script's final eval() call (which would dynamically execute its argument) to alert(). This modification allowed the raw, unexecuted JavaScript string to be displayed in a browser pop-up, revealing the underlying logic for analysis.
Input String Reconstruction
The JavaScript contained a function that processed user input from an HTML element with id="c". This input had to satisfy a series of nested conditional checks based on string length and regular expressions:
function processUserEntry() {
var userEntry = document.getElementById("c").value;
if (userEntry.length === 16) { // Condition 1: String length must be exactly 16 characters.
if (userEntry.match(/^be0f23/) !== null) { // Condition 2: Must begin with "be0f23".
if (userEntry.match(/233ac/) !== null) { // Condition 3: Must contain "233ac".
if (userEntry.match(/e98aa$/) !== null) { // Condition 4: Must end with "e98aa".
if (userEntry.match(/c7be9/) !== null) { // Condition 5: Must contain "c7be9".
// Logic for flag generation follows here
}
}
}
}
}
}
document.write('<input id="c"></input><button onclick="processUserEntry()">Ok</button>');
By carefully combining these five conditions, the unique 16-character input string required to satisfy all checks was deduced:
- Starts with:
be0f23 - Ends with:
e98aa - Contains:
233ac - Contains:
c7be9
The only string that fits all these criteria is be0f233ac7be98aa.
Flag Generation Logic
Once the correct input string was provided, the script proceeded to generate the flag. This involved initializing several arrays containing string fragments and then iteratively appending parts of these arrays to form the final flag.
var segmentA = ["fl", "s_a", "i", "e}"];
var segmentB = ["a", "_h0l", "n"];
var segmentC = ["g{", "e", "_0"];
var segmentD = ["it'", "_", "n"];
var allSegments = [segmentA, segmentB, segmentC, segmentD];
var constructedFlag = "";
for (var iteration = 0; iteration < 13; ++iteration) {
var currentSegmentSet = allSegments[iteration % 4]; // Cycle through the four segment arrays
constructedFlag += currentSegmentSet[0]; // Append the first element of the current array
currentSegmentSet.splice(0, 1); // Remove the appended element from its array
}
// The 'constructedFlag' variable now holds the final flag.
The loop executes 13 times. In each iteration, it selects one of the four fragment arrays using the modulo operator (iteration % 4). It then appends the first element of the selected array to constructedFlag and immediately removes that element using splice(0, 1). This dynamic modification of the arrays ensures that different fragments are picked in subsequent cycles, progressively building the flag. Executing this logic (either by providing the correct input in the browser or by running the code directly in the browser's console) reconstructs the full flag.
Reversing a Custom PHP Encryption Algorithm
This challenge involved analyzing and reversing a custom PHP encryption function to decrypt a given ciphertext and retrieve the embedded flag.
Analysis of the Encoding Function
The provided PHP script contained an encode function that took an input string, applied a series of transformations, and returned an encrypted string. A clear understanding of the exact sequence of operations was paramount for developing a successful decryption routine:
<?php
$encoded_string = "a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";
function encryptString($plain_text) {
// Operation 1: Reverse the input string
$reversed_text = strrev($plain_text);
$shifted_chars = '';
// Operation 2: Increment ASCII value of each character by 1
for ($idx = 0; $idx < strlen($reversed_text); $idx++) {
$char_val = substr($reversed_text, $idx, 1);
$ascii_plus_one = ord($char_val) + 1;
$shifted_chars .= chr($ascii_plus_one);
}
// Operation 3: Base64 encode the result
$base64_encoded = base64_encode($shifted_chars);
// Operation 4: Reverse the Base64 encoded string
$final_reversed = strrev($base64_encoded);
// Operation 5: Apply ROT13 transformation
return str_rot13($final_reversed);
}
highlight_file(__FILE__);
/*
The goal is to reverse this encryption algorithm to decrypt $encoded_string and find the flag.
*/
?>
The encoding steps, in their sequential order, are:
strrev(): Reverses the initial plaintext.- Character Shifting: Iterates through the reversed string, increments each character's ASCII value by one (
ord() + 1), and converts it back to a character (chr()). base64_encode(): Applies Base64 encoding to the character-shifted string.strrev(): Reverses the Base64 encoded string.str_rot13(): Applies the ROT13 substitution cipher to the final reversed string.
Developing the Decoding Function
To decrypt the provided $encoded_string, the inverse operations must be applied in the precise reverse order of the encryption process:
- Inverse of ROT13: The
str_rot13()function is its own inverse; applying it again decrypts the ROT13-encoded data. - Inverse of
strrev(): Similarly,strrev()is its own inverse; applying it again reverts the string reversal. - Inverse of
base64_encode(): This is achieved usingbase64_decode(). - Inverse of Character Shifting: This requires iterating through the characters and decrementing each character's ASCII value by one (
ord() - 1). - Inverse of
strrev(): The final step is another application ofstrrev()to reverse the initial reversal.
The PHP script for decrypting the ciphertext is as follows:
<?php
$encrypted_data = 'a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws';
// Step 1: Reverse ROT13 (ROT13 is self-inverse)
$after_rot13_dec = str_rot13($encrypted_data);
// Step 2: Reverse the string reversal (strrev is self-inverse)
$after_strrev_dec = strrev($after_rot13_dec);
// Step 3: Base64 Decode
$after_base64_dec = base64_decode($after_strrev_dec);
// Step 4: Decrement ASCII value of each character
$decremented_plain = '';
for ($k = 0; $k < strlen($after_base64_dec); $k++) {
$current_char = substr($after_base64_dec, $k, 1);
$decremented_ascii = ord($current_char) - 1;
$decremented_plain .= chr($decremented_ascii);
}
// Step 5: Reverse the initial string reversal (strrev is self-inverse)
$final_plaintext_flag = strrev($decremented_plain);
echo $final_plaintext_flag;
?>
Executing this script successfully reconstructs and outputs the original plaintext flag.