Anonymous 如何使用 UTF-16 ASCII 来欺骗 PHP 转义?

信息安全 php sql注入 统一码
2021-08-14 14:29:38

几个月前,Anonymous 使用 SQL 注入关闭了一个儿童色情网站。我在这篇文章中读到 Anonymous 声称“服务器正在使用带有转义的强化 PHP”,但他们能够“使用 UTF-16 ASCII 编码绕过它”。这意味着他们做了什么,究竟是什么?如何保护我的网站免受类似攻击?

4个回答

首先“UTF-16 ASCII 编码”是矛盾的,因为 UTF-16 和 ASCII 是互斥的编码方案。但大概他只是指使用 Unicode 绕过过滤机制。

一般原则是这样的:我们经常想到用 ASCII 编码的字符——“A”是数字 65,“z”是数字 122。但这不是唯一的字符编码方案;因为世界使用的不仅仅是英文字母,我们需要代表的字符远不止这些。因此,Unicode 可以表示从僧伽罗语到克林贡语的每种语言中的几乎每个字符。

以数字形式表示所有这些字符(大约 110 万个可能,并非全部使用)是一个真正的挑战。您可以使用 32 位,但这是浪费空间,因为 4 个字节中的 3 个通常为零。您可以使用可变长度,但是您不能进行恒定时间的子字符串操作。因此存在许多标准,其中之一是 UTF-16(您可能猜到它使用 16 位字符)。

并非所有程序员都习惯于处理多个字符集,即使底层框架通常会支持它们。所以有时过滤规则或预防措施将使用假设字符将以 UTF-8 或 ASCII 表示,它们通常是这样的。

所以过滤器会寻找一个给定的字符串,\"例如,它在 ASCII 和 UTF-8 中对应于模式 {92,34}。但是在 UTF-16 中它看起来不同;它实际上是 {0,92,0,34},它的差异足以让一个没有预料到的过滤器滑过。

虽然过滤器不理解 UTF-16,但底层框架可以,因此内容被规范化并与其他任何内容一样被解释,允许查询继续未过滤。

编辑添加:
请注意,PHP 在处理字符编码方面非常差;如果有的话,那就是低估了这个问题。默认情况下,PHP 将所有字符串都视为 ASCII,这意味着内部函数strstrpreg_replace简单地假设所有字符串都是 ASCII 编码的。如果这听起来很危险,那是因为它是。但在他们的辩护中,请记住 PHP 比 UTF-16 早了大约一年,而这一切都应该在 PHP 版本 6 中得到修复。

同时,创建了mbstring库来解决这个缺陷,但它既没有被广泛部署,也没有被低估。如果您有幸拥有此扩展,您可以在 php.ini 文件中使用mbstring.overload来强制将内部字符串处理函数替换为可识别多字节的替代方案。这也可以使用文件中的php_admin_value指令激活.htaccess

另一个有用的函数是mb_internal_encoding,它设置 PHP 内部使用的编码来表示字符串。通过使用与 unicode 兼容的内部编码,您可以减轻一些麻烦。我读过的至少一篇参考资料(但遗憾的是现在找不到)表明,通过将内部编码设置为 UTF-8,您可以对入站字符串进行额外处理,将它们标准化为单一编码。另一方面,至少有一个其他参考表明 PHP 在这方面表现得尽可能愚蠢,并且无论其编码如何,都只是简单地吞下未经修改的数据,并让您处理后果。虽然前者更有意义,但根据我对 PHP 的了解,我认为后者同样可能。

作为最后的选择;我只是部分开玩笑地提到这一点,就是不要使用 PHP,而是采用设计更好的架构。很难想出一个如此流行的框架,但它却像 PHP 那样存在如此多的基本问题。语言、实现、开发团队、插件架构、安全模型——PHP 被如此广泛地部署真是太可惜了。但这当然只是一种意见。

我不知道这是否是 Anonymous 使用的方法,但请查看http://bugs.mysql.com/bug.php?id=22243

Connector.Net(MySQL 的托管 .Net 驱动程序)中似乎存在错误。从链接的错误报告中:

.net 字符串以 UTF-16 编码。字符串被转换为 Windows-1252(SBCS 编码)以通过网络发送,在此转换期间,可能尚未检查的 unicode 字符将“变成”单引号。

错误报告继续列出包含问题 Unicode 字符的字符串并说:

具体来说,在第二个字符串中,问题引用是 unicode 字符 8242 ("\u8242")。当服务器接收到这个字符串时,引号将是一个单引号(ASCII 96)并且会破坏查询并且可以用作 sql 注入攻击。

链接的错误在 2009 年被标记为已修复错误的副本,但被利用的服务器完全有可能运行的是存在此问题的旧版本的 MySql。

我从文章中得出的结论是,该站点更多地依赖于“强化”技术,而不是良好的 sql 输入过滤/转义。没有证据表明儿童色情网站的 sql 代码没有缺陷。

绕过所谓的强化 PHP 过滤器通常很简单。例如,ModSecurity 可以很容易地被绕过,并且攻击者经常使用许多方法来绕过这些输入过滤器。

网站代码中也包含一些过滤器作为插件,这些过滤器在检查恶意输入之前不会正确地对输入进行 urldecode。

例如: %5e

如中所示:

id=0%5E(select%20position(0x61%20in%20(select%20id%20from%20users%20where%20num=1))=1)

通过使用“%bf%5c%27”、“%bf%27”、“%ef%bb%bf”、“%8c%5c”等字符,可以绕过所谓的强化来触发注射。

更糟糕的是白名单过滤器使用白名单允许的字符递归更新 $_GET,例如:

$cleansed = preg_replace( "/[^\s{}a-z0-9_\.\-]/i", "", urldecode( $get ) );

然后考虑这个: id=-1%20ui*o+s|e|l|e|c|t+1,^2,*3,[4,[5,]6,]7,<8,<9 ,>10>

虽然在过滤之前进行 urldecoding 的想法是一个好主意,但它完全没有意义,因为黑名单中的字符被删除,从而以原始形式提供注入向量。

事实上,这种方法可以增强攻击者绕过所谓的 PHP 强化器和过滤模块(如 modsecurity)的能力。

最后,请求以特定方式设计以绕过输入过滤,一旦绕过这些防御,实际站点代码本身必须首先具有错误的 DB 输入编码,以便注入向量触发而不管攻击者的声明,在这种情况下是匿名的。

只是一个疯狂的猜测。他们可以用 UTF-16 编码 ASCII 字符串,这样,可能用于检查危险用户输入的例程被愚弄/绕过。然后对该字符串进行解释,恶意输入没有被过滤。

这听起来像是开发人员使用了不安全的编码实践,或者某些库/应用程序已经过时,因此很危险。这不像匿名黑客/脚本有任何绕过魔法,这都是关于实验的。

大多数情况下,如果他们有 0days 或一些新技术来破解一切,他们不会让人们知道这件事。由于一些程序员/管理员的无能,他们经常使用老派的技术,这些技术仍然有效。安全很重要。