2-Web安全——union联合注入

目录

1. MYSQL相关知识

2. 联合查询注入

3. sql注入点

4. 联合注入的思路

5. 数字整型注入

6. 字符型注入

7. 闭合方式

8. 联合注入实践


1. MYSQL相关知识

在正式学习联合注入之前,先了解一下MYSQL数据库的相关知识,MYSQL 5.0版本后,MYSQL数据库中默认会有一个information_schema数据库,在这个数据库中有三张表非常重要,分别是:SCHEMATA,TABLES,COLUMNS。

SCHEMATA表中有一个SCHEMA_NAME字段,该字段存储了当前用户创建的所有数据库库名,如下所示:

TABLES表记录了当前用户创建的所有数据库的库名和表名,TABLE_SCHEMA字段记录了数据库的库名,TABLE_NAME字段则记录了数据库的表名。

COLUMNS表中记录了当前用户创建的所有数据库名和表名,以及表的字段名,TABLE_SCHEMA字段记录了数据库的库名,TABLE_NAME字段记录了表名,COLUMN_NAME字段记录了表的字段名:

一般数据库中会有很多个表存储着数据,在注入过程中我们首先要做的就是拿到目标数据库名,然后拿到当前数据库下的表,再从表中获取到需要的数据,以上几个所说的数据库表在注入过程中起到了非常重要的作用。

除了以上三个重要的表名和字段,我们还需记住以下常用的MYSQL函数:

database():当前网站使用的数据库

version():当前MYSQL数据库的版本

user():当前数据库使用的用户名

2. 联合查询注入

联合查询注入是使用union进行注入的一种方式,通常的应用场景是在一个网站的页面中,后端执行SQL语句查询数据库会将查询到的数据返回并显示到页面中。

3. sql注入点

注入点通常就是可以进行sql注入的地方,例如浏览器地址栏?id=1 ,搜索框,登录页面等等,都有可能存在sql注入。

例如上图中在地址栏输入?id=1',后台直接返回mysql的报错信息,这就是一个注入点。这里是为了方便学习和理解,暂且这么认为,之后深入学习我们会近一步认识注入点。

4. 联合注入的思路

通过union联合注入可以获取到目标网站的数据库里的所有用户名和密码,那如何获取呢?联合查询注入思路如下:

  1. 判断是否存在注入点,如果存在进一步判断注入点类是数字型还是字符型
  2. 判断使用group/order by的二分法判断union语句中前一个查询的列数
  3. 判断显示位,优化SQL语句将id改成一个不存在的数字
  4. 使用select查询目标数据库库名
  5. 使用select查询目标所有表名
  6. 使用select查询目标所有列名
  7. 查询所有用户名和密码

数据库中有CRUD(增删查改)几类基本操作,查询操作最为常用的,union联合注入是其中一种查询操作。

在数据库中所有表的字段都是有类型的,并且根据不同字段所代表的含义会具有不同的数据类型,对于union联合注入要查询的数据来说,数据类型基本以字符型和数字型为主,因此在进行SQL注入时需要判断是否为整型或字符型,首先需要判断是否存在注入和注入类型:

  1. 传入的参数能否改变影响页面的显示结果
  2. 输入的SQL语句是否报错,获取数据库的是否报错的信息提示,来判断合适的SQL语句注入方式
  3. 进一步判断注入类型(数字型或字符型)

本篇文章只介绍整型注入和字符注入,并不做深入了解。

5. 数字整型注入

整型注入就是SQL查询语句的where字段后面跟的条件没有使用单引号,整型注入的SQL查询语句通常是这样的:

select * from table where id = x;

在sqli-lab测试环境的URL: http://www.sqli.com/Less-2/ ,输入id=1进行测试:

后台直接把id为1的用户名和密码查出来了。

再输入id=1 and 1=2为例进行测试:

页面没有显示任何报错信息说明SQL语句的语法没有问题,虽然后台能成功执行SQL语句,但是WHERE后面进行了and的逻辑判断,条件1=2不成立,因此不会返回有效数据,页面显示正常,我们可以猜测这应该是一个整型注入。 另外,后台直接输出拼接成的SQL语句中1 and 1=2并没有被任何单引号或者括号包裹,也就是说我们输入的参数1 and 1=2被后台完全当做数字来处理了。

为了进一步验证这是一个整型注入,这次输入id='1 and 1=2'进行测试:

由于这是一个数字型注入,前端传入的参数’1 and 1=2’ 会被当做一个整体拼接成SQL语句,MYSQL数据库最终在执行SQL语句时只从’1 and 1=2’中截取了第一个字符,页面查询的结果也返回了id为1的用户名和密码。这显然不是一个字符型注入,因为如果是字符型注入的话,会直接报错。

在MYSQL数据库下运行该SQL语句,返回的结果是id=1的用户名和密码:

6. 字符型注入

字符型注入跟整型注入相反,查询的时候WHERE后面跟的条件加了单引号,字符型注入的SQL语句一般是这样的:

select * from table where id = ’x’;

测试URL为:http://www.sqli.com/Less-3/,以经典常用的id=1 and 1=2方式进行测试:

以同样的方式进行测试,字符型注入却能返回正确的结果,那么可以确定这是一个字符型注入(因为数字型的话,where后面的条件1=2不会成立)。从后台拼接的SQL语句来看,前端页面传入的参数id=1 and 1=2在后台经过了特殊处理,用单引号和括号引起来当做一个字符串来处理,拼接成SQL语句。

MYSQL成功执行SQL语句并返回正确结果:

在确定这是一个字符型注入后,我们还需要确定字符型注入的闭合方式,输入id=1 and 1=2’:

从MYSQL报错的信息提示中,后台在对sql语句进行闭合时是用')(单引号加括号的形式闭合的),那么可以确定字符型注入的闭合方式就是') --+或者') --%20 (%20在URL编码中其实就是代表空格),以') --+为例进行测试:

无论是联合注入还是哪一种sql注入方式,如果无法确定注入类型和sql语句的闭合方式的话,将无法进行下一步的渗透。

7. 闭合方式

确定了注入点和注入类型(字符型和数字型)后还需确定闭合方式,因为闭合方式对接下来的渗透工作非常重要。

对于数字型来说,不需要确定闭合方式,这里将重点介绍字符型的闭合方式.。

字符型的闭合方式主要有以下几种:

  1. 单引号
  2. 单引号加括号
  3. 双引号
  4. 双引号加括号

单引号闭合一般为:'$id' ,在进行判断闭合的时候一般是输入奇数个单引号进行判断,例如:1'

单引号加括号闭合一般为:('$id'),在判断闭合的时候一般是输入奇数个单引号加一个括号,例如:1''')

双引号闭合一般为:"$id",判断闭合方式也是和单引号一样,输入奇数个双引号,例如:1"

双引号加括号闭合一般为:("$id"),判断闭合方式跟单引号加括号一样,例如:1")

8. 联合注入实践

当我们确定一个网站的注入点和注入类型后,闭合方式和显示位后,就可以正式开始联合注入了,接下来使用group/order by语句二分法判断查询数据表中字段的列数(union语句中前一个查询的列数)。

order by语句在数据库的作用是用于对查询的结果集进行排序,还可以查询数据库表的字段列数,但目前我们并不知道数据库表的字段列数有多少,因此就需要结合order by语句来猜测:

order by后面跟的数字代表数据库表的列数,order by 1,2,3,4语句有4个数字(这些数字没有特殊含义,只是起到占用符的作用),我们猜测前一个查询的数据库表中有4个字段,但从MYSQL报错的提示来看,数据库表中并没有4个字段,因此我们减少一个,使用order by 1,2,3进行测试:

从页面显示的结果来看,后端成功执行SQL语句没有提示错误,说明数据库表的字段有3个。

接下来就是第三步了,判断显示位,需要优化SQL语句将id改成一个不存在的数字0或者-1,输入id=-1') union select 123,100,250 %23进行测试:

将id=-1修改成一个不存在的数字是为了把union后面的select语句查询出来的数据结果集展示在显示位,页面中SQL语句返回的结果是100和250,说明SQL语句中100和250这两个位置可以换成MYSQL语句,这里把100换成database()函数,查询当前数据库。

输入id=-1') union select 123,database(),250 %23测试,后端返回查询的结果集:

现在已经获取到当前数据库库名为security,接下来就要查询获取当前数据库下的所有数据库表,构造查询SQL语句:

select 123,table_name,250 from information_schema.tables where table_schema = 'security';

SQL语句虽然返回了查询到的所有结果,但是显示位页面只能显示一条数据集,还需要使用group concat()函数把所有结果集放到一行中显示到前台页面,最终构造成的SQL语句如下:

select 123,group_concat(table_name),250 from information_schema.tables where table_schema = 'security';

页面中显示的当前数据库security下所有的数据库表有:emails,referers,uagents,users。

按照联合注入的思路,前5步我们已经拿到了数据库名和所有的表名,下一步就是根据已知的库名和表名,获取指定表的所有字段。以users表为例,获取users表的所有字段,构造SQL语句:

这一步我们已知当前数据库库名,当前数据库的所有表名以及所有表的字段名,最后通过这些已知条件users表的获取用户名和密码了:

分析uniou联合注入主要部分代码:

<?php
if(isset($_GET['id']))
{
$id=$_GET['id'];

//执行SQL
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
	if($row){
  	echo "<font size='5' color= '#99FF00'>";
  	echo 'Your Login name:'. $row['username'];
  	echo "<br>";
  	echo 'Your Password:' .$row['password'];
  	echo "</font>";
		echo "<br/>";
		var_dump($sql);
  	}else {
	echo '<font color= "#FFFF00">';
	print_r(mysql_error());
	echo "</font>";  
	}
}
?>

程序获取到id后进行了一个判断将id拼接到SQL语句中,然后把查询到的数据返回到前台页面,并没有进行严格的过滤和校验,因此前端传入的SQL语句select 100,group_concat(username,'@',password),250 from security.users %23会直接当成SQL语句被执行。

注意:如果mysql数据库的版本小于4.0的话,是不支持union select联合查询的。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值