Sql注入(手工注入思路、绕过、防御)

一、Sql注入思路

1、判断注入点

GET参数、POST参数、以及HTTP头部等,包括CookieRefererXFF(X-Forwarded-for)UA等地方尝试插入代码、符号或语句,尝试是否存在数据库参数读取行为,以及能否对其参数产生影响,如产生影响则说明存在注入点。

1)、GET 注入

提交数据的方式是 GET,注入点的位置在 GET 参数部分。例如有这样的一个URL:http://xxx.com/news.php?id=1,id是注入点。

2)、POST 注入

使用 POST 方式提交数据,注入点位置在 POST 数据部分,通常发生在表单中。

3)、HTTP 头部注入

注入点在 HTTP 请求头部的某个字段中。比如存在 User-Agent 字段中,Cookie 字段中等。

2、判断数据库类型

判断网站使用的是哪个数据库,常见数据库如:MySQL、MSSQL(SQLserver)、Oracle、Access、PostgreSQL、BD2等等。

1)、端口扫描

如果可以对主机进行端口扫描,可以根据是否开启对应端口,来大概判断数据库类型。

数据库类型默认端口号
Oracle1521
SQL Server1433
MySQL3306
PostgreSql5432
2)、网站类型与数据库的关系
网站类型数据库类型
aspSQL Server,Access
.netSQL Server
phpMysql,PostgreSql
javaOracle,Mysql
3)、根据数据库特有的系统表判断
数据库类型特有的系统表
OracleSYS.USER_TABLES
SQL ServerSYSOBJECTS
MySQL(MySQL版本在5.0以上)INFORMATION_SCHEMA.TABLES
AccessMSYSOBJECTS
http://127.0.0.1/test.php?id=1 

例如,我们可以根据以上URL,拼接Sql语句来查询数据库特有的系统表数据条目,根据其是否返回查询结果来判断数据库类型:

http://127.0.0.1/test.php?id=1 and (select count(*) from sysobjects)>0 and 1=1
4)、根据返回的错误判断

如果我们在进行Sql注入的时候,可以看到数据库的错误信息,也可以通过返回的错误信息来判断数据库的类型

3、判断参数数据类型(闭合方式)

通过+1-1and 1=1and 1=2、注释符等与其各种变种,如与各种符号结合的and 1=1and '1'='1等等判断参数数据类型。先判断是否是整型,如果不是整型则为字符型,字符型存在多种情况,需要使用单引号'、双引号"、括号()多种组合方式进行试探。
理论上来说,只有数值型和字符型两种注入类型。SQL的语法,支持使用一个或多个括号包裹参数,使得这两个基础的注入类型存在一些变种,例如:数值型+括号、单引号字符串+括号、双引号字符串+括号等,有时可能会有多个括号包裹。

单引号'注入不成功的时候尝试1%df' 是否为宽字节注入

在这里插入图片描述

4、判断数据库语句过滤情况,从而选择相应的注入方式(联合查询、报错、盲注…)

正常输入Sql语句,通过查看回显来判断语句是否被过滤。如果order by被过滤则尝试绕过,如果无法绕过就无法得到列数,这时就无法使用联合查询注;如果页面没有显示位,同样无法使用联合查询注入;如果没有报错信息返回,则无法使用报错注入。

1)、联合查询注入

联合查询的前提是需要通过order by获取到列数,且页面上有显示位。判断显示位、获取所有数据库名、获取指定数据库所有表名、获取指定数据库指定表中所有字段名、获取具体数据。

2)、报错注入

报错注入的前提是页面会显示数据库报错信息。得到报错信息、获取所有数据库名、获取指定数据库所有表名、获取指定数据库指定表中所有字段名、获取具体数据。

3)、布尔盲注

Sql注入语句执行之后,可能由于网站代码的限制或者Apache等解析器配置了不回显数据,造成数据不能回显到前端页面。此时,我们需要利用一些方法进行判断或者尝试。当页面返回结果只有正确和错误这两种情况时,可以使用布尔盲注。

4)、时间盲注

当页面上没有显示位,也没有输出SQL语句执行错误信息。 正确执行和错误执行的返回界面一样,但是加入sleep语句之后,页面的响应速度会明显变慢。此时需要使用时间盲注。因此实际情况下手工时间盲注会花费大量时间,不符合实际,需要用工具或者脚本来进行注入。

5)、DNSlog带外注入

当我们进行注入时,如果页面无回显,且无法进行时间注入,那么就可以利用DNS解析这个通道,把查询到数据通过通道带出去,可以使用DNSlog带外注入。

二、绕过方式

后端语言可能对我们拼接的sql语句,进行过滤,常见的有关键字过滤,一些符号过滤以及函数过滤等。

1、过滤关键字

  • 分割关键字
    最常用的绕过方法就是使用/**/<>分割关键字

    sel<>ect
    sel/**/ect
    
  • 双写绕过
    根据过滤程度,有时候还可以用双写绕过

  • 编码绕过
    有时候可以尝试使用URL编码绕过、16进制编码绕过、ASCII编码绕过

  • 大小写绕过

2、过滤逗号

  • 简单注入可以使用join绕过
    # 原语句:
    union select 1,2,3
    # join语句:
    union select * from (select 1)a join (select 2)b join (select 3)c
    
  • 对于盲注的几个函数substr()、mid()、limit
    # substr和mid()可以使用from for的方法解决
    # 原语句
    substr(str, pos, len)                 # 截取字符串,从pos位置开始,截取字符串str的len长度
    mid(str, pos, len)                    # 截取字符串,从pos位置开始(从1开始),截取字符串str的len位
    # from for语句
    substr(str from pos for len)          # 在str中从第pos位截取len长的字符
    mid(str from pos for len)             # 在str中从第pos位截取len长的字符
    
    # limit可以用offset的方法绕过
    limit 1 offset 1                      # "limit 1"表示只返回1行结果,"offset 1"表示从结果集中跳过1行数据,所以表示从结果集中选择第2行,并返回该行作为结果
    

3、过滤空格

  • 使用注释/**/绕过
    # 例如sql查询
    select user() from security
    # 我们用注释替换空格,就可以变成:
    select/**/user()/**/from/**/security
    
    如果在sqlmap中,使用注释绕过,对于mysql数据库需要使用--tamper参数指定注释绕过空格的脚本:
    python sqlmap.py -u http://127.0.0.1/index.php?id=1 --batch --dbs --tamper=space2comment.py
    
  • 使用括号绕过
    # 例如sql查询
    select user() from user where 1=1 and 2=2
    # 如何把空格减到最少?
    # 观察到user()可以算值,那么user()两边要加括号,1=1和2=2可以算值,也加括号,去空格,变成:
    select(user())from user where(1=1)and(2=2)
    # 数据库名两边的空格,通常是由程序员自己添加,我们一般无法控制。所以上面就是空格最少的结果。
    # 在time based盲注中是一个非常实用的技巧
    http://www.xxx.com/index.php?id=(sleep(ascii(mid(user()from(2)for(1)))=109))
    
    在这里插入图片描述

4、过滤等号

当注入语句中把=过滤了,可以使用使用like、rlike、regexp进行绕过

like:不加通配符的like执行的效果和=一致,所以可以用来绕过。
rlike:模糊匹配,只要字段的值中存在要查找的 部分 就会被选择出来,用来取代=时,rlike的用法和上面的like一样,没有通配符效果和=一样
regexp:MySQL中使用REGEXP操作符来进行正则表达式匹配

在这里插入图片描述

也可以用<>来绕过

在这里插入图片描述

5、过滤引号

  • 十六进制编码绕过
    一般会使用到引号的地方是在最后的 where 子句中,比如:

    select * from t_student where name = "admin";
    

    当引号被过滤了的话, 'admin' 或者 "admin" 就没法用了,我们可以用 admin 的16进制 0x61646d696e 代替。

    在这里插入图片描述

  • ASCII编码绕过
    在这里插入图片描述

  • URL编码绕过
    使用URL编码绕过的前提条件是后端在处理接收到的参数进行了URL解码,并且对该URL解码是在使用了过滤函数之后才可以使用URL编码绕过。

6、过滤大于小于号

在使用盲注的时候,会用到二分法来比较操作符来进行操作,如果过滤了比较操作符,那么就需要使用到greatest()lease()来进行绕过。greatest()函数返回最大值,leaset()函数返回最小值。

# 原语句
select * from users where id=1 and ascii(substring(database(),0,1))>64;
# 过滤<、>符号后,使用greatest()后的语句
select * from users where id=1 and greatest(ascii(substring(database(),0,1)),64)=64;

7、等价函数绕过

  • hex()、bin()函数被过滤,可以使用ascii()函数

  • sleep()函数被过滤,可以使用benchmark()函数,函数benchmark(count, expression),参数count表示要执行表达式的次数,expression是要执行的表达式或语句,返回表达式的执行时间。

  • substring()、substr()mid()函数被过滤时,可以使用strcmp()函数strcmp(str1, str2)函数,用于比较两个字符串是否相等。如果 str1 等于 str2,则返回 0。 如果 str1 小于 str2,则返回一个负数。如果 str1 大于 str2,则返回一个正数。

    strcmp(left('password',1), 0x69) = 1 
    strcmp(left('password',1), 0x70) = 0 
    strcmp(left('password',1), 0x71) = -1
    
  • CONCAT_WS()、CONCAT()GROUP_CONCAT() 是用于字符串连接和聚合的函数。CONCAT_WS(separator, str1, str2, ...)函数用于将多个字符串连接在一起,并使用指定的分隔符进行分隔。CONCAT(str1, str2, ...)函数用于将多个字符串连接在一起。与 CONCAT_WS()不同,CONCAT()函数不会插入分隔符。

三、Sql注入的防御方式

1、限制数据类型

Java、C#等强类型语言几乎可以完全忽略数字型注入,例如:请求ID为1的新闻页面,其URL:http://www.secbug.org/news.jsp?id=1,在程序代码中可能为:

int id = Integer.parseInt(request.getParameter("id"));
//接收 ID 参数,并转换为 int 类型
News news= newsDao.findNewsById(id);			//查询新闻列表

攻击者想在此代码中注入是不可能的,因为程序在接收ID参数后,做了一次数据类型转换,如果ID参数接收的数据是字符串,那么在转换时将会发生Exception。因此,数据类型处理正确后,可以防止数字型注入。像PHP、ASP,并没有强制要求处理数据类型,这类语言会根据参数自动推导出数据类型,假设 ID=1,则推导ID的数据类型为Integer、ID=str,则推导 ID 的数据类型为string,这一特点在弱类型语言中是相当不安全的。如:

$id = $_GET['id'];
$sql ="select*from news where id = $id;";
$news = exec($sql);

攻击者可能把id参数变为 1 and 1=2 union select username,password from users;--,这里并没有对$id 变量转换数据类型,PHP自动把变量$id 推导为 string类型,带入数据库查询,造成 SQL注入漏洞。防御数字型注入相对来说是比较简单的,只需要在程序中严格判断数据类型即可。如:使用is_numeric()、ctype_digit()等函数判断数据类型,即可解决数字型注入。

2、特殊字符转义

通过加强数据类型验证可以解决数字型的 SQL 注入,字符型却不可以,因为它们都是 string类型,你无法判断输入是否是恶意攻击。那么最好的办法就是对特殊字符进行转义。因为在数据库查询字符串时,任何字符串都必须加上单引号。既然知道攻击者在字符型注入中必然会出现单引号等特殊字符,那么将这些特殊字符转义即可防御字符型SQL注入。例如,在PHP中最基本的就是自带的magic_quotes_gpc函数

3、使用预编译语句,绑定变量

使用预编译相当于是将数据于代码分离的方式,把传入的参数绑定为一个变量,用?表示,攻击者无法改变 Sql 的结构。

String query="select password from users where username='?' ";

在这个例子中,即使攻击者插入类似 admin' or 1=1# 的字符串,如果不做处理直接带入查询,那么query则变成了

query="select password from users where username='admin' or 1=1 ";

闭合了后面的引号,从而执行了恶意代码。而预编译则是将传入的 admin' or 1=1# 当做纯字符串的形式作为 username 执行,避免了上面说到的 Sql 语句中的拼接闭合查询语句等过程,可以理解为字符串与Sql语句的关系区分开。username 此时作为字符串不会被当做之前的SQL语句被带入数据库执行,避免了类似sql语句拼接、闭合等非法操作。

4、框架技术

随着技术发展,越来越多的框架渐渐出现,Java、C#、PHP等语言都有自己的框架。至今,这些框架技术越来越成熟、强大,而且也具有较高的安全性。
在众多的框架中,有一类框架专门与数据库打交道,被称为持久层框架,比较有代表性的有Hibernate、MyBatis、JORM等。

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值