Fat Client Pentests: Analyse und Proxying von Netzwerkverkehr in Desktop-Anwendungen

24. Juni 2025

Beim Testen von Desktop-Anwendungen, sogenannten Fat Clients oder Thick Clients, spielt die Analyse des Netzwerkverkehrs eine zentrale Rolle in jedem Penetrationstest. Die Möglichkeit, den von der Anwendung erzeugten und empfangenen Datenverkehr zu überwachen und zu verändern, ist häufig der Schlüssel zum Aufdecken sicherheitsrelevanter Schwachstellen. Hierfür ist es notwendig, eine sogenannte Machine-in-the-Middle-Position (MitM) einzunehmen, denn nur so kann der Traffic während der Übertragung mitgelesen oder verändert werden.

Unser Kollege Florian Haag, Managing Consultant und Experte für Fat Client Pentests, erläutert in diesem Deep Dive Artikel ausführlich, wie sich diese Position auch bei komplexen Anwendungen erreichen lässt, welchen Herausforderungen man dabei begegnet und welche Lösungen sich in der Praxis bewährt haben.

Erste Analyseschritte

Zu Beginn der Analyse einer Anwendung besteht der erste Schritt darin, sich einen Überblick über alle Netzwerkverbindungen zu verschaffen, die ein bestimmter Client öffnet. Da Microsoft Windows nach wie vor das am weitesten verbreitete Betriebssystem auf Arbeitsplätzen ist, haben wir eine Analyseumgebung eingerichtet, in der wir über volle Administratorrechte verfügen, um Prozesse umfassend untersuchen zu können. Wir beginnen mit dem Start der SysInternals Suite von Microsoft, insbesondere des Tools ProcMon. Dieses ermöglicht es uns, Dateisystem- und Registry-Ereignisse sowie netzwerkbezogene Aktivitäten der Anwendung aufzuzeichnen.

Die Zielanwendung für eine gewisse Zeit zu starten und zu nutzen, gibt uns Aufschluss darüber, mit welchen Endpunkten die Anwendung kommuniziert, während sie verwendet wird. Wir notieren alle dabei beobachteten URLs, IP-Adressen und Ports für die weitere Analyse. Genau diese Domains stehen im Fokus unseres Proxyings, da wir die Möglichkeit erhalten wollen, die übertragenen Daten zu lesen – sowohl bei eingehender als auch ausgehender Kommunikation.

Proxy-fähige vs Proxy-unfähige Clients

Nachdem wir nun wissen, welche Datenströme für uns interessant sind, wollen wir darüber sprechen, wie man eine Machine-in-the-Middle-Position (MitM) einnimmt und worauf dabei zu achten ist. Grundsätzlich gibt es zwei Arten von Anwendungen: Manche unterstützen Proxys entweder nativ oder über das zugrundeliegende Betriebssystem. Andere hingegen wurden speziell so entwickelt, dass ihr Datenverkehr nicht über einen Proxy geleitet werden kann. Besonders häufig ist dies bei sicherheitsrelevanter Software wie Antivirus-Agents der Fall, die ihren Datenverkehr gezielt vor Mitlesern und Manipulation schützen wollen.

Proxy ist nicht gleich Proxy: HTTP vs. Binäres Proxying

Ein weiterer Aspekt, den es zu berücksichtigen gilt, ist die Art des zu überwachenden Datenverkehrs. Das am häufigsten verwendete Protokoll für die Kommunikation zwischen Frontend- und Backend-Komponenten ist HTTP bzw. das verschlüsselte Pendant HTTPS. Bei Desktop-Anwendungen kommen jedoch oftmals binäre Protokolle zum Einsatz, entweder standardisierte oder sogar eigens entwickelte Formate. Zu den standardisierten binären Protokollen zählt beispielsweise Java RMI (Remote Method Invocation), das eine Remote Procedure Call-Funktionalität für Java-basierte Software bereitstellt. Die Client-Anwendung kodiert dabei sämtliche für den Funktionsaufruf notwendigen Informationen in einem dokumentierten binären Format und sendet dies zur Ausführung an die Server-Komponente.

Andere Anwendungen definieren komplett individuelle Protokolle („Contracts“) für die Kommunikation zwischen Client und Server. Häufig ist hier Reverse Engineering notwendig, um zu verstehen, wie die Nachrichten aufgebaut sind, welche Funktionen bereitgestellt werden und wie sich eigene Nachrichten erstellen lassen. Das Reverse Engineering von benutzerdefinierten Protokollen ist ein Thema für sich und wird in diesem Artikel nicht behandelt – vielleicht in einem späteren.

Welches Protokoll vorliegt, ist insbesondere für die Auswahl des passenden MitM-Proxys relevant. Der De-facto-Standard für HTTP/HTTPS-Traffic ist BurpSuite von PortSwigger. Beim Umgang mit binären Protokollen kommt die BurpSuite jedoch schnell an ihre Grenzen, und es fehlen komfortable Analysefunktionen. Zwar gibt es eine Erweiterung für die BurpSuite namens NoPE, die binäres Proxying ermöglicht. Allerdings hat diese Erweiterung, wie wir aus früheren Assessments feststellen konnten, auch Schwächen wie beispielsweise Stabilitätsprobleme. Unser bevorzugtes Werkzeug ist derzeit PETEP von warxim. Es ist ein großartiges Tool, das ähnlich wie BurpSuite aufgebaut ist, aber speziell für den Umgang mit binären Protokollen optimiert wurde – mit Funktionen wie dem Abfangen von Paketen, der Möglichkeit, Pakete zu wiederholen (Replay) und vielem mehr.

Möglichkeiten bei Proxy-fähigen Clients (Easy Mode)

Am einfachsten ist es, einen Proxy direkt in der Anwendung oder im zugrundeliegenden Framework zu konfigurieren. Bietet die Anwendung eine Möglichkeit, im Interface einen Proxy-Server einzutragen, ist das natürlich der offensichtlichste Weg. Manchmal lässt sich ein Proxy auch über das darunterliegende Framework einstellen. Das .NET Framework beispielsweise legt nach dem Kompilieren eine „app.config“-Datei neben der Anwendungsdatei an. Diese Datei kann genutzt werden, um sowohl die Anwendung selbst als auch das zugrunde liegende Framework zu konfigurieren. Mithilfe der „defaultProxy“-Direktive ist es möglicherweise möglich, die Anwendung dazu zu bringen, ihren Datenverkehr über einen Proxy zu leiten.

Ein einfaches Beispiel: Heißt die Anwendung „application.exe“, trägt die dazugehörige Konfigurationsdatei standardmäßig den Namen „application.exe.config“. Fügt man die folgenden Zeilen hinzu, wird „localhost:8080“ als Standardproxy für den gesamten HTTP/HTTPS-Verkehr gesetzt. Da „bypassonlocal“ ebenfalls aktiviert ist, wird auch der Verkehr zu localhost über den gewählten Proxy geleitet.

<configuration>
  <system.net>
    <defaultProxy enabled="true">
      <proxy
        proxyaddress="http://127.0.0.1:8080"
        bypassonlocal="false"
      />
    </defaultProxy>
  </system.net>
</configuration>

Falls die Anwendung oder das Framework keine so komfortablen Möglichkeiten für das Proxying bieten, kann alternativ auf die Einstellungen des Betriebssystems zurückgegriffen werden. Dieser Ansatz ist etwas weiter gefasst, da dabei nicht nur der Datenverkehr der analysierten Anwendung, sondern auch der anderer auf dem Betriebssystem laufender Programme erfasst wird. Für diesen Fall haben wir zuvor unsere Liste der Ziel-Sockets erstellt, um mithilfe entsprechender Filter gezielt zu arbeiten.

Unter Windows kann ein Proxy eingerichtet werden, indem man in einer erhöhten Eingabeaufforderung den Befehl netsh winhttp set proxy <proxy-ip/domain>:<proxy-port> ausführt. Eine grafische Benutzeroberfläche zur Konfiguration gibt es sowohl in der (alten) Systemsteuerung als auch im (neueren) Windows-Einstellungsmenü.

Unter Linux läuft alles über Umgebungsvariablen. Die beiden wichtigsten betreffen das HTTP- bzw. HTTPS-Proxying. Einen Proxy für beide Protokolle kann man wie folgt setzen:

export HTTP_PROXY=http://127.0.0.1:8080
export HTTPS_PROXY=http://127.0.0.1:8080

Wenn du einen Proxy auf diese Weise einrichtest, solltest du den Geltungsbereich deiner Änderungen beachten. Damit die Konfiguration systemweit wirksam ist, muss sie in einer globalen Konfigurationsdatei wie „/etc/environment“ oder „~/.profile“ vorgenommen werden. Wenn du die Umgebungsvariable jedoch nur in der Kommandozeile setzt, gilt die Änderung ausschließlich für die aktuelle Shell-Sitzung. Das kann jedoch auch von Vorteil sein: Startest du die Anwendung aus genau dieser Shell-Session heraus, wird dein Proxy nur den Datenverkehr abfangen, der von der als Kindprozess dieser speziellen Shell-Instanz laufenden Anwendung stammt.

Möglichkeiten bei Proxy-unfähigen Clients (Hard Mode)

Da du immer noch liest, gehe ich davon aus, dass alle oben beschriebenen Methoden nicht funktioniert haben oder nicht anwendbar waren. Jetzt ist es an der Zeit, ein bisschen trickreicher vorzugehen und das Zielsystem dazu zu bringen, mit unserer Proxy-Anwendung zu kommunizieren.

Um intern flexibel zu bleiben, greifen die meisten Anwendungen auf ihre Anwendungsserver oder andere Backend-Komponenten normalerweise über einen spezifischen Domainnamen zu. Bei jedem Verbindungsaufbau findet ein DNS-Lookup statt, bei dem der Domainname in die echte IP-Adresse aufgelöst wird, an die die Pakete gesendet werden sollen. Dieser DNS-Lookup kann entweder bei jedem Verbindungsversuch oder in manchen Fällen nur einmal beim Start der Anwendung erfolgen. Die Information wird danach im Client gecacht, und die Anwendung kommuniziert direkt mit dieser IP-Adresse. Da deine Anwendung bisher auf keine unserer Proxying-Versuche reagiert hat, besteht unser nächster Ansatz darin, sie mithilfe manipulierter DNS-Informationen dazu zu bringen, eine Verbindung zu unserem Proxy aufzubauen, indem wir ihr statt der legitimen Komponente die IP-Adresse unseres Proxys unterschieben.

Bevor wir ins Detail gehen, ein kurzer Hinweis zum Analyse-Setup: In diesem Stadium kann es hilfreich sein, mit zwei virtuellen Maschinen (VMs) zu arbeiten: Eine VM wird für die Zielanwendung genutzt (App-VM), die andere dient als Router (Router-VM). Die App-VM befindet sich im selben lokalen Subnetz wie die Router-VM, und letztere ist als Standard-Gateway auf der App-VM eingetragen. Auf diese Weise läuft der gesamte ausgehende Datenverkehr über die Router-VM, wo wir ihn analysieren können. Die Router-VM (in der Regel nutzen wir Kali Linux, da alle benötigten Tools bereits enthalten sind) hat zwei Netzwerkschnittstellen: Eine im lokalen Subnetz der App-VM und die andere in einem Netzwerk, das das Backend der Anwendung erreichen kann – sei es das Internet oder zum Beispiel ein Kunden-VPN bei einem professionellen Auftrag. Die Router-VM führt ein NAT (Network Address Translation) zwischen dem lokalen und dem ausgehenden Subnetz durch und ermöglicht das Weiterleiten von Verkehr.

Das folgende Skript erwartet dabei die Namen der lokalen (eingehenden) und der ausgehenden Netzwerkschnittstelle, setzt mithilfe von iptables das NAT um und aktiviert das Packet Forwarding. Das Skript muss mit Root-Rechten ausgeführt werden, da sowohl das Traffic-Forwarding als auch das Setzen von Firewall-Regeln hoch privilegierte Aktionen darstellen.

#!/bin/bash
source=$1
target=$2

iptables -P FORWARD ACCEPT
iptables -A FORWARD -i $source -o $target -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -t nat -A POSTROUTING -o $target -j MASQUERADE
sysctl net.ipv4.ip_forward=1

All dieser Aufwand mit einer zweiten VM ist notwendig, weil wir nun deutlich aggressiver in die Umleitung des Datenverkehrs eingreifen. Um sicherzustellen, dass unsere Tools einwandfrei arbeiten, ist es am besten, sie auf einer separaten VM laufen zu lassen, damit wir die Netzwerkeinstellungen der App-VM möglichst wenig beeinflussen.

Kommen wir nun zurück zur Umleitung per DNS. Die bequemste Möglichkeit, den DNS-Mechanismus auszuhebeln, bietet die Hosts-Datei: Unter Linux liegt sie in „/etc/hosts“ und unter Windows in „C:\Windows\System32\drivers\etc\hosts“. Diese Datei dient als statische Zuordnungstabelle für Domain-zu-IP-Auflösungen und enthält meist auch einen Eintrag für „localhost“. Du kannst hier einfach die gewünschten Domains hinzufügen und sie auf die IP-Adresse deines Proxys zeigen lassen. Dies ist allerdings nur auf Systemen möglich, auf denen du über Root- bzw. Administratorrechte verfügst, da das Ändern dieser Datei erhöhte Rechte voraussetzt.

Wenn eine Änderung der Hosts-Datei nicht möglich ist oder nicht der gewünschte Weg ist, kannst du auch einen eigenen DNS-Server auf deiner Router-VM betreiben. Unser Standard-Setup sieht folgendermaßen aus: Die Router-VM läuft mit dnsmasq, das sowohl als DHCP-Server für die App-VM fungiert, als auch die Verwaltung des lokalen Analysesubnetzes erleichtert. Zusätzlich kann dnsmasq einen DNS-Resolver bereitstellen oder selbst als DNS-Resolver dienen. Im folgenden Beispiel ist in Zeile 3 der DHCP-Bereich von .100 bis .200 im gewählten Subnetz definiert; die Leases sind 24 Stunden gültig. Die vierte Zeile kümmert sich um DNS und sorgt dafür, dass alle Anfragen an die Domain „appserver.com“ zur IP-Adresse 192.168.13.99 umgeleitet werden. So kann der gesamte Datenverkehr ganz einfach zum Proxy weitergeleitet werden.

interface=eth1
bind-interfaces
dhcp-range=192.168.13.100,192.168.13.200,24h
address=/appserver.com/192.168.13.99

Das nächste Werkzeug in unserem Arsenal besteht darin, noch tiefergehend in die Zielanwendung einzugreifen, um eine Interzeption des Netzwerkverkehrs zu erreichen. Unter Linux ist das Tool proxychains eine hervorragende Möglichkeit, die Verbindungswege zu seinen Gunsten zu beeinflussen. Proxychains kann für die meisten Distributionen direkt über den Standard-Paketmanager installiert werden, falls es nicht bereits vorhanden ist. Es kommt mit einer Konfigurationsdatei, überwiegend unter „/etc/proxychains4.conf“ zu finden. Am Ende der Datei kann man eine Kette von Proxys definieren (es sind also auch mehrere Proxys hintereinander möglich), über welche die Anfragen zunächst geleitet werden, bevor sie schließlich an die Zielanwendung weitergegeben werden. Außerdem kann das Proxy-Protokoll festgelegt werden: Neben HTTP und HTTPS lassen sich auch SOCKS4- oder SOCKS5-Proxys nutzen. Proxychains wird verwendet, indem man den Befehl beim Starten der eigentlichen Anwendung voranstellt, zum Beispiel: sudo proxychains ./application.

Proxychains arbeitet, indem es auf Betriebssystem-Ebene relevante Funktionsaufrufe zum Öffnen von Netzwerksockets abfängt („hookt“). Dadurch wird der Netzwerkverkehr umgeleitet, noch bevor das Paket tatsächlich gesendet wird. Ähnliche Technologien gibt es auch für Windows. Ein bekanntes, beim Schreiben dieses Textes jedoch leider veraltetes Werkzeug, ist EchoMirage. Dieses Projekt bietet ein GUI-Programm, das exakt dieselbe Funktionalität ermöglicht, indem es Netzwerkfunktionen abfängt und so den Datenverkehr mitschneidet. EchoMirage bietet eine eigene Benutzeroberfläche und eine Proxy-Historie, mit der sich der durchlaufende Datenverkehr beobachten und analysieren lässt. Da der Traffic abgefangen wird, kann man auch aktiv mit dem Datenstrom interagieren. Wie bereits erwähnt, ist das Tool leider nicht mehr gepflegt und nicht vollständig mit aktuellen Anwendungen, Betriebssystemen und Architekturen kompatibel. In den meisten Fällen hat es zwar funktioniert, allerdings gibt es mittlerweile bessere Alternativen. Dennoch: Wer mag, kann es gerne ausprobieren. Jedes Tool, das weiterhilft, ist ein gutes Tool!

Ein modernerer Ansatz zum Funktions-Hooking stammt aus der mobilen App-Analyse. Mit frida steht ein leistungsfähiges Framework zur Verfügung, mit dem man sich in laufende Prozesse einhaken und die Datenverarbeitung auf der nativen Plattform-API-Ebene manipulieren lässt. Damit kann man auch netzwerkbezogene Funktionen hooken und ähnlich wie mit EchoMirage den Datenfluss umlenken. Die Nutzung von frida läuft im Wesentlichen darauf hinaus, zuerst mithilfe von frida-trace die zu hookenden Funktionen zu identifizieren, dann einen JavaScript-Stub zu generieren, um den Funktionsaufruf zu verändern, und schließlich die Zielanwendung mit laufendem frida-Skript zu starten.

Zum Glück hat sich jemand die Arbeit gemacht, die gängigsten Netzwerk-APIs bereits als Hook vorzubereiten und daraus ein handliches Tool zu bauen: Warxim, der Entwickler bzw. Maintainer des oben erwähnten PETEP-Proxys, bietet mit deluder eine elegante Lösung für diese Herausforderung. deluder baut im Grunde auf frida auf und bringt bereits fertige Skripte für gängige Bibliotheken mit. Das Tool integriert sich nahtlos in den Workflow mit PETEP und ermöglicht es, den Datenverkehr direkt zu einer laufenden Instanz von PETEP umzuleiten und dort weiter zu analysieren. Auf diese Weise bleibt einem die gesamte Komplexität der Windows-API-Interaktion zur Datenverkehrsumleitung erspart.

Aufbrechen von SSL-verschlüsseltem Datenverkehr

Nun, da wir den Datenverkehr in unserem bevorzugten Tool sehen können, stellen wir manchmal ernüchternd fest, dass wir noch nicht am Ziel sind – der gesamte Verkehr ist weiterhin verschlüsselt. Das liegt daran, dass die Anwendung einen TLS-Handshake mit dem Server durchgeführt hat, zu dem wir den Datenverkehr proxyen, und wir daher keinen Einblick in die tatsächlich übertragenen Daten haben. Was wir brauchen, ist, die Anwendung dazu zu bringen, den Handshake mit unserem Proxy durchzuführen, sodass die Verschlüsselung an dieser Stelle endet. Nur so können wir den Inhalt der Pakete einsehen, bevor wir sie an den eigentlichen Server weiterleiten. In den meisten Fällen muss dafür vom Proxy eine eigene verschlüsselte Verbindung zum Anwendungs-Server aufgebaut werden, damit der zweite Teil der Übertragung wieder gesichert abläuft.

Ein kurzer Hinweis: Wir testen immer sogenannte Downgrade-Angriffe. Dafür deaktivieren wir SSL/TLS im Proxy komplett, sodass automatisch eine unverschlüsselte Verbindung genutzt wird. Wenn der Client dieses Downgrade akzeptiert und eine unverschlüsselte Verbindung erlaubt, kann man sich den ganzen Aufwand mit SSL/TLS oft sparen – ein klassischer Quick Win.

Eine weitere Hürde können Client-Zertifikate sein, falls die Entwickler deiner Zielanwendung besonderen Wert auf Kommunikationssicherheit legen. Der Vorteil: Diese Zertifikate müssen sich ebenfalls auf deiner Maschine befinden, damit die Verbindung funktioniert – du kannst sie also extrahieren. In Java-Anwendungen sind das häufig Keystores, die meistens mit dem Standardpasswort „changeit“ geschützt sind (ist ein anderes Passwort eingestellt, findet man es oft irgendwo im Anwendungscode). Unter Windows werden Zertifikate mitunter im zentralen Zertifikatsspeicher des Betriebssystems abgelegt. Lässt sich das Zertifikat von dort nicht exportieren, wurde es vermutlich als „unexportierbar“ markiert. Kein Grund zur Sorge: In deiner eigenen Analyseumgebung kannst du mit Tools wie mimikatz dennoch an die Zertifikate kommen. BurpSuite und PETEP können Client-Zertifikate verwenden. Nach dem Extrahieren musst du es nur noch in dein Proxy-Tool importieren, für die jeweilige Verbindung konfigurieren – und schon kannst du weiter angreifen!

Fazit

Wow, das ist eine ganze Menge an Möglichkeiten. In den meisten unserer Projekte führt eine der oben genannten Techniken dazu, dass wir eine funktionierende MitM-Position einnehmen und mit der Analyse der Netzwerkkommunikation unserer Zielanwendung beginnen können. Es gibt aber auch Fälle, die aufgrund spezieller Funktionen oder Schutzmechanismen der Anwendung individuelle Ansätze erfordern. Diese speziellen Themen sind jedoch zu umfangreich für den Moment.
Wie immer stehen wir auf den Schultern von Giganten. Es gibt zahlreiche großartige Tools, die uns im Alltag unterstützen – viele davon sind Open Source. Besonders hilfreich waren dabei die von warxim entwickelten Werkzeuge PETEP und deluder. Schaut auf jeden Fall mal auf seinem GitHub vorbei.

Falls ihr Fragen, Ergänzungen oder weitere Ideen zum Thema Proxying habt, freue ich mich sehr über euer Feedback! Also, zögert nicht, mich über GitHub zu kontaktieren oder auf LinkedIn zu vernetzen.


Sie möchten Fat Client Pentests in Ihrem Unternehmen durchführen oder haben Fragen zu unseren Penetrationstests? Kontaktieren Sie uns, wir unterstützen Sie gern.

Auch interessant:

usd München – Mehr als nur ein weiterer Standort

usd München – Mehr als nur ein weiterer Standort

„Die usd hat aktuell drei Standorte in Deutschland: Neu-Isenburg, Köln und München.“ Diesen Satz hört man bei uns oft – und fast immer wird München zuletzt genannt. Dabei hat gerade unser jüngster und kleinster Standort einiges zu bieten: eine starke Identität,...

mehr lesen
Andrea Tubach ist neue CEO der usd AG

Andrea Tubach ist neue CEO der usd AG

Bei der gestrigen ordentlichen Hauptversammlung der usd AG und der anschließenden Sitzung des neuen Aufsichtsrates wurden lange vorbereitete personelle Änderungen einstimmig beschlossen und danach in großer Freundschaft gefeiert: Andrea Tubach übernimmt den Vorsitz...

mehr lesen

Kategorien

Kategorien