還在用舊版 Apache OFBiz?
1. 簡介
報告提供了對 CVE-2026-45434 的完整技術分析,這是在 Apache OFBiz 中發現的一個重大漏洞。此缺陷允許攻擊者繞過強制密碼變更限制,進而達成遠端程式碼執行 (RCE)。該漏洞源於
LoginWorker.checkLogin()
方法中不當的認證機制,加上由客戶端控制的
requirePasswordChange
旗標,當與未受沙箱保護的
ProgramExport.groovy
端點串聯攻擊時,會升級為完整的系統淪陷。報告將深入探討此漏洞的技術細節、根本原因、攻擊向量、潛在影響及建議的修復方式。此外,也會與其他認證繞過漏洞進行比較,以突顯現代網頁應用程式中常見的安全性陷阱。
2. 漏洞描述
CVE-2026-45434 是一個複合式漏洞,包含兩個主要的認證缺陷,使攻擊者得以繞過預期的密碼變更流程,接著再結合第三個缺陷來達成 RCE [1] 。
-
CWE-287 (不當的認證機制):
Apache OFBiz 的
LoginWorker類別中的checkLogin()方法僅針對特定字串"error"進行驗證。因此,如果login()方法回傳"requirePasswordChange",checkLogin()會錯誤地將此視為成功的認證,允許請求繼續執行 [1] 。 -
CWE-287 (客戶端控制的認證流程):
原先用來強制執行密碼變更的
requirePasswordChange旗標,是直接從 HTTP 請求參數中讀取。這樣的設計選擇讓攻擊者可以將requirePasswordChange=Y注入到任何登入請求中,從而操控認證流程 [1] 。
這兩個缺陷結合後,使得擁有已鎖定帳號(即設有
requirePasswordChange
的帳號)有效認證的攻擊者,能夠繞過密碼變更的強制要求。透過注入
requirePasswordChange=Y
參數以及一組新密碼,攻擊者可以強制進行即時密碼變更,並立即取得所請求端點的存取權限。此影響又因第三個缺陷而進一步擴大:
-
CWE-306 (關鍵功能缺少認證) 與 CWE-94 (對程式碼生成的控制不當):
在 24.09.06 之前的版本中,
ProgramExport.groovy端點缺少適當的權限檢查及 Groovy 沙箱。這使得當建立已認證的 Session 後,可執行任意作業系統指令,將認證繞過漏洞轉變為完整的遠端程式碼執行漏洞 [1] 。
3. 嚴重程度
此漏洞的自我評估 CVSS 3.1 分數為 8.8(高度嚴重),其向量為
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
。使用 CVSS 4.0 評估得到的分數為 8.6(高度嚴重),向量為
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/E:P
[1]
。
評分理由:
- AT:P (攻擊需求:存在): 進行漏洞利用通常需要載入 OFBiz 的示範資料,以提供已知的預設認證。若為使用客製化認證的正式環境,則攻擊者需要先取得認證 [1] 。
- PR:L (所需權限:低): 攻擊者必須擁有至少一個 OFBiz 使用者帳號的有效認證。雖然 OFBiz 預設附帶示範帳號,但這些仍被視為認證 [1] 。
- SC/SI/SA: 高 (後續系統的機密性/完整性/可用性:高): 完整的作業系統層級淪陷使得攻擊者能夠橫向移動、從連線的資料庫中竊取資料,以及中斷伺服器上運作的所有商業程序 [1] 。
4. 受影響產品
所有在 24.09.06 之前的 Apache OFBiz 版本皆受影響,包括整個 18.12.x 分支以及 24.09.x 系列至 24.09.05 版 [1] 。
測試環境 (存在漏洞):
| 欄位 | 值 |
|---|---|
| 產品 | Apache OFBiz 24.09.05 |
| 執行環境 | OpenJDK 17.0.18 (Ubuntu 24.04) |
| 架構 | x86_64 |
| 部署方式 | 容器化 (Ubuntu 24.04, podman) |
| 示範資料 |
透過
./gradlew loadAll
載入
|
| 漏洞檔案 |
LoginWorker.java
(
framework/webapp
)
|
| 漏洞檔案 |
ProgramExport.groovy
(
framework/webtools
)
|
測試環境 (已修補):
| 欄位 | 值 |
|---|---|
| 產品 | Apache OFBiz 24.09.06 |
| 修補 Commit |
6516157
,
771efc4
,
c0592a3
|
| 修補摘要 |
移除客戶端控制的
requirePasswordChange
參數;為 ProgramExport 新增
ENTITY_MAINT
權限檢查與 Groovy 沙箱
|
5. 根本原因分析
5.1. 缺陷 1:
checkLogin()
只針對
"error"
進行驗證
LoginWorker.java
中的
checkLogin()
方法被設計用來將認證工作委派給
login()
方法。然而,其檢查認證失敗的條件判斷是有缺陷的。它只明確檢查
login()
的回傳值是否正好為
"error"
。如果
login()
回傳
"requirePasswordChange"
,此條件會評估為 false,導致
checkLogin()
錯誤地回傳
"success"
[1]
。
存在漏洞的程式碼片段 (LoginWorker.java - checkLogin()):
- // checkLogin — the OR chain that only checks for "error"
- if (UtilValidate.isEmpty(username)
- || (UtilValidate.isEmpty(password) && UtilValidate.isEmpty(token))
- || "error".equals(login(request, response))) {
- // ^^^ "requirePasswordChange".equals("error") → false → falls through
- request.removeAttribute("_LOGIN_PASSED_");
- // ... save previous URL ...
- response.setStatus(HttpStatus.SC_UNAUTHORIZED);
- return "error";
- }
- return "success"; // reached when login() returns "requirePasswordChange"
此邏輯錯誤是繞過機制的核心,因為它允許
login()
的特定非錯誤回傳值被視為成功的認證,從而繞過預期的安全措施
[1]
。
5.2. 缺陷 2:
requirePasswordChange
從攻擊者可控制的請求參數中讀取
第二個關鍵缺陷存在於
login()
方法本身,其中
requirePasswordChange
旗標是由 HTTP 請求參數決定的。這意味著攻擊者可以透過在請求中注入
requirePasswordChange=Y
來直接控制此旗標
[1]
。
存在漏洞的程式碼片段 (LoginWorker.java - login()):
- // login — requirePasswordChange from request parameter (attacker-controlled)
- boolean requirePasswordChange = "Y".equals(request.getParameter("requirePasswordChange"));
- if (!unpwErrMsgList.isEmpty()) {
- request.setAttribute("_ERROR_MESSAGE_LIST_", unpwErrMsgList);
- return requirePasswordChange ? "requirePasswordChange" : "error";
- }
- // After successful credential validation:
- if (requirePasswordChange) {
- Map<String, Object> inMap = UtilMisc.toMap(
- "login.username", username,
- "login.password", password,
- "userLoginId", username,
- "currentPassword", password,
- "newPassword", request.getParameter("newPassword"),
- "newPasswordVerify", request.getParameter("newPasswordVerify"));
- Map<String, Object> resultPasswordChange = dispatcher.runSync("updatePassword", inMap);
- if (ServiceUtil.isError(resultPasswordChange)) {
- return "requirePasswordChange"; // → checkLogin treats as success
- }
- userLogi...
當攻擊者提供有效認證以及
newPassword
和
newPasswordVerify
參數時,密碼變更會被即時處理。由於
login()
接著會回傳
"requirePasswordChange"
(而
checkLogin()
將其視為成功),因此會建立一個完全認證的 Session,攻擊者只需一次 HTTP 請求就能存取目標端點
[1]
。
5.3. 缺陷 3:
ProgramExport
缺少權限檢查與 Groovy 沙箱
使得 RCE 成為可能的最終元件是 OFBiz 24.09.05 中的
ProgramExport.groovy
腳本。此腳本在沒有任何權限檢查或安全沙箱的情況下執行使用者提供的 Groovy 程式碼。這允許已通過認證的攻擊者(透過繞過方式)以完整的 JVM 存取權限執行任意作業系統指令
[1]
。
存在漏洞的程式碼片段 (ProgramExport.groovy):
- // ProgramExport.groovy (24.09.05) — NO permission check, NO sandbox
- String groovyProgram = null // ...
- if (parameters.groovyProgram) {
- groovyProgram = parameters.groovyProgram
- }
- // ...
- GroovyShell shell = new GroovyShell(loader, binding, configuration)
- shell.parse(groovyProgram)
- shell.evaluate(groovyProgram) // arbitrary code execution
缺少
security.hasPermission()
檢查、
SecureASTCustomizer
或任何形式的危險模式阻擋清單,意味著攻擊者可以利用如
Runtime.getRuntime().exec()
或
ProcessBuilder
等功能,在底層作業系統上執行任意指令
[1]
。
5.4. 缺陷組合與攻擊流程
此攻擊鏈依序利用了這三個缺陷。通常,資料庫中標記為
requirePasswordChange=Y
的使用者會被重新導向到專用的 ChangePassword 表單。然而,攻擊者可以透過直接向任意端點(例如
ProgramExport
)提交
requirePasswordChange=Y
來繞過此機制(缺陷 2)。對此端點的
checkLogin()
方法會呼叫
login()
,後者會處理即時密碼變更。由於
checkLogin()
未能拒絕
"requirePasswordChange"
的回傳值(缺陷 1),請求會繼續傳遞給
ProgramExport
。接著,未受沙箱保護的
ProgramExport
(缺陷 3)會執行攻擊者的惡意 Groovy 程式碼,導致 RCE
[1]
。
圖 1:CVE-2026-45434 漏洞利用的順序圖
6. 攻擊情境
擁有 Apache OFBiz 執行個體(通常在預設通訊埠上)網路存取權限的攻擊者,可以利用 CVE-2026-45434 來達成 RCE。攻擊通常如下進行 [1] :
-
攻擊者偽造一個針對會執行認證之端點的 HTTP POST 請求,例如
/webtools/control/ProgramExport。 -
該請求包含一個 OFBiz 使用者帳號的有效
USERNAME與PASSWORD,以及requirePasswordChange=Y、newPassword、newPasswordVerify和一個惡意的groovyProgramPayload。 -
checkLogin()方法被呼叫,接著它會呼叫login()。 -
由於
requirePasswordChange=Y,login()會處理密碼變更並回傳"requirePasswordChange"。 -
由於其有缺陷的邏輯,
checkLogin()將"requirePasswordChange"視為成功的登入,並允許請求繼續執行。 -
ProgramExport端點收到請求,並在沒有任何安全限制的情況下執行惡意的groovyProgramPayload,導致 RCE。
範例惡意請求 (概念性):
- POST /webtools/control/ProgramExport HTTP/1.1
- Host: target.ofbiz.example.com
- Content-Type: application/x-www-form-urlencoded
- Content-Length: [calculated_length]
- USERNAME=demo&PASSWORD=ofbiz&requirePasswordChange=Y&newPassword=newpassword123&newPasswordVerify=newpassword123&groovyProgram=Runtime.getRuntime().exec("touch /tmp/pwned")
此概念性請求展示了攻擊者如何能夠在一次請求中變更密碼並執行一個簡單的指令(建立一個檔案
/tmp/pwned
),從而繞過預期的安全控制。
7. 影響
CVE-2026-45434 的影響非常嚴重,因為它授予未經認證的攻擊者(擁有有效認證)對執行 Apache OFBiz 的底層作業系統的完全控制權 [1] :
- 完整的系統淪陷: 攻擊者可以執行任意指令,從而完全控制伺服器。這包括安裝惡意軟體、建立新的使用者帳號或修改系統設定。
- 資料外洩: 存取作業系統讓攻擊者能夠讀取、修改或刪除 OFBiz 程序可存取的任何資料,包括儲存在連線資料庫中的敏感商業資料。
- 服務中斷: 攻擊者可以終止 OFBiz 程序、刪除關鍵檔案或引入惡意設定,導致應用程式及其使用者服務無法使用。
- 橫向移動: 擁有作業系統層級的存取權限後,攻擊者可以轉向攻擊網路內的其他系統,擴大其入侵範圍。
8. 修復與緩解措施
針對 CVE-2026-45434 的主要修復方式涉及解決上述三個缺陷 [1] :
-
修正
checkLogin()邏輯: 應更新checkLogin()方法,使其能正確識別"requirePasswordChange"為認證失敗,防止在強制要求密碼變更時請求繼續執行。 -
限制
requirePasswordChange參數:requirePasswordChange旗標不應由客戶端提供的 HTTP 請求參數所控制。其狀態應完全由伺服器端管理,通常是從資料庫中取得。 -
保護
ProgramExport.groovy:ProgramExport.groovy端點必須實作強健的權限檢查,並在安全的 Groovy 沙箱內運作,以防止任意程式碼執行。
Apache 在 24.09.06 版本中解決了此漏洞,方法是移除客戶端控制的
requirePasswordChange
參數,並為
ProgramExport
新增
ENTITY_MAINT
權限檢查與 Groovy 沙箱
[1]
。強烈建議使用者立即升級至 Apache OFBiz 24.09.06 或更新版本。
9. 更廣泛的啟示:認證繞過機制
Apache OFBiz 中的 CVE-2026-45434 漏洞與其他認證繞過缺陷有相似之處,突顯了軟體安全中重複出現的樣式。例如,在一份相關的技術分析
[2]
中討論到的 Nginx UI 中的 CVE-2026-33032 漏洞,同樣涉及因中介軟體套用不對稱而導致的認證繞過。在 Nginx UI 的案例中,有兩個端點
/mcp
與
/mcp_message
被用於 Model Context Protocol (MCP) 通訊。雖然
/mcp
受到 IP 白名單與認證中介軟體的雙重保護,但處理所有 MCP 工具呼叫的
/mcp_message
卻只受到 IP 白名單保護,完全遺漏了認證中介軟體
[2]
。
這種架構上的疏忽,使得未經認證的攻擊者能夠呼叫強大的 MCP 工具,例如
nginx_config_modify
或
restart_nginx
,從而完全控制 Nginx 伺服器
[2]
。CVE-2026-45434 與 CVE-2026-33032 的核心問題都在於未能一致地將安全控制套用於所有相關的程式路徑或端點。在 OFBiz 中,
checkLogin()
的邏輯缺陷加上由客戶端控制的參數,產生了一個非預期的繞過路徑。而在 Nginx UI 中,在關鍵端點上明確遺漏了認證中介軟體,也導致了類似的結果。
這些案例凸顯了縱深防禦策略以及對認證與授權機制進行嚴格安全審查的重要性。開發人員必須確保:
- 所有的認證決策在所有進入點都保持一致且強固。
- 永遠不要隱式信任客戶端提供的參數來控制安全關鍵的旗標或流程。
- 關鍵功能和端點受到多層安全機制的保護,包括適當的權限檢查以及對程式碼執行環境的沙箱隔離。
- 預設設定應為安全設計,避免「預設開放 (fail-open)」的行為而意外暴露系統。
此比較強調了認證繞過通常源於細微的邏輯缺陷或安全控制套用的不一致,而不是複雜的加密破解。在程式碼審查和架構設計上保持警覺,對於防止這類漏洞至關重要。
10. 結論
Apache OFBiz 中的 CVE-2026-45434 是一個重大安全漏洞,它結合了不當的認證邏輯、客戶端控制的參數以及未受沙箱保護的程式碼執行環境,從而實現遠端程式碼執行。
LoginWorker.checkLogin()
條件邏輯中的缺陷(將密碼變更要求誤解為成功登入),加上攻擊者能夠透過 HTTP 參數注入
requirePasswordChange
旗標的能力,共同創造了一條繞過預期安全機制的直接路徑。此繞過漏洞再與
ProgramExport.groovy
不受限制的程式碼執行能力串聯攻擊,將導致嚴重的後果,包括完整的系統淪陷與資料外洩
[1]
。
修復措施包括修正認證邏輯、限制客戶端對安全敏感參數的控制,以及為程式碼執行實作強健的權限檢查與沙箱。所有使用者務必立即升級至 Apache OFBiz 24.09.06 或更新版本。此漏洞強烈提醒我們,必須對程式碼進行縝密的審查、在應用程式的所有元件中一致地應用安全原則,以及警惕在安全關鍵環境中隱式信任客戶端提供資料的危險性。此外,這也凸顯了安全設計的開發實務的必要性,特別是在整合動態程式碼執行等強大功能時,以防止在複雜的軟體系統中出現類似的認證繞過與 RCE 漏洞。