Apache XML-RPC是一个java的XML-RPC库。XML-RPC是在XML的帮助下通过HTTP进行远程过程调用的协议。 Apache XML-RPC可以在客户端用于进XML-RPC调用,也可以在服务器端用XML-RPC公开一些函数。现在ws-xmlrpc库不被Apache支持。 最新版本是2013年发布的3.1.3版本。但是,许多应用程序仍然使用ws-xmlrpc库
新建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>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
主方法 App.java
public class App {
private static final int port = 8888;
public static void main(String[] args) throws Exception {
WebServer webServer = new WebServer(port);
XmlRpcServer xmlRpcServer = webServer.getXmlRpcServer();
PropertyHandlerMapping phm = new PropertyHandlerMapping();
phm.addHandler("User", User.class);
xmlRpcServer.setHandlerMapping(phm);
XmlRpcServerConfigImpl serverConfig = (XmlRpcServerConfigImpl) xmlRpcServer.getConfig();
serverConfig.setEnabledForExtensions(true);
webServer.start();
}
}
接收传入对象 User.java
public class User {
public String welcome(String name) {
return "welcome " + name;
}
}
运行 App.java 后即在 8888 端口建立 HTTP 服务。即可使用Burpsuite进行发包测试
参考 官网帮助文档 ,可以发现xmlrpc支持 java.io.Serializable 序列化后的base64编码的数据,但是需要引用命名空间 http://ws.apache.org/xmlrpc/namespaces/extensions 。所以使用 ysoserial 的 CommonsCollections5 生成payload。 因为本机测试环境为jdk1.8,用其它payload会爆错 。
然后使用certutil -encode 将生成的文件进行base64编码。
当客户端发送请求时, org.apache.xmlrpc.server.XmlRpcStreamServer.getRequest(XmlRpcStreamRequestConfig, InputStream) 方法会接收到该请求,该方法会将请求内容封闭到XMLReader对象中。
protected XmlRpcRequest getRequest(final XmlRpcStreamRequestConfig pConfig,
InputStream pStream) throws XmlRpcException {
final XmlRpcRequestParser parser = new XmlRpcRequestParser(pConfig, getTypeFactory());
final XMLReader xr = SAXParsers.newXMLReader();
xr.setContentHandler(parser);
try {
xr.parse(new InputSource(pStream));
} catch (SAXException e) {
Exception ex = e.getException();
if (ex != null && ex instanceof XmlRpcException) {
throw (XmlRpcException) ex;
}
throw new XmlRpcException("Failed to parse XML-RPC request: " + e.getMessage(), e);
} catch (IOException e) {
throw new XmlRpcException("Failed to read XML-RPC request: " + e.getMessage(), e);
}
final List params = parser.getParams();
return new XmlRpcRequest(){
public XmlRpcRequestConfig getConfig() { return pConfig; }
public String getMethodName() { return parser.getMethodName(); }
public int getParameterCount() { return params == null ? 0 : params.size(); }
public Object getParameter(int pIndex) { return params.get(pIndex); }
};
}
然后程序会走到 org.apache.xmlrpc.parser.XmlRpcRequestParser.endElement(String, String, String) 方法来获取各个元素的值
public void endElement(String pURI, String pLocalName, String pQName) throws SAXException {
switch(--level) {
case 0:
break;
case 1:
if (inMethodName) {
if ("".equals(pURI) && "methodName".equals(pLocalName)) {
if (methodName == null) {
methodName = "";
}
} else {
throw new SAXParseException("Expected /methodName, got "
+ new QName(pURI, pLocalName),
getDocumentLocator());
}
inMethodName = false;
} else if (!"".equals(pURI) || !"params".equals(pLocalName)) {
throw new SAXParseException("Expected /params, got "
+ new QName(pURI, pLocalName),
getDocumentLocator());
}
break;
case 2:
if (!"".equals(pURI) || !"param".equals(pLocalName)) {
throw new SAXParseException("Expected /param, got "
+ new QName(pURI, pLocalName),
getDocumentLocator());
}
break;
case 3:
if (!"".equals(pURI) || !"value".equals(pLocalName)) {
throw new SAXParseException("Expected /value, got "
+ new QName(pURI, pLocalName),
getDocumentLocator());
}
endValueTag();
break;
default:
super.endElement(pURI, pLocalName, pQName);
break;
}
}
当获取到最后的元素时,会执行 org.apache.xmlrpc.parser.RecursiveTypeParserImpl.endValueTag() 方法
protected void endValueTag() throws SAXException {
if (inValueTag) {
if (typeParser == null) {
addResult(text.toString());
text.setLength(0);
} else {
typeParser.endDocument();
try {
addResult(typeParser.getResult());
} catch (XmlRpcException e) {
throw new SAXException(e);
}
typeParser = null;
}
} else {
throw new SAXParseException("Invalid state: Not inside value tag.",
getDocumentLocator());
}
}
即会执行typeParser.getResult(),并且此时的typeParser为SerializableParser,
public class SerializableParser extends ByteArrayParser {
public Object getResult() throws XmlRpcException {
try {
byte[] res = (byte[]) super.getResult();
ByteArrayInputStream bais = new ByteArrayInputStream(res);
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (IOException e) {
throw new XmlRpcException("Failed to read result object: " + e.getMessage(), e);
} catch (ClassNotFoundException e) {
throw new XmlRpcException("Failed to load class for result object: " + e.getMessage(), e);
}
}
}
从而造成了反序列化命令执行漏洞。