Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過

酷酷的曉得哥發表於2020-07-01

作者: Badcode and Longofo@知道創宇404實驗室  

時間: 2020年2月9日

原文連結:

英文連結:


前言

2019年9月初我們應急了Nexus Repository Manager 2.x 命令注入漏洞(CVE-2019-5475),其大致的原因和復現步驟在    上公佈了,在應急完這個漏洞之後,我們分析該漏洞的修復補丁,發現修復不完全,仍然可以繞過,本篇文章記錄該漏洞的兩次繞過。雖然早釋出了兩次的修復版本,由於官方第二次更新公告太慢 ,所以現在才發。

幾次更新時間線:

  • CVE-2019-5475(2019-08-09)
  • 第一次繞過,CVE-2019-15588(2019-10-28)
  • 第二次繞過,未分配CVE,更新了公告影響版本(2020-3-25)

注:原始漏洞分析、第一次繞過分析、第二次繞過分析部分主要由Badcode師傅編寫,第二次繞過分析+、最新版本分析主要由Longofo新增。

原始漏洞分析

利用條件

  • 需管理員許可權(預設認證:admin/admin123)

漏洞分析

以下分析的程式碼基於 2.14.9-01 版本。

漏洞點是出現在 Yum Repository 外掛中,當配置 Yum 的   createrepo或者 mergerepo


程式碼層面會跳到   YumCapability 方法中。


Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


在上面 Path of "createrepo" 中設定的值會透過 getConfig().getCreaterepoPath() 獲取到,獲取到該值之後,呼叫 this.validate() 方法


Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


傳進來的 path 是使用者可控的,之後將 path 拼接 --version 之後傳遞給 commandLineExecutor.exec() 方法,看起來像是執行命令的方法,而事實也是如此。跟進 CommandLineExecutor 類的 exec 方法


Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


在執行命令前先對命令解析, CommandLine.parse(),會以空格作為分隔,獲取可執行檔案及引數。

最終是呼叫了 Runtime.getRuntime().exec()執行了命令。

例如,使用者傳入的 command 是 cmd.exe /c whoami,最後到 getRuntime().exec()方法就是 Runtime.getRuntime().exec({"cmd.exe","/c","whoami"})

所以漏洞的原理也很簡單,就是在 createrepo或者 mergerepo路徑設定的時候,該路徑可以由使用者指定,中途拼接了 --version字串,最終到了 getRuntime.exec()執行了命令。

漏洞復現

Path of "createrepo"裡面傳入 payload。


Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


Status 欄可以看到執行的結果


Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


第一次繞過分析

第一次補丁分析

官方補丁改了幾個地方,關鍵點在


Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


常規做法,在執行命令前對命令進行過濾。新增加了一個 getCleanCommand() 方法,對命令進行過濾。


Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


allowedExecutables是一個 HashSet,裡面只有兩個值, createrepomergerepo。先判斷使用者傳入的 command是否在 allowedExecutables裡面,如果在,直接拼接 params--version直接返回。接著對使用者傳入的 command進行路徑判斷,如果是以nexus的工作目錄( applicationDirectories.getWorkDirectory().getAbsolutePath())開頭的,直接返回 null。繼續判斷,如果檔名不在 allowedExecutables則返回 null,也就是這條命令需要 以 /createrepo或者 /mergerepo結尾。都透過判斷之後,檔案的絕對路徑拼接 --version  返回。

第一次補丁繞過

說實話,看到這個補丁的第一眼,我就覺得大機率可以繞。

傳入的命令滿足兩個條件即可,不以nexus的工作目錄開頭,並且以 /createrepo或者 /mergerepo結尾即可。

看到補丁中的 getCleanCommand()方法, new File(command)是關鍵, new File()是透過將給定的路徑名字串轉換為抽象路徑名來建立新的File例項。 值得注意的是,這裡面路徑字串是可以使用空格的,也就是

String f = "/etc/passwd /shadow";
File file = new File(f);

這種是合法的,並且呼叫 file.getName() 取到的值是 shadow 。結合這個特性,就可以繞過補丁裡面的判斷

String cmd = "/bin/bash -c whoami /createrepo";
File file = new File(cmd);
System.out.println(file.getName());
System.out.println(file.getAbsolutePath());

執行結果



可以看到, file.getName()的值正是 createrepo,滿足判斷。

第一次繞過測試

測試環境
  • 2.14.14-01 版本
  • Linux
測試步驟

Path of "createrepo"裡面傳入 payload。


Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


Status 欄檢視執行的結果


Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


可以看到,成功繞過了補丁。

在 Windows 環境下面就麻煩點了,沒有辦法使用 cmd.exe /c whoami這種形式執行命令了,因為 cmd.exe /c whoami經過 new File()  之後變成了 cmd.exe \c whoami,後面是執行不了的。可以直接執行exe,注意後面是還會拼接 --version的,所以很多命令是執行不了的,但是還是有辦法利用能執行任意exe這點來做後續的攻擊的。

第二次繞過分析

第二次補丁分析

在我提交上述繞過方式後,官方修復了這種繞過方式,看下官方的


getCleanCommand()  方法中增加了一個 file.exists()判斷檔案是否存在。之前的 /bin/bash -c whoami /createrepo這種形式的肯定就不行了,因為這個檔案並不存在。所以現在又多了一個判斷,難度又加大了。難道就沒有辦法繞過了?不是的,還是可以繞過的。

第二次補丁繞過

現在傳入的命令要滿足三個條件了

  • 不以nexus的工作目錄開頭
  • /createrepo或者 /mergerepo結尾
  • 並且這 createrepo或者 mergerepo這個檔案存在

看到 file.exists()我就想起了 php 中的   file_exists(),以前搞 php 的時候也遇到過這種判斷。有個系統特性,在 Windows 環境下,目錄跳轉是允許跳轉不存在的目錄的,而在Linux下面是不能跳轉不存在目錄的。

測試一下

Linux


Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


可以看到, file.exists()返回了 false

Windows


Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


file.exists()返回了 true

上面我們說了 new File(pathname),pathname 是允許帶空格的。在利用上面WIndows環境下的特性,把cmd設定成   C:\\Windows\\System32\\calc.exe \\..\\..\\win.ini

Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


經過 parse()  方法,最終到 getRuntime.exec({"C:\\Windows\\System32\\calc.exe","\\..\\..\\win.ini"}),這樣就能執行 calc了。

在上面這個測試 win.ini是確實存在的檔案,回到補丁上面,需要判斷 createrepo或者 mergerepo存在。首先從功能上來說,createrepo 命令用於建立 yum 源(軟體倉庫),即為存放於本地特定位置的眾多rpm包建立索引,描述各包所需依賴資訊,並形成後設資料。也就是這個 createrepo在Windows下不太可能存在。如果這個不存在的話是沒有辦法經過判斷的。既然伺服器內不存在 createrepo,那就想辦法建立一個,我首先試的是找個上傳點,嘗試上傳一個 createrepo,但是沒找到上傳之後名字還能保持不變的點。在 Artifacts Upload處上傳之後,都變成 Artifact-Version.Packaging這種形式的名字了, Artifact-Version.Packaging這個是不滿足第二個判斷的,得以 createrepo結尾。

一開始看到 file.exists()就走進了思維定勢,以為是判斷檔案存在的,但是看了官方的文件,發現是判斷檔案或者目錄存在的。。這點也就是這個漏洞形成的第二個關鍵點,我不能建立檔案,但是可以建立資料夾啊。在 Artifacts Upload上傳Artifacts 的時候,可以透過 GAV Parameters來定義。


  Group 設定為 test123 Artifact 設定為 test123 Version 設定成 1 ,當上傳 Artifacts 的時候,是會在伺服器中建立對應的目錄的。對應的結構如下


Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


如果我們將 Group設定為 createrepo,那麼就會建立對應的 createrepo目錄。

結合兩個特性來測試一下

String cmd = "C:\\Windows\\System32\\calc.exe \\..\\..\\..\\nexus\\sonatype-work\\nexus\\storage\\thirdparty\\createrepo";
File file = new File(cmd);
System.out.println(file.exists());
System.out.println(file.getName());
System.out.println(file.getAbsolutePath());

Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過

可以看到, file.exists()返回了true, file.getName()返回了 createrepo,都符合判斷了。

最後到 getRuntime()裡面大概就是 getRuntime.exec({"C:\Windows\System32\notepad.exe","\..\..\..\nexus\sonatype-work\nexus\storage\thirdparty\createrepo","--version"})

是可以成功執行 notepad.exe的。(calc.exe演示看不到程式哈,所以換成Notepad.exe)

第二次繞過測試

測試環境
  • 2.14.15-01 版本
  • Windows
測試步驟

Path of "createrepo"裡面傳入 payload。

Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過

檢視程式, notepad.exe 啟動了



可以看到,成功繞過了補丁。

第二次繞過分析+

經過Badcode師傅第二次繞過分析,可以看到能成功在Windows系統執行命令了。但是有一個很大的限制:

  1. nexus需要安裝在系統盤
  2. 一些帶引數的命令無法使用

在上面說到的 Artifacts Upload上傳處是可以上傳任意檔案的,並且上傳後的檔名都是透過自定義的引數拼接得到,所以都能猜到。那麼可以上傳自己編寫的任意exe檔案了。

第二次繞過分析+測試

測試環境
  • 2.14.15-01 版本
  • Windows
測試步驟

導航到 Views/Repositories->Repositories->3rd party->Configuration,我們可以看到 預設本地儲存位置的絕對路徑(之後上傳的內容也在這個目錄下):

Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


導航到 Views/Repositories->Repositories->3rd party->Artifact Upload ,我們可以上傳惡意的exe檔案:


Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


該exe檔案將被重新命名為 createrepo-1.exe (自定義的引數拼接的):



同樣在 Path of "createrepo" 裡面傳入 payload(這時需要注意前面部分這時是以nexus安裝目錄開頭的,這在補丁中會判斷,所以這裡可以在最頂層加 ..\ 或者弄個虛假層 aaa\..\ 等)


Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


可以看到createrepo-1.exe已經執行了:


Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


最新版本分析

最新版本補丁分析

第二次補丁繞過之後,官方又進行了修復,官方 主要如下

Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


刪除了之前的修復方式,增加了 YumCapabilityUpdateValidator 類,在 validate 中將獲取的值與properties中設定的值使用 equals 進行絕對相等驗證。這個值要修改只能透過 sonatype-work/nexus/conf/capabilities.xml


Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


最新版本驗證

前端直接禁止修改了,透過抓包修改測試:

YumCapabilityUpdateValidator.validate 斷到


Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 兩次繞過


可以看到這種修復方式無法再繞過了,除非有檔案覆蓋的地方覆蓋配置檔案,例如解壓覆蓋那種方式,不過沒找到。

不過 Artifacts Upload那裡可以上傳任意檔案的地方依然還在,如果其他地方再出現上面的情況依然可以利用到。




來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69912109/viewspace-2701631/,如需轉載,請註明出處,否則將追究法律責任。

相關文章