前言
漏洞比較簡單可以概括為token
硬編碼導(dǎo)致的任意方法調(diào)用組合RCE
。
主要講下當(dāng)時挖這個漏洞的過程,其實(shí)就是分析老漏洞挖到了新漏洞。
反序列化 or 任意方法調(diào)用?
/ServiceDispatcherServlet
接口之前出過反序列化所以先看看他怎么修復(fù)的


我們跟進(jìn)看看具體實(shí)現(xiàn)

這里GET|POST
都會進(jìn)入execCall

execCall
里會調(diào)用readObject
但是這個方法是他自己實(shí)現(xiàn)的

注意到NCObjectInputStream#resolveClass

此處添加了黑白名單判斷,且傳入的rootChecked
為false
,whiteList
為我們剛才傳入的InvocationInfo.class
所以這里使用白名單修復(fù)了之前的反序列化漏洞 然后我們回到readObject
這里研究一下怎么生成這個序列化數(shù)據(jù)


他先檢查了前四字節(jié)然后使用前四字節(jié)計算了數(shù)據(jù)長度判斷是否符合,最后使用nc.bs.framework.comn.NetObjectInputStream#readObject
進(jìn)行反序列化。這里我們可以直接套用它的序列化方法來進(jìn)行序列化數(shù)據(jù)的生成nc.bs.framework.comn.NetObjectOutputStream#writeObject

既然這里反序列化RCE
走不通我們繼續(xù)看execCall
的后續(xù)實(shí)現(xiàn)

從我們反序列化的對象invInfo
中獲取數(shù)據(jù)先進(jìn)入vertifyToken
進(jìn)行token驗(yàn)證,然后進(jìn)入invokeBeanMethod
進(jìn)行反射調(diào)用。我們看下vertifyToken
的實(shí)現(xiàn)

如果service
或者clientIP
在可信列表里則直接不進(jìn)行token驗(yàn)證,然后看vertifyTokenIllegal
怎么驗(yàn)證token

獲取userCode
傳入genToken

獲取tokenSeed
作為鹽然后sha1
加密

所以我們只要知道tokenSeed
即可偽造token

從nc.bs.framework.server.token.TokenUtil
可知tokenSeed
儲存在/ierp/bin/token/tokenSeed.conf
里我們直接查看安裝包和安裝后的這個文件發(fā)現(xiàn)沒有任何變化說明是硬編碼的不是啟動后隨機(jī)生成寫入文件的。
?

所以token
可以偽造,然后我們這里就有了一個調(diào)用符合下面規(guī)則方法的點(diǎn)

從歷史漏洞尋找RCE方法
上面我們找到了一個調(diào)用符合某些規(guī)則方法的點(diǎn),我們這里不跟入nc.bs.framework.naming.Context#lookup
查看具體實(shí)現(xiàn)然后找到符合的beanName&methodName
因?yàn)槿绻麑τ糜汛a不熟悉或者這個符合規(guī)則的方法很多但是要找到一個RCE
的可能也不太容易。正好挖洞之前分析過一個歷史漏洞。數(shù)據(jù)包如下
POST /service/esnserver HTTP/1.1
Host: 192.168.179.140:8088
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Token: 469ce01522f64366750d1995ca119841
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,fil;q=0.6
Cookie: currentToken=0d1001e88bd4373e7f07ff61caddeaf8; JSESSIONID=62C880D54C98B7CD3CEFF70A169A4DC2.server
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 60484
{"invocationInfo":{"ucode":"123","dataSource":"U8cloud","lang":"en"},"method":"uploadFile","className":"nc.itf.hr.tools.IFileTrans","param":{"p1":"shelltext","p2":"webapps/u8c_web/test1234.jsp"},"paramType":["p1:[B","p2:java.lang.String"]}
這個漏洞的原理我簡單講下



根據(jù)傳入的pathInfo
獲取obj
比如上面的包傳入的即為esnserver
,然后進(jìn)入獲取到的obj
的service
方法這里最后會進(jìn)入com.yonyou.esn.servlet.EsnServlet#doAction

也是先進(jìn)行Token
檢測,上面數(shù)據(jù)包頭部Token
也是用來繞過檢測的

他這個token
和tokenSeed
都是由用戶傳入的tokenSeed
對應(yīng)數(shù)據(jù)包中的ucode
過了token
檢測以后會進(jìn)入到com.yonyou.esn.ulink.LightAppService#processBusi

我這個代碼是修復(fù)了這個漏洞以后的加了包名限制。這里其實(shí)和我們上面反射調(diào)用方法是一樣的只不過這里是json
格式上面是反序列化。所以我們看下"method":"uploadFile","className":"nc.itf.hr.tools.IFileTrans"
修復(fù)了沒還是只加了包名限制反射調(diào)用。


解壓數(shù)據(jù)然后寫入我們指定的路徑?jīng)]有任何限制。
組合RCE
結(jié)合反序列化token
硬編碼,反射調(diào)用nc.itf.hr.tools.IFileTrans#uploadFile
可寫出POC
生成代碼

隱藏了關(guān)鍵代碼部分需要研究的請自行實(shí)現(xiàn)。

成功getshell

總結(jié)
這個漏洞本來不想寫這么麻煩的簡單分析一下調(diào)用過程很省時間,但是后面還是想按照當(dāng)時的挖掘思路來寫主要是想告訴讀者分析歷史漏洞的重要性。
每一個歷史漏洞都像是一把鑰匙,不僅能開啟當(dāng)時的那扇門,更可能為我們指向新的攻擊路徑。
復(fù)現(xiàn)不是終點(diǎn),而是新發(fā)現(xiàn)的起點(diǎn)!!!
閱讀原文:原文鏈接
該文章在 2025/9/30 10:27:12 編輯過