作者:kk
本文为作者投稿,Seebug Paper 期待你的分享,凡经采用即有礼品相送!
投稿邮箱:paper@seebug.org
在几天前,我就收到致远OA的RCE漏洞部分详情,但是并没有引起重视。当时获得的POC部分只包括了任意文件上传的数据包,但并没有其余详情,且数据包中重要数据都被编码过了。原以为又是一次恶作剧。没想到啊。 由于漏洞本身没什么好讲的,现在让我们来看看这个POC中涉及的编码算法,看看原始的POC中的编码数据是做什么的。 首先漏洞位置在htmlofficeservlet,通过一段时间的寻找我找到了一份旧的Seeyon OA的源码: https://github.com/zhf839428881/seeyon_v3x/
其中这个接口的实现在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的下载地址。
又过了一段时间,我找到了如下的文件: https://github.com/ExllntSuppt/ecology-OA/blob/master/iMsgServer2000.java
通过分析可以知道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("\r\n", 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= ..\..\..\ApacheJetspeed\webapps\seeyon\test123456.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。通过这两个参数可以推测这个漏洞至少已经被发现一个月了。
还可以看到漏洞写入的文件地址是..\..\..\ApacheJetspeed\webapps\seeyon\test123456.jsp
,由于服务器上的上传目录不是固定的,这个POC只能影响上传目录与默认配置一致的服务器。
最后,65!约等于8.2476506e+90,通过对Base64映射表的修改,我们可以得到65!-1种不同的变种Base64算法,因此也导致几乎不可能在只有密文的情况下对变种Base64的映射表进行爆破。 Base64算编码方法还是对称加密很难说,但是变种Base64绝对算是加密算法,映射表就是他的密文。
最最后,凯撒加密天下第一!
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/964/