现代化的web服务对比传统的web服务,增加了许多新的功能和特性,在这种情况下,数据库所存储的内容、与数据库交互的代码也发生了改变,同时也增加了许多攻击面。
本文将结合实际情况,谈谈现代化web服务可能暴露的一些SQLi攻击面和利用手段。
传统注入中,SQLi只用来做几件事:读web账号密码SELECT ... FROM admin
、伪造账号密码'or'='or'
、读root提权SELECT ... FROM mysql.user
、写shellSELECT ... INTO_OUTFILE ...
,但随着web业务需求和硬性标准的实施,SQLi已经不局限于这些简单的操作了,具体的业务情况也发生了变化。
传输与存储密文一致,指的是传输过程中提交给服务器的密文,与数据库内存储的密文一致,对于密码而言,尽管并不是明文存储,但攻击者可能并不需要获取明文。
笔者从事属于半标准性质的工作,依符合性评测而言,web 传输的内容应该进行加密,也就是我们的账号和密码不应该是明文传输,明文传输情况如下图所示:
而标准化的内容传输,如下图所示:
对于明文传输,准确的解决方案应该是前端做处理,然后后端再做处理,再与数据库内容进行匹配,如下图所示:
但再实际情况中,我们遇到了许多错误的解决方案:前端做处理后,直接与数据库内容进行匹配,如下图所示:
在传统的SQLi中,前端传输的一般是明文,而后端对明文进行处理,再与数据库进行匹配,攻击者获取到密文后需要进一步进行解密。
而这种问题的存在,将使得攻击者获取到数据库内加密的密码之后,不需要进行解密即可进行登陆操作。
依据符合性评测相关要求,管理系统应当具备日志功能,以记录 操作人 / 被操作功能 / 操作时间 等信息,如下图所示:
操作记录在数据库中的存在,SQLi至少可以有两个攻击面:获取敏感地址(后台) 和 相关记录点注入(Referer/X-Forward-For) ,前者的影响一目了然,我们来看一下后者,下面是一个全局防护样例代码:
<?php $_GET && SafeFilter($_GET); $_POST && SafeFilter($_POST); $_COOKIE && SafeFilter($_COOKIE); function SafeFilter (&$arr) { if (is_array($arr)) { foreach ($arr as $key =--> $value) { if (!is_array($value)) { if (!get_magic_quotes_gpc()) //不对magic_quotes_gpc转义过的字符使用addslashes(),避免双重转义。 { $value = addslashes($value); //给单引号(')、双引号(")、反斜线(\)与 NUL(NULL 字符)加上反斜线转义 } $arr[$key] = htmlspecialchars($value,ENT_QUOTES); //&,",',> ,< 转为html实体 &,"',>,< } else { SafeFilter($arr[$key]); } } } } ?>
通用全局防护大多是针对GET、POST、request,少部分有针对Cookie字段的防护,而header的其他字段,在全局情况下几乎不存在,其中最常见写入数据库的头为Referer
和X-Forwarded-For
。
此外,在对这两个字段进行SQLi防护的情况下,往往还有XSS的可能。
X-Forwarded-For:除了SQLi和ip伪造之外,在利用上我们还可以选择XSS来进行攻击。大部分在全局上针对的防护将优先对SQLi进行防护,而遗漏XSS问题。
Referer:在代码限制上,Referer
和X-Forwarded-For
唯一不同的就是,Referer
是必须是字符串,而X-Forwarded-For
本身就是不允许为字符串情况出现,所以大部分开源cms都通过IPlong来进行限制。Referer
因为考虑到是必须得字符串,所以很多在选择写入数据库时毫不犹豫的使用了安全转义或者预编译,从而忽略了XSS利用。
在传统的web服务中,登录凭证默认以文件形式存储,如PHPSESSION,随着业务的发展,大量文件形式的凭证将导致磁盘读写频繁和业务缓慢,因此凭证有被写到如redis这种缓存的,但也有存储到数据库中的:
以上为我们登陆后的sid,直接写入到了数据库,而我们进入通过的允许标识就是通过这种sid来判断是否具备权限,通过SQLi就可以获得sid,进而进行登录,这也是用户进行登陆后,没有进行退出而导致的。
有些案例更复杂,但本质类似,某cms审计文:
根据师傅们的指导和提示,发现phpcms在登录的时候会在v9_session表中执行replace into数据库操作,replace into 跟 insert 功能类似,不同点在于replace into 首先尝试插入数据到表中,如果发现表中已经有此行数据(根据主键或者唯一索引判断)则先删除此行数据,然后插入新的数据,否则直接插入新数据。
>
进入该表审查发现,表中保存的便是会话PHPSESSION的值。
>
>
仔细看v9_session表中的列名和字段值,发现 pc_hash 也是存在该表中的,在进行后台登录的的时 候需要附带随机生成的 pc_hash 值,本来以为要进行枚举,后面发现直接在数据库中便可以取到。
>
>
因此整个登录后台的过程和操作便更加简单些,首先分析phpcms_v9.6.2_UTF8\phpcms\modules\admin\index.php
中可以看到后台登录的一些验证过程,重点关注如下代码块,文件中的第89-102行,可以看到$_SESSION['pc_hash']
生成方 法,还有cookie的生成方法:
>
>
因为已经可以从数据库中获取session了,所以只要构造好对应的cookie参数对应的值便可以直接登录后台,当然前提是刚好获取到管理员正处在登录状态的session值,首先分析下cookie的生成过程:
>
- 跟入
phpcms_v9.6.2_UTF8\phpcms\modules\admin\index.php:97-101
的 set_cookie 方法,在phpcms_v9.6.2_UTF8\phpcms\libs\classes\param.class.php
中找到该方法,该方法是使用 sys_auth 对传入的数组的值,字符的值进行ENCODE操作 后,再使用 setcookie 下发到客户端。
>
- 跟入
sys_auth
方法在\phpcms_v9.6.2_UTF8\phpcms\libs\functions\global.func.php:384-430
发现代码块,这里在SQL注入的部分已经进行了分析。
>
因此利用任意文件读取获取到的 auth_key 构造cookie进行登录,登录的时候可以使用cookie manager 将构造好的cookie,添加的请求头部中,然后进行如下请求。
>
>
总的来说,还是通过SQL注入的方式,获取到了相关的凭证。
随着业务的发展,数据库里存储的敏感信息,不再只有账号密码,攻击面也随之增加,在对SQLi乃至整个web安全进行研究的时候,应当结合新的业务功能和需求,寻找新的攻击点,进行新的思考。