频道栏目
首页 > 安全 > 系统安全 > 正文
nginx url解码引发的waf漏洞
2013-06-22 09:37:18           
收藏   我要投稿

去年发现的ngx一个bug,直到最近有空才写了这篇。
Nginx ngx_unescape_uri函数在处理url decode时没有遵照标准的url decode,从而引起一系列使用该函数解码的waf
都存在绕过漏洞
出现该问题的函数位于src\core\ngx_string.c代码中ngx_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type)

 

void
 ngx_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type)
 {
     u_char  *d, *s, ch, c, decoded;
     enum {
         sw_usual = 0,
         sw_quoted,
         sw_quoted_second
     } state;
 
     d = *dst;
     s = *src;
 
     state = 0;
     decoded = 0;
 
     while (size--) {
 
         ch = *s++;
 
         switch (state) {
         case sw_usual:
             if (ch == '?'
                 && (type & (NGX_UNESCAPE_URI|NGX_UNESCAPE_REDIRECT)))
             {
                 *d++ = ch;
                 goto done;
             }
 
             if (ch == '%') {
                 state = sw_quoted;
                 break;
             }
 
             *d++ = ch;
             break;
 
         case sw_quoted:
 
             if (ch >= '0' && ch <= '9') {
                 decoded = (u_char) (ch - '0');
                 state = sw_quoted_second;
                 break;
             }
 
             c = (u_char) (ch | 0x20);
             if (c >= 'a' && c <= 'f') {
                 decoded = (u_char) (c - 'a' + 10);
                 state = sw_quoted_second;
                 break;
             }
 
             /* the invalid quoted character */
 
             state = sw_usual;
 
             *d++ = ch;
 
             break;
 
         case sw_quoted_second:
 
             state = sw_usual;
 
             if (ch >= '0' && ch <= '9') {
                 ch = (u_char) ((decoded << 4) + ch - '0');
 
                 if (type & NGX_UNESCAPE_REDIRECT) {
                     if (ch > '%' && ch < 0x7f) {
                         *d++ = ch;
                         break;
                     }
 
                     *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);
 
                     break;
                 }
 
                 *d++ = ch;
 
                 break;
             }
 
             c = (u_char) (ch | 0x20);
             if (c >= 'a' && c <= 'f') {
                 ch = (u_char) ((decoded << 4) + c - 'a' + 10);
 
                 if (type & NGX_UNESCAPE_URI) {
                     if (ch == '?') {
                         *d++ = ch;
                         goto done;
                     }
 
                     *d++ = ch;
                     break;
                 }
 
                 if (type & NGX_UNESCAPE_REDIRECT) {
                     if (ch == '?') {
                         *d++ = ch;
                         goto done;
                     }
 
                     if (ch > '%' && ch < 0x7f) {
                         *d++ = ch;
                         break;
                     }
 
                     *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);
                     break;
                 }
 
                 *d++ = ch;
 
                 break;
             }
 
             /* the invalid quoted character */
 
             break;
         }
     }
 
 done:
 
     *dst = d;
     *src = s;
 }

 

该函数在处理%号编码时,如果%后面第一个字符非16进制范围则会丢弃%,否则第二个字符非16进制范围则会丢弃%和第一个字符,具体表现为Sql注入关键字select如果写成s%elect经过ngx编码处理后则会变成slect从而绕过waf过滤规则,例如IIS asp 对s%elect的编码处理结果为select,还有%and经过ngx解码函数后会变为nd等等。
下面我们来看看国际知名web安全组织OWASP的开源WAF NAXSI,出现问题的代码位于naxsi_src\naxsi_utils.c中
 


 


 

/*
 ** Patched ngx_unescape_uri : 
 ** The original one does not care if the character following % is in valid range.
 ** For example, with the original one :
 ** '%uff' -> 'uff'
 */
 void
 naxsi_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type)
 {
     u_char  *d, *s, ch, c, decoded;
     enum {
         sw_usual = 0,
         sw_quoted,
         sw_quoted_second
     } state;
 
     d = *dst;
     s = *src;
 
     state = 0;
     decoded = 0;
 
     while (size--) {
 
         ch = *s++;
 
         switch (state) {
         case sw_usual:
             if (ch == '?'
                 && (type & (NGX_UNESCAPE_URI|NGX_UNESCAPE_REDIRECT)))
             {
                 *d++ = ch;
                 goto done;
             }
 
             if (ch == '%') {
                 state = sw_quoted;
                 break;
             }
 
             *d++ = ch;
             break;
 
         case sw_quoted:
 
             if (ch >= '0' && ch <= '9') {
                 decoded = (u_char) (ch - '0');
                 state = sw_quoted_second;
                 break;
             }
 
             c = (u_char) (ch | 0x20);
             if (c >= 'a' && c <= 'f') {
                 decoded = (u_char) (c - 'a' + 10);
                 state = sw_quoted_second;
                 break;
             }
 
             /* the invalid quoted character */
 
             state = sw_usual;
          *d++ = '%';
             *d++ = ch;
 
             break;
 
         case sw_quoted_second:
 
             state = sw_usual;
 
             if (ch >= '0' && ch <= '9') {
                 ch = (u_char) ((decoded << 4) + ch - '0');
 
                 if (type & NGX_UNESCAPE_REDIRECT) {
                     if (ch > '%' && ch < 0x7f) {
                         *d++ = ch;
                         break;
                     }
 
                     *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);
 
                     break;
                 }
 
                 *d++ = ch;
 
                 break;
             }
 
             c = (u_char) (ch | 0x20);
             if (c >= 'a' && c <= 'f') {
                 ch = (u_char) ((decoded << 4) + c - 'a' + 10);
 
                 if (type & NGX_UNESCAPE_URI) {
                     if (ch == '?') {
                         *d++ = ch;
                         goto done;
                     }
 
                     *d++ = ch;
                     break;
                 }
 
                 if (type & NGX_UNESCAPE_REDIRECT) {
                     if (ch == '?') {
                         *d++ = ch;
                         goto done;
                     }
 
                     if (ch > '%' && ch < 0x7f) {
                         *d++ = ch;
                         break;
                     }
 
                     *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);
                     break;
                 }
 
                 *d++ = ch;
 
                 break;
             }
          
             /* the invalid quoted character */
          *d++ = ch;
 
             break;
         }
     }
 
 done:
 
     *dst = d;
     *src = s;
 }

从上面代码我没看到作者已经发现ngx解码函数的问题并且做了处理,但是NAXSI作者只处理了第一种情况,也就是如果%后面的第一个字符不是16进制编码则会保留%,但是如果第一个字符是,而第二个字符不是。例如上面提到的s%elect编码处理后会变成slect,所以该代码依然存在编码处理畸形Waf规则被绕过的问题,除了select还有其他的很多关键字都可以绕过。
这里不仅是NAXSI,很多使用nginx解码代码的模块都存在该问题,例如nginx lua模块中的ngx.unescape_uri和ngx.req.get_uri_args等
下面我给出修正后的标准urldecode代码。

 

void
ngx_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type)
{
    u_char  *d, *s, ch, c, decoded;
    enum {
        sw_usual = 0,
        sw_quoted,
        sw_quoted_second
    } state;

    d = *dst;
    s = *src;

    state = 0;
    decoded = 0;

    while (size--) {

        ch = *s++;

        switch (state) {
        case sw_usual:
            if (ch == '?'
                && (type & (NGX_UNESCAPE_URI|NGX_UNESCAPE_REDIRECT)))
            {
                *d++ = ch;
                goto done;
            }

            if (ch == '%'&&size>1) {
                ch=*s;
                c = (u_char) (ch | 0x20);
                if ((ch >= '0' && ch <= '9')||(c >= 'a' && c <= 'f')) {
                ch=*(s+1);
                c = (u_char) (ch | 0x20);
                if ((ch >= '0' && ch <= '9')||(c >= 'a' && c <= 'f')) {
                state = sw_quoted;
                break;
                }
                }
                *d++ = '%';
                break;
            }

            if (ch == '+') {
                *d++ = ' ';
                break;
            }

            *d++ = ch;
            break;

        case sw_quoted:

            if (ch >= '0' && ch <= '9') {
                decoded = (u_char) (ch - '0');
                state = sw_quoted_second;
                break;
            }

            c = (u_char) (ch | 0x20);
            if (c >= 'a' && c <= 'f') {
                decoded = (u_char) (c - 'a' + 10);
                state = sw_quoted_second;
                break;
            }

            /* the invalid quoted character */

            state = sw_usual;

            *d++ = ch;

            break;

        case sw_quoted_second:

            state = sw_usual;

            if (ch >= '0' && ch <= '9') {
                ch = (u_char) ((decoded << 4) + ch - '0');

                if (type & NGX_UNESCAPE_REDIRECT) {
                    if (ch > '%' && ch < 0x7f) {
                        *d++ = ch;
                        break;
                    }

                    *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);

                    break;
                }

                *d++ = ch;

                break;
            }

            c = (u_char) (ch | 0x20);
            if (c >= 'a' && c <= 'f') {
                ch = (u_char) ((decoded << 4) + c - 'a' + 10);

                if (type & NGX_UNESCAPE_URI) {
                    if (ch == '?') {
                        *d++ = ch;
                        goto done;
                    }

                    *d++ = ch;
                    break;
                }

                if (type & NGX_UNESCAPE_REDIRECT) {
                    if (ch == '?') {
                        *d++ = ch;
                        goto done;
                    }

                    if (ch > '%' && ch < 0x7f) {
                        *d++ = ch;
                        break;
                    }

                    *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);
                    break;
                }

                *d++ = ch;

                break;
            }

            /* the invalid quoted character */

            break;
        }
    }

done:

    *dst = d;
    *src = s;
}



相关参考请见

https://code.google.com/p/naxsi/wiki/SecAdvisories
https://packetstormsecurity.com/files/120960/OWASP-WAF-Naxsi-Bypass.html
https://seclists.org/bugtraq/2013/Mar/133

点击复制链接 与好友分享!回本站首页
相关TAG标签 漏洞
上一篇:Android设备管理器漏洞
下一篇:端午节蓝屏之谜:金山系列软件同微软KB2839229冲突技术分析
相关文章
图文推荐
文章
推荐
点击排行

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站