Mark Thomas wrote:
On 27/05/2014 15:12, Konstantin Preißer wrote:
Hi André,

-----Original Message-----
From: André Warnier [mailto:a...@ice-sa.com]
Sent: Tuesday, May 27, 2014 3:06 PM

Mark Thomas wrote:
CVE-2014-0099 Information Disclosure

...

Description:
The code used to parse the request content length header did not check
for overflow in the result. This exposed a request smuggling
vulnerability when Tomcat was located behind a reverse proxy that
correctly processed the content length header.

I believe you, but I must admit that I don't really get what the problem is,
here.
If someone feels like explaining..
The fix for this issue also made me a bit curious (I don't know the background 
of the issue).

The old code for parsing the Content-Length header looked like this:

        long n = c - '0';
        long m;

        while (--len > 0) {
            if (!isDigit(c = b[off++])) {
                throw new NumberFormatException();
            }
            m = n * 10 + c - '0';

            if (m < n) {
                // Overflow
                throw new NumberFormatException();
            } else {
                n = m;
            }
        }

Where "b" is a byte-array containing ASCII decimal chars.

The code parses a decimal number like "123" by multiplying the current number 
(e.g. 12) by 10 (=120), then adding the next character (=123).

To check for an overflow, it checks if the "new" number is lower than the old 
one. Usually, when making a simple addition with positive numbers where the second one is 
low (0-9), this is enough as for an overflow, the first bit will go to 1, so the number 
is negative. E.g., when using signed bytes (8 bits):
0111111b (127) + 3 = 10000010b (-126)

However, the code above also does an multiplication by 10. For example, if the 
current number (signed long) is 6148914691236517205 (binary: 
101010101010101010101010101010101010101010101010101010101010101b) and the next 
character is '3', the calculation would be:

101010101010101010101010101010101010101010101010101010101010101b (6148914691236517205) * 1010b (10) = 101010101010101010101010101010101010101010101010101010101010010b (6148914691236517202)

101010101010101010101010101010101010101010101010101010101010010b 
(6148914691236517202) + 11b (3) =
101010101010101010101010101010101010101010101010101010101010101b 
(6148914691236517205)

In this case, the new number would == the old number, so the code " if (m < n)" 
would not detect the overflow.

E.g., if you run following code:

        long a = 6148914691236517205L;
        long b = a * 10 + 3;
        System.out.println(a == b);

it will print "true".


However, I don't know if such example is really the one that causes issues, as 
this number is pretty high (but I did not found how smaller numbers could cause 
overflows not to be detected). Maybe someone could comment on that.

Yes, you need to have a content-length above Long.MAX_VALUE for problems
to occur. That would be unusual to say the least for most (all?)
applications in normal usage but easy for a malicious user to set.

If the proxy handles the header correctly, the attacker is going to have
to send a *lot* of data to get this to work. Where things would get
interesting is if the proxy and Tomcat both had parsing issues but ended
up with different values. That would make request smuggling a lot easier.

Something else to consider. If an attacker can trigger this
"request/response offset" then any subsequent requests they make could
receive responses that contain data from other users. Even if they can't
control what that data is, that is still information disclosure.


Just for the sake of it, I don't really see how that last scenario could come to pass though. Let's say that user A sends such a malicious request to the front-end, and the front-end interprets it correctly as one request, and passes it on to tomcat over a connector connection. Then Tomcat misinterprets this as being several requests in a row, and distributes them to several threads, to process and return responses, onto that same connector connection. And then, the first Tomcat thread to be finished, would return a response to the first part of that request. And then the front-end would think that this is the response to the original request, return it to the first (malicious) user, and that would be that for this request (for the front-end). If in the meantime the front-end receives another request, and it could (at the end of the first request) send this other request to Tomcat, using the same connection. And then the front-end may receive the response to Tomcat's "second request" (which in fact was a second part of the first), but it would think that it was the response to the second request, and forward it to the "wrong" user. So now the 2 systems are out of sync. Tomcat keeps on processing parts of the first malicious request as being separate requests, and the responses to such requests are being sent back as responses to subsequent genuine requests arriving at the front-end, instead of being the real responses to those genuine subsequent requests. And the extent of this out-of-sync situation is determined by how many "hidden requests" that first malicious request managed to sneak past. So it is all very confusing for the genuine users who get nonsensical ansers to their genuine requests. But I do not see how the first malicious user could start getting the responses of other user's genuine requests. "His" original request is already long-answered, so he would not get any additional responses. Ultimately, the front-end will end up receiving more responses from Tomcat, than the number of requests it has received and has sent to Tomcat. But it should then discard these additional responses, since it doesn't have any pending request to satisfy with them.
Or am I missing something ?



---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org

Reply via email to