做为一枚纯正的甲方安全人员,代码审计是必备技能点。在实际工作中,代码审计无论是在安全测试中所占的投入比重,亦或是在发现产品/项目安全漏洞中的比例,都比黑盒渗透要大的多。因此,博主特意开辟了该代码审计系列博文,结合了博主在某厂实际工作中的工作经历和厂内大牛前辈们的经验,稍加提炼而成。
在正式开始专项之前,先简单介绍下“代码审计渗透测试”这个自创的新词:
一般来讲,Web安全测试主要分为黑盒测试
和白盒测试
:黑盒测试
是指测试人员不清楚Web具体的架构和实现,通过模拟一个或多个攻击角色进行渗透测试,发现潜在的安全漏洞和风险,它不会泄露系统源码。白盒测试
则需要开发人员提供相关的设计文档、源码、使用手册、业务逻辑等信息,对源代码直接进行安全分析,来寻找安全漏洞和可能存在的风险,它主要用于公司内部进行深度安全测试。代码审计是白盒测试常用的一种方法。代码审计渗透测试
则是将黑盒渗透测试的思路用于代码审计的一种方法,博主将从漏洞介绍、漏洞原理、测试思路和测试方法四个方面来阐述TOP N的Web安全漏洞的代码审计渗透测试。
漏洞介绍
SQL注入漏洞是Web常见的漏洞,通过此漏洞可能用来拖库、获取Web管理员账号和密码、权限提升等攻击,是Web安全中最严重的漏洞之一。
注意:本章节仅讨论
关系型
数据库,不适用非关系型
数据库。
漏洞原理
SQL注入(SQL Injection),就是通过把SQL命令插入到Web表单提交
或页面请求
的字符串中,最终达到欺骗数据库服务器执行恶意的SQL命令,从而达到攻击的目的。
用户输入的内容,传递到服务端后没有做有效的过滤,后端拿到用户输入的不安全的内容后当做SQL命令直接执行,从而改变了原来的业务的SQL运行流程或者在执行了原有的SQL语句后,附加执行了用户的恶意SQL语句,导致数据库信息泄露或者权限提升。
举个栗子:
username:
password:
前台需要输入用户名和密码。
后台的处理代码如下:
123 username = getRequestName("UserName");password = getRequestPassword("UserPasswd");sql = "SELECT * FROM USER WHERE name = '" + username +"' AND passwd = '" + password + "'";这里后台拿到前端传参后没有做任何处理,直接拼接了SQL语句,这就很容易被SQL注入了:
username: admin’ or ‘1’ = ‘1
password: anything
这时候,后台构造的语句即为:
1 sql = "SELECT * FROM USER WHERE name = 'admin' or '1' = '1' AND passwd = 'anything';来分析这个SQL语句,你会发现,不管密码输入什么(anything),这条语句总会返回true,因为where xx or ‘1’ = ‘1’ 是恒成立的。因此用户不需要知道用户名和密码就能够成功登录到系统。
从上面例子可以看出,用户可以构造出改变原本程序的运行逻辑的恶意代码,从而导致SQL注入。
更多SQL相关的知识详见:SQL_Injection
测试思路
渗透测试前,先要与开发人员进行沟通确认,找到存在用户输入并且需要操作数据库的地方,再去看是否进行了防SQL注入的安全机制,该安全机制是否能被绕过等。常见的比如使用预编译
:
PHP:可以使用
mysqli
、PDO
实现预编译
Java:可以使用preparestatement
预编译
在SQL注入的测试中,仅仅通过黑盒渗透是远远不够的,原因如下:
- AppScan/AWVS无法获取到所有的业务报文,Burp可以获取较多的报文,但是注入用例太少,不能进行全面的覆盖,因此导致很多工具都没法进行全面的SQL注入渗透测试
- 渗透测试工具没法进行一些需要特定条件才能注入成功的操作,或者是正常场景不会出现的情况,容易产生遗漏
因此针对SQL注入,强烈推荐使用本文源码分析
为主,黑盒测试为辅。源码分析遵从以下规则:
- 未使用预编译的,必须换为预编译,如果存在不能预编的场景,需要单独拿出来进行安全分析;
- 使用预编译的,必须使用占位符,不能针对sql拼接。
黑盒测试
在测试前,通过与开交流或者源码源码分析,了解Web中使用的所有数据库名称及版本号,包括关系型数据库(MySQL、Oracle、SQLite等),然后针对性测试,减少测试工作量。黑盒测试思路如下:
1)Web漏洞扫描
利用Web漏洞扫描工具如AppScan、AWVS、Burp自动扫描,可以发现部分注入SQL注入漏洞,针对发现的漏洞“点”,在源码分析漏洞产生的原因,然后全面排查类似问题。2)手工SQL注入
在Web界面中找到可能存在数据库操作的点,例如用户用户、数据查询、添加用户等场景,然后尝试手工测试,来发现比较隐蔽,以及工具难以覆盖的SQL注入漏洞。
源码分析
通过分析源码,识别可能被绕过的SQL注入场景,例如绕过正则、关键字过滤、编码等,常见的SQL注入根因如下:
- 直接拼接SQL语句,导致存在SQL注入
- 不正确使用预编译导致存在SQL注入
- 通过拼接,有过滤但过滤不全能被绕过导致SQL注入
- 第三方框架导致SQL注入(如Hibernate、iBatis……)
- 其它原因导致存在的SQL注入
测试方法
Web漏洞扫描在测试工具
中有详细介绍,因此本章节测试方法重点介绍手工SQL注入和Web源码分析SQL注入。
非预编译导致的SQL注入
大部分SQL注入漏洞的根因都是因为没有使用预编译,使用SQL语句拼接,导致用户构造的恶意语句改变了原有的SQL流程。测试方法如下:
1)通过手动搜索关键字分析SQL拼接场景是否存在漏洞,关键字如下:
PHP关键字:query
、mysql_query
、mysql_fetch_array
……
Java关键字:Statement
、.execute
、.executeQuery
、jdbcTemplate
、queryForInt
、queryForObject
、queryForMap
、getConnection
2)分析SQL执行的场景是否使用了预编译,以java为例,可以通过搜索关键字queryForInt
搜索到的代码假设如下:
STR_QUERY_FILE为SQL语句查询字符串,strFileName为客户端传参,也就是该参数由用户直接输入,即用户可控,这里并没有进行过滤,也没有进行预编译,直接使用format进行字符串拼接后执行SQL,因此此处是存在SQL注入漏洞的。
漏洞利用Exploit如下:
错误的预编译导致的SQL注入
有时候开发人员虽然使用了预编译,但是错误的预编译仍然是存在SQL漏洞的,因此代码审计时需要分析PreparedStatement时是否使用占用符,测试方法如下:
1)通过搜索关键字,先找到使用预编译的场景,关键字如下:
PHP关键字:prepare
、execute
Java关键字:preparedStatement
2)分析代码是否存在漏洞,预编译之前的参数绑定应该使用占位符,不应该直接使用拼接。例如:
这里虽然使用了preparedStatement预编译,但是在预编译之前,append拼接参数时已经被污染,所以这里也是无法防御SQL注入的。正确的方法应该如下:
过滤不完整导致SQL注入
在有些场景没办法使用预编译。比如建表的SQL语句没法使用参数绑定+预编译来放回SQL注入,这个时候就需要进行SQL动态拼接,而防止SQL注入就需要进行特殊字符的过滤,一般大家都喜欢使用黑名单,而黑名单却是最容易绕过,无法达到防护的目的。这种情况的测试方法如下:
1)在源码中搜索如下关键字,找到相关的代码:
PHP关键字:query
、mysql_query
、mysql_fetch_array
······
Java关键字:statement
、execute
、jdbcTemplate
、queryForInt
、queryForObject
、queryForMap
、executeQuery
、getConnection
······
2)分析测试是否存在绕过的风险,例如(PHP):
这里使用的是MYSQL数据库,没有使用参数绑定加预编译的形式,id参数来源于客户端请求,开发人员意识到存在SQL注入安全风险,进行SQL执行前使用了str_replace进行了字符过滤,将$key1中的字符替换为空,实际上这样过滤远远不够,使用union就可以轻松绕过,漏洞利用可以参考:
Hibernate导致的SQL注入(仅存在于java
)
Hibernate是一个开源的java框架,它对JDBC进行了非常轻量级的对象封装,它可以生成SQL语句,自动执行,使得java程序员可以自由的使用OOP思想来操纵数据库,因此深受欢迎。
Hibernate本身支持预编译,但是如果使用动态拼接,则也会存在SQL注入风险,测试方法如下:
1)判断web中是否使用了Hibernate,搜索如下关键字:
Hibernate关键字:org.hibernate
2)分析是否为SQL拼接,搜索一下关键字分析相关代码是否存在拼接:
关键字:.createQuery
例如:
代码中通过Hibernate来查询数据库,username为外部输入参数,然后与sql动态拼接,因此存在SQL注入漏洞。
安全的使用方法应该是使用占位符进行参数绑定
,再使用预编译
:
iBatis/myBatis导致的SQL注入(仅存在于java
)
iBatis(后被谷歌托管,改名为myBatis)是用于使用方便的数据访问工具,也主要作为数据持久层,与ORM(Hibernate)类似。i/myBatis中#是占位符,$是字符串拼接,所以尽量使用#可以避免SQL注入,而$则会存在风险,测试方法如下:
1)判断web中是否使用了iBatis/myBatis,在源码中搜索以下关键字,如果存在说明项目中使用了iBatis/myBatis,关键字如下:
iBatis关键字:import com.ibatis
myBatis关键字:import org.mybatis
2)判断SQL调用时,是否使用了$拼接。例如:
这里使用的就是$username$,进行了SQL拼接,username被用户恶意输入则造成了SQL漏洞。
正确的使用应该是:
其他原因导致的SQL注入
PHP的SQL注入更加灵活复杂,也存在上面类似的ORM框架,同理也就可能存在SQL注入风险,但是往往结果框架的包装后,就很难轻易的识别出这些安全风险,给白盒渗透测试带来困难。除了上述因素外,还有其他场景和因素导致SQL注入:
1)字符编码导致SQL宽字节注入
2)不正确的使用安全函数导致的SQL注入,尤其是PHP弱语言最容易发生
3)代码逻辑导致的SQL注入
尤其是代码逻辑,这个需要深入熟悉业务,也需要有较强的逻辑思维能力。
PS:
1)产品安全测试过程中测试量大,参数多,因此最高速有效的测试方法就是基于正常报文添加单双引号,然后查看数据库日志。当然如果你非常熟悉手工注入也可以直接构造出SQL来证明是存在漏洞的。
2)在实际排查的过程中,使用关键字搜索,大多数会得到很多结果,如果一个一个的去分析调用,参数是否可控,工作量相当大,并且很容易产生漏洞。这时,就需要开发人员先将能使用预编译的地方使用参数绑定+预编译,然后再去详细分析不能使用预编译的地方进行定点分析,提高开发和测试效率。