SQL注入基础详解

这篇文章是我个人对利用SQL注入爆破数据库的流程介绍及见解,假设环境无waf无检测,为最基础的入门教程。

测试环境

PHP+MySQL  

本文章基于该环境讲述

代码风格

为了在长长的代码语句中突出各数据类型,现在我把数据库执行函数用大写区分,数据用小写区分。

注入点

注入点一般存在于网站前端与后端进行数据交互的地方,即用户可控参数,如网站查询,用户登录,URL中的get请求。这些信息在后端已经创建好查询语句,一般形式都是:

$xxx =$_POST['xxx'];
$sql = "SELECT * FROM table WHERE xxx = '$xxx'";

一般提交的表单信息都会传入查询语句进行查询,但如果程序处理表单数据存在注入漏洞,则该交互处可称为注入点

进行SQL注入最重要的一步是找到注入点,可以使用:

a'
a' and 1=1#
a' and 1=2#
1+1
1+2

注入这些语句,判断服务端返回数据差异判断是不是注入点。更多的判断语句需要根据语句包含数据的方法进行构造,需参考下一段。

闭合

找到注入点,下一步需要闭合,然后再注入我们的SQL语句。

闭合即是让后端的SQL语句提前“终结”,整型数据处可能不需要进行闭合即可带入SQL语句执行,但字符型数据处必须进行闭合。

以此处代码为例:

$xxx =$_POST['xxx'];
$sql = "SELECT * FROM table WHERE xxx = '$xxx'";

它将接收的数据进行查询,不管数据里面是什么都当作字符串看待。但在构造查询语句$sql时用了单引号包含字符串,这操作也符合规范。但由于对于没有对数据$xxx进行安全过滤,我们可以传入一个'让其与左半部的单引号“成对”,然后这个'左边的部分会被当作一般数据带入数据库进行查询,右边的部分则“逃逸”了出来,在其右边构造查询语句并适当完善整条语句或直接注释掉后面部分。

SQL注入就是闭合原先的SQL语句并拼接上攻击者想要执行的SQL语句。
语句中常用的包含数据的方法:

'xxx'
"xxx"
('xxx')
("xxx")

我们在数据里拼接上右半部分即可。

猜测列数量

假设我们已经知道如何闭合语句了,所以这部分及后面只贴出关键语句,需要根据环境自行闭合执行。

猜测列数量对于后续步骤执行至关重要,尤其是使用union select联合查询。

UNION操作符用于合并两个或多个SELECT语句的结果集,这里需要注意的是:UNION内部的SELECT语句必须拥有相同数量的列,列也必须拥有相似的数据类型,同时,每条SELECT语句中列的顺序必须相同。

基础的,使用:

UNION SELECT null,#
UNION SELECT null,null#
UNION SELECT null,null,null...#
...

不断增加null的数量,直到服务端返回数据出现差异,一般表现为页面显示正常,则当前null的数量即是列数量。

查询用的是nullnull无类型,以免引起类型出错导致判断出错。

更简便的,我们可以用:

ORDER BY n#

n由1开始递增,直到服务端返回数据出现差异,一般表现为页面出错,则当前n-1即是列数量。

别称:
猜测列数量:爆字段

误解:
猜测出来的列数量不一定就是真实的数据库列数,这里得到的是查询得出的列数量。

查询基本信息

假设我们已经知道字段数了,那我先假设字段为3。

使用:

UNION SELECT 1,2,3#

查看服务端返回哪个数字,即把需要查询的东西替换掉该数字。如页面在原本显示正常信息的地方显示了2,即2代表的列是当前页面输出的列,我们可以在2处进行注入。像这样可注入的地方可能存在多个。

举个例子说明:
页面:注入语句及显示

数据库:列数量及位置

代码:展示排序,部分展示字段为其他数据库

因为查询到的是完整的一条信息,管理员根据需求选择其中的部分信息进行展示,而我们要得出数据库信息,就要在相应显示的位置进行查询。

特别提醒

如果UNION前面的查询有效,则页面展示的是UNION前面查询得出的数据,为此,我们需要使UNION前面的查询恒为假,最简单的方法是添加  

and 1=2

现在我们开始进行一些简单的信息查询:

and 1=2 UNION SELECT 1,user(),3#    查当前数据库用户
and 1=2 UNION SELECT 1,version(),3#  查当前数据库版本
and 1=2 UNION SELECT 1,database(),3# 查当前数据库名称

类似的信息还有很多,可自行收集代入查询。

查数据表

这里的数据表就是当前服务端使用的数据库里的各个表名称。

我们需要先了解一下information_schema,这是MySQL数据库自带的数据库,这个数据库存放的是所有数据库和数据表的元信息(关于信息的信息)。

information_schema里的tablescolumns表分别记录着数据库里所有的数据表和数据列。我们主要在这两个表里获取信息。

and 1=2 UNION SELECT 1,group_concat(table_name),3 from information_schema.tables where table_schema = database()#

这语句中的database()就是我们当前使用的数据库,我们也可以改成其他数据库进行查询,但需要注意字符型的数据需要使用引号包含,建议转换成16进制。

关于group_concat():

如果当前查询返回多组数据,如上面查数据表的语句,若数据库有多个表,则会返回多组数据。但由于只能显示一个对象,则无法展示且会报错,这时需要用group_concat()把数据合并成一个记录。

查数据列

假设我们成功得出当前数据库所有的表,我们可以接着挖掘表中的列名称。

and 1=2 UNION SELECT 1,group_concat(column_name),3 from information_schema.columns where table_name=0x61#   假设数据表为a,0x61为a的16进制转换

不断替换table_name后面的值,得出所有表中的列名。

查数据

这里是最后一步了。

假设在数据表a中得出列名为aa,bb,cc:

and 1=2 UNION SELECT 1,group_concat(aa,0x20,bb,0x20,cc),3 from 0x61#

group_concat()中的0x20当作分割符,方便数据分隔,以免混合在一起难以提取。

发表评论

电子邮件地址不会被公开。 必填项已用*标注