作者:且聽安全
原文鏈接:https://mp.weixin.qq.com/s/c0X8Ct2I2SP-H_pioMM12Q
漏洞信息
前端時間 Sophos Firewall 爆出了一個認證繞過漏洞 CVE-2022-1040 ,最近在深入分析 Sophos 服務架構的同時,完整復現了該漏洞。主要是在 User Portal 及 Webadmin 兩個接口存在認證繞過漏洞,漏洞巧妙利用了 Java 和 Perl 處理解析 JSON 數據的差異性,實現了變量覆蓋,從而導致認證繞過及命令執行。漏洞適用范圍為 Sophos Firewall v18.5 MR3 及以下版本。
環境搭建
首先從官網下載老版本虛擬機。本文研究下載版本為 VI-18.5.2_MR-2.VMW-380:

按照提示很容易完成安裝。Sophos 的 Web 接口主要通過 Java 實現,由啟動命令可以看出 Sophos 使用的是 openjdk 環境,如果在 Java 啟動參數中直接添加調試參數,將會出現找不到 libjdwp.so 動態鏈接庫的錯誤:


可以通過自行上傳完整 JDK 來解決這個問題:

重啟 Java 服務即可看到監聽的端口。
服務架構
Sophos 中的服務架構不是很復雜,主要使用了 Apache 、 Jetty 等 web 服務,第一層語言為 Java 通過網絡通信的方式與后端 Perl 服務進行交互:

0x01 Apache 配置
Apache 的啟動命令如下:
apache -d /_conf/httpd -DFOREGROUND
進入 /_conf/httpd 目錄,存放有 Apache 的配置文件,通過分析 httpd.conf,了解 Apache 引用了 /cfs/web/apache/httpd.conf 配置:
Define userportal_listen_port 65004
Define webconsole_https_port 65003
Define SSLCertificateFileWithPath "/conf/certificate/ApplianceCertificate.pem"
Define SSLCertificateKeyFileWithPath "/conf/certificate/private/ApplianceCertificate.key"
Define https_cert_valid true
從配置中可以看出 Apache 開放了兩個主要的端口 userportal 65004 和 webconsole 65003 :

在 Apache 配置目錄下搜索 ProxyPass,找到的代理轉發配置如下:
./ssl.conf:53: ProxyPass /webconsole/images !
./ssl.conf:54: ProxyPass /webconsole/css !
./ssl.conf:55: ProxyPass /webconsole/javascript !
./ssl.conf:56: ProxyPass /webconsole http://localhost:8009/webconsole
./ssl.conf:57: ProxyPassReverse /webconsole http://localhost:8009/webconsole
./userportal-static.conf:64: ProxyPass /userportal/images !
./userportal-static.conf:65: ProxyPass /userportal/CRSSL !
./userportal-static.conf:66: ProxyPass /userportal http://localhost:8009/userportal
./userportal-static.conf:67: ProxyPassReverse /userportal http://localhost:8009/userportal
代理轉發策略將 webconsole 和 userportal 端口分別代理到 8009 端口的不同 URL :
ProxyPass /webconsole http://localhost:8009/webconsole
ProxyPass /userportal http://localhost:8009/userportal
0x02 Jetty 配置
Jetty 配置文件為 /usr/share/jetty/start.ini,開啟了本地服務的 8009 端口:

Jetty 的啟動參數在 /usr/bin/jetty 腳本中配置,如果要修改可直接修改該文件最后 Java 執行部分。
0x03 CSC 配置
CSC 是 Sophos 的主要服務之一,主要負責啟動各個服務進程及提供 API 接口供其他程序服務調用。其啟動命令為:
csc -L 3 -w -c /_conf/cscconf.bin
CSC 為標準的 ELF 32bit 可執行程序,可通過逆向分析其中功能。cscconf.bin 中在 CSC 程序中有調用解壓,猜測是一個加密壓縮包。
/usr/bin/csc 由 C 語言編寫,負責啟動加載其他的服務以及加載 Perl 代碼,在虛擬機中 CSC 啟動部分服務如下:

程序中的 extract_conf 函數負責解密 cscconf.bin 并提取壓縮包中的內容:

decrypt_bin 函數主要是通過異或算法將 cscconf.bin 數據解密為 cscconf.tar.gz 壓縮包格式:

每次取 0x420 個字節通過 xor_decrypt 函數進行加密塊解異或解密,將解密后數據中的 0x400 個字節寫入 tar.gz 文件:

xor_decrypt 核心代碼如下,主要通過與 0x80DCB40 地址中實現存放的 64 字節逐一進行異或處理:

通過逆向該算法,使用 Python 編寫出加解密算法實現代碼。解密得到 cscconf.tar.gz 壓縮包,解開壓縮包目錄如下:

在壓縮包中的其中一個目錄名為 service ,推測 CSC 通過該目錄下的配置文件啟動相關服務:

補丁對比
配置 Sophos vmware 網卡連網后等待一段時間,將虛擬機上的文件與原來的文件進行對比,其中有兩個修改的地方,一處為 web.xml,另一處添加了 RequestCheckFilter.class 文件:

web.xml 增加了一段配置,主要給 Sophos Java 代碼添加 RequestCheckFilter過濾器,過濾器主要檢測 request 請求包中的 JSON 參數是否包含不可見字符:

檢測規則中,JSON 參數的每個字符都必須是 32~127 之間,如果超出范圍則會跳轉到登錄界面:

那么給我們的啟發就是此次漏洞和 JSON 參數中的不可見字符有著直接的關系,應該是字符編碼導致的認證繞過。
JSON 解析差異性分析
漏洞原理可簡單理解為 Java 在使用 unicode \u0000 時,JSON 認為 key 是兩個不同的 key 并沒有 0 字節截斷,當 Java 把含有 unicode 編碼的 key 發送給后端的 Perl處理時 \u0000 產生了截斷效果,使得帶有 unicode 編碼的 key 變為了 mode, Perl 可以處理重復 key 的 JSON,如果重復則后面覆蓋前面的值:

我們可以通過一個示例來對比不同語言處理 JSON 重復鍵的差異性。Java 處理帶有 unicode 編碼的 key 時可以正常解析:
import org.json.JSONObject;
import org.json.JSONException;
import java.io.*;
class test {
public static void main(String[] args) {
try{
System.out.println(new JSONObject("{ \"name\": \"test\", \"name\\u0000ef\": \"test2\"}"));
}catch (JSONException e){
System.out.println(e);
}
}
}

Perl 處理 Java 傳遞過來的零字節字符串就會產生截斷效果,在處理相同 key 值的 JSON 時會取最后一個 key 對應的 value :
#!/usr/bin/perl
use JSON;
my %rec_hash = ('a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5, 'a' => 6);
my $json = encode_json \%rec_hash;
print "$json\n";
其結果為 a 被覆蓋為了最后一個 a 的值:

不同語言對 JSON 解析的差異性是此次認證繞過漏洞的核心原理。Java 接收來自用戶的以下數據(其中 \u0000 后面的 ef 是為了讓 JSON中 key 的 hash 排序將 716 排到 151 的后面):
{"mode":151,"mode\u0000ef":716}
登錄認證分析
通過登錄 webadmin 獲取登錄數據包:

根據 /webconsole/Controller 及 mode=151 尋找 Java 代碼中的處理邏輯,首先分析 web.xml 中路由對應的 Servlet :


主要由 CyberoamCommonServlet 進行處理,該類主要解析 mode 值,并通過 mode 進行分發:

進入 _doPost 函數繼續進行分發, EventBean 從數據庫中獲取 mode 對應的屬性,其中就包括關鍵屬性 Requesttype :

通過 Requesttype 將請求分為了三種:

Requesttype為1,使用CyberoamAjaxHelper處理;Requesttype為2,使用CyberoamCustomHelper處理;Requesttype為其他值時,使用generateAndSendOpcode處理。
進入 CyberoamCustomHelper 之后,通過匹配 mode 值進行分發,將會進入 WebAdminAuth 類中進行處理:

process 函數對請求中的 JSON 字段進行解析,獲取 jsonObject 之后將會給 cscClient 通過 localhost:299 發送給 CSC 進程進行處理:

比較有意思的是 Sophos 通過 cscClient 返回的 Status code 判斷是否登錄成功,因此在 Java 代碼中是看不到登錄認證的完整過程的,如下圖所示如果返回為 200 則會生成合法 sessionBean :

合法 sessionBean 生成過程如下,將 session 中填充 username 、 userid 、 csrftoken 等關鍵信息:

因此我們只需要找到后臺返回 200 的函數即可。
CSC Perl API 分析
因為 webadmin login mode 151 的 Requesttype 為 2 ,在 _send 函數最后獲取返回值的時候使用 getStatusFromResponse 進行解析:

使用 eventBean 判斷 Requesttype ,因為該 eventBean 在數據包剛開始處理的時候就根據 mode 值在數據庫中進行搜索匹配,中間沒有修改的可能性。在如下 else 分支中需要獲取 JSON 結果的 status 字段:

因此在尋找可返回 200 的 mode 值時需要考慮的是要能夠同時返回 status 字段,通過搜索數據庫找到所有 Requesttype 為 2 的 OPCODE ,其中有 716 符合條件。注意 Perl 代碼獲取了 request 中的 accessaction 字段,并且需要該字段為 1 才能返回 200 。
漏洞復現
通過前面的分析,我們很容易構造特殊的 JSON 數據包實現認證繞過。如果數據包中返回 status 的為 200 ,并且 redirectionURL 路由為 index.jsp 即為認證成功,直接取 Set-Cookie 中的 JSESSIONID 進行使用:

在未登錄條件下在瀏覽器中添加上述認證后返回的 session,操作如下:

替換瀏覽器中的 JSESSIONID ,并在 url 處輸入 index.jsp 回車進行跳轉:

小結
通過漏洞還能夠獲取 admin 操作權限,因此可以向固件中添加惡意代碼,然后通過上傳固件的方式實現命令執行,或者通過修改配置等方式進入底層,方法有很多種不再詳細分析。
通過分析復現 CVE-2022-1040 認證繞過漏洞,學習了一種新的認證繞過思路,通過不同語言對 JSON 或者其他格式的數據處理上的差異實現變量覆蓋,完成漏洞利用。
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1925/
暫無評論