Lateral movement via MSSQL: a tale of CLR and socket reuse

栏目: IT技术 · 发布时间: 3天前



Recently, ourRed Team had to deal with a restricted scenario, where all traffic from the DMZ to the main network was blocked, except for connections to specific services like databases and some web applications. In this article, we will explain how we overcame the situation, covering the technical details. We also introduce mssqlproxy , a tool for turning a Microsoft SQL Server into a socks proxy.

Introduction and context

At some point, sysadmin access to a Microsoft SQL Server within the main network is gained, but only traffic to the 1433 port is allowed. After trying to launch a reverse shell via xp_cmdshell, the team realizes that connections back to the DMZ were also blocked.

Lateral movement via MSSQL: a tale of CLR and socket reuse
Restricted environment

In order to turn the SQL Server into a useful pivot, we decided to apply some of the old socket reuse techniques [ 1 ] usually related to one-way shellcodes. The idea is to reuse the connection itself between the client and the database to pass data through, turning it into a working socks proxy.

Lateral movement via MSSQL: a tale of CLR and socket reuse
Tunnel created via socket reuse

The first step is to execute code in the SQL Server process context. As extended stored procedures are going to be deprecated in future versions of MSSQL, we pay attention to Microsoft recommendations and thus, use CLR assemblies instead.

CLR Assemblies

The usage of CLR assemblies in post-exploitation is another well-known technique. This article from NetSPI [ 2 ] is a good starting point to understand how to implement your own assemblies.

In our case, we decided to develop an assembly which will be responsible of loading a DLL (written in C) and executing the whole networking logic. In the future, we may do all the work from the CLR itself.

To dynamically invoke a function of our DLL, we make use of LoadLibrary (to load the DLL) and GetProcAddress (to get a pointer to the function). However, we cannot call this function directly as we can do in C, because C# doesn’t support function pointers [ 3 ]. Here we first have to convert the pointer to a Delegate object, using Marshal.GetDelegateForFunctionPointer .

Server: socket’s hunt and proxyfication

Let’s move on to the DLL part, where all the logic resides. To reuse the client’s socket, the first step is to find it. Different approaches can be used to perform this task, each with its own advantages and drawbacks. In this case, we choose the less intrusive way: findport . It consists of socket handler bruteforcing (since it’s identified by an integer), iterating until the target socket is found. The same technique has been used in the past by our Red Team in MySQL scenarios related with persistance [ 4 ]. In this case, we can match the source address and port if the connection is made directly. If there are intermediaries (e.g. proxies, NAT/PAT) we can check the source address only if we are sure that there are no more connections from that host.

Since the whole process is triggered by a SQL query, we can obtain the source address of the request by querying CONNECTIONPROPERTY('client_net_address') . This way, if the connection is made via an intermediary, we can still get the right IP address without knowing it beforehand. If the connection is direct, we also provide the source port, obtained on the client side.

The findport bruteforce can be summarized as:

    // Iterate over all sockets
    for (i = 1; i < max_socket; i++) { 
        len = sizeof(sockaddr);

        // Check if it is a socket
        if (getpeername((SOCKET)i, (SOCKADDR*)&sockaddr, &len) == 0) {
            // Check if it is our target
			if (strcmp(inet_ntoa(sockaddr.sin_addr), client_addr) == 0 && (client_port == 0 || sockaddr.sin_port == htons(client_port))) {

We noticed that one connection opened multiple socket handlers, and other threads were interfering. To make sure we fully control the socket, we can duplicate it ( WSADuplicateSocket ) and close the original one ( closesocket ). After that, a initialization message is sent to the client, to announce that the proxy socks is ready.

SOCKS5 is a simple protocol that can be implemented in a few lines of code. The server code was simplified and adapted from this repo . We don’t want to reinvent the wheel, so the client stuff was developed on top of impacket , expanding the capabilities.

Client: 2.0

File upload/download

First of all, we need to somehow upload files to the server, since we need to host the DLL. This can be done by using Ole Automation Stored Procedures to instanciate an ADODB.Stream object [ 5 ].

EXEC sp_OACreate 'ADODB.Stream', @ob OUTPUT;
EXEC sp_OASetProperty @ob, 'Type', 1;
EXEC sp_OAMethod @ob, 'Open';
EXEC sp_OAMethod @ob, 'Write', NULL, 'C:\path\file.dll';
EXEC sp_OAMethod @ob, 'SaveToFile', NULL, 0x[HEXDATA], 2;
EXEC sp_OAMethod @ob, 'Close';
EXEC sp_OADestroy @ob;

Although not necessary for this scenario to work, we also added a download command. Files can be easily read from SQL using OPENROWSET with the BULK option [ 6 ].


Proxy mode

Relating to the proxy stuff, four commands have been implemented:

  • install : Creates the CLR assembly and links it to a stored procedure. You need to provide the -clr param to read the generated CLR from a local DLL file.
  • uninstall : Removes what install creates.
  • check : Checks if everything is ready to start the proxy. Requires to provide the server DLL location ( -reciclador ), which can be uploaded using the upload command.
  • start : Starts the proxy. If not -local-port is specified, it will start listening on port 1337.

Once the proxy is started, you can plug in your proxychains :wink:

Note:if your connection is not direct (e.g. proxies in between), you have to use the -no-check-src-port flag, so the server will only check the source address.


In this article we saw how to overcome a situation where the network restrictions only allow us to use a legitimate MSSQL service. This kind of isolation is extremely fragile and can be bypassed as long as the attacker can execute arbitrary code in the compromised service. The same approach can be translated to any other service that meets the conditions of loading custom code to hijack a client socket.


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网







CLR via C#

CLR via C#

Jeffrey Richter / 周靖 / 清华大学出版社 / 2015-1-1 / CNY 109.00

《CLR via C#(第4版)》针对CLR和.NET Framework 4.5进行深入、全面的探讨,并结合实例介绍了如何利用它们进行设计、开发和调试。全书5部分共29章。第Ⅰ部分介绍CLR基础,第Ⅱ部分解释如何设计类型,第Ⅲ部分介绍基本类型,第Ⅳ部分以核心机制为主题,第Ⅴ部分重点介绍线程处理。 通过本书的阅读,读者可以掌握CLR和.NET Framework的精髓,轻松、高效地创建高性能......一起来看看 《CLR via C#》 这本书的介绍吧!



SHA 加密
SHA 加密

SHA 加密工具