When testing a desktop application, also referred to as a fat client or thick client, the analysis of network traffic plays a central role in any penetration test. The ability to monitor and modify the data generated and received by the application is often the key to uncovering security vulnerabilities. Achieving this requires taking a Machine-in-the-Middle (MitM) position, as only this allows the traffic to be read or to modify the traffic in transit.
In this article, our colleague Florian Haag, Managing Consultant and expert in fat client pentests, explains in detail how to gain such a position even with complex applications, what challenges may arise in the process, and which solutions have proven effective in practice.

First Analysis Steps
When starting analysis of an application, the first step is to get an overview of all network connections a specific client is opening. As Microsoft Windows is still the most prevalent operating system on workstations, we have prepared an analysis environment in which we have full administrative permissions to be able to analyze processes in depth. We start by firing up the SysInternals Suite by Microsoft, more specifically the ProcMon utility. This gives us the capability to get file system and registry events, but also networking related actions the application is performing.
Starting and using the target application for some time will give us insights into which endpoints the application is talking with when used. We note down all the URLs, IP-addresses and ports we’re seeing for further analysis. These domains are the ones we are targeting for our proxying efforts, as we want to obtain the ability to read the data transferred to and from these services.
Proxy-aware vs Proxy-unaware Clients
Now that we know which traffic flows we are interested in, let’s talk about how to obtain a MitM position and what to consider. Generally, there are two types of applications out there. Applications are either built to support proxies natively or via means of the underlying operating system. Others are specifically built to avoid proxying of their traffic, most prominently security related software such as AV agents that want to protect their traffic from interception and modification.
Proxy != Proxy; HTTP vs Binary Proxying
Another aspect to consider is the kind of traffic that should be monitored. The most prevalent protocol for communication between front- and backend components is HTTP or the encrypted counterpart HTTPS. In the case of desktop applications, oftentimes binary protocols, either standardized or even custom ones, are used. Standardized binary protocols are for example Java RMI (Remote Method Invocation) which implements a Remote Procedure Call functionality for Java based software. The client application encodes all information necessary for the function call inside a publicly known binary format, sending it to the server-side endpoint for execution. Other applications define completely custom contracts for communicating between client and server. Oftentimes reverse engineering is required to gain an understanding of how the messages are constructed, which functionalities are exposed, and how to create own messages. Reversing custom protocols is a separate topic, not part of this article, maybe a later one.
The type of protocol we are dealing with is mostly relevant for the type of MitM proxy we want to use. The de facto industry standard for HTTP/HTTPS traffic is BurpSuite by PortSwigger. When dealing with binary protocols BurpSuite quickly is out of its comfort zone and lacks nice to have features for analysis. There is an extension available to BurpSuite called NoPE that brings binary proxying into Burp. But this extension also has its weak points, such as stability problems, as we discovered in previous assessments. Our current go-to tool is PETEP by warxim. It’s a great tool like BurpSuite but optimized for dealing with binary protocols. It has features like interception of packages, the possibility to replay packages, and many more.
Ways for Proxy-Aware Clients (Easy Mode)
The easiest way might be to set up a proxy inside the application or its framework. If the application offers the possibility to set a proxy server within its UI, that’s an obvious one. Sometimes a proxy server can be configured by the means of the underlying framework. The .NET framework for example places an “app.config” file besides your application binary after building it. This file can be used for configuring the app at hand, but also the underlying framework. With the help of the “defaultProxy” directive, it might be possible to convince the application to communicate through a proxy.
As a simple example. If your application is called “application.exe”, the relevant config file will be named “application.exe.config” by default. By adding the following lines, you’re setting “localhost:8080” as the default proxy for all HTTP/HTTPS traffic. Because “bypassonlocal” is set, traffic to localhost is also routed through your proxy of choice.
<configuration> <system.net> <defaultProxy enabled="true"> <proxy proxyaddress="http://127.0.0.1:8080" bypassonlocal="false" /> </defaultProxy> </system.net> </configuration>
If the application or framework doesn’t offer you such a convenient mechanism for proxying data, you can fall back to using settings of the operating system. This focus is a bit broader because you will capture the traffic of more applications running on the OS than just the one you’re analyzing. For this, we created our list of target sockets above to apply a proper filter. On Windows, configuring a proxy can be achieved by running
from an elevated command prompt. A UI is also available in the (old) control panel or the (newer) Windows setting menu.netsh winhttp set proxy <proxy-ip/domain>:<proxy-port>
On Linux, it’s all about environment variables. The two most relevant are related to HTTP/HTTPS proxying. Setting a proxy for both protocols can be done with:
export HTTP_PROXY=http://127.0.0.1:8080 export HTTPS_PROXY=http://127.0.0.1:8080
When setting a proxy this way, you need to consider the scope of your changes. For the change to be effective system-wide it has to be configured in a global configuration file such as “/etc/environment” or the “~/.profile”. By setting the environment on the command line, the changes are only effective for the session of this shell. But this can also be an advantage, because starting the application from this shell session, your proxy will only receive traffic from the application running as a child process of this specific shell instance.
Ways for Proxy-Unaware Clients (Hard Mode)
As you’re still reading, I guess all the above didn’t work or didn’t apply. So now it’s time to be less gentle and trick the target at hand into communicating with our proxy application.
To have flexibility in their internal network, most applications refer to their application servers or other backend components by a distinguished domain name. When a connection is initiated, a DNS lookup takes place, resolving the domain name to the actual IP address the packets should be sent to. This DNS lookup can take place on every connection attempt or, in some cases, only once on startup of the application. After that, the information is cached inside the client, and the IP address is used directly. As your application is not responding to any already made attempts of communicating through a proxy, our next approach is to trick it into connecting to it by modifying the DNS information, supplying the IP of our proxy instead of the legitimate component.
Before we dive into how to do it, a quick word regarding the analysis setup. If you’re at this point, it might be beneficial to have two VMs. One is meant for running the target application (app VM) and the other is serving as a router (router VM). The app VM is in the same local subnet as the router VM, and the router VM is configured as the default gateway on the app VM. This way all outgoing traffic is flowing through the router VM, where we can analyze it. The router VM (typically we use Kali Linux because it comes with all the tools required) has two network interfaces. One in the local subnet where the app VM resides and the other in a network that can reach the application backend. This can be either the internet or a customer VPN in case of a professional engagement. The router VM is performing a NAT between the local subnet and the outgoing subnet and enables traffic forwarding. The following script expects incoming (local subnet) and outgoing interface names, establishes the NATing by the means of iptables, and enables forwarding. The script needs to be run as root, as configuring packet forwarding and firewall rules are highly privileged operations.
#!/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 this effort with a second VM is because we are now becoming more aggressive in redirecting traffic. To ensure our tools are working properly, it’s best to run them on another VM where we are not interfering with networking settings as much as on the app VM.
But now back to redirecting DNS. The most convenient way to defeat DNS is by using the hosts file. On Linux in “/etc/hosts” on Windows in “C:\Windows\System32\drivers\etc\hosts”. This file serves as a static lookup table for domain to IP resolutions. Localhost is also mostly defined here. You can simply add your domains in here and point them to the IP address of your proxy (the router VM in case the above setup is used). This can only be done on machines where you have root-level access, as changing this file requires privileged permissions.
If changing the host file is not possible or not the route you want to go, you can also use your own DNS server running on your router VM. Our typical setup for this is the following: The router VM runs dnsmasq to supply DHCP for the app VM. This also makes managing the local analysis subnet easier, as you’re having a DHCP server by now. Furthermore, dnsmasq can push a DNS resolver or even serve as a DNS resolver itself. In the following snippet you see on line 3 the defined DHCP range from .100 to .200 of the chosen subnet with a validity of 24h for the DHCP lease. The fourth line is concerning DNS and redirects all requests bound to the domain “appserver.com” to the IP 192.168.13.99. This way traffic can be redirected easily to the proxy listener.
interface=eth1 bind-interfaces dhcp-range=192.168.13.100,192.168.13.200,24h address=/appserver.com/192.168.13.99
The next tool in our arsenal is interfering with the target application on a deeper level to achieve interception of network traffic. On Linux, the proxychains utility is a great tool for bending the routes in your favor. It can be installed for most distributions from the standard package manager, if not already present by default. It comes with a configuration file, usually located at “/etc/proxychains4.conf”. At the bottom you can define a chain of proxies; in this case multiple proxies are possible that receive the request first before being redirected to the target application. Furthermore, it is possible to define the proxy protocol. Besides HTTP and HTTPS, a SOCKS4 or SOCKS5 proxy can also be defined. Proxychains is used by prepending the command to the launch-command of your target application, such as:
.sudo proxychains ./application
Proxychains is working by hooking operating system level function calls related to opening network sockets. This way the traffic flow gets redirected before the packet is even sent. Similar technologies are available for Windows. A common, but while writing this sadly outdated, utility is EchoMirage. This project offers a GUI client that enables the exact same functionality by intercepting traffic through hooking networking-related functions. EchoMirage comes with an own UI and proxy history where you can watch and analyze the traffic as it's passing through. The possibility to intercept also enables you to interact with the traffic in transit. As mentioned above the tool is sadly outdated and no longer maintained. Therefore, it is not fully compatible with modern software, operating systems, and architectures. It worked in most cases, but better alternatives are available. But feel free to give it a shot; every tool that helps you is a good tool!
A more modern approach to function hooking is coming from the realm of mobile app analysis. With frida, a modern framework is available to hook into processes and tamper with the processing of data on the native platform API level. This can also be utilized to hook into networking-related functionality and change the direction of traffic flow, as EchoMirage does. Using frida basically boils down to identifying which functions you want to hook (by using frida-trace), generating a JavaScript stub to tamper with the function call, and finally starting the software with frida running your tamper script.
Luckily someone did the work for us to hook the most common networking APIs and pack it up into a neat tool to use. Warxim, the developer/ maintainer of the above-mentioned PETEP proxy, offers deluder to tackle this challenge. Deluder basically wraps around frida with tamper scripts for the common libraries already on board. It nicely integrates with PETEP and enables you to redirect traffic into a running instance of PETEP for further analysis. The whole complexity of dealing with the internals of the Windows API to redirect traffic can be avoided this way.
Breaking SSL Encrypted Traffic
Now that we can see the traffic in our tool of choice, we sometimes have the hard realization that we are not quite there; all traffic is still encrypted. This is due to the fact that the application performed a TLS handshake with the server we are proxying the traffic to and therefore we don’t have any insight into the actual data transferred. What we need is to force the application performing the handshake with our proxy, terminating the encryption there to enable us to read the packet's contents and then forward them to the actual server. For this case, it’s mostly required to open another encrypted connection from the proxy to the application server, transferring the traffic securely for the second half.
Quick note: We always test downgrade attacks. For this, disable SSL/TLS in your proxy altogether, defaulting to unencrypted channels. If the client is accepting this downgrade to an unencrypted connection, you can most likely avoid the headache of dealing with SSL/TLS altogether. A classic quick win.
Client certificates are another possibility if the developers of your target application are taking communication security seriously. The good thing is that those certificates need to be on your machine for it to work; therefore, you can extract them. In Java, for example, look for keystores (mostly protected with the default password “changeit”, if not, the application code contains it somewhere). On Windows, the certificates are sometimes stored inside the central certificate storage of the operating system. If you can’t export them from there most likely they were marked as “unexportable”. But no worries, as this is your analysis environment, you can use mimikatz to obtain those certificates as well. BurpSuite and PETEP are able to use client certificates, so after obtaining it, you just need to import it into your proxy tool, configure it for the respective connection you want to authenticate, and continue hacking.
Final Thoughts
Wow, that’s a lot to try. In most of our engagements, one of the above techniques leads to us obtaining a working MitM position and starting to analyze internals of the network communication of our target application. Some cases require custom approaches dependent on application functionalities or countermeasures taken by developers. Those topics are too much for now.
As always, we are standing on the shoulders of giants. A lot of awesome tools are out there that help us in our daily work, most of which are open source. Especially useful were the tools developed by warxim, PETEP, and deluder, so definitely check out his GitHub. If you have questions, additions, or further ideas on how to proxy, I’d highly appreciate feedback! So, feel free to reach out to me on GitHub or connect on LinkedIn.
Would you like to conduct fat client pentests in your company, or do you have questions about our penetration tests? Contact us; we will be happy to support you.