通过一个简单实例说明SQL注入,通过以下链接中的步骤,已经搭建了一个服务器:
基于Ubuntu的php+MySQL+apache2服务器搭建
验证登录时,采用以下代码:
<html>
<head>
<title>登录验证</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
</head>
<body>
$conn=@mysqli_connect("localhost",'chen','密码') or die("数据库连接失败!");;
mysqli_select_db($conn,"test") or die("您要选择的数据库不存在");
$name=$_POST['username'];
$pwd=$_POST['password'];
$sql="select * from userpassword where user='$name' and password='$pwd' ";
$query=mysqli_query($conn,$sql);
$arr=mysqli_fetch_array($query);
if(is_array($arr)){
echo 'welcome';
}else{
echo '您的用户名或密码输入有误';
}
?>
</body>
</html>
其中,执行的SQL语句为:
select * from userpassword where user='$name' and password='$pwd'
毫无疑问,该语句对于正常输入是能够满足功能需求的。但是当面对一些恶意输入时,会带来一些问题。例如当用户随意输入用户名,而密码为以下密码时:
任意字符+'or’1,例如a’or1
仍然可以成功登陆!
于是有万能密码一说,即:用户名:admin,密码:1’or’1
实际上该例子的本质为恶意改造SQL语句,当输入用户名admin,密码为1’or’1时,实际服务器执行的SQL语句为:
select * from userpassword where user='admin' and password='1' or '1'
由于and优先级高于or,所以user=‘admin’ and password='1’的结果无论是否为真,or运算之后也一定为真。所以实际该语句等同于执行:
select * from userpassword
于是仍然能获得数据库内容,从而验证通过登录成功。
同样的原理,只在用户名输入:’ or 1=1 – (–后有空格),密码不输入或随意输入,也可以登录成功(-- 代表注释后面所有内容)
该部分内容来自:张炳帅,《web安全深度剖析》,电子工业出版社
(1)数字型注入
当输入的参数为整型时,则有可能存在数字型注入漏洞。假设存在一条 URL 为:HTTP://www.aaa.com/test.php?id=1
可以对后台的 SQL 语句猜测为:
SELECT * FROM table WHERE id=1
判断数字型漏洞的 SQL 注入点:
① 先在输入框中输入一个单引号 ’
这样的 SQL 语句就会变为:
SELECT * FROM table WHERE id=1’,
不符合语法,所以该语句肯定会出错,导致脚本程序无法从数据库获取数据,从而使原来的页面出现异常。
② 在输入框中输入 and 1 = 1
SELECT * FROM table WHERE id=1 and 1 = 1
语句正确,执行正常,返回的数据与原始请求无任何差异。
③ 在数据库中输入 and 1 = 2
SELECT * FROM table WHERE id=1 and 1 = 2
虽然语法正确,语句执行正常,但是逻辑错误,因为 1 = 2 为永假,所以返回数据与原始请求有差异。
如果以上三个步骤全部满足,则程序就可能存在数字型 SQL 注入漏洞。
(2)字符型注入
当输入参数为字符串时,则可能存在字符型注入漏洞。数字型与字符型注入最大的区别在于:数字型不需要单引号闭合,而字符型一般需要使用单引号来闭合。
字符型注入最关键的是如何闭合 SQL 语句以及注释多余的代码。 假设后台的 SQL 语句如下:
SELECT * FROM table WHERE username = ‘admin’
判断字符型漏洞的 SQL 注入点:
① 还是先输入单引号 admin’ 来测试
SELECT * FROM table WHERE username = ‘admin’’
页面异常。
② 输入:admin’ and 1 = 1 – 注意:在 admin 后有一个单引号 ‘,用于字符串闭合,最后还有一个注释符 --(两条杠后面还有一个空格!!!)。 SQL 语句变为:
SELECT * FROM table WHERE username = ‘admin’ and 1 = 1 –
页面显示正确。
③ 输入:admin’ and 1 = 2 – 。SQL 语句变为:
SELECT * FROM table WHERE username = ‘admin’ and 1 = 2 –
页面错误。
满足上面三个步骤则有可能存在字符型 SQL 注入。
(3)其他类型 Cookie 注入、POST 注入、延时注入等。这些类型的注入归根结底也是数字型和字符型注入的不同展现形式或者注入的位置不同罢了。 以下是一些常见的注入叫法:
POST注入:注入字段在 POST 数据中 Cookie注入:注入字段在 Cookie 数据中
延时注入:使用数据库延时特性注入 搜索注入:注入处为搜索的地方 base64注入:注入字符串需要经过 base64 加密
3.1. 1 利用错误消息提取信息
SQL Server 数据库是一个非常优秀的数据库,它可以准确地定位错误信息,这对攻击者来说是一件十分美好的事情,因为攻击者可以通过错误消息提取自己想要的数据。
(a)枚举当前表或者列 利用’ having 1 = 1 –
(b)利用数据类型错误提取数据。
3.1.2 获取元数据
SQL Server 提供了大量视图,便于取得元数据。可以先猜测出表的列数,然后用 UNION 来构造 SQL 语句获取其中的数据。参考:三种数据库的 SQL 注入详解
3.1.3 ORDER BY 子句猜测列数
3.1.4 UNION 查询
基于UNION查询的特性:
3.1.5 利用SQL Server 提供的系统函数
SQL Server 提供了非常多的系统函数,利用该系统函数可以访问 SQL Server 系统表中的信息,而无需使用 SQL 查询语句。
3.1.6 存储过程
3.1.7 动态执行
基本思路是相同的,只不过两者使用的函数或者是语句稍有差异。
MySQL不会直接返回错误信息,所以首先从获取元数据开始。
3.2.1 获取元数据
MySQL 5.0 及其以上版本提供了 INFORMATION_SCHEMA,这是一个信息数据库,它提供了访问数据库元数据的方式。(暂不考虑数据库用户权限问题)
a.查询数据库信息:
use information_schema;
select * from SCHEMATA ;
可以看到数据库包含哪些DB及默认编码方式等信息;
SELECT DATABASE();可以获得当前使用的数据库。
b. 获得任一表的列信息:
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS where table_name='userpassword';
得到:
3.2.2 order by查询来判断表的列数
超出会报错!
3.2.3 UNION
该方法首先与order by类似,可以查询表的列数:
还可以查询一些敏感信息:
(待补充!)
3.2.4 内置函数的利用
a.load_file() 函数读文件操作
mysql可以通过该函数读取服务器上的文件(前提是有权限)
当利用该函数出现以下错误时:
ERROR 1 (HY000): Can’t create/write/read to file ‘xxxx’ (Errcode: 13 - Permission denied)
需要检查以下方面:
chmod -R 777 /xxx/xxx...
show variables like "secure_file_priv";
secure_file_priv参数说明
这个参数用来限制数据导入和导出操作的效果,例如执行LOAD DATA、SELECT … INTO OUTFILE语句和LOAD_FILE()函数。这些操作需要用户具有FILE权限。
-----如果这个参数为>空,这个变量没有效果;
-----如果这个参数设为一个目录名,MySQL服务只允许在这个目录中执行文件的导入和导出操作。这个目录必须存在,MySQL服务不会创建它;
-----如果这个参数为NULL,MySQL服务会禁止导入和导出操作。这个参数在MySQL 5.7.6版本引入
同样在配置文件/etc/mysql/mysql.conf.d中,对其进行修改:
secure_file_priv=''
若以上方法仍无法解决,看该链接:解决办法
总结如下:
利用sudo aa-status
查看如图
如果enforce mode中包括mysqld,需要
`sudo vi /etc/apparmor.d/usr.sbin.mysqld`
/var/log/mysql/ r,
/var/log/mysql/* rw,
/var/run/mysqld/mysqld.pid w,
/var/run/mysqld/mysqld.sock w,
/data/ r, 添加要生成文件的路径
/data/* rw,添加要生成文件的路径
将最后两行修改,然后:
sudo /etc/init.d/apparmor reload
至此可解决无法读写的权限问题。
b.into outfile 写文件操作
例如:SELECT '' into oufile 'C:\wwwroot\1.php'
c.连接字符串
MySQL 如果需要一次查询多个数据,可以使用 concat() 或 concat_ws() 函数来完成。
3.2.5 宽字节注入(notice!!!)
宽字节注入是由编码不统一所造成的,这种注入一般出现在 PHP + MySQL中。在 PHP 配置文件 php.ini 中存在 magic_quotes_gpc 选项,被称为魔术引号,当此选项被打开时,使用 GET、POST、Cookie 所接受的 单引号(’)、双引号(")、反斜线() 和 NULL 字符都会自动加上一个反斜线转义。如访问URL:http:/www.xxser.com/Get.php?id=’,显示如下:
单引号’被转义后就变成了’,在 MySQL 中,'是一个合法的字符,也就没办法闭合单引号,所以,注入类型是字符型时无法构成注入。
但是若是输入:%d5’,访问URL:http:/www.xxser.com/Get.php?id=%d5’,显示如下:
可以发现,这次单引号没有被转义,这样就可以突破 PHP 转义,继续闭合 SQL 语句进行 SQL 注入。
13992671355
17395691381
3.2.6 延时注入
在 MySQL 中有一个函数:sleep(duration),这个函数意思是在 duration 参数给定数秒后运行语句
通过对比判断sleep()函数是否执行,进一步判断是否注入成功。