Friday, July 15, 2011

Man in the middle on IPsec? Yes, we can!

IPsec seems to be much less used than TLS. Maybe because, from a developper perspective, setting up a TLS socket looks much more easier than creating IPsec Security Associations (SA) and Security Policies (SP). If we look at security tools, we have lots to (attempt to) hijack TLS channels but nothing for IPsec so far.
On the other side, IPsec may be used more and more within devices to communicate with service providers or mainteners in a secure way (e.g. refering to IPv6).

In general, the ESP protocol is used: see RFC 4303. AH is much less used to my experience. It can be run in tunnel mode (encapsulating new IP headers and so protecting internal routing flaws), or in transport mode (hence, used in a way more similar to TLS).
In order to enable IPsec channels automatically, IKEv2 has been designed, and is described in RFC 5996. Basically, IKEv2 does the following:
- generates an initial SA by running a diffie hellman exchange and negotiating IKEv2 security algorithms,
- informs about SP within traffic selectors (what flaws to protect),
- authenticate peers,
- if authentication is successful and traffic selectors from peers matches, it generates new SA for the IPsec layer by deriving further the initial DH secret and renegogiating security algorithms.
This last SA is then pushed to the IP networking stack. In general in Linux, pf_key and xfrm interfaces can be used to configure the kernel. Lots of options and protocol kung-fu exist within IKEv2: e.g. in case of tunnel mode, an internal IP address can be requested by a peer acting as a client (think road-warriors connecting to a VPN gateway).

IPsec SA configured with IKEv2 cannot be retrieved by passively eavesdropping on the IKEv2 negotiation (unless you can factorize the large prime numbers used during the DH exchange...): unlike TLS, IKEv2 / IPsec SA are not transferred over the network (in the main mode of TLS, keys are transferred protected with the server's public key).
With IKEv2, you need to be active in the middle of the negotiation to manage yourself DH secrets, or you need to get access afterwards on one of the 2 peers' system and extract IPsec SA (in general on Linux, you need to be root). This is also why in wireshark, IPsec SA have to be configured completely for decoding ESP packet, whereas with TLS you only need to put the server's RSA secret key to decode an entire TLS session.

Being in the middle of an IKEv2 and IPsec connection still requires to break peers' authentication!
The main IKEv2 protocol accepts certificate or pre-shared key authentication. The pre-shared key mode allows to bruteforce the password offline, after running a fake IKEv2 server against a legitimate client. With the certificate mode, you can expect to meet the same issues than with TLS certificate authentication (mis-verification, mis-configuration, mis-implementation...).
Furthermore, IKEv2 accepts EAP authentication methods: e.g. EAP-MD5, EAP-TLS, EAP-SIM... Some of these methods can be insecure (e.g. those which are not generating MSK to be transferred to the IKEv2 layer, those that are not mutual authentication methods, those that allow replaying authentication exchanges...). Some making use of passwords can be bruteforced offline in the same way than the IKEv2 pre-shared key too.
There are plenty of authentication methods for IKEv2, and hence plenty of ways to break it!

But the purpose of this post is not about IKEv2 but IPsec ESP. So, after you manage to break the authentication scheme between 2 peers, you will be able to run you own instances of IKEv2 with each peers (this is left for the reader!). By doing so, it is possible to establish IPsec ESP SA with client and server independently. As soon as I have those SA, I can man-in-the-middle the IPsec ESP channel!
My first thought was to handle this directly within Linux, however and especially with the tunnel mode of ESP, routing issues are certainly going to happen.
So what???
...
Python!
And pycrypto.
I made a short class that is instantiated with both client and server Security Associations. It is only made to work with IPsec ESP over UDP (the NAT-Traversal mode): this avoids having to deal with raw sockets. Once an ESP packet is received from one of the peer, it is deciphered with an SA, reciphered and transferred toward the other peer. Optionnaly, it duplicates the unciphered ESP packet on a local interface (over GRE to 127.10.10.10) to have the clear content of the hijacked channel without effort in wireshark. This last feature does not work on microsoft OSes; this won't provide good result with ESP transport mode too...

The source of the class can be found here. It requires configuration of the class attributes: local_ip, local_port, cli_addr, srv_addr. Then, it is instantiated with the SA established with each peers: SA_cli and SA_srv. Each must have 'SPIi' and 'SPIr' as keys indexing a list containing encryption key, integrity protection key and SPI strings.
The class is coded to work with AES-CBC-128 and HMAC-SHA1-96, but can be changed easily by modifying the length of iv and mac and using the appropriate calls to pycrypto and hashlib.

And believe me: this works impressively!
No need to address IPsec system and network interfaces configuration, tunnel routing issues, neither ESP padding...
Holly python!