-
Notifications
You must be signed in to change notification settings - Fork 96
/
get-log4jrcevulnerability.ps1
375 lines (350 loc) · 19.9 KB
/
get-log4jrcevulnerability.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
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
<#
Name: get-log4jrcevulnerability.ps1
Version: 0.2.4.4 (04th January 2022)
Author: Prejay Shah (Doherty Associates)
Thanks: Christopher Bledsoe (IPM Computers) for some bugfixes,
Robby Swartenbroekx (b-Inside) for some ideas,
Arctic Wolf for coming up with a way to detect patched files
Purpose: Detection of jar files vulnerable to the "Log4Shell" log4j RCE vulnerability (CVE-2021-44228)
Originally Utilizing JNDILookup detection method posted to https://gist.github.com/Neo23x0/e4c8b03ff8cdf1fa63b7d15db6e3860b with some slight modifications to make it more RMM friendly
Log4J 2.15 fixed the original Log4Shell CVE, but was suspectable to CVE-2021-44228 which was elevated to an RCE vulnerability and fixed in Log4J 2.16
I then adopted a method presented to me on behalf of ArcticWolf (https://github.com/rtkwlf/wolf-tools/blob/main/log4shell/log4shell_deep_scan.ps1) of extracting the JAR/WAR/EAR file to corroborate whether the Log4J 2.16 changes have been applied to a JAR file.
Robby S then suggested an amendment to be able to track the updated fix in 2.17 for CVE-2021-45105 that was introduced in Log4J 2.16.
NOTE: Only scans against NTFS File Format
RMM Output optimized for N-Central
Not currently compatible with Powershell 2.0
Have excluded files within windows\system32\spool\drivers from being scanned due to access denied issues disrupting the output.
0.1 Initial Release
0.1.1 Added Dedupe to Vulnerable .JAR Listings
0.1.2 Public Release
0.1.3 Found that use of -force isn't working for all scans. Have added a non forced mode to see what outputs can be obtained
0.1.4 Experimenting with Unicode/Robocopy to bypass 260 character file path limit / access denied errors
0.1.5 added support for PSEverything module
0.1.5.1 changed detection to be module based rather than command based
0.1.5.2 Cleaned up Output
0.1.6 Have revamped order to PSEverything, Robocopy, GCI
0.1.6.1 Fixed Typo, Modification for N-Central AMP Output of file names when robocopy is utilized
0.1.7 Some bugfixes courtesy of Christopher Bledsoe (IPM Computers). Who knew inaccessible cloud only JAR files would be an issue?
won't try checking an empty file path
should ignore those "placeholder.jar" files in things like dropbox cache
it uses "|" for the delimiter when reading the CSV/txt file created, so any file paths with "," in them should not get unintentionally split
0.1.7.1 Excluding spool\drivers jar files from being scanned
0.1.7.2 Updated gci to use -filter and -file rather than -include after finding it to be much more performant
0.1.8 Fix for Everything/gci compatibility, Update for Log4j 2.16 update
0.1.8.1 Improved Try/Catch methodology for when Everything search Fails. Thanks to Robby Swartenbroekx (b-Inside) for the assist.
0.1.8.2 Made robocopy window hidden by request
0.1.8.3 Improved Vulnerable File Output for RMM
0.1.9 Expanded Search Criteria to all fixed drives on a device, and added update for Log4j 2.17 Compatibility (Thx to Robby S)
0.2 Separated detection of Log4j 2.16 Patched and 2.17 Patched States
0.2.1 Adding better output for when Everything fails to scan via RMM PS wrapping
0.2.2 Expanded search to cover .jar/.war/.ear files, and partial fix for oddity with scanning certain file names (Thx to Robby S)
0.2.3 Adding more error logging to RMM output in order to surface files that aren't being scanned, amended query to exclude drives that aren't formatted to NTFS
0.2.4 Moved Tasks into Functions, Fixed output bug for number counts.
0.2.4.1 Changed robocopy export/csv import encoding to fix issue with display of special characters in file names
0.2.4.2 Bug with previous fix. I think I have now fixed the robocopy export/csv import encoding based on example files/folders available to me.
Have made further modifications to try to prevent N-Central RMM from showing a misconfigured error even when there is valuable output that could be shown
0.2.4.3 Started work on PS 2.0 Compatibility, working on some code cleanup as well. Thx to Robby S for his assistance with this!
0.2.4.4 Bugfix for file handles for scanned files not closing correctly in some cases (Thx to patilp08).
To Do:
Achieve PS 2.0 Compatibility
Look at lifting the restriction to only scan NTFS
Better Error Handling for N-Central RMM AMP usage
File/Folder exclusions for scanning/flagging as vulnerable
Detection/Exclusion of Cloud-Only OneDrive Files that cannot be scanned
#>
$Version = "0.2.4.3" # 23rd December 2021
Write-Host "`nget-log4jrcevulnerability $version" -foregroundcolor Green
$robocopycsv = $null
$log4junscanned = $null
$log4jvulnerablefiles = $null
$robocopycsvfile = "$env:temp\log4jfilescan.csv"
# still to implement
$folderexclusion = "`"$drive\windows.old`" `"$env:windir\softwaredistribution`""
#region functions
Function Scan-Files {
try {
if (get-module -listavailable | where-object {$_.name -like 'PSEverything'}) {
Write-Host "The almighty PSEverything module's Search-Everything command was found.`nDoing a new scan because we can..." -ForegroundColor Yellow
$log4jfiles = $null
$log4jfilescan = $null
$StopWatch = [system.diagnostics.stopwatch]::startNew()
$log4jfilescan = search-everything -global -extension jar,war,ear
$StopWatch.stop()
$Timetaken = $StopWatch.elapsed.totalseconds
if ($null -ne $log4jfilescan) {
Write-host "See? That only took $([math]::Round($($Timetaken),2)) seconds to scan all Fixed NTFS Drives for .jar/.war/.ear files!" -foregroundcolor Green
$log4jfilenames = $log4jfilescan
}
else {
$StopWatch.stop()
Write-Host $($StopWatch.elapsed.totalseconds) -ForegroundColor Red
Write-Host "Something went wrong with calling PSEverything, lets fallback to the next scan method." -ForegroundColor Yellow
Throw
}
}
else {
# Write-Host "Something went wrong with calling PSEverything, lets fallback to the next scan method." -ForegroundColor Yellow
Throw
}
}
catch {
#Run when PSEverything isn't found or it gave an error
$Drives = ([System.IO.DriveInfo]::getdrives() | Where-Object {$_.DriveType -eq 'Fixed' -and $_.DriveFormat -eq 'NTFS'}).Name
if (test-path $robocopycsvfile) {
remove-item $robocopycsvfile -force
}
try {
Write-Host "Attempting to use Robocopy to scan for JAR/WAR/EAR files on all Fixed NTFS Drives.." -ForegroundColor Yellow
foreach ($drive in $drives) {
$robocopyexitcode = (start-process robocopy -argumentlist "$drive THISDOESNOTEXIST *.jar *.war *.ear /S /XJ /L /FP /NS /NC /NDL /NJH /NJS /r:0 /w:0 /LOG+:$robocopycsvfile" -WindowStyle hidden -wait -passthru).exitcode
}
if (($? -eq $true) -or ($robocopyexitcode -eq '0') -or ($robocopyexitcode -eq '1')){
Write-Host "Robocopy Scan of applicable files appears to have succeeded ($robocopyexitcode)." -ForegroundColor Green
$robocopycsv = $true
#apparently robocopy unicode logging isn't true unicode.
# THerefore the log formatting needs manipulating to make sure the characters read through corrctly eg specifying as OEM/UNICODE/UTF32 encoding
if ($psversiontable.psversion -ge '3.0') {
$log4jfilescan = import-csv $robocopycsvfile -header FilePath -delimiter "|" -encoding OEM
}
else {
$log4jfilescan= import-csv $robocopycsvfile -header FilePath -delimiter "|"
}
$log4jfilenames = $log4jfilescan
}
}
catch {
# Robocopy exit codes are... odd Trying to circumvent a perceived failure
if (($robocopyexitcode -ne '0') -and ($robocopyexitcode -ne '1')){
Write-Host "WARNING: Robocopy Scan appears to have failed ($robocopyexitcode). Falling back to GCI.." -ForegroundColor Yellow
foreach ($drive in $drives) {
# multiple filetypes requires -include rather than -filter
# working on PS 2.0 Compatibility
if ($psversiontable.psversion -ge '3.0') {
$log4jfilescan += get-childitem $drive -file -include *.jar,*.war,*.ear -rec -force -ea 0
}
else {
$log4jfilescan += get-childitem $drive -include *.jar,*.war,*.ear -rec -force -ea 0
}
}
if ($? -eq $true) {
$log4jfilenames = ($log4jfilescan).fullname
}
else {
Write-Host "Get-ChildItem found $($log4jfilescan.count) files but posted an unsuccesful execution." -ForegroundColor Cyan
if (test-path $robocopycsvfile) {
if ($psversiontable.psversion -ge '3.0') {
$log4jfilescancsv = import-csv $robocopycsvfile -header FilePath -delimiter "|" -encoding OEM
}
else {
$log4jfilescancsv = import-csv $robocopycsvfile -header FilePath -delimiter "|"
}
Write-Host "Robocopy found $(($log4jfilescancsv.count) -1) files but posted an unsuccesful execution ($robocopyexitcode)." -ForegroundColor Cyan
}
$log4jfiles = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - ERROR: Unable to succesfully scan files"
$log4jvulnerable = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - ERROR: Unable to succesfully scan files"
$log4jvulnerablefilecount = '-1'
Write-Host $log4jfiles -ForegroundColor Red
Exit 1
}
}
}
}
}
Function Detect-CloudOnlyFiles {
<# Working on detection of Cloud-Only JAR Files - File Attributes are Offline and Archive?
$offLineAttribute = [System.IO.FileAttributes]::Offline
Get-ChildItem C:\ -file -filter *.jar -rec -ea 0 | Where { $_.Attributes -eq $offLineAttribute} | Foreach {Write-Host "Unable to Scan $_.Fullname due to being a Onedrive Cloud-Only File" -ForegroundColor Red}
#>
Foreach ($Drive in $Drives) {$robocopyexitcode = (start-process robocopy -argumentlist "$drive NULL *.jar *.war *.ear /S /ia:O /XJ /L /FP /NS /NC /NDL /NJH /NJS /r:0 /w:0 /LOG+:c:\temp\cloudonly.csv" -WindowStyle hidden -wait -passthru).exitcode}
}
Function Process-PotentiallyVulnerableFiles {
if ($robocopycsv -eq $true) {
$log4jpotentiallyvulnerablefiles = $log4jfilescan | foreach-object {
if (($null -ne $_.FilePath) -and ($_.FilePath -ne "")) {
if (($_.FilePath -notmatch "placeholder.jar") -and ($_.FilePath -notmatch "spool\\drivers")) {
if (Test-Path "$($_.FilePath)") {
select-string "JndiLookup.class" "$($_.FilePath)"
if ($? -eq $False) {
Write-Host "ERROR: Unable to scan $($_.FilePath)" -ForegroundColor Red
$log4junscanned = $log4junscanned + "<br>ERROR: Unable to scan $($_.FilePath)"
}
}
else {
Write-Host "ERROR: Path not found $($_.FilePath)" -ForegroundColor Red
$log4junscanned = $log4junscanned + "<br>ERROR: Path not found $($_.FilePath)"
}
}
}
} | select-object -exp Path | sort-object -unique
}
else {
$log4jpotentiallyvulnerablefiles = $log4jfilescan | foreach-object {
if (($_ -ne $null) -and ($_ -ne "")) {
if ($_ -notmatch "placeholder.jar") {
#write-host "CHECKING : " $_ -ForegroundColor Yellow
if (Test-path "$_") {
select-string "JndiLookup.class" "$_"
if ($? -eq $False) {
Write-Host "ERROR: Unable to scan $_" -ForegroundColor Red
$log4junscanned = $log4junscanned + "<br>ERROR: Unable to scan $_"
}
}
else {
Write-Host "ERROR: Path not found $_ " -ForegroundColor Red
$log4junscanned = $log4junscanned + "<br>ERROR: Path not found $($_.FilePath)"
}
}
}
} | select-object -exp Path | sort-object -unique
}
}
Function Process-VulnerableFiles {
try {
<#
if ($PSVersionTable.PSVersion -lt '5.0'){
Add-Type -Path C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.IO.Compression.FileSystem\v4.0_4.0.0.0__b77a5c561934e089\System.IO.Compression.FileSystem.dll
}
else {
#>
Add-Type -AssemblyName System.IO.Compression.FileSystem
}
Catch {
Write-Host "Message: $($_.Exception.Message)"
Write-Host "StackTrace: $($_.Exception.StackTrace)"
Write-Host "LoaderExceptions: $($_.Exception.LoaderExceptions)"
$log4junscanned = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') ERROR: Unable to proceed with scan of the $(($log4jpotentiallyvulnerablefiles).count) potentially vulnerable JAR/WAR/EAR files so will be unable to determine their sussceptability to Log4Shell`n$log4jpotentiallyvulnerablefiles"
#Write-Host $log4junscanned -ForegroundColor Red
$log4jvulnerablefilecount = '-1'
}
if ($log4jvulnerablefilecount -ne '-1') {
Foreach ($log4jpotentiallyvulnerablefile in $log4jpotentiallyvulnerablefiles) {
$jartoscan = [io.compression.zipfile]::OpenRead($log4jpotentiallyvulnerablefile)
$potentiallyvulnerable = $false
$CVE202145105patchedjar = $false
$CVE202144228patchedjar = $false
foreach ($Entry in $jartoscan.Entries) {
if ($Entry.Name -eq "JndiLookup.class") {
$potentiallyvulnerable = $true
}
elseif ($Entry.Name -eq "JndiManager.class") {
try {
$stream = $Entry.Open()
$reader = New-Object IO.StreamReader($stream)
$jarattributes = $reader.ReadToEnd()
# Apache Log4j 2.17 Fix. (Thx to Robby S - b-Inside)
$CVE202145105patchedjar = $jarattributes | Select-String -Pattern "isJndiContextSelectorEnabled" -Quiet
# Apache Log4j 2.15/2.16 Fix
$CVE202144228patchedjar = $jarattributes | Select-String -Pattern "allowedJndiProtocols" -Quiet
}
catch {
Write-Output $_
Write-Output "Result: ERROR"
exit 1
}
finally {
# Need the checks since we don't know where the try statements might fail
if ($reader) {
$reader.Close()
}
if ($stream) {
$stream.Close()
}
if ($jartoscan) {
$jartoscan.Dispose()
}
}
}
}
if ($potentiallyvulnerable -and $CVE202145105patchedjar) {
$CVE202145105patchedjarfiles += @($log4jpotentiallyvulnerablefile)
Write-Host "$($log4jpotentiallyvulnerablefile | split-path -leaf) has been fully patched to the current standard (Log4J 2.17)" -ForegroundColor Green
}
else {
$log4jvulnerablefiles += @($log4jpotentiallyvulnerablefile)
# which addresses all 3 known Log4Shell related Vulnerabilities: CVE-2021-44228, CVE-2021-45046, CVE-2021-45105
if ($potentiallyvulnerable -and $CVE202144228patchedjar) {
$CVE202144228patchedjarfiles += @($log4jpotentiallyvulnerablefile)
Write-Host "$($log4jpotentiallyvulnerablefile | split-path -leaf) has been patched to Log4j 2.15/2.16 which addresses `Log4Shell` (CVE-2021-44228), however is still vulnerable to CVE-2021-45105" -ForegroundColor Red
}
else {
Write-Host "$($log4jpotentiallyvulnerablefile | split-path -leaf) has not been patched to Log4j 2.15/2.16/2.17 and is possibly susceptable to the recent RCE (Log4Shell) and DoS exploits." -ForegroundColor Red
}
}
}
$log4jvulnerablefiles = $log4jvulnerablefiles | sort-object -Unique
if ($null -eq ($log4jvulnerablefiles).count) {
$log4jvulnerablefilecount = '0'
}
else {
$log4jvulnerablefilecount = ($log4jvulnerablefiles).count
}
}
}
Function Process-Output {
Write-Host "Log4J Vulnerable File Count: $log4jvulnerablefilecount" -ForegroundColor Cyan
if ($log4jvulnerablefilecount -eq '0') {
if ($null -eq $log4junscanned) {
$log4jvulnerable = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') OK: 0 Vulnerable JAR/WAR/EAR files were found"
write-host "`n`nStatus" -foregroundcolor Cyan
Write-Host "$log4jvulnerable" -ForegroundColor Green
}
else {
$log4jvulnerablefilecount = '-1'
}
}
if ($log4jvulnerablefilecount -eq '-1') {
write-host "`n`nStatus" -foregroundcolor Cyan
Write-Host "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') ERROR: Certain Files were unable to be scanned and will require further inspection to determine whether they are actually vulnerable" -ForegroundColor Yellow
Write-Host $log4junscanned -ForegroundColor Red
Write-Host "Log4J Vulnerable Files: $log4jvulnerablefilecount" -ForegroundColor Red
$log4jvulnerable = $log4junscanned
}
if ([decimal]$log4jvulnerablefilecount -ge '1') {
write-host "`n`nStatus" -foregroundcolor Cyan
Write-Host "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') WARNING: $log4jvulnerablefilecount Vulnerable File(s) found:" -foregroundcolor Red
$log4jvulnerablefiles | Foreach-object ({ Write-Host $_ -ForegroundColor Red })
Write-Host "Recommend that these Files be updated to utilize Log4J 2.17 at the earliest opportunity" -ForegroundColor Cyan
$log4jvulnerable = $log4jvulnerablefiles -join '<br>'
if ($null -ne $log4junscanned){
Write-Host "$log4junscanned" -ForegroundColor Red
$log4jvulnerable = "$log4jvulnerable<br>$log4junscanned"
}
}
}
#endregion
. Scan-Files
if ($null -eq $log4jfilescan) {
$log4jfiles = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') OK - No JAR/WAR/EAR Files were found on this device"
$log4jvulnerable = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') OK - No JAR/WAR/EAR Files were found on this device"
$log4jvulnerablefilecount = '0'
Write-Host "$log4jvulnerable" -ForegroundColor Green
}
else {
Write-Host "Determining whether any of the $(($log4jfilenames).count) jar/war/ear files found are potentially vulnerable to CVE-2021-44228 (Log4Shell) due to being capable of JNDI lookups..." -ForegroundColor Yellow
if ($null -eq $log4jfilescan) {
$log4jpotentiallyvulnerablefiles = $null
}
elseif ($null -ne $log4jfilescan) {
. Process-PotentiallyVulnerableFiles
}
$log4jpotentiallyvulnerablefilecount = ($log4jpotentiallyvulnerablefiles).count
if (($null -eq $log4jpotentiallyvulnerablefiles) -and ($null -eq $log4junscanned)){
$log4jvulnerable = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') OK - 0 Vulnerable JAR/WAR/EAR files were found"
write-host "Log4J CVE-2021-44228 Potentially Vulnerable Files:`n$log4jvulnerable" -ForegroundColor Green
}
elseif ($null -ne $log4jpotentiallyvulnerablefiles) {
Write-Host "$log4jpotentiallyvulnerablefilecount Potentially Vulnerable JAR/WAR/EAR file(s) were found:" -foregroundcolor Red
$log4jpotentiallyvulnerablefiles | Foreach-object ({ Write-Host $_ -ForegroundColor Red })
Write-Host "`nChecking the $($log4jpotentiallyvulnerablefiles.count) potentially vulnerable files for an actual vulnerability now that Log4j 2.17 has been released..." -foregroundcolor Yellow
. Process-VulnerableFiles
}
. Process-Output
}
if ($robocopycsv -eq $true) {
$log4jfiles = get-content $robocopycsvfile -readcount 0 | ForEach-Object{$_ -join '<br>'}
start-sleep 5
remove-item $robocopycsvfile -force
}
else {
$log4jfiles = $log4jfilenames -join '<br>'
}