作者:kk

本文為作者投稿,Seebug Paper 期待你的分享,凡經採用即有禮品相送!

投稿郵箱:[email protected]

在幾天前,我就收到致遠OA的RCE漏洞部分詳情,但是並沒有引起重視。當時獲得的POC部分只包括了任意文件上傳的數據包,但並沒有其餘詳情,且數據包中重要數據都被編碼過了。原以為又是一次惡作劇。沒想到啊。 由於漏洞本身沒什麼好講的,現在讓我們來看看這個POC中涉及的編碼演算法,看看原始的POC中的編碼數據是做什麼的。 首先漏洞位置在htmlofficeservlet,通過一段時間的尋找我找到了一份舊的Seeyon OA的源碼: github.com/zhf839428881

其中這個介面的實現在HtmlOfficeServlet.java文件內。

通過這份代碼我們知道介面對參數的獲取使用的是DBstep.iMsgServer2000.GetMsgByName方法。 關鍵代碼:

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
CurrentUserToSeeyonApp.set(request.getSession());

ApplicationContext ctx = (ApplicationContext) getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
HandWriteManager handWriteManager = (HandWriteManager) ctx.getBean("handWriteManager");
HtmlHandWriteManager htmlHandWriteManager = (HtmlHandWriteManager) ctx.getBean("htmlHandWriteManager");

DBstep.iMsgServer2000 msgObj = new DBstep.iMsgServer2000();
try {
handWriteManager.readVariant(request, msgObj);

msgObj.SetMsgByName("CLIENTIP", request.getRemoteAddr());

String option = msgObj.GetMsgByName("OPTION");

if ("LOADFILE".equalsIgnoreCase(option)) {
handWriteManager.LoadFile(msgObj);
}
else if("LOADSIGNATURE".equalsIgnoreCase(option))
{
htmlHandWriteManager.loadDocumentSinature(msgObj);
}
else if("LOADMARKLIST".equalsIgnoreCase(option))
{
handWriteManager.LoadSinatureList(msgObj);
}
else if("SIGNATRUEIMAGE".equalsIgnoreCase(option))
{
handWriteManager.LoadSinature(msgObj);
}
else if("SAVESIGNATURE".equalsIgnoreCase(option))
{
htmlHandWriteManager.saveSignature(msgObj);
}
else if("SAVEHISTORY".equalsIgnoreCase(option))
{
htmlHandWriteManager.saveSignatureHistory(msgObj);
}
else if("SIGNATRUELIST".equalsIgnoreCase(option))
{//調入印章列表
handWriteManager.LoadSinatureList(msgObj);
}
else if("SHOWHISTORY".equalsIgnoreCase(option))
{
htmlHandWriteManager.getSignatureHistory(msgObj);
}

handWriteManager.sendPackage(response, msgObj);
}
catch (Exception e) {
log.error("",e);
msgObj = new DBstep.iMsgServer2000();
msgObj.MsgError("htmoffice operate err");
handWriteManager.sendPackage(response, msgObj);
}

ThreadLocalUtil.removeThreadLocal();
}

又經過一段時間,我找到了DBstep資料庫,然而DBstep並沒有對參數進行加解密的操作。 卡了一段時間後,我發現此處的DBstep是被修改過的版本,對DBstep進行修改的是iweboffice中間件,而這個中間件屬於金格科技。 從金格科技的官網我找到了試用版的iweboffice,可惜其中的iMsgServer版本為2015,且找不到iMsgServer2000的下載地址。

又過了一段時間,我找到了如下的文件: github.com/ExllntSuppt/

通過分析可以知道DBstep.iMsgServer2000.GetMsgByName調用了DBstep.iMsgServer2000.DecodeBase64方法。 關鍵代碼:

public String GetMsgByName(String FieldName) {
int i = 0;
int j = 0;
String mReturn = "";
String mFieldName = FieldName.trim().concat("=");
i = this._$906.indexOf(mFieldName);
if (i != -1) {
j = this._$906.indexOf("
"
, i + 1);
i += mFieldName.length();
if (j != -1) {
String mFieldValue = this._$906.substring(i, j);
mReturn = this.DecodeBase64(mFieldValue);
return mReturn;
}
return mReturn;
}
return mReturn;
}

通過分析可以知道此處是一個Base64演算法的變種。 關鍵代碼:

public String DecodeBase64(String Value) {
ByteArrayOutputStream o = new ByteArrayOutputStream();
String m = "";
byte[] d = new byte[4];
try {
int count = 0;
byte[] x = Value.getBytes();
while (count < x.length) {
for (int n = 0; n <= 3; ++n) {
if (count >= x.length) {
d[n] = 64;
} else {
int y = this._$903.indexOf(x[count]);
if (y < 0) {
y = 65;
}
d[n] = (byte)y;
}
++count;
}
o.write((byte)(((d[0] & 63) << 2) + ((d[1] & 48) >> 4)));
if (d[2] == 64) continue;
o.write((byte)(((d[1] & 15) << 4) + ((d[2] & 60) >> 2)));
if (d[3] == 64) continue;
o.write((byte)(((d[2] & 3) << 6) + (d[3] & 63)));
}
}
catch (StringIndexOutOfBoundsException e) {
this._$907 = this._$907 + e.toString();
System.out.println(e.toString());
}
try {
m = o.toString(this.Charset);
}
catch (UnsupportedEncodingException ea) {
System.out.println(ea.toString());
}
return m;
}

對應Base64中的密文ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/= 該處變種演算法的密文為FxcYg3UZvtEz50Na8G476=mLDI/jVfC9dsoMAiBhJSu2qPKe+QRbXry1TnkWHlOpw 但是使用該密文無法正確對致遠POC中的密文進行解密,推測在致遠OA中,該密文被修改了。

聯繫了公司中有Seeyon OA的小夥伴,通過他的協助終於獲取到了Seeyon的密文:

gx74KW1roM9qwzPFVOBLSlYaeyncdNbI=JfUCQRHtj2+Z05vshXi3GAEuT/m8Dpk6

寫了個變種Base64互轉Base64的小腳本。

var a = "gx74KW1roM9qwzPFVOBLSlYaeyncdNbI=JfUCQRHtj2+Z05vshXi3GAEuT/m8Dpk6";
var b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var c = "OKMLlKlV";
var d = "";
function a2b(v) {
for (var i = 0; i < a.length; i++) {
if (a[i] == v) {
return b[i];
}
}
}

function b2a(v) {
for (var i = 0; i < b.length; i++) {
if (b[i] == v) {
return a[i];
}
}
}

for (var i = 0; i < c.length; i++) {
d = d + a2b(c[i]);
}

對POC中的加密參數進行解密後,我們得到如下數據。

參數 變種Base64 Base64 明文

DBSTEP OKMLlKlV REJTVEVQ DBSTEP

OPTION S3WYOSWLBSGr U0FWRUFTSU1H SAVEASIMG

currentUserId zUCTwigsziCAPLesw4gsw4oEwV66 Njk5MzAwNzk2OTYwMDAwMDI3MQ== 6993007969600000271

CREATEDATE wUghPB3szB3Xwg66 MjAxOS0wNS0yMA== 2019-05-20

RECORDID qLSGw4SXzLeGw4V3wUw3zUoXwid6 LTU1MDUyNTY1MDQ0MjM0NjIyMzc= -5505256504423462237

originalFileId wV66 MQ== 1

FILENAME qfTdqfTdqfTdVaxJeAJQBRl3dExQyYOdNAlfeaxsdGhiyYlTcATdN1liN4KXwiVGzfT2dEg6 Li5cLi5cLi5cQXBhY2hlSmV0c3BlZWRcd2ViYXBwc1xzZWV5b25cdGVzdDEyMzQ1Ni5qc3A= ......ApacheJetspeedwebappsseeyon est123456.jsp

needReadFile yRWZdAS6 ZmFsc2U= false

originalCreateDate wLSGP4oEzLKAz4=iz=66 MTU1ODI3NTE2NDgzNg== 1558275164836

POC中還有一個客戶端的IP,解密後為內網地址。

從這些明文中,我們知道這個POC中操作的資料庫是DBSTEP,進行的操作應該是保存圖片。其中還有用戶的ID和記錄ID的存在。根據CREATEDATE參數,操作時間是在2019-05-20。originalCreateDate參數是Unix時間,表示2019-05-19 22:12:44:836。通過這兩個參數可以推測這個漏洞至少已經被發現一個月了。

還可以看到漏洞寫入的文件地址是......ApacheJetspeedwebappsseeyon est123456.jsp,由於伺服器上的上傳目錄不是固定的,這個POC只能影響上傳目錄與默認配置一致的伺服器。

最後,65!約等於8.2476506e+90,通過對Base64映射表的修改,我們可以得到65!-1種不同的變種Base64演算法,因此也導致幾乎不可能在只有密文的情況下對變種Base64的映射表進行爆破。 Base64算編碼方法還是對稱加密很難說,但是變種Base64絕對算是加密演算法,映射表就是他的密文。

最最後,凱撒加密天下第一!


本文由 Seebug Paper 發布,如需轉載請註明來源。

歡迎關注我和專欄,我將定期搬運技術文章~

也歡迎訪問我們:知道創宇雲安全

如果你想與我成為朋友,歡迎加微信kcsc818


推薦閱讀:
相關文章