[Helix-server-dev] CR: 165604 - server vulnerable to infinite http request

[Helix-server-dev] CR: 165604 - server vulnerable to infinite http request

atin atin at real.com
Thu May 11 17:32:30 PDT 2006


Synopsis
========
sending a http request with an infinite number of headers (or very large
headers) caused the server to run out of memory.


Branches: SERVER_11_1, SERVER_CURRENT
Suggested Reviewer: dcollins


Description
===========
the http parser does not check for bounds on the number of http headers that
can be present in a http request, as a result of this an attacker can send a
valid http message (GET/POST/PUT/HEAD) followed by a very large number of http
headers to the server and cause it to run out of memory. the server basically
allocates memory for each incoming header and thus runs out of memory.

the fix was to do a limit check on the number of headers that are being
parsed and return a NULL HTTPMessage pointer and a boolean indicating that the
message was too large. this involved modifying the HTTPParser::parse() command
to take 3 arguments instead of its existing 2. when bMsgTooLarge is TRUE from
inside HTTPDemux we just close the connection.

another check to limit the size of the headers has also been put in, because
currently if the server receives a huge http header, it tries to allocate the
buffer which in isolation with a huge buffer or in conjunction with the above
method can be used to run the server out of memory.

it would b helpful to log an error/warning message in the log file identifying
the source address of the connection, but that is a non-trivial change and
given the time constraints it is not going to b done now.

the most brain dead part of this parsing is that each time the server receives
an incomplete message, it parses the message, but gets returned a NULL because
the message is incomplete. then as it receives more headers, it re-parses the
entire message even tho there were sufficient headers in the previous message
to create a valid HTTPMessage object. this is not being fixed in the cr.


Files Affected
==============
protocol/common/util/hxcloakedsocket.cpp
protocol/common/util/hxcloaksockv2.cpp
protocol/http/httpmsg.cpp
protocol/http/httppars.cpp
protocol/http/pub/httppars.h
server/protocol/http/http_demux.cpp
server/protocol/http/servhttppost.cpp
server/protocol/http/pub/http_demux.h


Testing Performed
=================
Unit Tests:
N/A

Integration Tests:
(1) ran jsimpson's endless-http-headers-jrs.pl script from the pr attachment.
    $ endless-http-headers-jrs.pl joust:19080
(2) ran the script with huge headers.
    $ endless-http-headers-jrs.pl joust:19080 "y" 100000 100

Leak Tests:
(1) against jsimpson's script.

Performance Tests:
N/A

Platforms Tested: linux-rhel4-i686
Build verified: linux-rhel4-i686


QA Hints
========
(1) Repeat Integration tests (1) and (2) as described above.
-------------- next part --------------
Index: protocol/common/util/hxcloakedsocket.cpp
===================================================================
RCS file: /cvsroot/protocol/common/util/hxcloakedsocket.cpp,v
retrieving revision 1.3
diff -u -w -r1.3 hxcloakedsocket.cpp
--- protocol/common/util/hxcloakedsocket.cpp	15 Nov 2004 18:40:10 -0000	1.3
+++ protocol/common/util/hxcloakedsocket.cpp	12 May 2006 00:42:18 -0000
@@ -1986,7 +1986,12 @@
                    {
                        pBufferContents = (char*)(const char*)m_pHTTPHeaderBuffer->GetBuffer();
                        nMsgLen = m_pHTTPHeaderBuffer->GetSize();
+#if !defined HELIX_FEATURE_SERVER
                        pMessage = (HTTPResponseMessage*)Parser.parse( pBufferContents, nMsgLen );
+#else
+                       BOOL bMsgTooLarge = FALSE;
+                       pMessage = (HTTPResponseMessage*)Parser.parse( pBufferContents, nMsgLen, bMsgTooLarge );
+#endif /* !HELIX_FEATURE_SERVER */
                        
                        hr  = HXR_FAIL;
                        if( pMessage && HTTPMessage::T_UNKNOWN != pMessage->tag())
@@ -2130,7 +2135,12 @@
     
     pBufferContents = (char*)(const char*)pBuf->GetBuffer();
     nMsgLen         = pBuf->GetSize();
+#if !defined HELIX_FEATURE_SERVER
     pMessage        = (HTTPResponseMessage*)Parser.parse( pBufferContents, nMsgLen );
+#else
+    BOOL bMsgTooLarge = FALSE;
+    pMessage        = (HTTPResponseMessage*)Parser.parse( pBufferContents, nMsgLen, bMsgTooLarge );
+#endif /* !HELIX_FEATURE_SERVER */
     ulHTTPStatus    = atoi(pMessage->errorCode());
 
     HX_DELETE(pMessage);
@@ -3012,7 +3022,12 @@
     HTTPParser Parser;
     char* pBufferContents = (char*)(const char*)pBuf->GetBuffer();
     ULONG32 nMsgLen = pBuf->GetSize();
+#if !defined HELIX_FEATURE_SERVER
     HTTPResponseMessage* pMessage = (HTTPResponseMessage*)Parser.parse( pBufferContents, nMsgLen );
+#else
+    BOOL bMsgTooLarge = FALSE;
+    HTTPResponseMessage* pMessage = (HTTPResponseMessage*)Parser.parse( pBufferContents, nMsgLen, bMsgTooLarge );
+#endif /* !HELIX_FEATURE_SERVER */
 
     //
     // ignore non-HTTP responses; thoseh will be processed by the
Index: protocol/common/util/hxcloaksockv2.cpp
===================================================================
RCS file: /cvsroot/protocol/common/util/hxcloaksockv2.cpp,v
retrieving revision 1.4
diff -u -w -r1.4 hxcloaksockv2.cpp
--- protocol/common/util/hxcloaksockv2.cpp	15 Nov 2004 18:40:10 -0000	1.4
+++ protocol/common/util/hxcloaksockv2.cpp	12 May 2006 00:42:18 -0000
@@ -1206,7 +1206,12 @@
     {
         res = HXR_FAIL;
         ulLength = pBuf->GetSize();
+#if !defined HELIX_FEATURE_SERVER
         pMess = (HTTPResponseMessage*)parser.parse( (const char*)pBuf->GetBuffer(), ulLength);
+#else
+	BOOL bMsgTooLarge = FALSE;
+        pMess = (HTTPResponseMessage*)parser.parse( (const char*)pBuf->GetBuffer(), ulLength, bMsgTooLarge);
+#endif /* !HELIX_FEATURE_SERVER */
         HX_ASSERT(pMess);
 
         //Verify we have HTTP response message.
Index: protocol/http/httpmsg.cpp
===================================================================
RCS file: /cvsroot/protocol/http/httpmsg.cpp,v
retrieving revision 1.11
diff -u -w -r1.11 httpmsg.cpp
--- protocol/http/httpmsg.cpp	9 Jul 2004 18:41:32 -0000	1.11
+++ protocol/http/httpmsg.cpp	12 May 2006 00:42:18 -0000
@@ -186,6 +186,7 @@
 }
 
 HTTPMessage::HTTPMessage()
+    : m_isCloakedMsg(FALSE)
 {
     setVersion(MAJ_VERSION, MIN_VERSION);
 }
Index: protocol/http/httppars.cpp
===================================================================
RCS file: /cvsroot/protocol/http/httppars.cpp,v
retrieving revision 1.5
diff -u -w -r1.5 httppars.cpp
--- protocol/http/httppars.cpp	27 Sep 2004 20:14:53 -0000	1.5
+++ protocol/http/httppars.cpp	12 May 2006 00:42:18 -0000
@@ -71,6 +71,9 @@
 
 HTTPParser::HTTPParser()
 {
+#if defined HELIX_FEATURE_SERVER
+    m_bMessageTooLarge = FALSE;
+#endif /* HELIX_FEATURE_SERVER */
 }
 
 HTTPParser::~HTTPParser()
@@ -467,7 +470,16 @@
     {
 	tok = scanner.nextToken("\n");
 	if (tok.hasValue())
+	{
+#if defined HELIX_FEATURE_SERVER
+	    if (tok.value().GetLength() > MAX_HTTP_HEADER_SIZE)
+	    {
+		m_bMessageTooLarge = TRUE;
+		return 0;
+	    }
+#endif /* HELIX_FEATURE_SERVER */
 		m_msglines.AddTail((void*)(new CHXString((const char*)tok.value())));
+	}
 	if(tok.lastChar() == MIMEToken::T_EOL)
 	{
 	    if(gotEOL && !tok.hasValue())
@@ -496,6 +508,8 @@
     }
 }
 
+#if !defined HELIX_FEATURE_SERVER
+
 /*
  * Parse message pointed to by pMsg with length nMsgLen.
  * If not a complete message, return 0, otherwise return
@@ -539,18 +553,83 @@
 	    nMsgLen = 0;
 	    return 0;
 	}
+	pHTTPMsg = parseRequest();
+	if (!pHTTPMsg || pHTTPMsg->majorVersion() > 0)
+	{
+	    nMsgLen = 0;
+	    HX_DELETE(pHTTPMsg);
+	}
+    }
+    return pHTTPMsg;
+}
+
+#else /* !HELIX_FEATURE_SERVER */
+/*
+ * Parse message pointed to by pMsg with length nMsgLen.
+ * If not a complete message, return 0, otherwise return
+ * HTTPMessage pointer.
+ */
+HTTPMessage* 
+HTTPParser::parse(const char* pMsg, UINT32& nMsgLen, BOOL& bMessageTooLarge)
+{
+    HTTPMessage* pHTTPMsg = 0;
+    clearMessageLines();
+    int msgOffset = scanMessageHeader(pMsg, nMsgLen);
+    if (m_bMessageTooLarge)
+    {
+	bMessageTooLarge = TRUE;
+	return 0;
+    }
+    else if (m_msglines.GetCount() > MAX_HTTP_HEADER_COUNT)
+    {
+	m_bMessageTooLarge = TRUE;
+	bMessageTooLarge = TRUE;
+	return 0;
+    }
+    if(msgOffset > 0)
+    {
+	if(m_msglines.GetCount() == 0)
+	{
+	    nMsgLen = 0;
+	    return 0;
+	}
 	CHXString* str = (CHXString*)m_msglines.GetHead();
+	if(strncasecmp((*str), "HTTP/", 5) == 0)
+	{
+	    pHTTPMsg = parseResponse();
+	}
+	else 
+	{
+	    pHTTPMsg = parseRequest();
+	}
+
+	if(pHTTPMsg)
+	{
+	    nMsgLen = msgOffset;
+	}
+    }
+    else
+    {
+	//nMsgLen = 0;
+
+	/* Might be a HTTP/0.9 request.  No extra blank line. Drat. */
+	if(m_msglines.GetCount() == 0)
+	{
+	    nMsgLen = 0;
+	    return 0;
+	}
 	pHTTPMsg = parseRequest();
 	if(!pHTTPMsg || pHTTPMsg->majorVersion() > 0)
 	{
 	    nMsgLen = 0;
-	    delete pHTTPMsg;
-	    pHTTPMsg = 0;
+	    HX_DELETE(pHTTPMsg);
 	}
     }
     return pHTTPMsg;
 }
 
+#endif /* !HELIX_FEATURE_SERVER */
+
 /*
  * Delete contents of m_msglines
  */
Index: protocol/http/pub/httppars.h
===================================================================
RCS file: /cvsroot/protocol/http/pub/httppars.h,v
retrieving revision 1.2
diff -u -w -r1.2 httppars.h
--- protocol/http/pub/httppars.h	9 Jul 2004 18:41:28 -0000	1.2
+++ protocol/http/pub/httppars.h	12 May 2006 00:42:18 -0000
@@ -64,13 +64,25 @@
 #include "hxslist.h"
 #include "httpmsg.h"
 
+#ifdef HELIX_FEATURE_SERVER
+// the limits are being put to prevent a DOS attack with infinite HTTP
+// requests (infinite headers within a HTTP message)
+#define MAX_HTTP_HEADER_COUNT	256
+#define MAX_HTTP_HEADER_SIZE	65536
+#endif /* HELIX_FEATURE_SERVER */
+
 class HTTPParser
 {
 public:
     HTTPParser();
     virtual ~HTTPParser();
 
+#if !defined HELIX_FEATURE_SERVER
     virtual HTTPMessage* parse(const char* pMsg, UINT32& nMsgLen);
+#else
+    virtual HTTPMessage* parse(const char* pMsg, UINT32& nMsgLen,
+	BOOL& bMessageTooLarge);
+#endif /* !HELIX_FEATURE_SERVER */
 
 protected:
     MIMEHeader* parseHeader(CHXString& str);
@@ -92,6 +104,7 @@
     void clearMessageLines();
 
     CHXSimpleList m_msglines;
+    BOOL m_bMessageTooLarge;
 };
 
 #endif	/* _HTTPPARS_H_ */
Index: server/protocol/http/http_demux.cpp
===================================================================
RCS file: /cvsroot/server/protocol/http/http_demux.cpp,v
retrieving revision 1.10.2.7.16.4
diff -u -w -r1.10.2.7.16.4 http_demux.cpp
--- server/protocol/http/http_demux.cpp	7 Apr 2006 20:28:35 -0000	1.10.2.7.16.4
+++ server/protocol/http/http_demux.cpp	12 May 2006 00:42:23 -0000
@@ -86,7 +86,8 @@
     m_pResponse(NULL),
     m_pSavedMessage(NULL),
     m_ReadState(DEMUX_READ_MSG),
-    m_bClosed(FALSE)
+    m_bClosed(FALSE),
+    m_ulMsgLen(0)
 {
     m_pParser = new HTTPParser;
 }
@@ -337,14 +338,23 @@
         IHXBuffer* pNewBuf = NULL;
         IHXBuffer* pContentBuf = NULL;
         BOOL bFirstRun = FALSE;
+	BOOL bMsgTooLarge = FALSE;
 
         if (m_ReadState == DEMUX_READ_MSG)
         {
             HTTPMessage* pMsg = NULL;
             uDataUsed = uDataLen;
-            pMsg = m_pParser->parse((const char*)pData, uDataUsed);
+	    m_ulMsgLen += uDataLen;
+            pMsg = m_pParser->parse((const char*)pData, uDataUsed, bMsgTooLarge);
             if (pMsg == NULL)
             {
+		if (bMsgTooLarge)
+		{
+		    if (pBuf)
+			pBuf->Release();
+		    Close(HXR_FAIL);
+		    return;
+		}
                 break;
             }
 
@@ -407,6 +417,8 @@
                     if (iLen <= 0 || iLen > 0xffff)
                     {
                         DPRINTF(D_ERROR, ("HTTP: Bad content length %d\n", iLen));
+			if (pBuf)
+			    pBuf->Release();
                         Close(HXR_FAIL);
                         return;
                     }
Index: server/protocol/http/servhttppost.cpp
===================================================================
RCS file: /cvsroot/server/protocol/http/servhttppost.cpp,v
retrieving revision 1.1
diff -u -w -r1.1 servhttppost.cpp
--- server/protocol/http/servhttppost.cpp	16 Nov 2004 03:02:43 -0000	1.1
+++ server/protocol/http/servhttppost.cpp	12 May 2006 00:42:23 -0000
@@ -1413,13 +1413,14 @@
             // Force to new length (including possible bin data at end)
             m_strResultHeader.ReleaseBuffer(ulLength + size);
 
+	    BOOL bMsgTooLarge = FALSE;
             HTTPParser Parser;
             HTTPResponseMessage* pMessage = 0;
 
             UINT32 ulHeaderLength = m_strResultHeader.GetLength();
 
             // Parse headers from message
-            pMessage = (HTTPResponseMessage*)Parser.parse((const char*)m_strResultHeader, ulHeaderLength);
+            pMessage = (HTTPResponseMessage*)Parser.parse((const char*)m_strResultHeader, ulHeaderLength, bMsgTooLarge);
             if (pMessage && pMessage->tag() == HTTPMessage::T_RESP)
             {
                 // We now have the entire header!
Index: server/protocol/http/pub/http_demux.h
===================================================================
RCS file: /cvsroot/server/protocol/http/pub/http_demux.h,v
retrieving revision 1.5.2.2.18.1
diff -u -w -r1.5.2.2.18.1 http_demux.h
--- server/protocol/http/pub/http_demux.h	7 Apr 2006 20:28:36 -0000	1.5.2.2.18.1
+++ server/protocol/http/pub/http_demux.h	12 May 2006 00:42:23 -0000
@@ -171,6 +171,7 @@
     HTTPMessage*                m_pSavedMessage;
     DEMUX_READ_STATE            m_ReadState;
     BOOL			m_bClosed;
+    UINT32			m_ulMsgLen;
 };
 
 #endif  /* _HTTP_DEMUX_H_ */


More information about the Helix-server-dev mailing list
 

Site Map   |   Terms of Use   |   Privacy Policy   |   Contact Us

Copyright © 1995-2007 RealNetworks, Inc. All rights reserved. RealNetworks and Helix are trademarks of RealNetworks.
All other trademarks or registered trademarks are the property of their respective holders.