经典漏洞-SQL注入
本文最后更新于:2022年8月10日 下午
1 原理 & 类型
1.1 SQL 注入原理
基本的SQL查询语句如下所示,其中 $id
为提交的变量,当这个参数没有做非常严格的过滤,通过对 $id
的构造,数据库能输出攻击者想要获取的内容,就造成了SQL注入的漏洞。
1 |
|
Tips:
$id
参数可能存在于任何参数传递的位置:
- GET/POST/PUT/DELETE 参数
- X-Forwarded-For(详细内容待补充)
- 查询、搜索
- 页码、选择菜单
- 账号、密码
- 文件名
1.2 SQL注入的类型(按技巧分类)
- 盲注
- 布尔盲注:只能从应用返回中推断语句执行后的布尔值
- 时间盲注:应用没有明确的回显,只能使用特定的时间函数来判断
- 报错注入:应用会显示全部或者部分的报错信息
- 堆叠注入:有的应用可以加入 ; 后一次执行多条语句
- 联合查询注入:
2 检测手段
2.1 注入点是否存在 & 数据类型
2.1.1 添加单引号
修改参数 $id
的值,在其中加入单引号,将SQL查询内容不闭合,如下所示,页面可能会显示部分或者全部数据库报错内容(如果没有被过滤),则存在注入点,且注入类型为字符型。
1 |
|
2.1.2 添加运算符
传入的参数并没有显示在报错信息中,则传入的参数前面可能没有引号包裹,于是注入的类型可能是数字型。可以通过在id后添加运算符,如-1,+2,来观察页面的内容是否会变成对应的 id 来判断是否存在注入点,此时注入类型为数字型。
如下所示,将id变为1+1,+号在URL中会被编码为空格,所以我们需要提前将+号进行URL编码,变为:%2b
1 |
|
2.2 注入类型
2.2.1 堆叠注入
分号 ;
在sql中意味着一条语句的结束,可以在分号后继续构造第二条语句,数据库依然会继续执行,这样就会存在堆叠注入。
Tips: 堆叠注入存在局限性
- 结果不会在页面显示
- 接口或者数据库引擎不支持
1 |
|
2.2.2 报错注入
2.2.2.1 联合查询注入
表的字段数量判断
在mysql、mariadb、oracle等数据库中,可以使用数字来代替对应字段(列)的名称,如果数据库中没有对应的字段(列),就会报错。使用 order by 让 $id
按照不同的字段排序,直到 order by 后的数字超出了字段数量,则会返回报错信息。
1 |
|
显示字段内容判断
union 联合查询时,当前面的语句为假,则后面的查询结果会显示到相应的位置。则可以通过在前面查询语句中添加 and 1=2 使语句变假,来判断页面显示的内容为表中的哪几个字段。然后使用database()和version()替换相应的字段位置就可以查询数据库名称和版本号。
1 |
|
2.2.2.2 group by重复建冲突报错
1 |
|
2.2.2.3 xpath语法错误
xpath 语法报错注入,主要利用extractvalue()和updatexml()函数。
使用方法:
1 |
|
2.2.3 盲注
2.2.3.1 布尔盲注
判断数据库名长度
改变length(database())=的数值,当条件为真时,页面就会正常显示
1 |
|
判断数据库名
使用 sbustr 函数一位一位截取数据库名,如果为真,则页面正常显示
1 |
|
2.2.3.2 时间盲注
当页面没有布尔状态时,可以通过观察页面响应时间是否变长来判断输入的条件是否正确。
1 |
|
3 数据库判断
3.1 常见前端与数据库类型匹配
前端语言 | 数据库 |
---|---|
sap | sql server,Access |
.net | sql server |
php | PostgreSQL, Mysql |
java | mysql, oracle |
3.2 数据库标志性信息
数据库 | 信息查询 |
---|---|
oracle | select@@version – |
sql server | select banner from v$version |
mysql | select @@version, version() –, lenth(user)>0 |
postgreSQL | select version() – |
3.3 字符串处理方式
数据库 | 处理方式 |
---|---|
sql server | id=1 and ‘a’+’b’=’ab’ – |
mssql | id=1 and ‘a’+’b’=’ab’ |
mysql | id=1 and ‘a’+’b’=’ab’, ‘ab’=concat(‘a’,’b’) |
oracle | id=1 and ‘a’+’b’=’a’||’b’, ‘ab’=concat(‘a’,’b’) |
postgresql | id=1 and ‘a’+’b’=’a’||’b’, ‘ab’=concat(‘a’,’b’) |
3.4 特殊符号与注释
- null 和 %00 是Access支持的注释。
- # 是MySQL中的注释符,返回错误说明该注入点可能不是MySQL,另外也支持’– ‘,和/* */注释(注意mysql使用– 时需要后面添加空格)
- – 和 /* */ 是Oracle,SQL server和MSSQL支持的注释符,如果正常,说明可能就是这仨了。
- ; 是子句查询标识符,在Oracle中不支持多行查询,返回错误,很可能是Oracle数据库。
1 |
|
4 绕过
4.1 关键字过滤
- 第一种是替换关键字,这种方式可以直接通过双写关键词等方式来进行绕过;
- 第二种是关键词直接拦截,这种情况下可以通过变换函数等方式来进行绕过。
4.2 空格过滤
- 使用/**/即SQL语句中的注释语句来绕过。
- 使用双写URL编码绕过,例如(%20 → %2%200)。
- 通过特殊符号(反引号、加号、括号等等),绕过空格。(在MySQL中,括号是用来包围子查询的。因此,任何可以计算出结果的语句,都可以用括号包围起来。)
- 通过科学计数法绕过:where id=0e1union select 1,2
- Fuzz所有字符,查找可替代空格的字符例如 %09 %0a %0b %0c %0d。
URL编码 | 符号 |
---|---|
%09 | TAB 键(水平) |
%0a | 新建一行 |
%0c | 新的一页 |
%0d | return 功能 |
%0b | TAB 键(垂直) |
%a0 | 空格 |
数据库 | 空白字符 |
---|---|
SQLite3 | 0A 0D 0C 09 20 |
MYSQL5 | 09 0A 0B 0C 0D A0 20 |
PosgresSQL | 0A 0D 0C 09 20 |
Oracle 11g | 00 0A 0D 0C 09 20 |
MSSQL | 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 |
4.3 逗号过滤/拦截
拦截逗号后,意味着大部分的联合查询注入都失效,这个时候可以尝试进行盲注。
substring和if语句都用到了逗号,因此无法使用。
substring可以用substr()函数来进行代替,if语句可以通过and语句来进行代替。
4.4 单引号拦截/过滤/转义
当引号存在过滤和转义的情况下,可以这样处理:
4.4.1 利用编码特性绕过。(宽字节注入)
比较典型的使用编码就是GBK编码:使用前面的字符吃掉后面的字符(斜杆)。
待补充
4.4.2 利用反斜杠\绕过
待补充
4.4.3 将 utf-8 转换为 utf-16 或 utf-32
待补充
4.5 数字被过滤/拦截绕过
数字被屏蔽时,用响应函数结果来构造。
- ceil() :为向上取整函数
- floor():为向下取整函数
- ! ~~:起到取反的效果
- pi() :π值
- version():利用当前版本号进行数字的构造
- false:值为0
- true:值为1
4.6 等号过滤
- 当等于号=呗过滤时,可以使用:like,in 等字符替换
1 |
|
4.7 其他关键字过滤/拦截绕过
关键字被过滤时,可以通过双写或大小写、其他编码例如十六进制、URL编码来进行绕过:
关键字 | 双写 | 大小写 | 其他编码 | 符号 |
---|---|---|---|---|
and | aandnd | and | an\x64 | && |
select | selselectect | SelECt | selec\x74 | |
or | oorr | oR | o\x72 | || |
union | uniunionon | uNioN | unio\x6e |