一、前言
在项目中的需要增加detectSQLi和detectXSS方法,故研究一下libinjection中的源码结构。
如下图所示,在规则引擎中调用了detectSQLi之后,会跑到libinjection中执行libinjection_sqli,然后执行libinjection_sqli_init()初始化libinjection_sqli_state结构体,回调函数赋值给结构体中的lookup函数指针成员,接着调用libinjection_is_sqli()函数并返回结果:
二、结构流程
2.1 libinjection_is_sqli()函数结构
|->libinjection_is_sqli()
|->libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_NONE | FLAG_SQL_ANSI);
if|->sql_state->lookup() //回调函数是libinjection_sqli_lookup_word()
else|->reparse_as_mysql(sql_state)
else|->libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_NONE | FLAG_SQL_MYSQL);
|->sql_state->lookup()
|->libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_SINGLE | FLAG_SQL_ANSI);
if|->sql_state->lookup()
else|->reparse_as_mysql(sql_state)
else|->libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_SINGLE | FLAG_SQL_MYSQL);
|->sql_state->lookup()
if|->libinjection_sqli_fingerprint(sql_state,FFLAG_QUOTE_SINGLE | FLAG_SQSL_MYSQL);
else|->sql_state->lookup()
2.2 拿其中的几个函数来说明
|->libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_NONE | FLAG_SQL_ANSI);
|->libinjection_sqli_reset(sql_state, flags);
|->libinjection_sqli_init(sf, sf->s, sf->slen, flags) //初始化libinjection_sqli
|->
|->libinjection_sqli_fold();
|->st_clear(&last_comment); //clear last_comment变量
|->libinjection_sqli_tokenize(sf)
|->parse_string_core() //处理字符串
|->is_backslash_escaped() //判断是否是转义
|->is_double_delim_escaped() //判断是否是两个相同的字符转义
|->fnptr=char_parse_map[ch];(*fnptr)(sf);//在循环中进行对应的parse_xxx回调函数,表放在代码框下面
|->接下来就是不断的匹配string的特征为相应的字母,匹配表放在代码框下面
|->syntax_merge_words(sf, &sf->tokenvec[left], &sf->tokenvec[left+1]) //查看是否两个tokens可以合并
|->sf->lookup(sf, LOOKUP_WORD, tmp, sz3) //接下来有此函数介绍
if|->sql_state->lookup() //回调函数是libinjection_sqli_lookup_word()
|->libinjection_sqli_check_fingerprint(sql_state) //通过黑名单和不是白名单两方面判断
|->libinjection_sqli_blacklist(sql_state)
|->is_keyword()
|->bsearch_keyword_type(key, len, sql_keywords, sql_keywords_sz) //从sql_keywords全局变量中匹配
|->libinjection_sqli_not_whitelist(sql_state)
|->bsearch_keyword_type(str,len, sql_keywords, sql_keywords_sz) //从sql_keywords全局变量中进行匹配
else|->reparse_as_mysql(sql_state)
else|->libinjection_sqli_fingerprint(sql_state, FLAG_QUOTE_NONE | FLAG_SQL_MYSQL);
截图为特征值匹配的sqli_token_types表:
上图中有个对应表,参考http://www.freebuf.com/column/170926.html,也可以看每一个parse_xxx的具体实现:
'k' /*例如COLUMN,DATABASES,DEC等会被识别为该值*/
'U' /*EXCEPT,INTERSECT,UNION等会被识别为该值*/
'B' /*GROUP BY,LIMIT,HAVING*/
'E' /*INSERT,SELECT,SET*/
't' /*SMALLINT,TEXT,TRY*/
'f' /*UPPER,UTL_HTTP.REQUEST,UUID*/
'n' /*WAITFOR,BY,CHECK*/
'1' /*所有数字会被识别为1*/
'v' /*CURRENT_TIME,LOCALTIME,NULL*/
's' /*单引号和双引号*/
'o' /*+=,-=,!>*/
'&' /*&&,AND,OR*/
'c' /*注释符*/
'A' /* COLLATE*/
'('
')' /* not used? */
'{'
'}'
'.'
','
':'
';'
'T' /* TSQL start */ /*DECLARE,DELETE,DROP*/
'?'
'X' /* unparsable, abort */ /* “/*!*/” */
'F' /* not really a token */
'\\'
注册相关回调函数的表如下:
2.3 举例说明:
我们输入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_xss部分
在libinjection_xss函数中分别对
DATA_STATE,VALUE_NO_QUOTE,VALUE_SINGLE_QUOTE,VALUE_DOUBLE_QUOTE,VALUE_BACK_QUOTE)的状态进行libinjection_is_xss()函数判断
|->libinjection_is_xss()
|->libinjection_h5_init() //初始化state回调函数
|->h5_state_data
|->h5_state_eof //当<符号不存在的时候
|->h5_state_tag_open //当<符号存在的时候
|->h5_state_markup_declaration_open(hs) //if ch == CHAR_BANG
|->h5_state_doctype(hs);//DOCTYPE类型
|->h5_state_cdata(hs); //[[cdata类型
|->h5_state_comment(hs);
|->h5_state_end_tag_open(hs) //else if (ch == CHAR_SLASH)
|->h5_state_bogus_comment(hs) //else if (ch == CHAR_QUESTION)
|->h5_state_bogus_comment2(hs) //else if(ch == CHAR_PERCENT) 符号%
|->h5_state_tag_name(hs) //大小写英文字母和CHAR_NULL情况
|->h5_is_white(ch) //如果遇到空白符号
|->h5_state_self_closing_start_tag; //如果遇到/符号
|->h5_state_data //如果遇到>符号
|->h5_state_before_attribute_name
|->h5_skip_white(hs)
|->h5_state_self_closing_start_tag(hs) //case CHAR_SLASH
|->h5_state_data //case CHAR_GT
|->h5_state_attribute_name(hs) //default
|->h5_state_attribute_value_single_quote
|->h5_state_attribute_value_quote(hs, CHAR_SINGLE)
|->h5_state_eof //idx == NULL
|->h5_state_after_attribute_value_quoted_state //idx != NULL
|->h5_is_white(ch) //if
|->h5_state_self_closing_start_tag(hs) //else if (ch == CHAR_SLASH)
|->h5_state_data //if(ch == CHAR_GT)
|->h5_state_before_attribute_name(hs) //else
|->h5_state_data //else if(ch == CHAR_GT)
|->h5_state_before_attribute_name(hs) //else
|->h5_state_attribute_value_double_quote
|->h5_state_attribute_value_quote(hs, CHAR_DOUBLE)
|->h5_state_attribute_value_back_quote
|->h5_state_attribute_value_quote(hs, CHAR_TICK)
|->while(libinjection_h5_next(&h5)) //循环判断
|->is_black_tag(h5.token_start, h5.token_len) //判断是否是黑名单tag,tag列表在代码框下面
|->is_black_attr(h5.token_start, h5.token_len) //判断是否是黑名单attr,attr列表在代码框下面
|->is_black_url(h5.token_start, h5.token_len) //下面四个url后缀变量在代码框下面
|->htmlencode_startwith(data_url, s, len)
|->htmlencode_startswith(viewsource_url, s, len)
|->htmlencode_startswith(javascript_url, s, len)
|->htmlencode_startswith(vbscript_url, s, len)
匹配成功则为xss攻击。