前言
最近由于工作需要在看libinjection的源码,查到一位师傅发了一篇绕过libinjection的文章,
https://www.o2oxy.cn/2772.html
由于URL编码和HEX编码的问题,感觉这位师傅写的不是很对,就发评论跟他讨论,当然他整个libinjection的流程分析还是很不错的。
讨论了半天,这位师傅又抛出了一个payload,说libinjection也检测不出来
1={
date(if(mid((updatexml(1,concat(0x7e,(select user()),0x7e),1)),1,1)='1',2,1))}
尝试了一下的确如此:
翻看MySQL语法分析的产生式
很久之前看到过这种注入的手法,当时没有深究,这次打算好好研究一下。
mysql的SQL解析语法文件:
https://github.com/mysql/mysql-server/blob/8.0/sql/sql_yacc.yy#L9899
(这个是yacc的产生式文件,有兴趣的师傅可以去找找lex&yacc的资料,顺便看看编译原理更佳~)
PS:开头注释表明5.7之后语法分析就没有变过
看这个文件懵逼了很久,最后直接搜索’{’(三个字符)找到了关键点:
'{' 标志符 表达式 '}'
是simple_expr(简单表达式)的一种,走PTI_odbc_date这个类的解析方法
https://github.com/mysql/mysql-server/blob/8.0/sql/parse_tree_items.cc#L619
通过注释可以看出,该功能是为了解析ODBC的转义形式语法而写的,并且最后如果标志符
部分不是d
、t
、ts
,就会直接返回表达式
的内容。
实际上效果上来说,标志符
部分可以任意写,表达式
部分都可以正常执行
所以可以尝试通过这种形式进行SQL注入防御的绕过。
MySQL手册相关
https://dev.mysql.com/doc/refman/8.0/en/expressions.html
https://downloads.mysql.com/docs/refman-4.1-en.a4.pdf
这块感谢同事donky16师傅,实际上手册中有写这种用法,之前搜关键词braces
和curly brackets
,就是没搜curly braces
。。。。:
上面那波分析,多少有点走弯路了。
从手册中可以看出,从3.23开始就已经存在了ODBC转义语法。
回到Payload
1={
date(if(mid((updatexml(1,concat(0x7e,(select user()),0x7e),1)),1,1)='1',2,1))}
payload中,date并不代表一个函数,而是一个标识符
, 后面整个括号包裹的if部分是表达式
,并且可以不适用括号分割而,转而使用空格之类的进行分割:
可以看到两个效果是一样的,都可以正常触发报错,说明updatexml函数已经被执行了。
但是后一种写法是可以被libinjection检测出来的:
而libinjection中实际上已经考虑了ODBC转义的情况:
原因分析
libinjection中有种token类型为bareword
bareword可能被认为:label 、 句柄 、函数 、 普通字符串
libinjection特定位置上只有非关键字列表(方法名、变量名、select union之类的关键字)才会被认为是bareword。
在libjection的token折叠函数中,’{’+BAREWORD的组合会进行折叠,而像’{date’这种形式会被解析成’{’+FUNCTION的Token串,不会被折叠导致了绕过。
{foo expr}
而在ODBC转义语法的语境中,上述foo
位置都会被认为是标识符
或者说bareword
。
所以此处解决办法有两个:
- 加特征,将’{’+FUNCTION的情况包进去
- 优化折叠代码,考虑’{’+FUNCTION的情况
最后选用了第二种方法进行完善,可以正常的检测出来该SQL注入:
引用内容
libinjection源码(语义分析SQL注入检测):
https://github.com/client9/libinjection
感谢这位师傅,涨知识了~:
https://www.o2oxy.cn/2772.html
MySQL源码:
https://github.com/mysql/mysql-server/
MySQL手册:
https://dev.mysql.com/doc/refman/8.0/en/expressions.html