导语 作为一名安全运营人员,每天总会处理各种各样的安全事件/告警,而其中最为常见的莫过于SQL注入告警。而面对SQL注入告警时最重要的就是确定是否注入成功,但不同类型的SQL注入确定的方法又不一样,有些是通过一个请求就可以确定是否注入成功而有些则需要观察多个请求后才能确定是否成功。而本文则是简单归纳总结如何通过流量日志快速判断SQL注入是否成功。
文章大纲
不同数据库的区别
常见SQL注入方式
布尔类型注入
错误类型注入
联合类型注入
堆叠类型注入
时间类型注入
总结
一、不同数据库的区别
这里主要关注的是在SQL注入上的差别,而不是功能、性能上的差别。
1.字符上的区别
不同数据库对于同一个字符都可能有不同的含义和处理方式,因此同样的注入payload在不同数据中会有不一样的表现形式。
下面简单举例:
“+”号:在MSSQL中,“+”号代表字符串拼接,因此“a+b”的结果是“ab”.
空格: 在MySQL中,空格代表字符串拼接,因此“a b”的结果是“ab”.
“||”号: 在Oracle中,“||”号代表字符串拼接,因此“a||b”的结果是“ab”.
以MySQL数据库为例,演示“+”号和空格拼接字符串的区别:
2.函数上的区别
对于同一个功能,不同的数据库中的函数名可能不一样。
以时间函数为例,
在MySQL中的函数名为“sleep()”,
在MSSQL中则是使用语句的形式“waitfor {Delay 'time'|Time 'time'}”,
而Oracle中则是使用“DBMS_LOCK.SLEEP()”
以MySQL和MSSQL为例,演示两者不同的区别(时间延迟):
MySQL中,使用“sleep”函数:
MSSQL中,使用“waitfor delay”
3. 结构特点上的区别
不同的数据库在设计上总会有着自己的特点,而这些特点往往会被攻击者用于进行注入或者更进一步的攻击。
在MySQL数据库中,最具有特色的莫过于Information库了,因为这个库中保存了大量的敏感数据,小至字段名、表名和数据库名,上至敏感配置和用户密码你都能在其中找到。
通过information库获取已有表名:
每当提起MSSQL时就不得不提xp_cmdshell,因为通过它可以在系统上执行任意系统命令。虽然在后来的版本中被默认关闭,总可以通过各种手段进行开启。
开启前后对比:
开启前:
开启后:
在众多数据库中,oracle对于权限的管理是最为严格的,因此攻击者在对oracle数据库进行注入时往往需要进行提权操作。而oracle数据库本身的设计是较为完善的,所以往往需要利用数据库本身的漏洞进行提权。
已有的oracle漏洞
二、常见SQL注入方式
SQL注入有多种不同类型的注入方式,最为简单的分类就是根据有无回显来进行分类。但实际中为了能够更加高效的对SQL事件/告警进行确认,因此我们需要更加细分。
布尔类型注入
该类型的注入主要依赖于表达式、函数或查询的结果是否正确从而影响页面输出结果来判断是否存在注入。
简单举例说明,下面假设对参数id(数值类型)进行攻击,后端数据库是mysql:
表达式类型:?id=1 and 0
从运算符上分析,1和0做and运算的结果是0,此时数据库应该查询id为0的结果。但对于后端处理逻辑而言“ and 0”这部分数据理应是无效的(正确过滤情况下),因此应该查询id为1的结果。此时攻击者只要通过判断页面显示id为1还是id为0的结果就可以初步判断这里是否存在注入点。
函数类型: ?id=1 and length(username()>5)
从上面给出的payload可以看出,当攻击者需要进一步获取信息时往往会用到数据库中内置的函数(或通过查询语句获取)。然后通过预先设置好的条件来控制页面的输出结果。
从流量日志上进行可以发现该注入方法除了攻击payload中出现相关表达式/函数以外,更重要的一个特点是会对同一个注入点出现多次且次数较为稳定的尝试。
为什么会同一个注入点会出现多次尝试?
这是因为攻击者在对某些信息做枚举尝试。前面说了,该方法只会影响页面的输出结果而不会直接显示想要获取的结果,因此需要做大量尝试才能确定结果。攻击者在这种情况下需要进行两个阶段的循环尝试:
循环一:确定要获取数据的长度。
循环二:确定要获取数据的值。
为什么次数较为稳定?
这里的次数是指循环二中的尝试次数,正常情况下数据库中的保存值(如数据库名、表名、字段名或其他敏感信息)都是特定字符范围的,最为常见的就是a-zA-Z0-9加一点常见的标点符号。因此对于攻击者而言只需要遍历一次对应位置的常见字符就能确定该位置的具体值。
过程举例
出现错误类型注入时往往是攻击者最开心的,因为该方法利用成本低无需大量的尝试,同时能够在返回的数据中看到想要的结果。
该方法在流量日志中进行确认时需要把重心放在返回的数据包中,但一般情况下数据往往是通过十六进制进行保存的,因此无法直接关键词搜索需要先进行解码。在解码后需要确定攻击者所要获取的数据是否出现在返回数据包中。一般情况下所获取的数据往往会被对应数据库的错误信息而包围,因此一旦在返回的数据包中出现数据库的错误信息则很大可能情况是被攻击成功的。
错误信息中包含攻击者要获取的信息:
该类型的注入方法与布尔类型注入方法有共通点:也是需要页面结果来进行确定。但略微不同的是所查询的结果是会出现在返回的数据中。
该类型的注入方法在payload上会带上“union”,因此较好识别。在进行确认时需要关注返回数据中是否出现payload中所查询的数据。同时攻击者在攻击过程中会出现少量用于确定字段数的尝试,而在尝试中一般会用“null”进行填充(mysql数据库为例)。确定字段数的原因是因为“union”关键字的前后两个查询语句的字段数需要一致,而用“null”填充则是可以避免因为字段类型不同而导致错误。
直接从名字上可能稍微有点难以理解该类型的具体注入方法,但换个角度解释的话就能更好的理解:payload中一定会出现“;”分号,且分号后面跟的是一个完整、能直接执行的SQL语句。
在实际过程中堆叠类型注入确实比较少见的,因为这与后台查询所用的api有所关联,但直接在数据库是能够执行成功的。
在流量侧中该类型的注入因为可以执行完整的SQL语句,因此既可能是有页面回显的,也有可能是没有页面回显的,要根据所使用的payload进行确定。
该类型比较特殊,需要计算请求包和返回包的时间戳之差来判断,而不是单纯的通过返回包的数据特征。这里的时间戳是指http请求中所记录的时间,而不是指事件/日志自身的时间戳。而具体的时间范围则要查看payload中所使用的时间,一般而言都是5秒左右。
实际流量日志中会有多个时间戳:
三、总结
实际中攻击者所采用的攻击手法变化多端,因此只能简单介绍常见注入方法的特点,以便在面对大量此类告警时如何能够快速的进行是否注入成功的判断。后面我们会进一步介绍如何通过自动化的方法来进行处理。
该账号主要围绕智能化技术如何帮助企业提升网络安全水平展开,内容涉及机器学习、大数据处理等智能化技术在安全领域的实践经验分享,业界领先的产品和前沿趋势的解读分析等。通过分享、交流,推动安全智能的落地、应用。欢迎关注~