摘要

這份報告探討了大型語言模型(Large Language Model, LLM)產生密碼的內在不安全性,這種現象常被稱為「Vibe Password Generation」 [1] 。與專門設計用於均勻隨機性的密碼學安全偽亂數產生器(cryptographically-secure pseudorandom number generators, CSPRNG)不同,LLM 從根本上就是為了預測 token 而建構,導致其輸出具有可預測性,因此產生的密碼不安全。本文詳細說明了這種可預測性背後的機制,並分析了在大型語言模型(LLM)產生密碼中觀察到的模式,同時探討 LLM 在資安領域的雙重角色:一方面在密碼產生上存在弱點,另一方面卻可能加速攻擊性安全研究,例如 COM 物件利用 [2] 。我們提供了技術見解、程式碼範例和架構圖來說明這些概念,並提出了加強數位安全實務的建議。

從技術實測看Vibe Password:LLM生成密碼的弱點與CSPRNG的不可撼動地位 | 資訊安全新聞

1. 簡介

人工智慧(Artificial Intelligence, AI)的快速進展,尤其是大型語言模型(Large Language Models, LLMs),已導致它們在各個領域被廣泛採用。雖然 LLM 在自然語言處理、程式碼生成和問題解決方面提供了前所未有的能力,但它們應用於安全關鍵任務(例如密碼生成)時,卻帶來了重大的挑戰。所謂的「Vibe Password Generation」概念,即使用者甚至 coding agents 依賴 LLM 來產生密碼,已成為一個令人擔憂的趨勢 [1] 。這種做法本質上存在缺陷,因為 LLM 的核心運作方式——依據已學習模式進行逐步 token 預測——與安全密碼生成的原則背道而馳。安全的密碼需要高熵值(High entropy),這意味著每個字元應從一個大的字元集中以均勻的機率被選取,使其對攻擊者來說無法預測。而這正是密碼學安全偽亂數產生器(CSPRNG)所能達到的 [1]

相反地,大型語言模型(LLM)的設計目的是產生統計上可能的序列,而非真正隨機的序列。它們的輸出 token 取樣過程本質上偏向於模式和常見序列,這顯著降低了產生密碼的實際熵值,儘管對人眼來說它們看似複雜。這份報告將深入探討 LLM 產生輸出與真正隨機序列之間的技術差異,凸顯為何前者用於加密目的極其不安全。此外,我們將探討 LLM 在網路安全領域中矛盾的角色,檢視這些同樣的模型,雖然在安全密碼產生上失敗,但如何能被用來加速攻擊型安全研究,特別是在像 COM 物件利用這樣的領域 [2]

2. LLM 密碼產生的技術分析

2.1. Token 預測 vs. 均勻分佈

使用 LLM 產生密碼的根本缺陷在於它們的運作機制。LLM 的功能是根據前面的 tokens 和她們龐大的訓練資料,預測序列中的下一個 token。這種預測性意味著生成字元的機率分布與均勻分布相差甚遠。一個真正安全的密碼,每個字元位置從允許的字元集中被選取的機率應該相等,使得猜測在計算上不可行。這就是所謂的均勻機率分佈。

相比之下,LLM 會為潛在的下一個 tokens 分配機率,即使透過調整溫度設定來追求更高的隨機性,其底層的分佈仍然是傾斜的。這種偏誤導致了可觀察到的模式,以及對某些字元或序列的偏好,正如對 LLM 產生密碼的實證研究所顯示的那樣 [1] 。例如,某些字元可能以更高的頻率出現,或者特定的字元組合可能更常見,使得這些密碼容易受到統計分析和暴力破解攻擊。

2.2. 熵值估算

密碼強度是以位元組的熵值來量化衡量的,它代表了攻擊者平均需要猜測的密碼可能數量的對數(以 2 為底)。熵值越高表示密碼越強。傳統的密碼熵值計算器通常依賴字元統計、常見密碼格式和字典攻擊來估算強度。雖然這些工具可能因為 LLM 產生密碼的長度和字元多樣性而給出高熵值,但這些估算可能具有誤導性 [1]

原始研究強調了兩種估算熵值的方法:字元統計和 logprobs [1] 。以字元統計為基礎的方法,例如 KeePass 或 zxcvbn 等工具使用的方法,常常高估 LLM 產生密碼的強度,因為它們假設字元是均勻分佈的。然而,當分析 LLM 在產生過程中分配的實際 token 機率(logprobs)時,會發現真正的熵值顯著較低。這種差異之所以出現,是因為 LLM 並非均勻地取樣字元;相反地,它們是根據預測的 likelihood 來選擇 tokens,這就引入了可預測性。

2.3. 案例研究:Claude 4.6 密碼模式

對 LLM 產生密碼的實證測試(Empirical testing),例如 Claude Opus 4.6 發現了特定模式,這些模式使其降低了安全性。原始研究觀察到,在 50 個產生的密碼中,有許多表現出強烈的相似性 [1] 。例如,大量的密碼以大寫字母開頭,通常是 'G',後面緊跟著數字 '7'。此外,某些字元如 'L', '9', 'm', '2', '$', 和 '#' 出現在幾乎所有產生的密碼中,而其他字元則很少被使用。同時也注意到,在任何單一密碼中缺乏重複字元,這個特性在真正隨機序列中統計上不太可能出現,但卻受到 LLM 的偏愛 [1]

為了說明這種可預測性,請考慮以下由 Python 腳本生成的模擬 LLM 風格密碼,該腳本旨在模仿這些觀察到的模式:

Simulating 10 LLM-like passwords:
g7&2Q$!L4Px#mzc%
P7Q$!2#mLx&4(5bD
u7x&!4mL#PQ2$Mg?
u9mQx#4P!$2L&boN
J7!Q#xP&Lm$42qe^
K7L2#4&P!xm$Q[=<
Z4#mPQx&2$!LkqzT
J7L!x$24P&mQ#W[U
t7!P24$#Qm&Lx8IX
G7mQx&4#P2$L!5Yg
    

從模擬輸出中可以明顯看出,以 'G7' 或 'P7' 開頭的模式,以及像 '$', '#', 'L', 'm', 'Q', '2', '&', 'x', 'P', '4', '!' 等字元的頻繁出現,都非常清晰可見。這說明了,即使不知道 LLM 的內部機率,其輸出也表現出足夠的偏誤,可以被統計分析並可能被利用。

3. LLM 在攻擊型安全研究的應用

雖然 LLM 被證明對於產生安全密碼不可靠,但它們在理解、產生和分析程式碼方面的能力,使其成為攻擊型安全研究領域中的強大工具。這呈現出一種雙重性:LLM 可能透過可預測的輸出無意中製造漏洞,但它們也能加速複雜系統中漏洞的發現和利用。例如,LLM 已成功整合到攻擊型安全研究中,用於識別和驗證透過 COM 物件的橫向移動技術 [2]

LLM 在此 context 中的應用涉及自動化傳統上需要大量手動分析和逆向工程的任務。這包括列舉 COM 物件和生成功能性概念驗證(POC)攻擊程式碼。透過利用 LLM,安全研究人員可以顯著減少開發先進攻擊能力(Advanced offensive capabilities)所需的時間和資源,簡化在複雜系統中識別可攻擊路徑的過程 [2]

3.1. 利用 LLM 加速 COM 物件利用

使用 LLM 加速 COM 物件利用的 process 通常包含兩個主要階段:列舉和驗證 [2]

3.1.1. 列舉階段

在列舉階段,一個客製化的 C# 程式會系統性地列出在 Windows 登錄檔中註冊的 COM 類別。目標是識別符合兩個關鍵標準的 COM 物件:它們必須支援分散式元件物件模型(Distributed Component Object Model, DCOM)以進行遠端實例化和互動,並且它們必須暴露可被濫用以在目標系統上執行任意程式碼的方法或介面 [2] 。該工具會提取全面的 metadata,包括方法簽章(Method Signature)、權限級別以及對相關型別程式庫的參考,並將這些資料以結構化的 JSON 格式輸出,供進一步分析。一個關鍵的挑戰是,如何以程式方式判斷一個 COM 類別是否具有直接的程式碼執行能力,通常會關注特定的型別程式庫,例如 stdole ,其中包含像 StdFont 這類因歷史攻擊模式而知名的類別 [2]

3.1.2. 驗證階段

驗證階段利用 LLM,特別是像 GPT-4.1 這樣的模型,來為已識別的橫向移動向量生成功能性的概念驗證(POC)攻擊程式碼。這個 process 高度依賴精心設計的提示詞,並提供相關的範例程式碼來引導 LLM 的生成。提示詞的結構目的在確保 LLM 產生完整且可執行的 C/C++ 程式碼,並遵守特定的技術要求 [2]

LLM 的互動包含了系統提示詞和使用者提示詞。 系統提示詞 設定了 LLM 作為 Windows 安全研究專家的角色,並概述了核心輸出指南,例如生成完整、可運作的 C/C++ 程式碼,包含所有必要的 header、引入和相依性,並遵循與提供的範例相同的技術 [2] 使用者提示詞 則提供了 LLM 生成 POC 所需的具體技術細節。這包括了關於攻擊性質(例如 trapped_object )、攻擊向量(例如 IDispatch → ITypeInfo → StdOle → StdFont → System.Object → Assembly.Load )的資訊,以及概述關鍵步驟的 generation_hints ,例如遠端實例化 COM 物件、取得 IDispatch 介面、導航至包含的型別程式庫、尋找可 hijack 的類別(例如 StdFont ),並最終透過 trapped object 存取 .NET reflection。此外,也指定了 registry_requirements ,例如 AllowDCOMReflection=1 OnlyUseLatestCLR=1 。詳細的 COM 類別資訊,包括 clsid prog_id server_path 和介面細節,也會被提供。即使是簡化版的範例程式碼,其 inclusion 也能顯著提升 LLM 產生變體的可靠性,這證明了具體、可運行的範例能使 LLM 更好地理解所需的模式,並產生更準確且功能完整的程式碼 [2]

4. 架構分析

4.1. LLM 密碼產生流程

LLM 產生密碼的 process,雖然看似直接,但實際上涉及了 tokenization、機率預測和取樣之間複雜的相互作用。由於 LLM 的核心設計是逐步預測最可能的 token,引入了一個根本性的偏誤,損害了安全密碼所需的隨機性。

graph TD A["User Prompt:
"Generate a password""] --> B{LLM Tokenizer} B --> C[Input Tokens] C --> D{LLM Core Model} D --> E[Probabilistic Token
Prediction] E --> F{"Sampling
(e.g., Top-k, Nucleus)"} F --> G[Output Token] G --> H{Password String} H --> I["Insecure Password
(Predictable)"] D --> J[Learned
Patterns & Biases] J --> E

圖 1:LLM 密碼產生工作流程。 此圖說明了 LLM 如何處理產生密碼的請求。核心模型受到從訓練資料中學習到的模式和偏誤的影響,並執行機率性的 token 預測。。然後,取樣機制選擇下一個 token,最終產生一個密碼字串。這個密碼字串儘管看起來複雜,但由於 LLM 的設計,本質上是可預測的。

4.2. 由 LLM 加速的 COM 物件攻擊工作流程

與密碼產生相反,LLM 可以有效地整合到攻擊型安全的工作流程中,以加速漏洞的發現和攻擊。下圖說明了一個簡化的 COM 物件攻擊工作流程,突出了 LLM 可以提供顯著幫助的關鍵點。

graph TD A[Initial Reconnaissance] --> B{"COM Object Enumeration
(C# Tool)"} B --> C[JSON Metadata Output] C --> D{"LLM Prompt Engineering
(System + User Prompts)"} D --> E{"LLM
(e.g., GPT-4.1)"} E --> F[Generate
C/C++ POC Exploit Code] F --> G{Human
Review & Refinement} G --> H[Automated
Testing & Validation] H --> I[Successful
COM Object Exploitation] D --> J["Example Code
(for LLM guidance)"] J --> E

圖 2:LLM 加速的 COM 物件攻擊工作流程。 此圖概述了 COM 物件攻擊的各個階段,強調了 LLM 的角色。在初始偵察和自動化 COM 物件列舉之後,提取的 metadata 和精心設計的提示詞被輸入到 LLM。LLM 接著產生 C/C++ POC 攻擊程式碼,該程式碼在成功攻擊之前會經過人工審查和自動化測試。

5. 程式碼分析

5.1. 模擬 LLM 風格密碼產生

以下的 pseudocode 展示了 VIBE 的核心生成邏輯,強調了系統特定獨特資料與密碼學混合的整合。

  1. /* * VIBE Core Generation Logic
  2. Purpose: Generate high-entropy password using environmental variables
  3. */
  4. string GenerateVibePassword(int length, string userSalt) {
  5. // Step 1: Initialize Entropy Buffer
  6. byte[] entropyPool = new byte[128];
  7. // Step 2: Collect System Telemetry (Variable Info)
  8. // This mirrors techniques used in hardware-based fingerprinting [6]
  9. var systemData = GetSystemTelemetry();
  10. var timestamp = GetHighResolutionTicks();
  11. // Step 3: Cryptographic Mixing
  12. // Using HMAC-SHA256 to mix the user salt with environmental entropy
  13. byte[] mixedKey = PerformHMAC(systemData, userSalt + timestamp);
  14. // Step 4: Map entropy to character set
  15. string charset = "
  16. ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789!@#$%^&
  17. *";
  18. StringBuilder password = new StringBuilder();
  19. for (int i = 0; i < length; i++) {
  20. // Use the mixedKey to select characters
  21. int index = mixedKey[i % mixedKey.Length] % charset.Length;
  22. password.Append(charset[index]);
  23. }
  24. return password.ToString();
  25. }

在此實作中,GetSystemTelemetry() 函式至關重要。正如關於進階 RAT 架構 [3] 的研究所指出的,將輸入金鑰與系統特定資料(如 CPU 識別碼或記憶體偏移量)結合,能顯著增加外部複製的難度。

5.2. 用於利用的 COM 客戶端程式碼結構分析

雖然關於 LLM 加速 COM 物件利用的原始研究並未提供完整的 C/C++ 程式碼,但它透過 LLM 提示詞和生成提示詳細說明了所生成的概念驗證(Proof-of-Concept, POC)程式碼的結構和功能 [2] 。預期生成的程式碼需要處理複雜的 COM 物件實例化、介面查詢和方法呼叫,並利用 DCOM 進行遠端互動。為 LLM 生成程式碼概述的關鍵步驟包括:

  1. // Example structure of LLM-generated C/C++ COM exploitation code
  2. #include <iostream>
  3. #include <objbase.h>
  4. #include <comdef.h>
  5. #include <atlbase.h>
  6. // Necessary headers for DCOM and type information
  7. // ...
  8. int main()
  9. {
  10. CoInitializeEx(NULL, COINIT_MULTITHREADED); // Initialize COM library
  11. CLSID clsidTarget; // CLSID of the target COM object (e.g., FileSystemImage)
  12. // ... Get CLSID from registry data ...
  13. COSERVERINFO csi = {0};
  14. // ... Populate csi for remote DCOM connection ...
  15. // 1. Remote Instantiation of Target COM Object
  16. // Using CoCreateInstanceEx for remote object creation
  17. IUnknown* pUnk = NULL;
  18. HRESULT hr = CoCreateInstanceEx(
  19. clsidTarget,
  20. NULL,
  21. CLSCTX_REMOTE_SERVER,
  22. &csi,
  23. 1,
  24. &IID_IUnknown,
  25. (void**)&pUnk
  26. );
  27. if (FAILED(hr))
  28. {
  29. // Handle error
  30. std::cerr << "Failed to create remote COM object: " << std::hex << hr << std::endl;
  31. CoUninitialize();
  32. return 1;
  33. }
  34. // 2. Obtain IDispatch Interface and Call GetTypeInfo
  35. IDispatch* pDisp = NULL;
  36. hr = pUnk->QueryInterface(IID_IDispatch, (void**)&pDisp);
  37. if (FAILED(hr))
  38. {
  39. // Handle error
  40. std::cerr << "Failed to get IDispatch interface: " << std::hex << hr << std::endl;
  41. pUnk->Release();
  42. CoUninitialize();
  43. return 1;
  44. }
  45. ITypeInfo* pTypeInfo = NULL;
  46. hr = pDisp->GetTypeInfo(0, LOCALE_USER_DEFAULT, &pTypeInfo);
  47. if (FAILED(hr))
  48. {
  49. // Handle error
  50. std::cerr << "Failed to get ITypeInfo: " << std::hex << hr << std::endl;
  51. pDisp->Release();
  52. pUnk->Release();
  53. CoUninitialize();
  54. return 1;
  55. }
  56. // 3. Navigate to Containing Type Library
  57. // This involves parsing ITypeInfo to find referenced type libraries (e.g., stdole)
  58. // ...
  59. // 4. Find Hijackable Class (e.g., StdFont)
  60. // Within the type library, identify classes like StdFont that can be hijacked.
  61. // This often involves manipulating registry keys like 'TreatAs' to redirect instantiation.
  62. // ...
  63. // 5. Use ITypeInfo.CreateInstance to Obtain Trapped Object
  64. // Create an instance of the hijacked class, which will actually be the redirected object.
  65. IUnknown* pTrappedObject = NULL;
  66. hr = pTypeInfo->CreateInstance(NULL, IID_IUnknown, (void**)&pTrappedObject);
  67. if (FAILED(hr))
  68. {
  69. // Handle error
  70. std::cerr << "Failed to create trapped object: " << std::hex << hr << std::endl;
  71. pTypeInfo->Release();
  72. pDisp->Release();
  73. pUnk->Release();
  74. CoUninitialize();
  75. return 1;
  76. }
  77. // 6. Access .NET Reflection via Trapped Object
  78. // Through the trapped object, access .NET reflection capabilities to execute arbitrary code.
  79. // This is the core of the exploit, often involving Assembly.Load and method invocation.
  80. // ...
  81. // Cleanup
  82. if (pTrappedObject) pTrappedObject->Release();
  83. if (pTypeInfo) pTypeInfo->Release();
  84. if (pDisp) pDisp->Release();
  85. if (pUnk) pUnk->Release();
  86. CoUninitialize();
  87. return 0;
  88. }

程式碼說明: 這段 pseudocode 概述了由 LLM 生成的用於 COM 物件利用的 C/C++ 程式的預期結構。它從 COM 初始化開始,接著使用 CoCreateInstanceEx 遠端實例化一個目標 COM 物件。程式碼隨後查詢 IDispatch 介面,這對於透過 GetTypeInfo 取得型別資訊至關重要。接下來的步驟涉及瀏覽型別程式庫,以識別可 hijack 的類別(例如 StdFont ),並使用 ITypeInfo.CreateInstance 來取得一個 trapped object。最後也是最關鍵的一步是,透過這個 trapped object 存取 .NET reflection 功能以實現任意程式碼執行。這種由 LLM 提示詞引導的詳細結構,展示了模型在理解複雜技術要求和模式後,產生複雜攻擊程式碼的能力。

6. 安全建議

鑑於 LLM 產生密碼的內在可預測性,以及 LLM 加速的攻擊型安全技術日益複雜,以下幾點建議對於加強數位安全至關重要:

  • 避免使用 LLM 產生的密碼: 應教育使用者不要直接使用 LLM 產生的密碼。相反地,他們應該依賴專門的密碼管理工具,這些工具利用密碼學安全偽亂數產生器(CSPRNG)來實現真正的隨機性和高熵值 [1]
  • AI 代理的開發者指南: 將 AI 代理整合到其工作流程中的開發者,特別是 coding agents,必須明確指示這些代理使用安全的密碼生成程式庫和方法。這可以防止在應用程式中無意間建立不安全的 credentials [1]
  • AI 模型訓練: AI 實驗室應考慮訓練其模型識別並標記產生密碼的請求,將使用者引導至安全的替代方案,或直接將 CSPRNG 整合到其工具中以處理此類敏感任務。
  • 加強對 AI 生成程式碼的程式碼審查: 在使用 LLM 生成程式碼時,特別是在安全敏感的 context 中,嚴謹的人工審查和自動化安全測試(例如靜態應用程式安全測試 - SAST動態應用程式安全測試 - DAST)至關重要。這有助於識別和減輕由 LLM 引入的漏洞,包括與 COM 物件利用相關的漏洞 [2]
  • 持續監控與威脅情資: 組織應實施對可疑活動的持續監控,特別是與 COM 物件實例化和執行相關的活動。隨時了解有關 LLM 加速攻擊技術的最新威脅情資也至關重要。
  • 最小權限原則: 對所有系統和應用程式套用最小權限原則,限制任何成功攻撃(無論是人為或 AI 驅動)的潛在影響。

7. 結論

大型語言模型的出現帶來了變革性的能力和新穎的安全挑戰。雖然 LLM 擅長需要模式識別和生成的任務,但它們專為 token 預測而設計的根本特性,使其不適合產生密碼學上安全的密碼。從 LLM 產生密碼中觀察到的可預測性,如統計偏誤和重複出現的模式所證明,顯著降低了它們的熵值,並使其容易受到各種攻擊向量的攻擊 [1]

相反地,LLM 的分析和程式碼生成能力已被證明是攻擊型安全研究中的強大資產,加速了像 COM 物件中發現的這類複雜漏洞的發現和利用 [2] 。這種雙重性凸顯了對 LLM 能力和局限性進行細緻理解的關鍵需求。透過遵循密碼管理的最佳實務,對 AI 生成的程式碼實施嚴格的安全措施,並持續適應不斷變化的威脅情勢,我們可以在利用 LLM 的優勢的同時,降低其相關風險。