CVE-2016-8747

| categories notes 
tags CVE 

http://svn.apache.org/viewvc/tomcat/tc8.5.x/trunk/java/org/apache/coyote/http11/Http11InputBuffer.java?r1=1774166&r2=1774165&pathrev=1774166&diff_format=f

--- a/java/org/apache/coyote/http11/Http11InputBuffer.java
+++ b/java/org/apache/coyote/http11/Http11InputBuffer.java
@@ -296,12 +296,16 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
     void nextRequest() {
         request.recycle();

-        // Copy leftover bytes to the beginning of the buffer
-        if (byteBuffer.remaining() > 0 && byteBuffer.position() > 0) {
-            byteBuffer.compact();
+        if (byteBuffer.position() > 0) {
+            if (byteBuffer.remaining() > 0) {
+                // Copy leftover bytes to the beginning of the buffer
+                byteBuffer.compact();
+                byteBuffer.flip();
+            } else {
+                // Reset position and limit to 0
+                byteBuffer.position(0).limit(0);
+            }
         }
-        // Always reset pos to zero
-        byteBuffer.limit(byteBuffer.limit() - byteBuffer.position()).position(0);

发送如下请求:

GET / HTTP/1.1
Host: 127.0.0.1:8890
User-Agent: curl/7.51.0

GET /abc HTTP/1.1
Host: 127.0.0.1:8890

c=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

将导致c参数的值不仅仅是A,还会包含上一个请求(可能是别人的请求)的内容。

p position
l limit
c capacity

byteBuffer的内存布局

|______|__________|____________________|
0      p          l                    c

我们构造一个请求,0-p为一个正常的请求。p-l为上面AAAA...那个请求。两个请求放在一个socket里发送。

当Tomcat处理完第一个请求的时候,将执行compact操作

    public ByteBuffer compact() {

        System.arraycopy(hb, ix(position()), hb, ix(0), remaining());
        position(remaining());
        limit(capacity());
        discardMark();
        return this;
    }

内存布局变成

|__________|______|____________________|
0          p1     l                   l1,c

p1=(l-p), l1=c

注意这里,0--(l-p)之间变成了原本p--l的内容,而l1--l之间的内容为buffer的缓存内容,可能是上一个请求的内容。

然后执行`byteBuffer.limit(byteBuffer.limit() - byteBuffer.position()).position(0);`

内存布局变成

|__________|______|__________|__________|
p2         p1     l          l2         c

p2=0, l2=l1-p1

这样,Tomcat继续处理一个socket里的下一个请求。但是这时,limit是不正确的,正确的limit应该是p1。由于limit不正确,因此请求错误的包含了l--l2之间的内容,而该部分内容,是buffer中缓存的内容。将导致恶意构造的请求可以读取到别人发送的内容。

补丁正确的处理了这个问题。

类似这样的问题,有可能发生的很不隐蔽。比如这个。只在8.5.7-8.5.9中存在。因为这个错误将导致一个socket中包含两个http请求时,后一个请求会出现异常,因此很快就能得到反馈,发现问题。如果情况再特殊一点,后一个请求不抛出异常的话,那么比较可能导致一个潜伏的漏洞。

服务器为了性能,重复利用buffer,也是一个安全隐患。
If you liked this post, you can share it with your followers !