SQL注入

原理

SQL注入的原理来说,其实还是很简单的,首先先来一段php代码:

1
2
3
4
5
6
<?php
$id = $_GET['id'];
$sql = "SELECT * FROM users WHERE user_id = '$id'";
$result = mysql_query($sql) or die('<pre>' . mysql_error() . '</pre>' );
$num = mysql_numrows($result);
?>

就是获取用户传过来的id的值,然后到数据库里去找。比如URL栏里输入的是example.com/?id=1,那么构造的查询语句就变成了:

1
SELECT * FROM users WHERE user_id = '1'

因为id是由我们来进行控制的,所以就可以构造诸如' or '1' ='1来进行注入。实际上构造的是下面这样的:

1
SELECT * FROM users WHERE user_id = '' or '1' ='1'

分类

这里的分类主要是针对注入使用的技巧来分的。

  • 回显注入:可以直接在存在注入点的当前页面中获取返回结果。
  • 报错注入:程序将数据库的返回错误信息直接显示在页面中,虽然没有返回数据库的查询结果,但是可以构造一些报错语句从错误信息中获取想要的结果。
  • 盲注:程序后端屏蔽了数据库的错误信息,没有直接显示结果也没有报错信息,只能通过数据库的逻辑和延时函数来判断注入的结果。根据表现形式的不同,盲注又分为based boolean和based time两种类型。
  • 堆叠注入:在一条sql语句之后可以使用分号进行分隔,然后继续输入新的sql语句来进行恶意破坏。

练手平台

https://github.com/Audi-1/sqli-labs 由于这个repo的php支持的版本比较老,所以需要修改下,到sqli-labs/sql-connections这个目录下面,然后使用如下的脚本即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python3
# -- coding=utf-8 --
import os

# first, back up the file
os.popen('cp setup-db.php setup-db.php.bak')

# replace the outdated content
with open('setup-db.php', 'r') as f:
content = f.read()
content = content.replace("mysql_query($sql)","mysqli_query($con, $sql)")
content = content.replace("mysql_error()","mysqli_error($con)")
content = content.replace("mysql_fetch_array($result)","mysqli_fetch_array($result, MYSQLI_BOTH)")
content = content.replace("mysql_fetch_array($result1)","mysqli_fetch_array($result1, MYSQLI_BOTH)")
content = content.replace("mysql_connect($host,$dbuser,$dbpass)","mysqli_connect($host,$dbuser,$dbpass)")
content = content.replace("mysql_real_escape_string($value)","mysqli_real_escape_string($con, $value)")
content = content.replace("mysql_select_db($dbname, $con)","mysqli_select_db($con, $dbname)")

with open('setup-db.php', 'w') as f:
f.write(content)

其实就是把一些过时的函数替换成最新的,即可使用。

或者也可以使用docker来安装,更加简单方便:docker run -dt --name sqli-lab -p 80 acgpiano/sqli-labs:latest安装完成之后看一下是哪个端口映射了容器的80端口即可。

注入点

由上面的分析可知,其实只要你的sql语句中有能让用户决定的内容,而且你做的检查不够,就有可能会被SQL注入。常见的有这些:

  • GET/PUT/header等参数,在原理中已经看到了使用GET进行注入的。
  • 文件名字

MySQL举例

首先有一点是确定的,需要更好的理解SQL注入,肯定是需要你很好的掌握数据库的一些操作,或者理解它的一些概念的;还需要了解那些编写CURD的程序员是怎么编写程序的,通俗地讲就是他们写出来的SQL语句是怎么样的,这样你才能进行注入。下面是通过Mysql版本5.7进行的一些演示。

基础表格

装完MySql之后,通过查看数据库可以发现有以下四个数据库:

  • information_schema
  • mysql
  • performance_schema
  • sys

这里最重要的就是information_schema这个数据库,这个数据库里面存了数据库和数据表的元信息。当然这个数据库里面有非常多的表格,这里主要分析一个叫TABLES的表格。

首先查看一下我们所有的数据库的所有表格:

1
select table_schema from `TABLES`

会找出所有的表格所属的数据库,所以量会很大。

一些tricks

  • 查看版本 select @@version;

如何防范

对付SQL注入的最佳方法应该是:使用静态占位符,使用这个方法可以根治SQL注入。

其他的一些辅助性策略:不显示错误信息(解决报错注入)、检验输入值的有效性(从网上找一些正则表达式过滤掉一些字符)、设置数据库的权限(有效减少损失)。