• Oxford: +44 (0)1865 877830 
  • Manchester: +44 (0)161 713 0176 
  • Edinburgh: +44 (0)131 541 0118 
  • New York: +1 646-781-7580 
  • Dubai: +971 (0)4 427 0429

Fun with SQL Injection using Unicode Smuggling

You are here



Fun with SQL Injection using Unicode Smuggling

During a recent test, I ran into a curious SQL injection vulnerability that required some old but still valid tricks to bypass certain restrictions, and then some imagination to fully exploit it and get command execution on the vulnerable server.

First off, identifying the SQL injection was trivial, our good old friend, the single quote, helped me with this. The response below is what the website returned when a single quote was sent via the vulnerable parameter:


  "$id": "1",

  "HasError": true,

  "Error": "Cannot insert duplicate key row in object 'dbo.AddressType' with unique index 'UX_AddressType_Name'. The duplicate key value is (test').

insert into  AddressType (Name,IsInUse,SystemRequired) values  ('test''',

insert into  AddressType (Name,IsInUse,SystemRequired) values  ('test''',0,\r\n insert into  AddressType (Name,IsInUse,SystemRequired) values  ('test''',0,0)\r\nThe statement has been terminated.",

  "ErrorCode": 998


One thing to note from the error message above is that the single quote in the payload is being doubled, so it seemed as if the developers decided to replace every single quote with 2 double quotes.

This was quite suspicious, so after doing some research, I found a very old technique from 2007 called Unicode Smuggling (https://www.owasp.org/images/d/d4/OWASP_IL_2007_SQL_Smuggling.pdf). The idea is simple and is used in many other type of attacks; you send a Unicode character similar to the character being blacklisted (in this case replaced), and if you are lucky it will get converted into what you actually wanted to send. In this case, we have the Unicode character U+02BC (ʼ) that can be used for these type of attacks, so I gave it a go with the following payload:


"columnValue": "SQLiʼ,0,0);WAITFOR DELAY ʼ0:0:10ʼ;--"


And the response from the server, with the time of the response highlighted in green, was as shown below:


After this, and to my surprise, I verified that the database user had sysadmin permissions on the Microsoft SQL Server 2016 database, which meant that after a couple more requests I enabled our good old friend "xp_cmdshell" and had a basic shell on the server.

As lucky as I was, I ran into issues with the payload size, which made it really difficult to run useful commands. After trying a few different techniques to exfiltrate data via DNS requests and other esoteric options, I figured out a somehow elegant way of delivering longer payloads without running into the length issue that I commented before, by breaking down exploitation as detailed below:

  1. I created a new temporary table:

  1. Stored the results of "xp_cmdshell" in it:

  1. Then I used the vulnerable SQL query to read the output from the previous table. As I am quite lazy, and didn’t want to have to make another request to read the results, I forced a casting error, so I could read the output of the payload directly!

As you can see, by combining some well-known but not common bypasses with some imagination, it's still possible to have good fun with SQL injection!

Posted by Antonio

Leave a comment