Closed endnil closed 2 years ago
I've found that setting a directory to be not accessible is silently ignored, and setting a file in an accessible directory to be not readable reports an error, but also continues. Can you be specific as to what circumstances can result in the scanner not behaving as expected ?
@wdormann did you explicitly (deny) or implicitly (no matching allow) provoke the access denied message, and did you do so on the file, or on the folder level?
The above screenshot is the result of two filesystem operations:
Hello @wdormann I have checked the syntax there is issue with the syntax. make changes in the syntax as per standard syntax, it will fix the issue.
I get no errors running the script in Powershell, but if I run in ISE some servers complete it without displaying the Green play button at completion.
If I run gci 'c:\' -rec -force -include *.jar -ea 0 |
foreach {select-string "JndiLookup.class" $_} | select -exp Path
it returns the attached error saying access is denied, but I have access and assume it's due to a character limit in the file path.
I can't claim to know much of anything about PowerShell, but I've committed the indirect handling of files to look at as 7bab00c4c5da27bdf10533c4eea94e9e9652a267 Since I don't really know what the underlying cause may be, I'll have to rely on anybody who has spoken up here to see if it has changed anything.
I get this error while running script. I am remote signed into the server. Edit: I am logged in as Administrator and running PS in Administrator.
The important question is: Did the scanner stop at the point of encountering the file, or is it simply letting you know that it encountered a file that it cannot read along the way?
The important question is: Did the scanner stop at the point of encountering the file, or is it simply letting you know that it encountered a file that it cannot read along the way?
It stopped and came back with the PS C:\tmp>
prompt.
As an experiment, try changing "-ErrorAction Ignore" to "-ErrorAction SilentlyContinue" in the ps1 script.
As an experiment, try changing "-ErrorAction Ignore" to "-ErrorAction SilentlyContinue" in the ps1 script.
Same error as before. No change.
When the Force parameter is removed its working, however that's NOT the goal. Anyone has a suggestion? Its breaking my scan as well, same as @bewell...
This is shown (removed the path At):
Get-ChildItem : Access is denied
+ CategoryInfo : NotSpecified: (:) [Get-ChildItem], UnauthorizedAccessException
+ FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.GetChildItemCommand
The script still runs and detects exploitable log4j instances, tested this by placing a log4j jar in multiple places on the C drive of a server and running the script.
I do still see the error, but each time it scans it does still detect the vulnerable .jar You can uncomment line 55 to see the scan running on all files.
If it returns to the console line with no warning, that would suggest the script didn't find any vulnerable jars.
That might be what your observing James however for me its crashing on my own Windows 10 machine with access denied (when adding the -Force parameter to Get-ChildItem).
Have you uncommented line 55? You may see it scan through files.
@wdormann might be worth adding a line to output if there are no vulnerable log4j files found.
Yes I tried, odd thing is that it doesn't throw the access denied but it stops scanning after a certain point, i know that cause ive added some additional logging. And I'm able to compare between scans when the Force parameter is removed. The list of files that it scans through is much longer without the force parameter, whilst it should be the other way around...
There's now a note at the end to distinguish between completing and finding nothing, and not completing. I'll probably need a test case to confirm what several seem to be seeing if I want any hope of fixing it, though. That is, for any specific files/directories that cause the script to unexpectedly terminate, I'd need to know what the underlying filesystem properties are that cause it.
Yeah I understand @wdormann, its something ive read before. Also ive written a script myself to tackle the log4net issue with a similar command and that doesnt stop:
$files = Get-Childitem –Path C:\ -Recurse –Force -ErrorAction SilentlyContinue | where-object { $.Name -match 'log4net.dll' -and $.DirectoryName -like 'C:*' } | Select-Object -Property DirectoryName
The difference I don't understand. What's the best way to make the folder (filesystem properties) visible causing this issue?
BTW I have built a script based on checkjndi.ps1 and tested it with Intune as proactive remediation script (running daily). Works fine as long as the Force parameter isnt used. Im managing +700 machines, at least we have something running.
I suppose the benefits of using -Force
(don't ignore hidden files) don't outweigh the risks (potential incomplete scan).
If anybody in this thread is still seeing trouble after it's been removed, speak up now.
I have tried removing the -Force and the script only scans a dozen files before it stops. The scan is being done on the C:\ which definitely has more than a dozen files on it. This is happening on one machine running Windows Server 2012 R2. Any suggestions?
I'm working on a patch - there's some potential for the script to eat accessdenied exceptions at https://github.com/CERTCC/CVE-2021-44228_scanner/blob/8052f456987db1d2e18bcbf95cbc7ee87e9dabfe/checkjndi.ps1#L50 and https://github.com/CERTCC/CVE-2021-44228_scanner/blob/main/checkjndi.ps1#L76 (and anywhere else there's a catch {}
)
This should, at the very least, provide some diagnostic information when the scanner fails to enumerate folders. https://github.com/CERTCC/CVE-2021-44228_scanner/pull/23
I got the latest code, looks like its failing for me when using the force is true param at: "21-12-2021 17:57","Warning","Unable to scan C:\$Recycle.Bin\S-1-5-21-505421461-1408518508-922709458-44569\$IKUETHS.zip : InvalidDataException". Output window shows Get-ChildItem : Access is denied.
That indicates that C:$Recycle.Bin\S-1-5-21-505421461-1408518508-922709458-44569$IKUETHS.zip can't be extracted by System.IO.Compression.ZipFile - which I think is a good indication that it can't be executed by Java, but I'm not sure. That warning means the exception has been handled, and probably isn't causing the crash.
Can you run $Error[-1] | select *
and see if there is any detail available in the exception?
The thing is, the System.IO.Compression.ZipFile
stuff is already wrapped in a try/catch block.
So a file not being a valid zip should be just fine. I suspect that there's something tricky going on. Perhaps the $
is implying that the "file" is really an NTFS ADS?
The thing is, the
System.IO.Compression.ZipFile
stuff is already wrapped in a try/catch block. So a file not being a valid zip should be just fine. I suspect that there's something tricky going on. Perhaps the$
is implying that the "file" is really an NTFS ADS?
I think the zip file is a red herring. The exception is happening elsewhere, hopefully it's being captured by $error.
Same issue as before using the new script.
Same issue as before using the new script.
Run with -Verbose and see what it's telling you it scanned.
Like such: Checkjndi.ps1 C:\ -verbose -force?
Like such: Checkjndi.ps1 C:\ -verbose -force?
$error.clear()
./Checkjndi.ps1 C:\ -verbose -force
$error[-1] | select *
Should give clear out any errors in the session, invoke the script with verbose output and then show the last logged error on the console. This will give you the best shot at finding out what when wrong with the script.
Ive added the latest code to my Intune Remediation script, tweaked it a little, added a logging function, and its working however without the force param but its fine for now. Thanks for all the hard work guys!!
PSMessageDetails :
Exception : System.UnauthorizedAccessException: Access is denied ---> System.ComponentModel.Win32Exception:
Access is denied
--- End of inner exception stack trace ---
at System.Management.Automation.Utils.NativeDirectoryExists(String path)
at System.Management.Automation.SessionStateInternal.IsItemContainer(CmdletProvider provider
Instance, String path, CmdletProviderContext context)
TargetObject :
CategoryInfo : NotSpecified: (:) [Get-ChildItem], UnauthorizedAccessException
FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.GetChildItemCommand
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
ScriptStackTrace : at Get-Files, <...path exhibited...>\Checkjndi.ps1: line 47
at
Another example but I think the same:
PSMessageDetails :
Exception : System.UnauthorizedAccessException: Access is denied ---> System.ComponentModel.Win32Exception:
Access is denied
--- End of inner exception stack trace ---
at System.Management.Automation.Utils.NativeDirectoryExists(String path)
at System.Management.Automation.SessionStateInternal.IsItemContainer(CmdletProvider provider
Instance, String path, CmdletProviderContext context)
TargetObject :
CategoryInfo : NotSpecified: (:) [Get-ChildItem], UnauthorizedAccessException
FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.GetChildItemCommand
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
ScriptStackTrace : at Get-Files, C:\Temp\Checkjndi.ps1: line 47
at
OK, took another crack at it with #27. I know get-childitem can be on the finnicky side (it also fails with very long paths). I think a lot of that is fixed in PowerShell Core - but I really don't know for sure.
now were getting somewhere, the error output is different:
PSMessageDetails :
Exception : System.UnauthorizedAccessException: Access to the path 'C:\Documents and Settings' is denied.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileSystemEnumerableIterator1.CommonInit() at System.IO.FileSystemEnumerableIterator
1..ctor(String path, String originalUserPath, Stri
ng searchPattern, SearchOption searchOption, SearchResultHandler`1 resultHandler, Boolean check
Host)
at System.IO.DirectoryInfo.EnumerateDirectories()
at Microsoft.PowerShell.Commands.FileSystemProvider.Dir(DirectoryInfo directory, Boolean rec
urse, UInt32 depth, Boolean nameOnly, ReturnContainers returnContainers)
TargetObject : C:\Documents and Settings
CategoryInfo : PermissionDenied: (C:\Documents and Settings:String) [Get-ChildItem], UnauthorizedAccessExcepti
on
FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
ScriptStackTrace : at Get-Files, C:\Temp\Checkjndi.ps1: line 50
at
C:\Document and Settings is a junction point, a file system shortcut to c:\users. So let me do explain that im running the script, like I always do, under 'NT authority\system' to simulate it running via Intune (or SCCM). From an elevated command prompt: psexec.exe -i -s %SystemRoot%\system32\cmd.exe.
So how do we bypass this problem :-)
For the Intune remediation script, the base code is perfect, I just add this to the bottom:
try { if ($global:foundvulnerable -eq $false){ Write-Log -Message "No issue found, exit with code 0" -Severity Information exit 0 } else { Write-Log -Message "Issue found, exit with code 1 " -Severity Error exit 1 }
} catch { $errMsg = $_.Exception.Message return $errMsg exit 1 }
Simple logging function:
function Write-Log { [CmdletBinding()] param( [Parameter()] [ValidateNotNullOrEmpty()] [string]$message,
[Parameter()]
[ValidateNotNullOrEmpty()]
[ValidateSet('Information','Warning','Error')]
[string]$severity = 'Information'
)
[pscustomobject]@{
Time = (Get-Date -f g)
Severity = $severity
Message = $message
} | Export-Csv -Path "C:\Windows\Logs\Software\Check_JNDI_Intune_REV2.log" -Append -NoTypeInformation
}
It looks like it's antivirus software interfering in our case (problems even with 7bab00c). With it disabled, $f = get-childitem -path "c:\" -file -recurse -force -erroraction ignore -include "*.zip"
raised no exception, and $f
contained the list of matched files. With the antivirus enabled, the same invocation raised UnauthorizedAccessException
, and $f
was null. Having toggled the antivirus a couple of times, it started working every time, though! This is also only happening on some machines.
Interestingly, removing -include "*.zip"
allowed the call to complete without raising an exception, and a list of files was returned. Replacing -include "*.zip"
with -filter "*.zip"
also worked, although -filter
is more limited.
The best solution for us at this point seems to be to filter manually, with something like $f = get-childitem -path "c:\" -file -recurse -force -erroraction ignore | where { $_.fullname.endswith(".zip") }
.
This SO question talks about problems with Get-ChildItem
and reparse points. I initially tried using Get-ChildItem -Force -Recurse -Attributes !Hidden, !System, !ReparsePoint
as suggested in the second answer, but that didn't solve the problem. The approach using Get-WmiObject
does avoid the exception, but it seems dramatically slower than Get-ChildItem
.
Using the Windows Search Index could be nice, since it returns results almost instantaneously, but Windows Search only includes a limited set of paths by default. Anyway, here's the code I tried, based on that article:
$query = "SELECT System.ItemName, System.ItemPathDisplay, System.ItemTypeText, System.Size FROM SystemIndex WHERE System.FileExtension = '.zip'"
$objConnection = new-object -comobject adodb.connection
$objrecordset = new-object -comobject adodb.recordset
$objconnection.open("Provider=Search.CollatorDSO;Extended Properties='Application=Windows';")
$objrecordset.open($query, $objconnection)
Try { $objrecordset.MoveFirst() }
Catch [system.exception] { "no records returned" }
do
{
Write-host ($objrecordset.Fields.Item("System.ItemName")).value `
($objrecordset.Fields.Item("System.ItemPathDisplay")).value `
($objrecordset.Fields.Item("System.ITemTypeText")).value `
($objrecordset.Fields.Item("System.Size")).value
if(-not($objrecordset.EOF)) {$objrecordset.MoveNext()}
} Until ($objrecordset.EOF)
$objrecordset.Close()
$objConnection.Close()
$objrecordset = $null
$objConnection = $null
[gc]::collect()
Hopefully one of these things helps others.
Does GCI create read-locks prior to attempting to read?
+Since @JaapSchram reported that this happens even when running as NT-AUTHORITY\SYSTEM, this makes me suspect we run into something like Integrity Level Control violations—there's only one thing more powerful than Local System (aka NT-AUTHORITY\SYSTEM) in terms of Integrity Level, and that's TrustedInstaller. (don't get the idea of starting to mess with taking Ownership to bypass that however.)
Also, it should be noted that folders such as C:\Documents and Settings\ aren't 'normal' NTFS junctions. UAC compatibility shimming acts on those folders iirc. (if they were simple junctions, you could browse into them in explorer.) Unfortunately I have very little time to contribute to this, otherwise I'd research this more properly so this is all from hazy memories from years ago, and might have misstated a few things.
@ednil can you attach procmon to powershell while the AV is active? (make sure your user actually has debug permissions, many enterprise environments remove this permission from the administrators group, which makes tools like procmon behave strangely, but without reporting errors.)
Back online, ill test the latest code in a moment or two..
When running with a domain admin account the script runs but with a lot of 'WARNING: Unable to scan C:\Documents and Settings : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand'.
With NT-AUTHORITY\SYSTEM im getting still access denied, and stops running unfortunately.
So indeed @no-identd, I agree with your statement. Ill do one more test and run the script from Intune with the force param enabled.
NT-AUTHORITY\SYSTEM error output:
PSMessageDetails :
Exception : System.UnauthorizedAccessException: Access to the path 'C:\Documents and Settings' is denied.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileSystemEnumerableIterator1.CommonInit() at System.IO.FileSystemEnumerableIterator
1..ctor(String path, String originalUserPath, Stri
ng searchPattern, SearchOption searchOption, SearchResultHandler`1 resultHandler, Boolean check
Host)
at System.IO.DirectoryInfo.EnumerateDirectories()
at Microsoft.PowerShell.Commands.FileSystemProvider.Dir(DirectoryInfo directory, Boolean rec
urse, UInt32 depth, Boolean nameOnly, ReturnContainers returnContainers)
TargetObject : C:\Documents and Settings
CategoryInfo : PermissionDenied: (C:\Documents and Settings:String) [Get-ChildItem], UnauthorizedAccessExcepti
on
FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
ScriptStackTrace : at Get-Files, C:\Temp\Checkjndi.ps1: line 49
at
I really think this should be closed. The original issue (no output if there's an access denied) seems to be resolved. @JaapSchram's testing seems to confirm this. There are going to be a lot of edge cases here, they should probably be tracked individually.
Agreed.
Enough work has been done indeed. I'm able to use the Force param running under NT-AUTHORITY\SYSTEM with the below changed code. It will throw quite often for example - Unable to scan C:\Documents and Settings : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand" But it wont stop, thanks all.
function Get-Files { $jars = @();
Get-Childitem –Path C:\ -File -Recurse -Force -ErrorAction SilentlyContinue -ErrorVariable UnscannablePaths | where-object { ($_.Name -like '*.jar' -or $_.Name -like '*.war' -or $_.Name -like '*.ear' -or $_.Name -like '*.zip' -or $_.Name -like 'JndiLookup.class' ) -and $_.DirectoryName -like 'C:\*' } | % { $jars += $_.DirectoryName + '\' + $_.Name };
foreach ($Exception in $UnscannablePaths) {
Write-Log -Message "Unable to scan $($Exception.TargetObject) : $($Exception.FullyQualifiedErrorID)" -Severity Warning
}
return $jars
}
Just for posterity, support for accepting input from the pipeline was added in #25, so you can use your own method (or even non-PowerShell software) to enumerate paths, without modifying the script.
If it's text, make sure it's one path per line. If it's PowerShell objects, use the parameter name toplevel
or the alias PSPath
, and pass that into checkjndi.ps1 on the pipeline.
I agree that the initial issue was solved but maybe we should track this in some follow up issue ticket (even if the original participants might lack time to continue working on the deep end of it), I still find this very odd, albeit not quite unexpected. Tagging @markrussinovich & @ionescu007 cuz they're the first two people my brain goes to when thinking about debugging garbage like this before begrudgingly opening an MS call instead (which I won't do here, because ugh MS calls+I lack the time anyway)
Have you guys considered any methods other than GCI for locating target files? You might have a look at robocopy. As surprising as it may seem, robocopy can be an order of magnitude faster than GCI at simply locating files and cataloging them for processing, especially on very large volumes.
$Drives = ([System.IO.DriveInfo]::GetDrives() | Where-Object {$_.DriveType -eq 'Fixed'}).Name
ForEach ($drive -in $Drives) {
$robocopyExitCode = (Start-Process robocopy -ArgumentList "$drive C:\DOESNOTEXIST *.jar *.war *.ear /S /XJ /L /FP /NS /NC /NDL /NJH /NJS /r:0 /w:0 /UNILOG+:$env:temp\log4jfilescan.csv" -WindowStyle Hidden -Wait).ExitCode
}
In fact, there's an example of using robocopy specifically for log4j research here: https://github.com/N-able/ScriptsAndAutomationPolicies/blob/master/Vulnerability%20-%20CVE-2021-44228%20(Log4j)/get-log4jrcevulnerability.ps1
As described in this reddit thread,
Get-ChildItem
can return nothing when an "Access is denied" error (System.UnauthorizedAccessException
) occurs, despite-ErrorAction Ignore
. I found that this happened on some systems when scanning a whole drive from the root directory.The solution suggested by
OPconfused
, i.e. building the list of files manually, worked in my case. The "Access is denied" error is still shown, but the script proceeds to check the files that are found.