SQL injection vulnerability has been out there for a very long time and over a period of time, many researchers had presented tons of techniques for exploiting the vulnerability and exfiltrating data. However, there are certain techniques that are quite uncommon and very efficient in data exfiltration. In this article, we will have a look an in-depth look at some of these techniques and understand how we can implement them in real world scenarios and exfiltrate data efficiently. The test bed used for this article can be download from here. We will be looking at following techniques:
Binary Conversion/Binary Anding Bin2POS
All of above techniques follow a trade off between length of payload, no of requests sent and time taken to fetch the data. In this case, we convert end character into its binary equivalent and iterate over binary sequence to get the original value back. To understand it easily, let us have a look at following sample payload.
select mid((lpad(bin(ascii(mid((select database()),1,1))),8,0)),2,1)
Payload Breakdown
To break down the above payload we need to look at innermost query as all of other functions are working on its results. So the first function executes as select database(); which we all know will return current database name as following.
The second function “mid()” selects first character from the output of database() function.
The third function generates an ASCII equivalent of character obtained from mid() function.
Finally, the fourth character creates a binary equivalent of character ‘s’.
As the bin function return the binary equivalent without padding, so we are using lpad function to pad zeros to the end result to balance the loop used in the final script.
As we already know that ASCII character ranges from 0 to 255, so the first character of the final binary sequence will always be zero. This leaves us with seven requests for each character, which obviously is a large no of requests but it takes less time in fetching larger data. Let’s run our POC script to determine how many requests it would take to determine the database name.
As can be seen, it took us seven requests per character to fetch out an eight character word. This awesome technique was presented by Roberto Salgado in Blackhat USA 2013. In this case, the author uses a function named find_in_set which finds the location of the character in a character set by returning the index of the character. Further, the author converts the returned index into binary and retrieves the character.
Example
Let us fetch the database name i.e. “security” again from our previous example. In order to that we need to fetch it one character at a time. We can use following function for that. mid(‘security’,1,1) -> ‘s’ Lets us define one character set as follows Char_set = [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z] Now if we use find_in_set(substr(‘sahil’,1,1)), it will return 19 as the character ‘s’ is placed at 19th position in character set. We will now be converting the obtained index into binary using the following function. bin(find_in_set(mid(‘security’,1,1))) –> 1011 (4 requests) As can be seen, we now have 4 bytes of binary to be retrieved, however there is some challenge in using this technique which is “determining the end of binary sequence” i.e. how the automated program will know that the binary sequence is ended, and it should stop retrieving the position data for any particular character. Well, in this case, the author is using mySQL variables to determine whether the mid/substr function returns None if yes then sleep for some time and break out of the loop. Now, you might be thinking, will it take one additional request to fetch out binary sequence? The answer is no because we already knew that all characters in our character set would already have one byte. Example: for character a , it should ideally take 2 requests , one for actual binary data and one for determining the end of binary sequence, but we already knew that a is present at first position i.e For a : 1,b:10,c:11,d:100 So here we always need to fetch from the second character instead of first character. Complete Payload:
((SELECT @a:=MID(BIN(FIND_IN_SET(ascii(MID((select database()),1,1)),’48,49,50,51,52,53,54,55,56,57,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,58,59,60,61,62,63,64,91,92,93,94,95,96,123,124,125,126,32,9,10,13,11,12′)),2,1))=@a) AND (IF(@a!=”,@a,sleep(1)))
Payload Breakdown
In order to breakdown the above payload we will again tear it a part by innermost functions first. The innermost function which is select database() will return database name.
The second function is a substr function, selecting the first character of the database name.
In the third function, we are obtaining ASCII equivalent of the first character of the database name.
Finally, in the fourth function, we are locating the position of 115 in defined set.
The fourth function returned that 115 is located at the 29th index of defined character set, let’s convert this value into binary.
As can be seen in the above screenshot, we have 5 bytes to fetch out now, but as bin function does not pad the resultant binary sequence, and also we don’t want to send extra requests for fetching this sequence. We will use the property of substr/mid function i.e. it does not return anything when requested index is greater than actual length.
Example
As can be seen, the word ‘sahil’ is of 5 characters in length, however, if we ask the mid function to fetch out six characters, it will return None.
Well using the above technique we can determine the end of binary sequence i.e. when the mid function does not return anything sleep for 1-2 seconds, thus effectively breaking out of the loop and moving on to next character.
As can be seen in the above screenshot, we are retrieving 6th character. However, we have seen in our previous query, the binary sequence for the location of letter ‘s’ was of 5 characters in length but as we are retrieving the 6th character, our if the condition gets triggered and thus indicating binary sequence is ended. Let’s run our POC script to determine how many requests it would take to determine the database name.
As can be seen, it took us 39 requests to determine the database name as compared to binary conversion. POC scripts shown in this article can be downloaded from here. https://www.owasp.org/index.php/SQL_Injection https://github.com/Audi-1/SQLi-labs http://websec.ca/blog/view/optimized_blind_SQL_injection_data_retrieval https://www.youtube.com/watch?v=YBSVI5CF9mk