MITM Attack — Reverse Engineering an Encrypted Service
Kevin Raynel8 min read
Legal disclaimer
In France, reverse engineering is allowed as long as it serves a goal of interoperability.
Here, we will not be accessing data that should not have been accessible otherwise. Before trying to access data in a way that was not exactly meant to, it is always preferable to contact the company or individuals who are serving this data in the first place.
I contacted Silaexpert regarding this article. I have detected no real security hole in their client, so they gave their approval for its publication.
That being said, let’s start!
Context
The context is quite simple. The generation of payslip at Theodo is outsourced to another company: Silaexpert. We were given our credentials, and a url to access our vacations count and our payslips.
This all starts with a link: http://www.silaexpert01.fr/silae.
This link does not work in Chrome, Firefox or Safari. It does not run on a Windows 10 computer, so you can forget connecting from home on your nice gaming rig.
It seems to only work on Internet Explorer on Windows 7: bad news for all the developers at Theodo running Linux or OSX.
The black box
Well, obviously, you could find other solutions. You can always borrow a friend or colleague computer running Windows. Great, now all your files are available on a shared computer.
Or you can download a free virtual machine from Microsoft: https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/. For this particular purpose, the VM with IE9 on Windows 7 works well.
So, now you have the virtual machine up and running, you can go to the url and… Whaaat ? It is downloading a fat client and starts some kind of install process. Just from that link. Thank you IE.
Finally, you can login and painfully download your last payslip. And start again next month. If you reset the virtual machine, because, you know, licenses.
Ain’t nobody got time for that!
Let’s jump in
Some tooling
I want to download my file directly from my Mac or Linux computer without going through all this VM nonsense. In order to do this, I have to send the right request to the remote server (I still do not know its address, which is probably hard coded in the downloaded fat client).
So, first things first, let’s sniff all that network traffic going out of the VM.
You can use any network sniffing tool available for your platform:
- Wireshark, avaiblable for most systems, maybe overkill here
- Charles Proxy on Mac
- mitmproxy if you like python
- Fiddler on Windows (beta available for Mac and Linux)
I personnally used Charles Proxy, in spite of its hideous icon.
- Start the HTTP proxy on the host machine. Go in Proxy > Proxy Settings and note the port it is using.
- Start your VM.
- Open a command line, run
ipconfig /all
and note the Default Gateway IP. This is your host IP. - Open Internet Explorer, go to Internet Options > Connections > LAN Settings and set the proxy settings with the IP and port from previous steps.
- Open a command line, run
- Done! You should see all traffic going out of the VM appearing in Charles.
Now, if you login into the fat client and download a payslip, you should have enough information to start thinking.
Data analysis
You now have a complete exchange between the client and the server.
The first thing you notice is that all exchanges are done without SSL/TLS encryption. Free data, yeah! That being said, you could still have sniffed encrypted traffic by trusting the fake CA generated by your proxy on the virtual machine.
You can notice several things:
- A single webservice endpoint: http://www.silaexpert01.fr/SILAE/IWCF/IWCF.svc. All requests go to this url.
- The protocol used is SOAP, without any web security module enabled.
- The login request seems to be different from all other requests, which all look alike.
- There is a bunch of base64 encoded data.
You can try to decode the base64 encoded data, but you will not be able to get anything readable. It is probably encrypted binary data.
The login request
The first request made to the server contains only one interesting piece of data, a key K
:
<RSAKeyValue>
<Modulus>odfDJj...pEQ2RQ==</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
This key K
is the public RSA key used by the client. The response also contains a key, using the same format, and some kind of identifier $USR
. This received key is the public RSA key that the server will use.
We just witnessed an RSA key exchange. RSA cannot be used to encrypt large amount of data. Depending on the padding technique used, with a 2048 bit key (used here), you can encrypt at most 245 bytes. Not much.
A technique commonly used is to use a private/public RSA key to encrypt a symmetric key, which is then used during the exchange.
Good, I have the client and RSA keys, now what?
Good question. Here, you are either a genius or you need a bit of help.
Hell yeah, I’m a genius!
Ok, you are a genius (I never doubted it), you take the second message, and decode the base64 binary.
echo "AQABAAAOE6jZqMmDnFHefXddjDq...W5A=" | base64 -D | hexdump -C
If you do this several times, over several login request, you might notice a pattern.
The first few bytes are always 01 00 01 00 00
. This can be seen directly in the base64: the first letters are always “AQABAA”. Something quite interesting is that 00 01 00 00
is the binary representation, on 4 bytes, little endian, of the decimal 256. Nice!
Another thing you might notice is at offset 261 (1 + 4 + 256), the bytes are always 20 00 00 00
. This is 32 in decimal.
Here is the genius part. A common symmetric encryption system is AES 256. It uses an initialization vector (IV) of… 32 bytes.
Little recap:
- The first byte is always
01
. - The 4 next bytes give the size of the AES key: 256 bytes.
- The next 256 bytes represent the actual AES key, encrypted using the public RSA key
K
which was sent to the server during the first phase. - The 4 next bytes give the size of the IV: 32 bytes.
- The next 32 bytes define the IV.
- The rest of the message is still unknown. Probably the login/password encrypted using the AES key.
Even geniuses sometimes need a little push
There is only one AES, but many variants. You can try them all, but it will take a really long time and lots of developments.
We did not use all the cards in our hand at this point. Remember the fat client? It is our key to the encryption issue.
Ready to play a game?
My site is not really a site, it uses internet explorer to download an executable binary, which runs only on Windows and asks for specific libraries on Windows 10… In which language is the client developped?
You guessed it, .NET!
Easily decompilable. Note that Java would have been fine too.
Let’s take a look at this code. You can get the url for the .exe
by reading the other proxified requests. Import the .exe
into the decompiler (I used the trial version of .Net Reflector in the Windows VM), and look for some interesting code. No luck this time, nothing.
Except for this one little DLL, Sila.WCFDLL.dll
. Export it, and re-import it into the decompiler. And bingo, you have the serialization/deserialization procedures, with all the security stuff.
We were right about the AES. It uses the Rijndael 256 variant of the AES encryption with a custom serialization routine. If you are not a genius and missed the previous paragraph, you have here all the code for re-creating the message.
Oh, in case you were wondering about, the first byte is 01
when it contains AES information, 00
in all other cases.
Exploiting that knowledge
Here, you are blocked. You cannot decrypt any of the AES keys, because you need the private RSA keys. The only option to get this AES key is to write a Man In The Middle (MITM) server, which intercepts every request between the client and the server and acts as the client for the server, and as the server for the client.
Credits: Miraceti — Man in the middle attack — CC BY-SA 3.0
Once that server is written (avaiblable at https://github.com/kraynel/sila-cli/blob/master/server.js), and you have fixed all the buffer/byte conversion/RSA/mcrypt issues, you can reload your VM and login one more time.
Only this time, you will use the “Map Remote” option of Charles proxy to redirect all traffic going to http://www.silaexpert01.fr/SILAE/IWCF/IWCF.svc to your local endpoint, which will in turn decrypt the exchange and request the real endpoint.
You now have all requests, decrypted, in binary form.
Serialization/Deserialization
Requests are not directly readable. They are not using xml serialization but a custom, in house serialization technique. Who does that? Why?
But we have the code so we can look into it. And port it to JS (https://github.com/kraynel/sila-cli/blob/master/cbaSerialization.js).
We now have all the requests, and their responses, in plain, readable JS. By playing with the different parameters, we can identify two interesting requests:
AcquisitionBulletins
, which lists the avaiblable payslips;GenererPdf
, which downloads a payslip as a pdf file.
Congrats, you know everything about this service! You can write your own client!
Some thoughts
- Never re-code security features which already exist in libraries.
- RSA key exchange, then AES encryption is basically TLS. Use HTTPS!
- Please do not code your own serialization routines. Some really good libraries are already out there.
- Use known standards (REST or, if you really need it, SOAP) for your exchanges, and prefer formats which are easily interoperable (JSON, XML).
- Beware of replay attacks! Here, if I save the AES keys used with my MITM server, I can replay a request and decode the answer. The server would send me exactly the same response, every time, even a long time after the initial request.