CVE-2019-19781 遠程代碼執行漏洞深入分析
來源 https://paper.seebug.org/1117/
環境搭建
虛擬機下載地址:https://www.citrix.com/downloads/citrix-gateway/
然后選擇下載NSVPX-ESX-13.0-47.22_nc_64.zip這個文件,該文件是ovf,可以直接用vmware來倒入並打開
選擇導入
先后配置 IP地址,子網掩碼,的網關地址,然后選擇4確認
默認用戶名密碼都是 nsroot
然后訪問之前設置的IP地址,出現以下界面則環境搭建成功
漏洞復現
網上下載exp
下載地址 https://codeload.github.com/jas502n/CVE-2019-19781/zip/master
使用方式
輸入要執行的命令
漏洞復現成功
漏洞分析
首先查看我們的請求路徑,由於利用了目錄遍歷漏洞,所以訪問的真實uri路徑是
/vpns/portal/scripts/newbm.pl
我們去Apache 的配置文件httpd.conf 中看一下路徑的配置,這里簡單解釋下這幾項配置
首先介紹一個MOD_PERL技術
MOD_PERL技術將PERL解析器編譯到APACHE服務器中一起等待客戶端請求。 MOD_PERL技術在APACHE中一共有三種代碼運行方式:
- PerlRun模式:這個模式主要兼容舊式CGI程序,僅僅使用APACHE中的PERL解析器對代碼進行解析不進行緩沖。
- Registry模式:這個模式的Perl代碼在被訪問過以后會被編譯成為APACHE API模塊並且存儲在緩沖之中,為了保證運行APACHE會選擇性的產生、不只一個副本在內存中。
- APACHE API模式:這個模式在APACHE啟動的時候就直接將代碼編譯駐留在緩沖之中。
配置完Apache和Perl模塊后,可以用perlinfo函數查看系統環境相關變量。下圖是在虛擬機中跑出來的效果,用的是XAMPP套件的老版本。
實現MOD_PERL技術,就需要我們先編寫一個APACHE模塊代碼
例子
然后再在http.conf里做如下的配置
PerlModule example;
<Location "(service)$"> SetHandler perl-script PerlHandler example </Location>
這樣當用戶訪問的時候會被這個APACHE模塊處理。
正常情況下我們是無法訪問/vpns/portal/這個路徑下的任何東西的,因為路徑限制我們訪問不到,但是恰好這次該系統還有一個目錄遍歷漏洞,這樣我們可以訪問的范圍就擴大了不少
首先我們攻擊的第一步會請求一個uri “/vpn/../vpns/portal/scripts/newbm.pl”
我們首先看一下
這里的Handler模塊 我們訪問 Citrix ADC VPX 虛擬機的以下路徑
可以看到該路徑 ( /var/tmp/netscaler/portal/templates/ 或者 /netscaler/portal/templates/ ) 下有以下文件
后綴為.pm的文件 即為Perl Module,也就是 Perl 模塊。在這里我們看到了處理請求/vpns/portal的默認木塊 Handler.pm
我們打開看一下源碼
該模塊只有兩個函數 error函數沒什么好看的,我們重點觀察handler函數.
不難發現handler函數中調用了另一個模塊UserPrefs模塊,調用了UserPrefs的一個new方法
new( ) 方法是對象的構造函數。我們去觀察一下UserPrefs的源碼
構造函數是類的子程序,它返回與類名相關的一個引用。將類名與引用相結合稱為“祝福”一個對象,因為建立該結合的函數名為 bless ( ),其語法為:
bless YeReference [,classname]
YeReference
是對被“祝福”的對象的引用,classname
是可選項,指定對象獲取方法的包名,其缺省值為當前包名。既在當前代碼中返回一個名為UserPrefs的對象。然后我們調用UserPrefs對象的csd()方法。
我們看一下csd ()方法的實現細節
結合網上的exp我們發現了一個關鍵的變量
以下是EXP的源碼
這個username參數存儲的是我們客戶端傳遞來的 請求頭中的“NSC_USER”的值。
我們看看exp中是怎么定義這個值的
“NSC_USER”的值中的%cdl值是一個隨機值,主要用於寫入文件的文件名
至於對"NSC_NONCE"這個請求頭的處理,只是做了簡單的字符串校驗,並不是說校驗了用戶名和密碼,所以第二次請求訪問后台生成的XML時,"NSC_NONCE"“NSC_USER”兩個請求頭的值可以是任意不含特殊字符的字符串,所以該漏洞利用時並不需要提前知道后台登陸密碼。
根據上面的源碼截圖我們看到了username的值,接下來我們看程序是如何處理username這個變量的。
可以看到在第61行代碼調用了 fileread函數並將username變量作為參數傳入
我們看到這這么一行注釋
如果文件不存在或者已刪除,則根據username創建一個新的文檔。看到這里我們大致明白了這段代碼的作用,就是以username變量為依據判斷某路徑下是否有同名文件的存在,如果文件不存在或者已刪除已損毀則以username作為文件名創建一個文件。
那么這個創建的文件存放在哪里我們在源碼中查找一下
可以看到 默認生成文件的路徑是“/var/vpn/bookmark”, 也就是說正常情況下我們訪問,生成的和username同名的文件時在該路徑下的。
但是由於生成文件時並未對傳入的“NSC_USER”這個頭部有任何過濾。其實應該是有的,只不過默認情況下是給注釋掉了。由下圖的注釋可以看出
所以在沒有過濾的情況下,程序就以“../../../netscaler/portal/templates/filename” 這樣生成的文件就保存在了,我們可以通過目錄遍歷漏洞訪問到的目錄下了。
此時我們可以控制,服務端程序在指定目錄下創建文件了,但是僅僅這樣是不夠的,我們還需要將我們的payload一起寫入我們的文件中。
我們已經知道了,如果要在指定位置創建文件,就需要在執行時調用UserPrefs對象的csd()方法。以此為依據,再能訪問的范圍內尋找可以利用的類。我翻看了一下,有不少類中都調用了UserPrefs對象的csd()方法,但是並有可以用來寫入payload的點,例如themes.pl,可以調用UserPrefs對象的csd()方法生成文件,但是卻無法向文件中寫入payload,我們測試一下,先修改一下exp,將第一次請求的文件改成themes.pl
雖然會提示上傳失敗,但是我們直接從后台來看確實生成了一個同名的xml文檔
看一下這個xml文檔的內容
很顯然這樣生成的xml文檔里沒有任何有價值的或者可以利用的東西
我們經過一段時間查看源碼發現了一個有利用價值的perl程序,即請求路徑為“/vpns/portal/scripts/newbm.pl”實際物理路徑為"/netscaler/portal/scripts/newbm.pl"的newbm模塊。通過下圖我們可以看到在http.conf中配置 Alias的作用是別名配置
我們看一下newbm.pl的源碼
我們從源碼中可以看到 newbm.pl 滿足了我們所需要的條件
首先調用了UserPrefs對象的csd()方法生成文件
然后用四個變量接受我們從前台POST傳入的payload,然后存儲到一個哈希中,經測試"title","UI_inuse","descr"均可用來寫入payload
最中我們通過POST傳入的四個參數,被寫入到我們通過UserPrefs對象的csd()方法生成xml文檔中了。以下截圖就是該文檔的最終形態。
我們可以看到,我們要執行的payload語句已經寫入 bookmark標簽的“title”標簽中了。
至此exp的第一次請求結束,所做的事情就是指定在服務端的“/netscaler/portal/templates/”路徑下生成一個xml文檔,然后向該文檔內寫入payload。
接下來就是exp執行的第二步
也就是我們要想辦法訪問到這個xml文檔,然后還能夠讓程序解析並執行我們的payload也就是 [% template.new('BLOCK' = 'print "+ cmd + "') %]
這串代碼
按照順序來,我們先考慮如何可以訪問這個xml文檔。首先第一反應就是去看http.conf文件,生成的xml文檔的物理路徑是“/netscaler/portal/templates/” 看一看有沒有該路徑的一個映射地址,這樣我們就可以直接訪問了,可惜並沒有,也就是說像exp第一步直接訪問newbm.pl的方式是行不通了。但是此時我們回到一開始的原點,即訪問路徑“/vpns/portal/”的默認處理模塊 Handler.pm
我們觀察上面兩個截圖的代碼,我在后台搜過半天並沒有發現這個\$r-path_info()
方法屬於那個模塊,不過根據這個if判斷 用的是eq來對比切用來對比的是一個具體的文件名稱,緊接着就將\$r-path_info()
返回的結果賦值給了“tmplfile”變量,接下來很關鍵,也是該漏洞最中可以訪問我們生成的xml文檔並解析執行其中payload的根本原因,
“template”變量指向的是Template對象的一個引用,我們可以看到Template->new{}
是創建一個Template對象。
這里Template是perl的一個模塊,Template Toolkit。
簡單介紹一下Template Toolkit,在許多使用Perl進行“模板化”的方法中,Template Toolkit被廣泛認為是功能最豐富的工具之一。與其他模板系統一樣,模板工具包允許程序員將Perl代碼和自定義宏嵌入HTML文檔中,以便即時創建自定義文檔。但是與其他工具不同,Template Toolkit在生成HTML方面與在生成XML,PDF或任何其他輸出格式時一樣容易。
看到這里我們應該明白了,Template Toolkit就是perl下的一個功能非常強大的模板引擎,那這么一來,一切就都解釋的通了
接下來就在第32行我們調用Template->process()
方法,process()調用 該方法來處理指定為第一個參數的模板$input。這可以是文件名,文件句柄(例如GLOB或IO::Handle)或對包含模板文本的文本字符串的引用。可以傳遞包含模板變量定義的其他哈希引用。
我們的xml文檔里寫入的payload之所以可以被解析並執行就是因為調用了Template對象的process方法,具體該模板引擎是如何解析xml文檔的,牽扯到語法生成樹和語義分析限於篇幅原因就不細講了,感興趣的朋友可以自己深入去了解學習
這里我們演示一下這個模板引擎解析的效果
更多的關於Template Toolkit這個模板引擎的功能非常強大而且教程網上也有很多,大家可以自行去學習和使用。
至此CVE-2019-19781 Citrix ADC遠程代碼執行漏洞,分析完畢。
============ End