The Insecurity of Security Through Obsecurity

现今,关于SQL注入的话题在安全行业早已广为人知,研究人员总会发现一些很值得分享的独特注入手法。在我的研究中有过这样一串无意义的字符—&#MU4<4+0 —通过开发端的一些独特的编码方式,可以把它转换成可利用的攻击向量。现在,让我从头说起。

当我们在某web应用中遇到一个登录表单,我们可以采取很多方法进行进一步的测试。我们首先想到的当然是以"用户名:admin 密码:'OR 1=1-"提交一个登录请求。不出所料,应用返回了有关错误信息的响应。不过应用却以一种异于平常的方式响应:口令字段填充了" ( QO!.>./* "字符串。我首先想到的是:"它不会碰巧把管理员口令返回给我了吧?它至少看起来像是随机生成的。鉴于此,我用刚获取到的值作为密码,又一次进行了登录尝试。 这次登录失败了,并且我又获得一组新的值:" ) SL"+?+1′ "。不出所料,这次的响应数据没有正确编码HTML 标记字符。因此,如果我们能够弄清楚其中的机制,我们也就能在登录界面构造出一个反射型XSS。这样我们就可以将研究范围缩小,以单个字符为研究目标。

为了发掘其中的规律,我之后又进行了几次登录测试。通过分别提交带有字符"a"、"b"、"c"的三次请求,也许能有更一步的突破。 结果,似乎每次响应的都是字母表中的下一个字母——相应的我得到的是"b","c","d"。 接下来,我尝试增加字符串的位数。假如我提交"aa",它理应响应"bb"。但事与愿违,应用却返回了"b^"。接下来看看提交更多位的同字母字符串又会出现什么样的结果,这次我提交了"aaaaaaaaaaaa"(12个a),应用却意外的返回了 "b^c^b^b^c^b^"。 至此,我们做一下分析:可以清楚的看到应用程序或多或少暴露了一些字符的转换机制方面的细节。整个机制看起来像是一种重复序列,因为本次响应的前6个字符与后6个字符相同。

但是至今为止,我们只是以人类可读字母的形式研究了这些字符。但在计算机的世界,字符同样有多种表示方法。其中一种比较常见方法是转化为相应的ASCII码值,计算机会将读到的每一个字符转换为相应的数值。如,字母"a"将会被转换成十进制97。通过将这些字符以数字编码,我们更加容易从中确定其内部机制。下面是一个我用的ASCII转换表网站,可以帮我们节省不少时间:

www.theasciicode.com.ar

既然我们已经确定应用存在每6个字符一次的重复,就让我们看看在数字形式上这种转换又是一种怎样的形式。首先,我们从提交"aaaaaa"入手,相应的应该得到"b^c^b^"。但是将字符串转换成相应的ASCII值,又会是怎么样的结果呢?以计算机"语言",对于"aaaaaa"我们相应的提交十进制 "97,97,97,97,97,97",我们得到返回值"98,94,99,94,98,94"。以此来看,好像每个字符都有一个转换对应值。对两组数据进行矩阵减法:[97,97,97,97,97,97]- [98,94,99,94,98,94] = [-1,3,-2,3,-1,3]。这样我们得到一个特殊序列,通过它我们可以将ASCII码数值转换为对应值,以注入我们想要反射的正确字符。

最后,我们要开始运用PoC(Proof of Concept,概念验证方法)构造注入来发掘这个潜在的XSS漏洞。由上我们已经知道,该应用对输入字符存在一个"每6个字符做一次转换"的机制,并且 已经知道其中的对应关系。就像:"><img/src="h"onerror=alert(2)//, 我们需要提交 !A:llj.vpf<%g%mqduqrp@`odur+1,.2.以达到将正确代码转换成可利用注入语句目的。当然,我们需要一个弹窗来直观的确认我们推导出来的该应用设计的机制是正确的。现在已经运用概念验证法成功地证实了XSS的存在,让我们回到最初的SQL 注入测试。当我们想要提交"' OR 1=1- ",就需要将其转换为"&#MU4<4+0"来提交。其中一个注入语句中的空格由于被-1转换,产生了一个位于"U"和"4"之间的不可打印字符。进行适当的URL编码后,发给应用的数据就变成了"%26%23MU%1F4%3C4%2B0"的形式。然后我们就兴奋的看到我们以管理员的身份成功登陆了。

回想互联网的早期——很久以前在我开始学习信息安全的时候,这种攻击形式非常流行。可是现在的开 发者普遍在登陆表单处采用适当的参数化查询,所以很难出现这种问题。很遗憾这款特殊的应用并不是为美国市场开发的,它可能是某一新兴发展中国家的菜鸟程序 员编写的。出现这种漏洞的原因是开发者的疏忽——在我们都知道应该进行Hash操作的地方只进行了简单的转码。如果该应用在密码输入错误后没有在密码框返 回任何内容,那么通过这种方法来进行黑盒测试的话,这个SQL漏洞仍然是漏网之鱼。这让我们更关注这种潜在的漏洞存在形式,但它对应用的安全测试来说仍然 处在模棱两可的阶段。

最后,我编写了一部分后台代码来复现这个漏洞。当然也可以通过替换+与-,将注入代码进行不同的转换,然后它就可以做出我预计的反应:


<!DOCTYPE html>
<html>
<body>
<form name="login" action="#" method="post">
<?php
$strinput = $_POST['password'];
$strarray = str_split($strinput);
for ($i = 0; $i < strlen($strinput); $i++) {
    if ($i % 6 == 0) {
        $strarray[$i] = chr(ord($strarray[$i]) - 1);
       }
    if ($i % 6 == 1) {
        $strarray[$i] = chr(ord($strarray[$i]) + 3);
       }
    if ($i % 6 == 2) {
        $strarray[$i] = chr(ord($strarray[$i]) - 2);
       }
    if ($i % 6 == 3) {
        $strarray[$i] = chr(ord($strarray[$i]) + 3);
       }
    if ($i % 6 == 4) {
        $strarray[$i] = chr(ord($strarray[$i]) - 1);
       }
    if ($i % 6 == 5) {
        $strarray[$i] = chr(ord($strarray[$i]) + 3);
       }
}
$password = implode($strarray);
echo "Login:<input type=\"text\" name=\"username\" value=\"" . htmlspecialchars($_POST['username']) . "\"><br>\n";
echo "Password:<input type=\"password\" name=\"password\" value=\"" . $password . "\"><br>\n";
// --- CODE SNIP ---
$examplesqlquery = "SELECT id FROM users WHERE username='" . addslashes($_POST['username']) . "' AND password='$password'";
// --- CODE SNIP ---
?>
<input type="submit" value="submit">
</form>
</body>