Recently I started playing with the awesome PowerUpSQL tool by guys at NetSPI. I was interested in the ability to attack an Active Directory (AD) environment using access to a SQL Server, that is, not leaving the database layer as long as possible. Fortunately, during a Red team engagement few weeks back, I had a chance to play with PowerUpSQL extensively. Turns out that it is very much possible to enumerate and attack not only the current domain but a trusting forest in a Two-Way External Trust as well from the database layer. Let's have a look at it!
I have mapped the client network to my lab on a much smaller scale. We have access to the SQL Server ops-sqlsrvone where we have public privileges and can communicate to only selected machines on the defensiveps.com forest.
It is possible to enumerate the current domain accounts using PowerUpSQL using interesting fuzzing methods. In our lab setup we know that there is a trust relationship from offensiveps with a forest called defensiveps (we can use PowerView, netdom or Get-ADTrust). But PowerUpSQL does not provide a way of specifying an alternate domain to enumerate accounts. We can change a single variable in the code to use an alternate domain.
Now, it is possible to enumerate interesting information from the target domain which is in a different forest. After modifying the value of $Domain variable, import the PowerUpSQL module and run the below command:
Neat! We got a list of target domain's users, groups, computers etc.
The enumeration done above helped in listing SQL Servers in the defensiveps domain. Now, mimicking the network I encountered during the assessment, it is possible to access only dps-sqlsrvdev, couple of DCs and some terminal servers in the defensiveps network directly. So, let's enumerate dps-sqlsrvdev:
Note that our current user is listed as a login above and that is why it is possible to enumerate the above. Let's check the current privileges we have:
No sysadmin privileges. We can check it manually as well (I am using HeidiSQL as a client):
We can go ahead with a brute-force attack as there are some interesting SQL server logins and generally, account lockouts are not enabled in SQL Server databases and nobody really looks at the logs of authentication failure in SQL Server, at least, on non-production servers. But we are not going to do that right now.
We can also enumerate linked servers for dps-sqlsrvdev. Let's do it:
Nice! A server, dps-sqlsrvtwo, in the defensiveps domain - which we enumerated earlier as well - is linked to the current database. Note that it is possible to run arbitrary SQL queries on the linked database even if we have only public privileges on both the initial and destination servers with the privileges configured in the link. Read more about hacking SQL Server links in the amazing blog by Antti. Link enumeration can be done manually as well:
So, dps-sqlsrvdev has a linked server dps-sqlsrvtwo.
Now, to execute queries on the destination server (dps-sqlsrvtwo) we can use Openquery as suggested by Antti in the blog linked above. Let's see our current user and if we have sysadmin privileges:
Turns out that we have only public privileges with a user called dbuser and the target server is SQL Server 2016 SP1.
Now, we can try various methods from PowerUpSQL for privilege escalation on SQL Server. The problem is we can't access dps-sqlsrvtwo directly and AFAIK, there is no way to execute these commands on a linked server using the tool. So, we need to try the methods manually, one by one. During the Red Team assessment, I found out a user which we can impersonate on a linked server. So, let's use that in our lab setup as well. To list all the users which we can impersonate from our current user can be listed using the following SQL query stolen directly from this amazing blog by Scott. We are going to use the query inside Openquery so that it can be executed on the linked server:
Looks like we can impersonate a user called "reportsuser". But a command like below is most likely to fail:
Why? Because, apparently, it is not possible to use EXECUTE AS without getting our privileges revert to the original 'dbuser'. I tried WITH NO REVERT option as well but soon realized that it may work only when sending the query directly to a database. Please see this MSDN documentation on EXECUTE AS.
No luck there! Let's look for another interesting privilege escalation avenue - trustworthy database. Read this very useful blog, once again by Scott, to understand more about trustworthy database. We can use the following query - taken from the blog referenced above - to enumerate trustworthy databases on the target linked server:
A trustworthy database 'reports_db'! Let's list users with db_owner role on the server:
Now, let's see if our current user - dbuser - is db_owner of reports_db on the linked server. In place of checking the role, let's try to create a stored procedure in the reports_db database which can help us in privilege escalation. Please note that to create a stored procedure RPC Out must be enabled for the linked server - which is not enabled by default but quite common in case of linked servers. The idea is to create a stored procedure which gets executed as OWNER which is the user 'sa'. Use below query to create a stored procedure on the linked database.Our stored procedure makes dbuser a sysadmin. Now, let's execute the stored procedure.
Neat! Now we can access all databases and tables on the target server. PowerUpSQL provides very useful commands for pillaging a database (and that is what I used first in the assessment to capture some juicy data) but we are not going to use them. See this blog post for details about that.
In many cases, this is one of the major goals of red team assessments, staying within the database layer we have access to multiple SQL servers across forest trust and juicy information stored within them. Since we have not done anything very unusual or noisy up to now, there are very low chances of detection. In fact, in my lab I have Microsoft Advanced Threat Analytics (ATA) set up and there was no detection of the attack. Obviously, because we did not communicate to the domain controller at all and ATA looks at only the DC traffic. Take that ATA!
Since, RPC Out is enabled on the linked server and we have sysadmin privileges, it is possible to enable xp_cmdshell and achieve OS command execution! Please note that if xp_cmdshell was already enabled on the linked server, we could execute OS commands without RPC Out while using only Openquery! Use below to enable xp_cmdshellAnd let's see the privileges of the database process:
Great! Looks like the SQL server process is running with a domain user (sqlprodadmin) privileges. We can now hunt for a DA token from the normal domain user privileges we have on dps-sqlsrvtwo. Let's use the awesome PowerView for the DA token hunting. Remember that we cannot access our linked server dps-sqlsrvtwo directly and we do not have command execution on dps-sqlsrvdev. To load PowerView on dps-sqlsrvtwo, we can download and execute it in memory using PowerShell one-liner.
The one-liner needs to be encoded so that the URL doesn't mess up with the syntax of SQL query. Also, make sure that PowerView is modified a bit to include call to functions in the script itself and to receive the output the function calls must be piped to Out-Host.
And let's execute the encoded command:
Awesome! Looks like on the server dps-srvjump a DA token is available and our current user has local admin access. Let's dump NTLM hash of the DA - Administrator from dps-srvjump using Invoke-Mimikatz.
Finally, let's use these hashes with Invoke-Mimikatz to run a command on the DC of defensiveps. The DC of defensiveps is accessible from our machine in the offensiveps forest.
Bingo! DA access in the target forest!
We started with a non-admin domain user and worked our way to multiple SQL Servers while staying only at the database layer. We also got domain admin/enterprise admin in a trusting forest! :)
SQL Server Level
Multiple common mitigations like having limited linked databases and not enabling RPC Out on linked servers would have helped. Also, restricted allocation of privileges, even the public login, will help. One of the databases we encountered later on was running with a domain user's privileges. This is disastrous as it opens up many opportunities for privilege escalation on the domain level! Restricting privileges with which the database processes run is always desired.
Many improvements can be made. Allowing a local administrator on a box where a Domain Admin can log in is very very dangerous and results in disastrous situations like the one we saw above. If there is a box where DAs' privileges are required no other administrative account should be present. Logs will also tell you about a successful DA authentication from a forest if someone is looking for such information. Also, Selective Authentication can help in forest trust scenarios.
A note on ATA
I have Microsoft ATA setup in the lab where all the attacks took place. Since, ATA is the new sheriff in town let's discuss it a bit. ATA detect anomalies by looking at the traffic destined to the DC(s) by port mirroring. If we can limit ourselves to those attacks and techniques where there is no or minimal interaction with a DC, it is possible to avoid ATA and still get access to the most interesting machines and information. ATA thrives on Red Teamer/attacker's desire of going for DA rights as soon as possible. It is not always necessary to go after DA and use Golden ticket/Skeleton key/Credentials replay attacks for achieving the goal of an assessment unless, of course, you want to brag about it in your report :D Of course, there are bypasses for ATA as well but why bypass it when you can avoid it :)
Hope you enjoyed the post. Please leave comments, feedback and questions.