This challenge consists of exploiting and SQL injection with some tricky things. Let's take a look at it.
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.
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.
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.
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.
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!
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.
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.
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.
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 as parameter's name
Select in SQL sentence
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.
KEYWORDS=['SELECT', .. ]
for keyword in KEYWORDS:
if keyword in query_string.upper():
return 403,"Your request is blocked by waf"
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:
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
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:
Copyright (c) 2006-2017 sqlmap developers (http://sqlmap.org/)
See the file 'doc/COPYING' for copying permission
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.HIGHEST
def tamper(payload, **kwargs):
Replaces greater than operator ('>') with 'NOT BETWEEN 0 AND #'
Replaces equals operator ('=') with 'BETWEEN # AND #'
* Microsoft SQL Server 2005
* MySQL 4, 5.0 and 5.5
* Oracle 10g
* PostgreSQL 8.3, 8.4, 9.0
* 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
retVal = retVal.replace("SELECT","SE\x01LECT")
retVal = retVal.replace("WHERE","WH\x01ERE")
retVal = retVal.replace("FROM","FR\x01OM")
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://220.127.116.11/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:
According to the results, the current database has two tables: news
. 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
To do it, we paramatetrize SQLMap as follows:
$ sqlmap -u 'http://18.104.22.168/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
And that's it! Our desired string: