This article was published in Pentest Magazine’s PenTest: Brothers in Arms: Pentesting and Incident Detection issue.
We were recently contacted to test out an online, multi-player game, where we needed to be able to proxy the encrypted traffic sent from the game client to the server. We were not aware of the underlying protocol beforehand, only that the communication took place over TLS.
The purpose of this article is to describe a simple methodology we used during this engagement to debug the interception of encrypted traffic between an application and a server, where the interception is not simply a case of installing any self-signed certificate in the trust store of the browser or operating system.
This article is targeted towards junior pentesters and is useful when testing thick clients that handle their own certificate verification, but where SSL pinning is not implemented (out of the scope of this article).
The first thing we needed to do was see what kind of traffic was being sent between the client and server. Our initial setup was as follows:
- Game client running on Windows VM
- Host OS used for testing (Linux)
Step 1 ‒ Gain an initial understanding of the traffic
By running Wireshark and firing up the game client, we were able to capture traffic and gain an indication of what protocols were being used for communication. It was at this point that we observed the fact that Transport Layer Security (TLS) was being utilized to encrypt communications. We also observed the fact that the destination port on the game server was 443/tcp hinting at the possibility that the underlying protocol could be HTTP, but at this point this was only an assumption.
We also saw the fact that the game client was sending out a DNS request for a specific sub-domain. This would later prove to be quite important (refer to Step 4).
Step 2 ‒ Setting up automated interception
The next thing we needed to do was to establish a man-in-the-middle position between the game client and server. To do this, we would first need to force all traffic from our Windows VM to our Host Machine. We would then need to utilize SSL Certificate Injection and check what kind of verification the game client performed on our custom-generated certificates.
There are many ways to carry out the above, but an easy and effective way to do this as a first step is to use the widely-popular tool Cain & Abel. Cain has the ability to ARP poison victims (for more information please refer to http://www.oxid.it/ca_um/topics/apr.htm), effectively forcing all traffic to pass through the attacker. Further, Cain also has the ability to auto-generate self-signed certificates on the fly and perform an SSL splitting attack.
An SSL Splitting attack is basically getting the client to accept an invalid certificate, so the client data is encrypted with the attacker’s public key. When the attacker receives the data, they decrypt it and establish a new, valid encrypted connection with the intended recipient of the data and send it off. This process works bidirectionally, hence an attacker is able to intercept encrypted traffic between the game client and server.
Because Cain won’t work properly when run on the same machine as the victim, we needed to use another VM. So our setup at this point was as follows:
- Router (192.168.0.1)
- Game client running in Windows 10 VM (192.168.0.26)
- Cain running on Windows 7 VM (192.168.0.27)
- Host OS used for testing (Linux) (192.168.0.19)
All of the above machines were running in bridged networking mode to make it easier to ARP Poison our VM running the game client. Essentially, we needed to poison the VM running our game client (192.168.0.26) and our network device (192.168.0.1) so all traffic to and from the game client would be re-routed to us.
Step 3 ‒ Automated interception
With all our VMs up and running, and our victim and network device successfully poisoned using Cain, we proceeded to fire up the game client and see if we could intercept some encrypted traffic.
After doing so, we could see in Cain’s APR tab, that Cain was generating self-signed certificates on-the-fly and injecting them into the communication between the game client and server. Before the game client could successfully accept the Cain’s self-signed certificate, we had to add the certificate located at C:\Program Files (x86)\Cain\Certs to our Windows 10 VM Trusted Root Certificates Store (for more information refer to https://technet.microsoft.com/en-us/library/cc754841(v=ws.11).aspx) and we were then able to view decrypted traffic in a relatively straightforward manner.
After viewing the traffic, we noticed the underlying protocol was HTTP, so we obviously wanted to utilize our favourite intercepting proxy ‒ Burp Suite ‒ to test the application.
Step 4 ‒ Routing encrypted traffic through specific tools
So we needed to pass everything through Burp, which was running on our host machine. Again there are a few ways to do this with varying levels of difficulty, depending on the exact situation.
In Step 1, we observed a DNS request to a back-end server that was responsible for handling API calls. In Step 3, it was possible to see the HTTP requests being sent to this very same server; essentially, all functionality within our scope was being handled through this endpoint. Hence, the easiest way for us to route traffic through our Burp Proxy was to change the hosts file on our Win10 VM and add an entry whereby we would force the DNS resolution of the URL used by the game client to resolve to our host machine running Burp.
A small note: because the destination port was 443/tcp and because we were running Burp as a non-root user on our host machine, we had to employ some iptables trickery in order to forward all incoming traffic on port 443 to another port which Burp could bind to without root Privileges:
iptables is out of the scope of this article, but we’ll briefly break down the above commands:
-t: indicates what table we want to manipulate.
-F flushes all rules in the selected table. This is to ensure there are no previous rules that might affect our interception.
-A: append one or more rules to the end of the selected chain (in our case this is the PREROUTING chain, which as you guessed, will be processed before the traffic is forwarded). Basically, add a rule for the selected table and the specified chain.
–dport: destination port. Basically, the port the OS should listen to in order to apply our rule.
-J: The action iptables should carry out in case the incoming traffic matches our rule. In this case, REDIRECT is an iptables extension which redirects all incoming traffic from the port specified by.
–dport to the port specified by –to-ports.
–to-ports: the port that all incoming traffic should be redirected to. In this case, this was the port that our Burp Proxy was listening on.
-L: List the rules in a chain or all chains.
The next step was to configure Burp Proxy to listen on all interfaces, support invisible proxying, and add Burp’s CA certificate to our Win10 VM Trusted Root Certificates Store.
After editing our Win10 VM hosts file, setting up our iptables rule and firing up Burp, we were ready to see if we could pass traffic through our proxy. The first step was to ensure that generic traffic could pass through Burp, so by opening a browser on our Win10 VM and entering the URL we had created an entry for in our hosts file, we could check whether or not traffic was being redirected to our host machine and being intercepted by Burp.
Some notes: It is important to make sure that any firewalls do not interfere with our iptables rule, since most Linux firewalls utilize iptables. The quickest way to do this is to either add a rule to allow incoming connections to a specific port or disable temporarily the firewall. Further, flush all rules for the NAT table and then append the rule needed for port forwarding.
Step 5 ‒ Debugging the routing of encrypted traffic through specific tools
After everything was up and running our setup at this point was:
- Game client running in Windows 10 VM. The hosts file was edited with an entry that would resolve the URL of the back-end server to the IP address of our host machine.
- Host OS used for testing (Linux). This machine was running Burp, and an iptables rule was utilized to port forward all traffic from port 443 to port 8080 (both TCP) because Burp was running as a non-root user.
We were finally good to go and start the game client in order to begin testing! Unfortunately, once we fired up the game client, we started seeing alerts in Burp without being able to intercept any traffic. This was also obvious from the fact that our game client wouldn’t fully initialize. The alerts in Burp indicated that the connection was being closed.
By firing up Wireshark, we could see that the client was rejecting the certificate used by Burp. At first, this might seem unusual since we successfully used a self-signed certificate with Cain.
To investigate further, we proceeded to check the event log of the Windows 10 VM using the Event Viewer as shown below.
The error message issued by Schannel indicates that the certificate could not be validated correctly. Although it is not very descriptive and it does not help us understand why our certificate was rejected, the error code mentioned in the above message could give us some clues. The next step was to search the web for the error code 0x80092012 which helped us conclude that it was related to the certificate’s revocation status.
Certificate revocation allows certificate owners to revoke their certificate in case their private key is compromised. There are two common ways for verifying the revocation status of a certificate; Certificate Revocation Lists (CRLs) and the Online Certificate Status Protocol (OCSP). A Certificate Revocation List (CRL) is basically a list of serial numbers for certificates that have been revoked and OCSP was created as an alternative to CRL. A CRL is always issued by the CA which issues the corresponding certificates. For more information about certificate revocation please refer to: https://www.digicert.com/util/utility-test-ocsp-and-crl-access-from-a-server.htm
To debug further, we used the certutil.exe command-line program, which is installed as part of the Certificate Services in our Windows 10 VM. We used the –verify argument to verify the revocation status of our certificate which was generated by Burp.
From the above screenshot, it is evident that the revocation status of the certificate is unknown (CERT_TRUST_REVOCATION_STATUS_UNKNOWN (0x40)). Considering that we were able to intercept the traffic using the certificate generated by Cain, we proceeded to verify Cain’s self-signed certificate.
The screenshot illustrates that certutil does not display any errors about Cain’s certificate revocation status. The problem turned out to be the fact that the CA-signed certificates generated by Burp do not include any information about the certificate’s revocation status, thus Schannel could not validate them.
The reason why Cain’s self-signed certificate was validated correctly was because self-signed certificates are not signed by a CA, hence they do not need to include any information about CRL/OCSP.
To explain further, Burp has four ways of generating a certificate:
- A self-signed certificate with PortSwigger as the CommonName. This would not work because Schannel would issue an error that the CommonName (PortSwigger) does not match the server’s domain name (*.playfabapi.com).
- A CA-signed certificate per-host. This is what we used, which turned out to be problematic due to the absence of CRL/OCSP information.
- A CA-signed certificate for a specific hostname. This would cause the same problems we described above due to the fact that it is signed by Burp’s CA and it does not include any CRL/OCSP information.
- Custom certificates in PKCS#12 format. This method will work as long as we use a self-signed (i.e., not signed by a CA) certificate which has the correct CommonName.
By exporting the certificate used by Cain (or generating one using OpenSSL), and importing it into Burp, it was possible to successfully route and intercept encrypted traffic through Burp. This is why using Cain as a first step is useful, because it generates self-signed certificates to match the ones used by the intended destination of the victim. It should be noted that Burp Proxy accepts custom certificates in PKCS#12 format, so we needed to convert our CRT certificate to PKCS#12. There are many ways to do the conversion, however, an easy and quick way is to use an online converter provided by SSLShopper (https://www.sslshopper.com/ssl-converter.html).
The certificate was validated correctly and we were finally ready to begin testing!
An important thing to note here is that the game client did not employ any SSL/TLS pinning. If it had, the connection between client and server would not have been established. In that case, it would have been necessary to understand how the client performed certificate pinning. Did it use a local file, or was the certificate hard-coded into the client binary? Since this article is all about debugging the process of intercepting encrypted traffic, we will mention the following steps in trying to deduce how the client performed the certificate verification:
- Use Cain’s auto-generated SSL Certificate Injection feature to see if the client accepts self-signed certificates.
- Try to import the certificate used by Cain into the operating systems Certificate Trust Store.
- Use Wireshark, certutil and Windows Event Viewer to identify any error messages that could help you investigate further.
- Try and search the local game client files to look for certificates (this is a form of SSL pinning) that the client might use for verification. If such a file exists, replacing it with the one served by Cain might bypass the verification.
- If none of the above is successful, then it is likely the client binary utilizes a hard-coded certificate and hence employs SSL pinning. At this point the pinning needs to be bypassed, which is out of the scope of this article.
The point of this article and the above steps is to quickly understand which situation a pentester is faced with so they can resolve the issue as fast as possible in order to test the application they need to, hence the debugging!