The other day I was running a Nessus scan against a client and saw Plugin ID 81576 (Jetty HttpParser Error Remote Memory Disclosure). I’ve seen a number of Jetty servers on penetration testing engagements, but this is the first time I had seen this vulnerability listed. Nessus provided a link to the security advisory and to an excellent blog post written by the vulnerability discoverer, which included a nice Proof-of-Concept (PoC) Python script.
The PoC provided by the researchers clearly shows that if any of the request headers contain an unprintable character, such as a Null Byte (\x00), the server will produce an error message that contains 16 bytes of memory from a previously used buffer. In addition, they showed that by increasing the length of the bad header, an attacker could read further into the buffer. In other words, if you have a buffer that contains 32 bytes, you can read the first 16 bytes by sending 1 bad character in the header and you can read the next 16 bytes by sending a second request with 16 bad characters in the header.
My research showed that the server didn’t always return 16 bytes; sometimes it would return 17 bytes. In addition, many of the returned bytes were null bytes or carriage returns (\r) or line feeds (\n). Taking all of this into account, I wrote a script that would start by sending 1 bad character and receive however many bytes the server sent. It would store these bytes into a string and then increase the number of bad characters sent by the number of bytes received. This allowed me to read the buffer in 16-17 byte chunks. If the server response contained only null bytes or only ‘\r\n’, then I would ignore the response.
The script continues to build up the buffer until it has read 1000 bytes or until the script has had to wait a total of 2 minutes. The script will wait 5 seconds if the response is only null bytes. This gives the server time to put new data into the buffer. So far, the largest amount of data I have been able to read is about 250 bytes. I don’t know if that is because of the buffer size or because of the timeout limitation.