SQLi via xPath

Exploiting error-based SQL injections via xPath functions.

All began by adding a single quote, the most common SQL injection testing character.

identifying an SQL injection

Server will responde with a visible unhandled error.

You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '''' ORDER BY fecha DESC' at line 1

unhandled error displayed on web page

Quick explanation why the error is displayed, the unhandled error occurs first because the query has not been parameterized in the DB (allowing the introduction of unexpected queries) and when the error throw from the DB is not capture in the backend server.

Once SQL injection has been identified, time to validate if it can be exploited. Identify != exploit.

One way to validate the injection is to get the number of columns from the current query:

getting the number of columns from the original query

Once the number of columns is obtained (9 columns), use this information to form a new union query. The new query allows us having control to make own calls to the DB.

nesting a new query to the original

However a ‘403 Forbidden’ response is received. And in case keep trying either obfuscating/encoding characters next time will be a block.

blocking request by Imunify360

But even from a block response you can get information. And Imunify360 is used as WAF protection.

Knowing the used technology, could try to find specific bypass methods. With a quick search, multiple results come out, such as:

Obfuscate SQL injection by ahmetumitbayrm

Does not work. The WAF will have been updated since the past 2020.

As everything in this direction is getting complicated or failing, is a good time to change, look for an alternative way that the Imunify360 WAF does not expect.

There is an alternative way to exploit, indirectly the unhandled error (read it some time ago). The idea is to be able to control the error message. Have the ability to decide what data to show in the trace.

To do that, let’s talk first about XML Path Language (XPath). More information about what XPath is.

xPath definition

Summarizing XPath is a language originally built to search and find elements in an XML. Later also introduced in SQL language to retrieve data from a database.

XPath was implemented in MySQL in version 5.1. Of all the functions, the interesting one for this case is ExtractValue(), allows you to read from an XML and return the desired attribute. ExtractValue(xml_frag, xpath_expr) receives xml_frag as the first parameter, which will be the name of the attribute, and xpath_expr as the second parameter, which will be an XPath expression.

ExtractValue -> Image take from Nairuz Abulhul. Original post: Peter Wood

Understanding how the function works, now let’s try to validate the sqli.

Quick remember! don’t have to use the ExtractValue function correctly, but make it fail to produce a new error message “controlled” by us.

To better understand this part I am going to do some tests locally simulating the exploitation.

previously testing the original injection locally

The error generated from the second query is the one that can NOT be handled by us. So ExtractValue comes to the game, xml_frag parameter will look for an attribute = column THAT DOES NOT EXIST, for example a column called semicolon ‘ ; ‘ and from xpath_expr a MySQL function will be used. This way when the SQL query fails (beacuse the xml_frag semicolon) the function will be executed.

previously testing the XPath injection locally

0x3b is the hexadecimal representation of the semicolon. XPath injection works, but not quite. What’s weird? Well it seems that not all the expected output is observed. @localhost it’s not the current user but root@localhost.

This occurs because functions in XPath have a character limitation. Therefore, to be able to visualize all the output, multiple functions have to be concatenated at the same time.

solving XPath character limitation locally

XPath injection is now tested on the web

noticia'+and+extractvalue(0x3b,+concat(0x3b,current_user()));--+-

XPath injection

Try to read the first table of the DB.

noticia'+and+extractvalue(0x3b,+concat(0x3b,+(select+concat (0x3b,table_name)+from+infromation_schema.tables+where+table_schema= 'dbXXXXrod'+limit+0,1)));--+-

XPath injection II

This could now be automated and exfiltrate the entire DB.

The report that I read a long time ago and that helped me for this exploitation is:

https://medium.com/r3d-buck3t/error-based-xpath-sql-injection-in-openemr-2abbc0379ac5

Thank you %27