介绍

很多人说 Web 应用渗透测试是一个应用已有工具和方法的已知区域,但其实每个项目都会遇到一些新的技术和令人感兴趣的新场景,这些挑战令人兴奋。我们从相对简单的 SQL 盲注入手,展现其如何被利用到可以执行远程代码。我们在测试的过程中会用到 Dunacn,这是我们用来辅助 SQL 盲注利用的简单框架。

SQL盲注

最初的漏洞存在于应用编辑器,提交的参数之一只是简单的连接成了 SQL 查询语句,而没有进行检查和过滤。但是我不能通过应用程序的响应或错误信息来得到注入的结果。因此,这是一个简单的盲注场景,但是对于从数据库中提取想要的数据仍然不容易。为了简单起见我使用了 SQLmap,但是我无法得到工具可识别的注射参数。像往常一样,我还是使用了 Duncan,Duncan 可以提取像这样场景下的数据。我创建了一个简单的模块,使用下列表达式来执行请求(后端使用的数据库是 PostgreSQL):

"ascii(substr(cast((%s) as varchar),%d,1))<%d"

Duncan 会用可执行的请求参数来代替第一个占位符(%s),第二个占位符指明了我要猜测的字符位置,第三个占位符是猜测的字符值。Duncan 会开始执行多线程的请求来检索注入请求的结果。在映射数据库结构后,我就有希望快速得到能登录应用管理交互界面的密码。

$ wc -l mytarget.py # Including blank lines, imports, and everything...
22 mytarget.py
$ python run_duncan.py --use mytarget.SimpleDuncan --query "select password from users where id=1" --pos-end 33 --threads 5 --charset 0123456789abcdef
deadbeefdeadbabedeadbea7deadc0de

为了提高效率,我使用自定义的字符集,并且基于之前测试的结果设置了最大长度,看起来该应用程序以十六进制存储了密码的 MD5 哈希值。我成功的得到了一些重要用户的密码哈希值,我也得到了我自己的哈希值以便进行验证。我尝试了帐号、密码、相关信息的多种组合,但是这都和数据库中存储的不一致。

深入研究

很明显,在密码的哈希过程中有“盐”的参与,但我不知道“盐”是什么。有可能是前缀形式、后缀形式或者级联哈希构造(如 MD5(MD5(password)||salt))等,但是没办法猜出它使用的是哪种方法。幸运的是,我记起我看过的一篇文章,讲的是如何使用数据库的元数据来 SELECT 当前执行的请求,他称之为 SQL inception。如果哈希的计算在数据库中而不是在应用程序中计算,这就是一个机会,我可以捕捉设置哈希过的密码的请求,从而了解哈希值是如何生成的。首先,我必须要找出我注入了什么。尽管我猜测我使用 UPDATE 指令 修改了用户表,但有些其他请求被我的注入破坏也是有可能的,那么在这种情况下这个方法就是不切实际的。幸运的是我是正确的,看起来我确实成功注入了 UPDATE 指令:

注:事实上,我可以利用并行执行来检索 UPDATE,但是我选择了用另一个请求注入,虽然这样的利用过程变得效率更低。

$ python run_duncan.py --user mytarget.SimpleDuncan --query "select upper(query) from pg_stat_activity" --pos-end 6 ascii-end 97
UPDAT

我试图找到设置密码的那部分请求,但有趣的是我得到了完全不可信的结果。几经测试后,我决定找出更多关于 pg_stat_activity 表的信息:

$ python run_duncan.py --user mytarget.SimpleDuncan --query "select cast(count(*) as varchar) from pg_stat_activity" --pos-end 4 --charset 0123456789
35

似乎其他人也在使用数据库,最初的请求得到了错误的数据。为了解决这个问题,我使用 WHERE 来限制我请求得到的结果:

$ python run_duncan.py --user mytarget.SimpleDuncan --query "select cast(strpos(upper(query),'MD5') as varchar) from pg_stat_activity WHERE query like '%S2S2S2%'" --pos-end 4 --charset 0123456789
156

由于 WHERE 子句将会被包含在 ps_stat_activity 表的查询属性中,它保证了查询请求可以包含它本身。找到调用 MD5 函数的位置我就可以查看参数了:

$ python run_duncan.py --user mytarget.SimpleDuncan --query "select upper(query) from pg_stat_activity WHERE query like '%S2S2S2%'" --pos-end 4 --ascii-end 97 --pos-start 150 --pos-end 180 --threads 10
ORD = MD5('MYPASS#SOMESALT#'

清楚了哈希值是如何构建的,我就可以在基于 GPU 的破解器中加载之前收集的管理员哈希。oclHashcat 在破解 MD5 时的表现很好,大部分的密码都可以通过暴力破解得到,特别是在“盐”不变的情况下。现在我可以使用破解后的密码,加上用 WPScan 枚举的用户名可以登录一个 WordPress 博客的管理面板。在 WordPress 的管理页面,我以 PHP 模版的名义上传了一个后门程序,此时我就可以以服务器高级权限来执行远程代码了。

结论

在这篇文章中,我展示了 SQL inception 这项技术应用到渗透测试中如何解决实际问题,以及如何根据 Duncan 的结果进行微调来得到最佳结果。我们也想强调密码的管理和存储仍然是简单 Web 应用和复杂企业系统的主要弱点。我们给开发者和管理员的建议:

  • 使用参数化请求或 ORM 用于数据库访问,已经到了 2015 年,你不应该再使用手工拼接的 SQL 请求了!
  • 使用现代密码扩展算法用于密码存储,Bcrypt、PBKDF2、Scrypt 和 yescrypt 都各有优势,从实际情况来看,这些设计都能显著的提高攻击者破解的难度。MD5 和 SHA-* 的设计都不是适合密码存储而是标定指纹。
  • 使用尽量长的、独特的“盐”,前面提到的大多数算法都允许你自己定制“盐”
  • 你系统的安全性不应该依赖于“盐”或所使用算法的保密性
  • 使用离线的密码管理器,不同的系统永远不要使用相同的密码
  • 如果不得不将密码记在心里,使用长密码短语来代替传统的密码。教你的用户用相同的方式来贯彻落实密码策略,而不是用长密码来代替复杂的密码
如何找到SQL注入中的盐-RadeBit瑞安全