热门关键字:   网站安全  系统安全  安全漏洞  黑客攻防  网络安全

简单分析SQL注入语义分析库Libinjection

发布时间:2018-05-14 13:27文章来源:freebuf文章作者:chenjc 点击次数:
摘要:这次主要讲开源SQL注入语义分析库libinjection,如果有发现其他开源SQL语义分析库的欢迎告知。libinjection的程序分析由Simon友情提供,需要看完整报告的可以加群看。 ...

 

一、前言

这次主要讲开源SQL注入语义分析库libinjection,如果有发现其他开源SQL语义分析库的欢迎告知。libinjection的程序分析由Simon友情提供,需要看完整报告的可以加群看。

二、libinjection程序分析 

简单分析SQL注入语义分析库libinjection

从流程图上看,libinjection首先是初始化issqlii变量,接着设置数据结构并初始化变量state,libinjection_sqli_init()函数将初始化SQL检测所需的结构体,之后通过libinjection_is_sqli()函数进行具体分析,如果存在issqli,则将SQL注入识别特征复制进fingerprint变量并返回,如果不存在则将fingerprint变量设置为空并返回。 

简单分析SQL注入语义分析库libinjection

上图是总的函数关系图,libinjection_sqli_init()函数的主要工作是将 SQL注入识别特征码(指纹)加载进结构体,并完成各种内置变量的初始化。libinjection_is_sqli()的处理代码如下,根据代码来分析 

int libinjection_is_sqli(struct libinjection_sqli_state * sql_state) {     const char *s = sql_state->s;     size_t slen = sql_state->slen;      if (slen == 0) {         return FALSE;     }      libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_NONE | FLAG_SQL_ANSI);     if (sql_state->lookup(sql_state, LOOKUP_FINGERPRINT,                           sql_state->fingerprint, strlen(sql_state->fingerprint))) {         return TRUE;     } else if (reparse_as_mysql(sql_state)) {         libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_NONE | FLAG_SQL_MYSQL);         if (sql_state->lookup(sql_state, LOOKUP_FINGERPRINT,                               sql_state->fingerprint, strlen(sql_state->fingerprint))) {             return TRUE;         }     }      if (memchr(s, CHAR_SINGLE, slen)) {         libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_SINGLE | FLAG_SQL_ANSI);         if (sql_state->lookup(sql_state, LOOKUP_FINGERPRINT,                               sql_state->fingerprint, strlen(sql_state->fingerprint))) {             return TRUE;         } else if (reparse_as_mysql(sql_state)) {             libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_SINGLE | FLAG_SQL_MYSQL);             if (sql_state->lookup(sql_state, LOOKUP_FINGERPRINT,                                   sql_state->fingerprint, strlen(sql_state->fingerprint))) {                 return TRUE;             }         }     }      if (memchr(s, CHAR_DOUBLE, slen)) {         libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_DOUBLE | FLAG_SQL_MYSQL);         if (sql_state->lookup(sql_state, LOOKUP_FINGERPRINT,                               sql_state->fingerprint, strlen(sql_state->fingerprint))) {             return TRUE;         }     }      return FALSE; }         

1.判断用户输入的字符串长度是否合法,为零则返回FALSE。(即没有发现SQL注入)

2.执行SQL注入识别函数(libinjection_sqli_fingerprint,无引号,标准SQL语法),获取字符串识别特征码

3.执行结构体内的查询函数(这里为libinjection_sqli_lookup_word,二分查找算法),对比第二步获取的识别特征是否与SQL注入识别特征匹配。

4.如果第三步发现SQL注入特征匹配结果为真,则返回true,并将SQL注入识别特征匹配到的fingerprint写入结构体,

5.如果检测失败,则调用reparse_as_mysql()函数判断是否存在“(dash-dash-[notwhite]) 注释”或“’#’ 运算符号”,如果存在则再次执行SQL识别函数(libinjection_sqli_fingerprint,无引号,MYSQL语法),

6. 同时执行结构体的分析函数(libinjection_sqli_lookup_word,二分查找算法)进行特征匹配检测,如果结果为真,则返回true,并将SQL注入识别特征匹配到的fingerprint写入结构体。

7.如果前面的判断没有返回结果,将扫描参数(即用户输入的字符串)查找是否存在单

引号。如果为真接着重复上述检测步骤。

8.如果前面的判断依旧没有返回结果,将扫描参数(即用户输入的字符串)查找是否存

在双引号。如果为真则接着执行SQL注入识别函数

(libinjection_sqli_fingerprint,双引号,MYSQL语法)

同时执行结构体的分析函数(libinjection_sqli_lookup_word,二分查找算法)进行特征匹配检测结果为真,则返回true,并将SQL注入识别特征匹配到的fingerprint写入结构体。

9.如果前面三种判断均无结果则默认该参数(用户输入的字符串)不存在SQL注入。

三、libinjection实例分析

上面已经对libinjection的程序进行了简单的分析,下面通过一个具体的例子来了解libinjecton的处理流程。

首先来看libinjection对特征码的定义

typedef enum {     TYPE_NONE        = 0   /*无实际意义,仅对位数进行填充*/     , TYPE_KEYWORD     = (int)'k'  /*例如COLUMN,DATABASES,DEC等会被识别为该值*/     , TYPE_UNION       = (int)'U'  /*EXCEPT,INTERSECT,UNION等会被识别为该值*/     , TYPE_GROUP       = (int)'B'    /*GROUP BY,LIMIT,HAVING*/     , TYPE_EXPRESSION  = (int)'E'   /*INSERT,SELECT,SET*/     , TYPE_SQLTYPE     = (int)'t'  /*SMALLINT,TEXT,TRY*/     , TYPE_FUNCTION    = (int)'f'   /*UPPER,UTL_HTTP.REQUEST,UUID*/     , TYPE_BAREWORD    = (int)'n'  /*WAITFOR,BY,CHECK*/     , TYPE_NUMBER      = (int)'1'   /*所有数字会被识别为1*/     , TYPE_VARIABLE    = (int)'v'  /*CURRENT_TIME,LOCALTIME,NULL*/     , TYPE_STRING      = (int)'s'   /*单引号和双引号*/     , TYPE_OPERATOR    = (int)'o'  /*+=,-=,!>*/     , TYPE_LOGIC_OPERATOR = (int)'&'   /*&&,AND,OR*/     , TYPE_COMMENT     = (int)'c'     /*注释符*/     , TYPE_COLLATE     = (int)'A'  /* COLLATE*/     , TYPE_LEFTPARENS  = (int)'('          , TYPE_RIGHTPARENS = (int)')'  /* not used? */     , TYPE_LEFTBRACE   = (int)'{'         , TYPE_RIGHTBRACE  = (int)'}'     , TYPE_DOT         = (int)'.'     , TYPE_COMMA       = (int)','     , TYPE_COLON       = (int)':'     , TYPE_SEMICOLON   = (int)';'     , TYPE_TSQL        = (int)'T'  /* TSQL start */ /*DECLARE,DELETE,DROP*/     , TYPE_UNKNOWN     = (int)'?'     , TYPE_EVIL        = (int)'X'  /* unparsable, abort  */   /* “/*!*/”  */     , TYPE_FINGERPRINT = (int)'F'  /* not really a token */     , TYPE_BACKSLASH   = (int)'\\' } sqli_token_types;  

libinjection将输入的数据依据上述的定义进行转换,之后就会得到SQL注入识别特征,或者说指纹,然后通过二分查找算法,在特征库中进行匹配,匹配到则报SQL注入漏洞

例如:

我们输入SQL注入的检测语句

and 1=1

libinjection会将其转换为s&1,其中单引号依据定义被转换为s,and被转换为&,数字被转换为1

' UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL--

libinjection会将其转换为sUEvc,其中单引号依据定义被转换为s,UNION ALL被转换为U,SELECT被转换为E,NULL被转换为v,后面相同的NULL合并为一个v,–注释符被转换为c

libinjection在转换完后,通过二分查找算法对内置的8000多个特征进行匹配,匹配到则将SQL注入识别特征复制进fingerprint变量并返回。

通过上述两个例子就可以知道libinjection对数据的转换逻辑,其中针对一些特殊情况会有特殊处理,文章篇幅有限这里不讲,有兴趣可以去看代码。

四、总结

SQL注入语义分析库libinjection相比传统正则匹配识别SQL注入的好处在于速度快以及低误报,低漏报 。

速度快体现在该库全程比较耗性能的就一二分查找算法,相对于正则对性能的消耗来说可以忽略不计,这点从火焰图上可以很明显的看出来,所以无论特征数是800,8000还是80000,对处理速度来说都不会有太大影响,而正则匹配规则达到千条以上就能明显感觉到性能的变化。

低误报呢,以前在测试modsecurity2.0的owasp规则的时候,那个误报率简直感人。但是如果要把规则写细,降低误报率的话,那规则数必然会上去进而对性能产生一些影响。SQL注入语义好就好在,要想满足他的匹配规则,一般来说必须满足三个特征以上,比如s&1或者sUEvc,而每个特征要么是特殊字符,要么是SQL语句的保留字,所以正常情况下用户输入,很少会出现这种误报这种情况。

低漏报呢,则在于那8000多的识别特征了基本上把可能的路都堵死了,如有发现能在实际场景中绕过的,欢迎交流。

标签分类:

上一篇:看我如何通过XSS获取经纬度
下一篇:VulnHub渗透实战Billu_b0x