看到mod_proxy的漏洞公告,就下载apache的代码看了看,度过了漫长的听人废话的时间。大致的过程如下:
首先看漏洞公告说的,漏洞出现在ap_proxy_date_canon函数里面,公告这这里, http://secunia.com/advisories/26636/。直接搜索函数名,定位到如下地址:modules/proxy/proxy_util.c的第293行,代码比较长:
代码:
粗略的看一遍,没发现啥大的问题。决定先看看apr_pstrdup函数,因为从这上面的代码来看,apr_pstrdup应该是在分配内存。这里有一个小的tips,怎么样搜索到函数的定义。
代码:
首先在当前目录和子目录中查找所有包含这个字符串的行,然后去掉有=的,去掉有return的,去掉二进制文件,去掉前面有(的,去掉前面有,空格的,去掉有;的,剩下的就是函数的定义了。这个查找根据不同的代码风格,灵活的去写。
apr_pstrdup函数是在srclib/apr/strings/apr_strings.c中的第69行定义的,代码很简单,就是拷贝了一下字符串,而且注意了长度,没什么还利用的。只是其中又调用了另外一个函数apr_palloc,利用上面同样的办法,定位到srclib/apr/memory/unix/apr_pools.c的594行,这个代码比较复杂,我没有仔细去看就判断它没问题。因为这个是很底层的内存池的函数,调用很频繁,如果有安全问题就不会仅仅是ap_proxy_date_canon出现问题了,而是整个apache都会有问题。
这样看来,想在ap_proxy_date_canon中找个可利用的溢出是不可能的了,唯一可能的地方是apr_palloc(p, 30)这里,但是下面用apr_snprintf做了限制,于是就开始看别的方面。
最终发现函数中有这样的片段:
代码:
这里取出了时间之后,通过确定,的位置来判断时间的格式,但是后面没有计算q的长度就直接读取q[8],q[11]等等,类似Last-Modified字段出现Wed, 05 Sep这样的字符串,会导致数组读取越界。这个是从代码推测的,我并没有去测试。
现在看看这个函数是如何调用的,先从如何写apache的mod入手。首先申明结构体module AP_MODULE_DECLARE_DATA,这个结构体的最后一个成员指针,指向了该module的注册函数。在注册函数里面关联需要处理的时间,触发功能函数。mod_proxy结构如下:
代码:
注册了proxy_http_handler函数和proxy_http_canon函数来处理一些请求。最终有问题函数的调用流程如下:
proxy_http_handler---->ap_proxy_http_process_response---->ap_proxy_read_headers---->process_proxy_header---->ap_proxy_date_canon
这样看来,只是在apache作为正向代理或者反向代理,处理real server返回的数据,在解析http头中的"Date", "Expires", "Last-Modified"等三个字段的时候,遇到畸形的时间结构,mod_proxy模块发生数组访问越界。就攻击而言,反向代理无法攻击,除非能控制到real server。如果是正向代理,到是可以自己伪造一个web服务器,然后设置apache为代理去访问伪造的web server,返回畸形的时间头给apache代理尝试攻击。
首先看漏洞公告说的,漏洞出现在ap_proxy_date_canon函数里面,公告这这里, http://secunia.com/advisories/26636/。直接搜索函数名,定位到如下地址:modules/proxy/proxy_util.c的第293行,代码比较长:
代码:
PROXY_DECLARE(const char *) ap_proxy_date_canon(apr_pool_t *p, const char *x1) { char *x = apr_pstrdup(p, x1); int wk, mday, year, hour, min, sec, mon; char *q, month[4], zone[4], week[4]; q = strchr(x, ','); /* check for RFC 850 date */ if (q != NULL && q - x > 3 && q[1] == ' ') { *q = ' / 0 '; for (wk = 0; wk < 7; wk++) if (strcmp(x, lwday[wk]) == 0) break; *q = ','; if (wk == 7) return x; /* not a valid date */ if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' || q[17] != ':' || strcmp(&q[20], " GMT") != 0) return x; if (sscanf(q + 2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year, &hour, &min, &sec, zone) != 7) return x; if (year < 70) year += 2000; else year += 1900; } else { /* check for acstime() date */ if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' || x[16] != ':' || x[19] != ' ' || x[24] != ' / 0 ') return x; if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour, &min, &sec, &year) != 7) return x; for (wk = 0; wk < 7; wk++) if (strcmp(week, apr_day_snames[wk]) == 0) break; if (wk == 7) return x; } /* check date */ for (mon = 0; mon < 12; mon++) if (strcmp(month, apr_month_snames[mon]) == 0) break; if (mon == 12) return x; q = apr_palloc(p, 30); apr_snprintf(q, 30, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", apr_day_snames[wk], mday, apr_month_snames[mon], year, hour, min, sec); return q; } |
粗略的看一遍,没发现啥大的问题。决定先看看apr_pstrdup函数,因为从这上面的代码来看,apr_pstrdup应该是在分配内存。这里有一个小的tips,怎么样搜索到函数的定义。
代码:
grep -r apr_pstrdup * | grep -v "=" | grep -v "return" | grep -v "Binary" | grep -v "(apr_pstrdup" | grep -v ", apr_pstrdup" | grep -v ";" |
首先在当前目录和子目录中查找所有包含这个字符串的行,然后去掉有=的,去掉有return的,去掉二进制文件,去掉前面有(的,去掉前面有,空格的,去掉有;的,剩下的就是函数的定义了。这个查找根据不同的代码风格,灵活的去写。
apr_pstrdup函数是在srclib/apr/strings/apr_strings.c中的第69行定义的,代码很简单,就是拷贝了一下字符串,而且注意了长度,没什么还利用的。只是其中又调用了另外一个函数apr_palloc,利用上面同样的办法,定位到srclib/apr/memory/unix/apr_pools.c的594行,这个代码比较复杂,我没有仔细去看就判断它没问题。因为这个是很底层的内存池的函数,调用很频繁,如果有安全问题就不会仅仅是ap_proxy_date_canon出现问题了,而是整个apache都会有问题。
这样看来,想在ap_proxy_date_canon中找个可利用的溢出是不可能的了,唯一可能的地方是apr_palloc(p, 30)这里,但是下面用apr_snprintf做了限制,于是就开始看别的方面。
最终发现函数中有这样的片段:
代码:
char *q, month[4], zone[4], week[4]; q = strchr(x, ','); /* check for RFC 850 date */ if (q != NULL && q - x > 3 && q[1] == ' ') { *q = ' / 0 '; for (wk = 0; wk < 7; wk++) if (strcmp(x, lwday[wk]) == 0) break; *q = ','; if (wk == 7) return x; /* not a valid date */ if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' || q[17] != ':' || strcmp(&q[20], " GMT") != 0) return x; ................. |
这里取出了时间之后,通过确定,的位置来判断时间的格式,但是后面没有计算q的长度就直接读取q[8],q[11]等等,类似Last-Modified字段出现Wed, 05 Sep这样的字符串,会导致数组读取越界。这个是从代码推测的,我并没有去测试。
现在看看这个函数是如何调用的,先从如何写apache的mod入手。首先申明结构体module AP_MODULE_DECLARE_DATA,这个结构体的最后一个成员指针,指向了该module的注册函数。在注册函数里面关联需要处理的时间,触发功能函数。mod_proxy结构如下:
代码:
static void ap_proxy_http_register_hook(apr_pool_t *p) { proxy_hook_scheme_handler(proxy_http_handler, NULL, NULL, APR_HOOK_FIRST); proxy_hook_canon_handler(proxy_http_canon, NULL, NULL, APR_HOOK_FIRST); } module AP_MODULE_DECLARE_DATA proxy_http_module = { STANDARD20_MODULE_STUFF, NULL, /* create per-directory config structure */ NULL, /* merge per-directory config structures */ NULL, /* create per-server config structure */ NULL, /* merge per-server config structures */ NULL, /* command apr_table_t */ ap_proxy_http_register_hook/* register hooks */ }; |
注册了proxy_http_handler函数和proxy_http_canon函数来处理一些请求。最终有问题函数的调用流程如下:
proxy_http_handler---->ap_proxy_http_process_response---->ap_proxy_read_headers---->process_proxy_header---->ap_proxy_date_canon
这样看来,只是在apache作为正向代理或者反向代理,处理real server返回的数据,在解析http头中的"Date", "Expires", "Last-Modified"等三个字段的时候,遇到畸形的时间结构,mod_proxy模块发生数组访问越界。就攻击而言,反向代理无法攻击,除非能控制到real server。如果是正向代理,到是可以自己伪造一个web服务器,然后设置apache为代理去访问伪造的web server,返回畸形的时间头给apache代理尝试攻击。