开篇:
最近学习了libinjection库的sql注入部分,写篇总结。libinjection在GitHub上作为一个开源的sql注入和xxs攻击词法解析库还是挺流行的,它比较独特的是没有使用正则表达式而是一个完善的特征库来匹配检查SQL注入。相比传统正则匹配识别SQL注入在于速度快以及低误报,低漏报 。
然后就是来自开发者的牛B:
No memory allocation
No threads
No external dependencies
Fixed stack size
>100k checks a second
简单介绍下SQL注入:
SQL注入攻击就是通过操作输入来修改SQL语句,用以达到执行代码对WEB服务器进行攻击的方法。 在post/getweb表单、输入域名或页面请求的查询字符串中插入SQL命令,最终使web服务器执行恶意命令的过程。
Web页面:
后台数据库查询:
----> select * from users where username = '输入' and password = '输入'
用户恶意输入
-----> admin' and 1 = 1; --
SQL语句改变:
---> select * from users where username = 'admin' and 1 = 1; --and password = '输入'
正文:
libinjection将输入的数据依据它定义好的特征码进行转换, 之后就会得到SQL注入识别特征,或者说指纹, 在特征库中进行匹配。
这个库的简单例子:
源代码:
#include <stdio.h> #include <strings.h> #include <errno.h> #include "libinjection.h" #include "libinjection_sqli.h" int main(int argc, const char* argv[]) { struct libinjection_sqli_state state; int issqli; const char* input = argv[1]; size_t slen = strlen(input); /* in real-world, you would url-decode the input, etc */ libinjection_sqli_init(&state, input, slen, FLAG_NONE); issqli = libinjection_is_sqli(&state); if (issqli) { fprintf(stderr, "sqli detected with fingerprint of '%s'\n", state.fingerprint); } return issqli; }
编译运行:
$ gcc -Wall -Wextra examples.c libinjection_sqli.c $ ./a.out "-1' and 1=1 union/* foo */select load_file('/etc/passwd')--" sqli detected with fingerprint of 's&1UE'
接下来介绍一下重要的结构体
----------------------------------------------------------------------------------
解析函数的定义:
typedef size_t (*pt2Function)(sfilter *sf);
static const pt2Function char_parse_map[] = {
&parse_white, /* 0 */
&parse_white, /* 1 */
&parse_white, /* 2 */
&parse_white, /* 3 */
&parse_white, /* 4 */
&parse_white, /* 5 */
&parse_white, /* 6 */
....
&parse_word, /* 252 */
&parse_word, /* 253 */
&parse_word, /* 254 */
&parse_word, /* 255 */
};
这里可以看出有255个解析函数,作为一个程序员应该对这个数字很敏感,刚好一个字节。这里其实会根据你输入的字符串的字节选择解析函数;对什么标点符号啊,字母啊,数字和运算符
之类的都有不同的函数处理可以说这种处理方式是很巧妙的。
这里我举个例子:
-1' and 1=1 union/* foo */select load_file('/etc/passwd')--
你输入这个字符串,解析按空格分隔开,这里比较重要的点是没有闭合 ’或“也按字符串处理 -1' 转换为特征码s(string);
第二个字串 and 关键字 特征码为 &;
1 = 1这里比较特殊它按数字来转了(具体可以跟代码来深入研究) 特征码为 1;
union 关键字联合查询 特征码为 U;
select 关键字特征码为 E;
所以合起来就是s&1UE;
你可以参考下面这个表来推其他类型的特征码
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_sqli_init()函数将初始化SQL检测所需的libinjection_sqli_state结构体,这个结构体在后面十分重要。
libinjection_is_sqli()函数主要功能函数,判断是否为sql注入,返回bool结果
libinjection_sqli_lookup_word()函数从特征库查找,与生成指纹是否匹配
libinjection_sqli_fingerprint()函数生成SQL语句指纹
结束:
libinjection库是对payload进行解析的,简单来说是对用户输入进行监控,查找用户输入是否有构成SQL注入的可能;还有一点就是它的特征库是允许用户修改的(关注 src/fingerprints.txt ),增加或删除,因此可以定制和完善属于你自己的SQL注入特征库。以及使用这个库的产品 ------------ 开源防火墙ModSecurity。