CVE-2023-34188: Other (NVD-CWE-Other)
value: The HTTP server in Mongoose before 7.10 accepts requests containing negative Content-Length headers. By sending a single attack payload over TCP, an attacker can cause an infinite loop in which the server continuously reparses that payload, and does not respond to any other requests.
CWE description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
CVSS v3 base score: 7.5
Published: 2023-06-23T20:15Z
Repository: https://github.com/cesanta/mongoose
Language: C
Files changed: mongoose.c, src/http.c, test/unit_test.c
Commit date: 2023-05-16 18:52:33 +0300
Commit message: Added check for negative value for HTTP Content-Length header

Diff: diff --git a/mongoose.c b/mongoose.c
index 2bdfdac35..b954d2409 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -1461,11 +1461,13 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
   }
 
   mg_http_parse_headers(s, end, hm->headers,
                         sizeof(hm->headers) / sizeof(hm->headers[0]));
   if ((cl = mg_http_get_header(hm, "Content-Length")) != NULL) {
-    hm->body.len = (size_t) mg_to64(*cl);
+    int64_t content_len = mg_to64(*cl);
+    if(content_len < 0) return -1;
+    hm->body.len = (size_t) content_len;
     hm->message.len = (size_t) req_len + hm->body.len;
   }
 
   // mg_http_parse() is used to parse both HTTP requests and HTTP
   // responses. If HTTP response does not have Content-Length set, then
diff --git a/src/http.c b/src/http.c
index 3136dfcd5..c24181966 100644
--- a/src/http.c
+++ b/src/http.c
@@ -229,11 +229,13 @@ int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) {
   }
 
   mg_http_parse_headers(s, end, hm->headers,
                         sizeof(hm->headers) / sizeof(hm->headers[0]));
   if ((cl = mg_http_get_header(hm, "Content-Length")) != NULL) {
-    hm->body.len = (size_t) mg_to64(*cl);
+    int64_t content_len = mg_to64(*cl);
+    if(content_len < 0) return -1;
+    hm->body.len = (size_t) content_len;
     hm->message.len = (size_t) req_len + hm->body.len;
   }
 
   // mg_http_parse() is used to parse both HTTP requests and HTTP
   // responses. If HTTP response does not have Content-Length set, then
diff --git a/test/unit_test.c b/test/unit_test.c
index 698ba3091..e1c992d5a 100644
--- a/test/unit_test.c
+++ b/test/unit_test.c
@@ -788,10 +788,14 @@ static void test_http_server(void) {
   ASSERT(gethm(buf).body.len == 23);
 
   ASSERT(fetch(&mgr, buf, url, "GET /..ddot HTTP/1.0\n\n") == 301);
   ASSERT(fetch(&mgr, buf, url, "GET /..ddot/ HTTP/1.0\n\n") == 200);
   ASSERT(cmpbody(buf, "hi\n") == 0);
+  ASSERT(fetch(&mgr, buf, url, "GET /a.txt HTTP/1.0\n"
+        "Content-Length: -123\n\n") == 0);
+  ASSERT(fetch(&mgr, buf, url, "POST /a.txt HTTP/1.0\n"
+               "Content-Length: -123\n\n") == 0);
 
   {
     extern char *mg_http_etag(char *, size_t, size_t, time_t);
     struct mg_http_message hm;
     char etag[100];

Vulnerable code:     hm->body.len = (size_t) mg_to64(*cl);
    hm->body.len = (size_t) mg_to64(*cl);
Fixed code:     int64_t content_len = mg_to64(*cl);
    if(content_len < 0) return -1;
    hm->body.len = (size_t) content_len;
    int64_t content_len = mg_to64(*cl);
    if(content_len < 0) return -1;
    hm->body.len = (size_t) content_len;
  ASSERT(fetch(&mgr, buf, url, "GET /a.txt HTTP/1.0\n"
        "Content-Length: -123\n\n") == 0);
  ASSERT(fetch(&mgr, buf, url, "POST /a.txt HTTP/1.0\n"
               "Content-Length: -123\n\n") == 0);