Apache Tomcat 8.0.3 denial of service by using a Content-Length 0

2014.06.01
Credit: markt
Risk: Medium
Local: No
Remote: Yes
CWE: CWE-20


CVSS Base Score: 5/10
Impact Subscore: 2.9/10
Exploitability Subscore: 10/10
Exploit range: Remote
Attack complexity: Low
Authentication: No required
Confidentiality impact: None
Integrity impact: None
Availability impact: Partial

Correct regression introduced in 8.0.0-RC2 as part of the Servlet 3.1 non-blocking IO support that broke handling of requests with an explicit content length of zero. This is the fix for CVE-2014-0095 --- tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProcessor.java 2014/03/17 14:17:13 1578391 +++ tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProcessor.java 2014/03/17 14:17:19 1578392 @@ -242,13 +242,6 @@ /** - * Is a body present for the current request? This is determined by the - * presence of the content-length header with a non-zero value. - */ - private boolean bodyPresent = false; - - - /** * Indicates that a 'get body chunk' message has been sent but the body * chunk has not yet been received. */ @@ -902,7 +895,6 @@ // Recycle Request object first = true; endOfStream = false; - bodyPresent = false; waitingForBodyMessage = false; empty = true; replay = false; @@ -975,12 +967,10 @@ } waitingForBodyMessage = false; - first = false; // No data received. if (bodyMessage.getLen() == 0) { // just the header - // Don't mark 'end of stream' for the first chunk. return false; } int blen = bodyMessage.peekInt(); @@ -1061,9 +1051,8 @@ * @return true if there is more data, false if not. */ protected boolean refillReadBuffer(boolean block) throws IOException { - // If the server returns an empty packet, assume that that end of - // the stream has been reached (yuck -- fix protocol??). - // FORM support + // When using replay (e.g. after FORM auth) all the data to read has + // been buffered so there is no opportunity to refill the buffer. if (replay) { endOfStream = true; // we've read everything there is } @@ -1071,14 +1060,30 @@ return false; } + if (first) { + first = false; + long contentLength = request.getContentLengthLong(); + // - When content length > 0, AJP sends the first body message + // automatically. + // - When content length == 0, AJP does not send a body message. + // - When content length is unknown, AJP does not send the first + // body message automatically. + if (contentLength > 0) { + waitingForBodyMessage = true; + } else if (contentLength == 0) { + endOfStream = true; + return false; + } + } + // Request more data immediately - if (!first && !waitingForBodyMessage) { + if (!waitingForBodyMessage) { output(getBodyMessageArray, 0, getBodyMessageArray.length, true); waitingForBodyMessage = true; } boolean moreData = receive(block); - if (!moreData && ((first && !bodyPresent) || (!first && !waitingForBodyMessage))) { + if (!moreData && !waitingForBodyMessage) { endOfStream = true; } return moreData; @@ -1160,9 +1165,6 @@ // Set the content-length header for the request request.setContentLength(cl); } - if (cl != 0) { - bodyPresent = true; - } } else if (hId == Constants.SC_REQ_CONTENT_TYPE || (hId == -1 && tmpMB.equalsIgnoreCase("Content-Type"))) { // just read the content-type header, so set it @@ -1519,8 +1521,8 @@ finished = true; // Swallow the unread body packet if present - if (first && request.getContentLengthLong() > 0 || waitingForBodyMessage) { - receive(true); + if (waitingForBodyMessage || first && request.getContentLengthLong() > 0) { + refillReadBuffer(true); } // Add the end message @@ -1539,7 +1541,7 @@ if (empty) { try { refillReadBuffer(false); - } catch (IOException e) { + } catch (IOException timeout) { // Not ideal. This will indicate that data is available // which should trigger a read which in turn will trigger // another IOException and that one can be thrown. @@ -1682,12 +1684,7 @@ if (endOfStream) { return -1; } - if (first && req.getContentLengthLong() > 0) { - // Handle special first-body-chunk - if (!receive(true)) { - return 0; - } - } else if (empty) { + if (empty) { if (!refillReadBuffer(true)) { return -1; }

References:

http://svn.apache.org/viewvc?view=revision&revision=1578392


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top