2007年9月5日星期三

[Tips]apache mod_proxy简要分析

author: yunshu#ph4nt0m.org
date: 2007-9-6
http://www.ph4nt0m.org

Text Mode

这几天要连续培训5天,下午的时候在会场快闷死了。还好看到mod_proxy的漏洞公告,就下载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 *= 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== ' ') {
    
*= ' \ 0 ';
    
for (wk = 0; wk < 7; wk++)
        
if (strcmp(x, lwday[wk]) == 0)
        
break;
    
*= ',';
    
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 -"=" | grep -"return" | grep -"Binary" | grep -"(apr_pstrdup" | grep -", apr_pstrdup" | grep -";"

首先在当前目录和子目录中查找所有包含这个字符串的行,然后去掉有=的,去掉有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== ' ') {
    
*= ' \ 0 ';
    
for (wk = 0; wk < 7; wk++)
        
if (strcmp(x, lwday[wk]) == 0)
        
break;
    
*= ',';
    
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代理尝试攻击。

总的来说,这个漏洞没多大意义,不过也许是我看错了,呵呵。

没有评论: