注入300:使用原始MD5散列的SQL注入
昨天的CTF面临的一个挑战是看似不可能的SQL注入,价值300点。挑战的要点是提交一个密码给一个PHP脚本,在用于查询之前将会用MD5散列。乍一看,这个挑战看起来不可能。这是在游戏服务器上运行的代码:
唯一的注射点是第一个mysql_query()
。没有MD5的复杂性,易受攻击的代码将如下所示:
$ r = mysql_query(“SELECT login FROM admins WHERE password ='”。$ _GET ['password']。“'”);
如果密码foobar
被提交给脚本,这个SQL语句将在服务器上执行:
SELECT login FROM admins WHERE password ='foobar'
这可能是微不足道的利用。我可以提交密码,' OR 1 = 1; --
而不是:
SELECT login FROM admins WHERE password =''OR 1 = 1; - '
...这将返回admins
表中的所有行,并欺骗脚本授予我访问页面。
但是,这个挑战比这个困难得多。由于PHP的md5()
功能是先加密密码,这就是发送到服务器的内容:
SELECT login FROM admins WHERE password ='[输出md5函数]'
那么我怎么可能注入SQL时,MD5会破坏我提供的任何东西?
诀窍:原始MD5哈希在SQL中是危险的
在这个挑战中的诀窍是PHP的md5()
函数可以以十六进制或原始形式返回其输出。这md5()
是方法签名:
字符串md5(字符串$ str [,布尔$ raw_output = false])
如果MD5的第二个参数是true
,它将返回丑陋的原始位,而不是一个很好的十六进制字符串。原始MD5哈希在SQL语句中是危险的,因为它们可以包含对MySQL有特殊意义的字符。例如,原始数据可能包含允许SQL注入的引号('
或"
)。
我用这个事实来创建包含SQL注入代码的原始MD5哈希。
但是这可能需要几年的时间来计算
为了花更少的时间蛮力强制MD5哈希,我试图想到尽可能短的SQL注入。我想出了一个只有6个字符长:
“|| 1;#
我很快写了一个C程序,看看我可以蛮力MD5有多快。我的上网本可以使用libssl的MD5函数每秒计算大约500,000次MD5哈希值。我的快速(可能是错误的)数学告诉我,每一个散列都有一个28万亿的概率,包含我想要的6个字符的注入字符串。
所以这只需要2年,每秒50万次哈希。
优化:缩短注射弦
如果我能够缩短我的注射字符串,甚至可以减少一个字符,我会减少256个哈希计算的数量。考虑到这个问题一段时间,并与MySQL玩了很多,我能够缩短我的注射到只有5个字符:
'||' 1
这会产生一个像这样的SQL语句(假设我的注入恰好落在了MD5哈希的中间,假装xxxx
是随机数据):
SELECT login FROM admins WHERE password ='xxx'||'1xxxxxxxx'
||
等同于OR
,1
当用作布尔值时,以a开头的字符串被转换为整数。因此,我的注射等同于:
SELECT登录从管理员WHERE密码='xxx'或1
通过只删除一个字符,这使我下降了2.3天的计算。还不够快,但越来越近。
砍掉另一个角色,并进行更多的改进
由于从1到9的任何数字都可以在我的注射中工作,所以我可以缩短我的注射线'||'
,然后检查注射线是否跟着1到9的数字(非常便宜的检查)。这将同时减少我的MD5计算256倍,并使它有9倍的可能性,我会找到一个可用的注射字符串。
因为||
是一样的OR
,我也可以检查它(2倍加速)和所有情况下的变化(16倍加速)。在远程双核桌面上运行我的程序而不是我的上网本,让我的速度再次提高了10倍。
最后的散列
在计算出只有1900万个MD5哈希之后,我的程序找到了一个答案:
内容:129581926211651571912466741651878684928 计数:18933549 十六进制:06da5430449f8f6f23dfc1276f722738 原始的:?T0D ?? o#??'or'8.N =?
所以我提交了密码129581926211651571912466741651878684928
到PHP脚本,它的工作!我能看到这个表格:
最后一步
挑战的最后一步是将MD5哈希转换为密码。我本可以使用像约翰这样的暴力破解者,但是我只是搜索了Google。密码已经被opencrack.hashkiller.com破解了13376843
。
本文作者为Mr.Bai,转载请注明。