As a SQL engineer with an interest in security I’m convinced that security is a concept that should be implemented throughout an organization. Just placing a (properly configured) firewall and forcing password policies doesn’t suffice.
In light of that last statement I wondered whether or not it was possible to gain access to a MS SQL server without having domain credentials. And guess what, it is. In the rest of this post I’ll explain how I gained access. And more important, how you can prevent it. While searching for a method to achieve my goal I came across the following script.
For those of you not patient enough to read the original blogpost:
You create an Ettercap template that allows you to place yourself in the middle between server and client (man in the middle). By ARP (Address Resolution Protocol) spoofing you will make sure all packets from client to server will be intercepted. Each packet is inspected by Ettercap and has two outcomes:
- The packet contains another query than the one you want to replace. Ettercap forwards it to the server without replacing anything.
- The packet contains the query you wish to replace. Ettercap replaces the query and sends the packet to the server.
A schematic representation of the attack. Green is the normal path, red is the new path created by me using ARP spoofing.
Should you need extra info about the used techniques:
In his approach the author of the blogpost was using a very specific query such as ‘select * from products’. I wanted to improve this method to solve the following disadvantages:
- You need to know which databases are present on the server to find the right query.
- A user performing a ‘select’ usually doesn’t have the rights to create other users.
- A thoughtful user might notice the unexpected feedback ‘command executed successfully’.
As some of you might know, the information displayed in the object explorer of the SQL management studio is actually build by performing a bunch of queries on the system databases. And who uses a management studio to perform tasks…. Right the sysadmins.
So, I started a Wireshark trace while logging in to SQL Management Studio to figure out if my evil plan had any chance of success. All I had to do was go through the packets and find a query long enough to fit my ‘CREATE LOGIN …’ query.
This seemed like a good candidate:
select value_in_use from sys.configurations where configuration_id = 16384
create login Testing1 with password='test01', check_policy=off;
I have my query to replace. All I needed now is a client and server. To prevent my post of going into deep (remember, we want to secure SQL) I will not discuss this in detail. But, if I were to use my ‘tactic’ in the wild I would do the following:
- Use ‘dig’ to list all DNS records in the domain.
- Search for machines clearly showing me:
- Mgt (or a variant): Find myself a management server, a big change of having the management studio installed.
- Sql (or a variant): Find myself a MS SQL database server
Maybe it’s time to reconsider those meaningful server names as well? At this point I have the following:
- A client (management studio) and database server (MS SQL)
- A good query to replace with my own
I’ll execute my script using the following command:
./SQLInject.sh -s 192.168.1.11 -c 192.168.1.1 -o "select value_in_use from sys.configurations where configuration_id = 16384" -i "create login Testing1 with password='test01',check_policy=off;" Ettercaptemplate.temp
To prevent too much repetition , I’ll not be adding my login to the sysadmin role. Let’s just assume that the user ‘causing’ my login to be created will also allow me to add it to the sysadmin role granting me my desired full access. I would just launch another instance of the script replacing my new query with ‘alter role …’.
Once my script is launched I only have to wait for my console to report a succeeded replacement. Please note that if your company has 100 SQL servers I could just launch 100 instances of my script.
Let’s do a trial run on a SQL server installed without any security configured.
Login is not present on our system:
Login with SQL authentication:
Our original query was discovered and replaced:
Whilst testing I also noticed this approach had some unexpected advantage. If my query is executed successfully the end user actually sees no feedback what so ever! Meaning, this technique is invisible to the end user.
Now, let’s test with Windows authentication (this is supposed to be safer, right?):
Drop our login:
Login with Windows authentication:
Let’s configure Kerberos (adding another layer of security). Spoiler alert, for those of you understanding Kerberos you’ll know this will not solve our issue.
I’ve rebooted my workstation as well to clear my old Kerberos tickets.
Log in to SQL and see if our account creation succeeds:
As expected with Kerberos enabled we are still vulnerable to this kind of attack.
What else could we do to protect ourselves?
Let’s try enabling encryption. After enabling encryption off course a SQL restart was required. But it looks like we got there:
As you can see our Ettercap is able to detect the SQL traffic, but since it is encrypted there is no way for the tool to read the queries and perform a replacement.
Out of the box SQL security is at a very low level. It is highly recommended to configure extra security. There are other ways to protect yourselves to this kind of attack (prevent ARP spoofing for one). But if you prevent SQL Injection pretty much all man in the middle attacks will become impossible. It goes without saying that you should still prevent SQL Injection by proper application development as well.
All tests were executed on Windows 2016 Server machines patched up to January 2018. The SQL version installed is SQL 2017. The SSMS was version 17.4.
For our Linux environment I used the ‘Kali-linux-2017-3’ VM template available on the Kali website.