SQL注入
SQL注入当属漏洞之王,具有高危害性,且利用条件低,利用以及绕过检测方式多种多样。这篇笔记主要从SQL注入的危害性,以及SQL注入的条件,注入点的判断以及寻找,注入的方式以及类型,不同业务场景下对应的不同query方式利用,成功注入后应该进行的步骤,在具有root高权限账户时候应该进行的操作下,防御以及后端过滤方式和Waf绕过,一些本人见过的查询语句闭合方式。
SQL注入的危害性
SQL注入的危害性不用多说,可以拿到get shell,拿到数据库数据,操作数据库数据,root高权限下的跨库以及跨站操作,对文件的非法读写。
- 以get方式提交的请求中url若带有+,空格,/,?,%,#,&,=等特殊字符的时候,可能在服务端无 法获得正确的参数值,应该先将这些字符转为对应的URL编码,比如+ %2B,/ %2F, 空格 %20,? %3F,% %25,# %23,& %26,= %3D.
SQL注入的条件
- 只要参数可控,即可能存在注入。
- 有和数据库进行交互的地方,即可能存在注入点,就算是参数头也有可能。
这里联想到漏洞的两个条件:可控的参数+存在可能被攻击者利用的函数。
对有无注入点的判断
老方法一般使用and观察网页是否报错进行判断,比如select … where id = 1 and 1=1显示正常但是select … where id=1 and 1=2显示错误。新方法为随便传参数,看服务器收不收,只要正常接收即可能存在注入点,需要进行测试,若返回404则代表有检测,但也可能能够绕过
不同的注入方式
-
数据注入
数据注入即对提交的参数进行注入,有如下步骤:
- 判断注入参数类型为数字型还是字符串型。数字型一般在查询语句中是如
id=1 and 1=1
,即不需要单双引号进行闭合。如果是字符串型的话考虑查询语句是采用单引号还是双引号进行的闭合。单引号则传递1' and 1=1 #
该参数实际在后端拼接成sql语句为"select * from users where id='1' and 1=1 # '"
,井号用于忽略后面的条件以及忽略不闭合后一个单引号的影响。 双引号闭合则为'select * from users where id="1" and 1=1 # "'
同时,拼接的时候还可能有括号,比如select * from users where id=(('1'))
这时候需要传递id=1')) and 1=1 #
进行引号和括号的闭合。不传递#号也是可以的,将id的后一个引号也闭合掉即可,id=1' and '1' = '1
。总而言之,闭合方式多样,但是目前本人看到的还是以引号和括号间的组合。 - 根据不同的数据传输格式进行注入
- 判断注入参数类型为数字型还是字符串型。数字型一般在查询语句中是如
-
盲注(无回显的情况)
- 报错注入
- 报错注入的条件是请求会返回错误的sql语句信息。报错注入的写法比较多
- 通过floor报错,注入语句如下:
and select 1 from (select count(),concat(version(),floor(rand(0)2))x from information_schema.tables group by x)a);
- 通过ExtractValue报错,注入语句如下:
and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));
- 通过UpdateXml报错,注入语句如下:
and 1=(updatexml(1,concat(0x3a,(select user())),1))
- 通过NAME_CONST报错,注入语句如下:
and exists(selectfrom (selectfrom(selectname_const(@@version,0))a join (select name_const(@@version,0))b)c)
- 通过join报错,注入语句如下:
select * from(select * from mysql.user ajoin mysql.user b)c;
- 通过exp报错,注入语句如下:
and exp(~(select * from (select user () ) a) );
- 通过GeometryCollection()报错,注入语句如下:
and GeometryCollection(()select *from(select user () )a)b );
- 通过polygon ()报错,注入语句如下:
and polygon (()select * from(select user ())a)b );
- 通过multipoint ()报错,注入语句如下:
and multipoint (()select * from(select user() )a)b );
- 通过multlinestring ()报错,注入语句如下:
and multlinestring (()select * from(selectuser () )a)b );
- 通过multpolygon ()报错,注入语句如下:
and multpolygon (()select * from(selectuser () )a)b );
- 通过linestring ()报错,注入语句如下:
and linestring (()select * from(select user() )a)b );
- 通过floor报错,注入语句如下:
- 报错注入的条件是请求会返回错误的sql语句信息。报错注入的写法比较多
- 布尔盲注
用于页面没有回显,且布尔式/逻辑表达式返回false的时候页面无法正常显示的情况- 基础语句:
- count()函数:统计查询结果的数量;
- length(str)函数:返回字符串 str的长度;
- left()函数: left(database(),1)=‘s’ left(a,b)从左侧截取a的前b位,正确则返回1,错误返回0
left((select database()),1)=‘s’ 同样的意思 - regexp : select user() regexp ‘r’; user()的结果是root@localhost,regexp为匹配root的正则表达式
- like : select user() like ‘ro%’; 匹配与regexp相似
- substr(a,b,c): select substr() xxxx; substr(a,b,c)从位置b开始,截取a字符串的c位长度
- mid(a,b,c): select mid(user(),1,2); mid(a,b,c)从位置b开始,截取a字符串的c位长度
- ascii() 将某个字符转化为其ascii值
- limit 0,1:元素索引是从0开始(不是1) 从元素索引位置为1的数据(即第2位)开始输出一个值
- 使用:
and left(database(),1)='s'
and database() regexp 's'
and database() like 's%'
and substr((select database()),1,3)='sec'
and ascii(substr((select database()),1,1))>110
步骤:数据库长度->数据库名/ASCII->数据库表个数->数据库表长度->数据库表名/ASCII->列名的数量1->列名的长度->列名名称/ASCII->字段数量->字段长度->字段名/ASCII
- 基础语句:
- 报错注入
-
延迟注入
延时注入的应用场景是,在我们输入and 1或者and 0的时候,页面的返回无变化,即布尔盲注不好用,这个时候可以通过and sleep(5)来判断一下页面的响应时间,相应时间在五秒多一点的话,说明此处可以使用延时注入- 基础语句
延时注入会用到布尔盲注的所有函数,包括:length()、substr()、ascii()函数 ,然后配合if和sleep函数的使用。
select user from test where id=1 and if((ascii(substr(database(),1,1))=116),sleep(5),1)
- 基础语句
-
dnslog
dns服务器在你发起域名解析请求的时候会生成一个日志,会记录请求的时间,请求的域名以及映射的ip。这个DNS的服务器不是我们的,因此我们需要自己搭建一个DNS服务(麻烦),选择开放的DNS平台,这些平台会爆出DNS解析日志
比如http://dnslog.cn/.
DNSlog注入工具DnsLogInj
原理就是网站无回显,无报错信息,无法延迟的情况下利用sql语句在数据库中的执行,然后用UNC命名格式(通用命名规则)构建DNS服务器地址并进行子域名解析,以dnslog的方式显示出来。
-
利用不同的提交请求方式
- 比如原来的方式是get提交那么可以改为post试一下,如果php那边使用的是request接收则可以正常发送
-
请求头注入
- Cookie注入。看Cookie是否会和数据库进行交互,比如某网站在登录的时候会查询数据库中是否存在了Cookie中的user字段,有则代表该用户已经登录,那么可以在cookie中添加and 1=1绕过登录机制验证。
- 有些数据库可能会把http参数比如用户的浏览器信息写入数据库,此时也可能存在注入。
-
其他注入方式
- 二次注入
该操作可以适用于单引号被转义的情况,先插入指定的可利用语句,比如admin ’ #,那么此时后端查询该新增用户的数据库的语句为'select * from users where user=admin ' # and passwd=......"
。同时,如果进行修改密码,那么将会是update users set password=123 where user =admin ' #
。因此,把admin的密码改为123.此时就可以获得admin的密码 - 堆叠注入
- 用;隔开多个注入语句,比如select * from users,insert into…;
- 用途:在发现一个注入点的时候可以使用多个语句进行注入比如先select后插入用户名和密码。
- 加密的参数
- 知道加密方式后可以通过脚本进行解密,然后配合sqlmap开扫
- 二次注入
不同业务场景下的query方式
比如用户身份校验:在登录的时候采用select查询,注册的时候采用insert,删除账户的时候采用delete,修改密码的时候采用update。
开发方的防御以及绕过
-
Php的防御
- php魔术引号
- 魔术引号开启后编译的时候遇到’都会在其前面加一个/进行转义
- 绕过方式:进行16进制编码(因为mysql支持以16进制形式输入命令)
- Hex编码
- 魔术引号开启后编译的时候遇到’都会在其前面加一个/进行转义
- 判断是否为整数
- php的is_int
- 这时候无法绕过,因为没有存在纯数字型的注入
- 因此,找注入点的时候尽量找字符串型的话成功率高一点
- php的is_int
- 过滤相应的字符串
- php的str_replace
- 绕过:各种大小写,双写(但是作用可能不大)
- php魔术引号
-
Waf的防御
防护软件的机制大多数是识别关键字
绕过方式: -
更改提交方法
- 比如原本使用post方法,考虑改用get方式提交,因为WAF可能会只会对post请求提交的参数进行检测,对get不进行检测。但是还要考虑到使用get方式提交后,会对请求的敏感操作进行拦截,比如database(),因此可能仍然需要对绕过请求方式提交后的参数进行变化以绕过后续的参数检测。
-
大小写混合
- 没什么用,用来绕过waf太低级了。
-
各种大小写,双写(但是作用可能不大)
- 针对正则匹配的时候对大小写不敏感,或者将敏感字段直接替换为空。
-
解密编码
1.有些请求的参数会先进行编码比如base64加密后再传递到服务端,因此进行注入的时候需要知道其加密方式然后对要注入的参数进行加密,在配合sqlmap使用的时候可以考虑使用sqlmap的–temper的进行如Base64编码等编码加密操作。 -
注释符混用
- /* */ 使用data/**/base()逃避关键字的检测,语句效果同database()。
2./*!.....*/
mysql为了保持兼容,会吧一些特有的仅在mysql上用的语句放在/!../中,这样的语句放在其他数据库是不会执行的。 - union select 这两个单词放在一起被拦截的时候(比如2020年安全狗)可以配合单行注释符和空格的形式进行绕过,比如
union #aaa select *
即
union %23 aaa %0A select *
- /* */ 使用data/**/base()逃避关键字的检测,语句效果同database()。
-
等价函数替换
- like代替=
select * from table_name where table_schema like database()
- version()替换为@@version
- datadir()替换为@@datadir
- hex()将一个字符串或者数字转为十六进制格式的字符串,bin()十进制转二进制等同于conv()。ascii(),
- benchmark(重复次数, 执行的函数)代替sleep()
- and && or ||
- like代替=
-
特殊符号混用
-
借助数据库的特性
- 比如上文提到的/*!.. */
-
http参数污染
- 即本来只能传递指定参数个数的请求中传递多个参数,看看服务器是以哪个参数为准。比如传递一个参数的时候服务器可能以最后一个参数为准,这是在php+asp的情况下。所以这时候需要知道服务器信息
-
垃圾数据溢出
- 参数里面写一堆东西
-
Fuzz模糊测试
- 使用一些waf绕过脚本进行操作,进行fuzz模糊测试,比如
http:125.3.3.4/api?id=1+字典
- 使用一些waf绕过脚本进行操作,进行fuzz模糊测试,比如
-
静态资源访问
/api?id=1 union
会被拦截,但是/api/test.txt ? id=1 and union
不会被拦截且能正常访问,因为为了节省WAF资源可能识别的可能只是是api下的id
-
注释符与http参数污染的混用
- 假设在进行参数污染的情况下,像?id=1/**&id=-100 union select 1,2,3 # */,这里有点脑洞,思路是waf识别到union select 但是由于在请求中这两个东西被包含在注释符
/* */
里面,因此waf认为是安全的,但是实际由于进行了http参数污染导致接收到的参数id为select 1,2,3 # */
- 假设在进行参数污染的情况下,像?id=1/**&id=-100 union select 1,2,3 # */,这里有点脑洞,思路是waf识别到union select 但是由于在请求中这两个东西被包含在注释符
-
sqlmap一些相关的设置
- 防止过于频繁的测试封IP:设置延迟注入。
- 设置user-agent爬虫头
- 代理池进行随机ip
- 妙用temper
-
爬虫的白名单
- 伪造user-agent
- 进行敏感操作可能被封ip
-
使用工具时候的一些注意事项
- 有些时候waf会针对一些工具的指纹进行拦截,这时候通过比对自己工具发的数据包和正常请求的数据包,然后进行修改即可
补充点
注意闭合的情况多种多样,甚至可能出现(((" “)))这种三个括号的闭合方式。
根据实战经验,使用’进行闭合没反应也不报错可能闭合方式为”,包括(“”)((“”))