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 led to our discovery of an SQLi zero-day within DNNGos xBlog plugin for the open-sourced Windows CMS DNN Software, formerly known as DotNetNuke. Furthermore, this vulnerability was found in a client’s public-facing website without authentication mechanisms or input fields. The remainder 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 susceptible to an SQLi vulnerability being introduced if proper compensating controls are not put into place. This is because the front end will send queries to the underlying SQL server on behalf of the website’s visitors. In the instance of xBlog when victors clicked on a navigation link to a blog post the front-end would send an 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 SQL injection (SQLi) vulnerability, the attacker cannot directly retrieve human-readable information from the underlying SQL server. Instead, the attacker must infer the results of queries by analyzing changes in HTTP responses to distinguish between successful and unsuccessful queries. This can be achieved through common techniques such as time-based delays or status code analysis. For example, an attacker might use conditional SQL logic to trigger specific HTTP status codes, such as 200 OK for success or 302 Found for redirection, allowing them to infer the query’s outcome indirectly. 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 divided by zero, if the “master” database is trustworthy the SQL server would generate an error, and an 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
Uncovering a blind SQL injection vulnerability requires a thorough understanding of a website’s or web application’s underlying infrastructure. For security researchers, this highlights the importance of performing comprehensive reconnaissance during the early stages of an engagement. The more information a researcher gathers about the technologies supporting a target application, the more efficient their testing process will be.
For instance, during the reconnaissance phase of an assessment involving the xBlog zero-day, we identified that the website’s back end was powered by MSSQL. This insight was obtained using tools like WhatWeb, which revealed the technology stack and identified DNN Software DotNetNuke as the CMS. By referencing DNN Software’s documentation, we confirmed that the SQL server in use was MSSQL, which allowed us to tailor our SQL injection testing to this specific database type. This reduced the need to test multiple SQL syntaxes, streamlining the vulnerability discovery process. Additionally, by interacting with the application as a typical user during reconnaissance, we were able to identify key HTTP requests and potentially vulnerable endpoints. This approach not only defined the application’s attack surface but also ensured that when the injection testing phase began, we already had a clear understanding of what to test and where to focus our efforts.
Using tools like Burp Suite Repeater, we tested for SQL injection vulnerabilities across all parameters in identified HTTP requests. The first step in testing for SQL injection is to verify the existence of a potential vulnerability. A common technique involves testing the application’s response to input that could disrupt or terminate an SQL query. For example, when testing string parameters, sending a single quote (‘) and then two single quotes (”) as parameter values can help identify inconsistencies. If a single quote triggers an error, but two single quotes do not, it suggests that the query structure can be influenced, signaling a possible SQL injection vulnerability.
For numeric parameters, arithmetic operations can be used to test for SQL injection. For instance, if a parameter named ‘id’ has a value of 2 (e.g., http://evilcorp.com?id=2), replacing it with 1+1 (e.g., http://evilcorp.com?id=1+1) and observing whether the response remains consistent can indicate the potential for SQL injection. Similarly, testing with SQL keywords like ‘NULL’ versus random strings such as ‘asdf’ can provide insights. For example, if ‘NULL’ as a parameter value returns an HTTP 200 status code, but a gibberish string like ‘asdf’ returns a 302, further testing is warranted to confirm if a vulnerability is present. While these techniques offer an efficient starting point, the nuances of SQL injection testing vary widely based on the application’s configuration and the type of SQL back end in use. Exploring these nuances and advanced exploitation techniques would require a separate, dedicated discussion.
Connecting the Dots: xBlog Exploitation
By using the ‘NULL’ SQL keyword detection technique, we were able to identify unauthenticated blind SQL injection against xBlog. To demonstrate the impact of this vulnerability we extracted data from the target MSSQL server. This was accomplished using conditional logic with the MSSQL SUBSTRING 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 checks 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 first be cast to a number then 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 had been completely extracted as the below images demonstrate. It is important to note in the images the queries are URL encoded.
Conclusion
As with any vulnerabilities identified in third-party vendor code, InvokeSec promptly reached out to DDNGo on behalf of our client to facilitate triage and resolution. DDNGo has since developed a fix for the vulnerability, which is currently available in beta. The beta release can be downloaded for those using xBlog version 6.5.0 or earlier and seeking to mitigate the issue before the official patch release. We want to thank DDNGo for promptly addressing this vulnerability. The vulnerability has been submitted for a CVE identifier and this article will be updated once that is received.
Update February 7, 2025 – This finding has been assigned CVE-2024-55212.