A Real-World Example of Blind SLQi
There is no such thing as a routine penetration test. Vulnerabilities can slip through the cracks for years before being discovered or new ones introduced in the latest patch. This mindset is what lead to our discovery of a SQLi zero-day within DNNGos xBlog plugin for the open-sourced Windows CMS dnnsoftware (formerly known as DotNetNuke.) Furthermore, this vulnerability was found in a clients public facing website where no authentication mechanisms or inputs fields were present. The rest of this blog will go into detail, about the methodology used to find this unauthenticated SQLi zero-day.
First some of you may be wondering how a website with no input fields or authentication mechanisms can be vulnerable to SQLi. If a website stores data within a database, it’s suspectable to a SQLi vulnerability being introduce if proper compensating controls are not put into place. This is because the frontend will send queries to the underlying SQL server on behalf of the websites visitors. In the instance of xBlog when victors clicked on a navigation link to a blog post the frontend would send a HTTP GET request with parameters asking for the specific blog post to be delivered back to the visitors. Each one of these parameters must be individually tested for SQLi.
What is Blind SQL Injection?
In a blind SQLi vulnerability the attacker cannot rely on the underlying SQL server to present human readable information back them. The attacker must rely on inferring HTTP responses to compere a successful query to an unsuccessful one. This can be done a couple common ways: time-based delays or status codes of HTTP responses. For instance, an attacker can use conditional logic to force a HTTP status code of 200 or 302. If the underlying database is MSSQL the payload would like similar to the following:
SELECT (CASE WHEN(SELECT is_trustworthy_on FROM SYS.databases where name = 'master')=1 THEN 1/0 ELSE null END);
Since the query is dividing by zero if the “master” database is trustworthy the SQL server would generate an error, and a HTTP 302 status code would be sent back to the attacker. Time based works similar but instead of dividing by zero the attacker would use syntax to delay the HTTP response by a few seconds.
Discovery
Discovering a blind SQLi vulnerability is all about understanding the underlying infrastructure of a website (or web application.) What this means for the penetration tester is too due their due diligence during the reconnaissance phase of an engagement. The more information about the technologies used for a website the pentester knows before the actually testing begins the better. For example, before the vulnerability/exploitation phase of the pentest we found the xBlog zero day on we already knew the websites underlying SQL server was MSSQL thanks to tools like “whatweb” analyzing the technology stack and stating dnnsoftware (DotNetNuke) as the CMS. With this information we read the dnnsoftware documentation to find out the SQL server in use was MSSQL (https://www.dnnsoftware.com/docs/administrators/setup/set-up-sql.html.) This information reduced the flavor of SQL syntax we had to use down to one which allowed us to test more efficiently for SQLi vulnerabilities. Also, during reconnaissance we familiarized ourselves with the functions of the website by interacting with it as a normal user. This allowed us to identify the attack surface of interesting website HTTP request and endpoints. By the time injection testing begun we already knew what we were looking for and where.
By sending the interesting HTTP request to Burp Suite repeater we were able to quickly test for SQLi across all parameters. The way to test for SQLi is first verifying a precursor exists for it. For example, you could send a single quote (‘) and a than two single quotes (‘‘) for a parameters value and compare the results. If a single quote gives you an error and two single quotes do not you can move on to the exploitation phase of SQLi and try to extract data. The quote approach works well for sting parameters as the goal is to terminate a query string early. Similarly, if the parameter you are testing is a number you can try and use arithmetic. For example, you have a parameter named “id” with the value of 2 (http://evilcorp.com?id=2) in a request you can replace it with 1+1 (http://evilcorp.com?id=1+1) and if you get the same result SQLi may be possible. Another way to try and identify if SLQi is possible is by sending the SQL keyword “null” as a parameters value one request and a gibberish string the next like “asdf” and comparing the HTTP response status codes. If the HTTP status codes are different (null=200 and asdf=302) more in depth testing for SQLi must be done. Different ways and nuances of testing for SQLi deserves a standalone blog post.
Connecting the Dots: xBlog Exploitation
By using the null SQL keyword detection technique, we were able to tell unauthenticated blind SQLi injection should be possible against xBlog. To show impact of this vulnerability we extracted data from the MSSQL server. We did this by using conditional logic with MSSQL SUBSTRING (https://www.w3schools.com/sql/func_sqlserver_substring.asp) function to break up query results into individual characters, which can be enumerated separately. The MSSQL syntax to do this is:
SELECT (CASE WHEN (SUBSTRING((SELECT TOP (1) email FROM users),1,1)='a') THEN+1/0+ELSE null END;
The above query queries the first character of the email address of the top record in the users table and check to see if it equals ‘a’ (HTTP 302 response) or not (HTTP 200 response.) The data that is being extracted is an email address, so case sensitivity does not matter. However, if extracting a password, the substring should be casted to a number first than conditional logic applied. Let’s assume the first character of the email address is ‘a’ the next query would be:
SELECT (CASE WHEN (SUBSTRING((SELECT TOP (1) email FROM users),1,2)='aa') THEN+1/0+ELSE null END;
The queries would keep incrementing until the email address has been completely extracted as the below images display. It is important to note in the images the queries are URL encode.
Conclusion
As with all vulnerabilities we find in third party vendor code we reached out to DDNGo on behalf of our client to facilitate triage. Currently, DDNGo has coded a fix that is in beta but can be found here for download https://demo8.dnngo.net/DNNGo_xBlog_09.09.10_PA.zip. If you are using version 6.5.0 or older of xBlog and want to mitigate this vulnerability before the official release, you’ll have to install the beta version. We want to thank DDNGo for addressing this vulnerability in a timely manner. The vulnerability has been submitted for a CVE identifier and this article will be updated once that is received.