Microsoft SQL, Beacon, and YOU

If you're a frequent reader of my blog, you might know that mssql_clr_payload is my favorite Metasploit module. This module provides authenticated code execution to Windows hosts over the Microsoft SQL service, typically seen on TCP port 1433. There are a handful of reasons this module is so good, but before we talk about them we need to discuss the classic method of code execution in MSSQL: xp_cmdshell.

xp_cmdshell: not just for Windows XP

xp_cmdshell is a Microsoft SQL Stored Procedure that allows attackers and system administrators alike to run command prompt commands on the underlying Windows operating system. This stored procedure can only be enabled and executed by a member of the database owners (dbo) group. After using mssqlclient.py to log into the database as a database admin, we can run OS commands with xp_cmdshell.

Running whoami and hostname commands using xp_cmdshell
Running whoami and hostname commands using xp_cmdshell

From here we could run a payload stager command, such as a powershell one-liner or mshta command.

Running an mshta stager command using xp_cmdshell
Running an mshta stager command using xp_cmdshell

This probably works more than it doesn't, but the process tree created from these actions is extremely suspicious. Any defender worth their salt could instantly see the evil that is happening to their system.

Process tree created from running an mshta command with xp_cmdshell
Process tree created from running an mshta command with xp_cmdshell

A better alternative: mssql_clr_payload

Instead of spawning all of these processes and getting ourselves caught, we could use mssql_clr_payload to inject shellcode directly into the SQL server process! No spawning new processes and no temporary payload files on disk. So how does it work? Under the hood, mssql_clr_payload creates a temporary SQL stored procedure which loads and executes shellcode in a new local thread. This code is only a few lines long and is shown below:

After this code is compiled as a DLL, it is hex encoded and used to create a new stored procedure with the following SQL queries:

CREATE ASSEMBLY [bYPUJKKUSmt] AUTHORIZATION [dbo] FROM <HexEncodedSqlClrPayload.dll> WITH PERMISSION_SET = UNSAFE

CREATE PROCEDURE [dbo].[gVFGEzbc](@huLHhBVAqsf AS NVARCHAR(MAX)) AS EXTERNAL NAME [bYPUJKKUSmt].[StoredProcedures].[ExecuteB64Payload]

The shellcode is loaded and executed by running the newly created stored procedure:

EXEC [dbo].[gVFGEzbc] '<Base64EncodedShellcode>'

Finally, the assembly and stored procedure are removed from the database, and settings are restored as appropriate.

Typically this module uses Meterpreter shellcode, but it is easy to bring your own shellcode by setting the module payload to use generic/custom and specifying your own shellcode file.

Setting the module to use Cobalt Strike Beacon shellcode as the custom payload
Setting the module to use Cobalt Strike Beacon shellcode as the custom payload

After running the module, we get a new beacon inside of the sqlservr.exe process!

New beacon checkin from execution of mssql_clr_payload
New beacon checkin from execution of mssql_clr_payload

A note on not crashing the SQL server

Since our beacon lives inside of the SQL server process, it's very important to take precautions not to crash the process. Nobody wants a dead SQL server. By default, most shellcode is generated with exit function of process. This means when we shut down our beacon, it internally calls the ExitProcess function, terminating all threads inside the process, including those belonging to the SQL server.

Metasploit payload with exitfunc of process
Metasploit payload with exitfunc of process

Instead, we need to generate shellcode with an exitfunction of thread. Metasploit makes this easy, and you can change it just by setting exitfunc to thread. Cobalt strike, however, does not expose the ability to generate shellcode with exitfunc of thread from its GUI. Instead, we can use a simple aggressor script to generate this kind of shellcode:

After generating shellcode with the proper architecture and exit function, we can finally shell our SQL server without crashing it!

Exiting beacon
Exiting beacon

After we exit the beacon process, the beacon thread exits itself, leaving the other SQL server threads unaffected. The SQL server continues to function as normal.

Logging back into the SQL server to confirm the process is still running
Logging back into the SQL server to confirm the process is still running

This article was updated on July 11, 2022