Skip to content

Move .NET method invocation logging to after the needed type conversion is done for method arguments#24853

Closed
daxian-dbw wants to merge 2 commits intoPowerShell:masterfrom
daxian-dbw:memberInvokeLog
Closed

Move .NET method invocation logging to after the needed type conversion is done for method arguments#24853
daxian-dbw wants to merge 2 commits intoPowerShell:masterfrom
daxian-dbw:memberInvokeLog

Conversation

@daxian-dbw
Copy link
Member

PR Summary

This PR is to fix a MSRC report about a bug in the .NET method logging implementation. The report is not considered a vulnerability, so I'm submitting the fix in GitHub.

The .NET method invocation logging is currently called before PowerShell does any of the needed type conversion for the method arguments, and it results in inaccurate information being logged to AMSI.

The fix is to move .NET method invocation logging to after the needed type conversion is done for method arguments.
The following is the comparison between before and after the fix when turning on the console logging for the AMSI notification feature:

Before the fix -- it logs .GetType(<Foo>)

C:\>pwsh -noprofile
PowerShell 7.4.6
PS C:\>
class Foo {
>>     [string] ToString() {
>>         return "System.Management.Automation." + ([char[]]@(0x41, 0x6D, 0x73, 0x69, 0x55, 0x74, 0x69, 0x6C, 0x73) -join "")
>>     }
>> }
PS C:\>
$utilTypeName = [Foo]::new()

=== Amsi notification report content ===
<Foo>.new()
=== Amsi notification report success: True ===

=== Amsi notification report content ===
<System.Object>.new()
=== Amsi notification report success: True ===
PS C:\>
$utilType = [PSObject].Assembly.GetType($utilTypeName)

=== Amsi notification report content ===
<System.Reflection.RuntimeAssembly>.GetType(<Foo>)
=== Amsi notification report success: True ===
PS C:\>
exit

After the fix -- it logs .GetType(<System.Management.Automation.AmsiUtils>)

C:\>E:\arena\source\PowerShell\src\powershell-win-core\bin\Debug\net9.0\win7-x64\publish\pwsh.exe -noprofile
PowerShell 7.5.0-preview.3-197-g36740ab4a21a1cd51f8cd795f295a86aa5c91d34
PS C:\>
class Foo {
>>     [string] ToString() {
>>         return "System.Management.Automation." + ([char[]]@(0x41, 0x6D, 0x73, 0x69, 0x55, 0x74, 0x69, 0x6C, 0x73) -join "")
>>     }
>> }
PS C:\>
$utilTypeName = [Foo]::new()

=== Amsi notification report content ===
<Foo>.new()
=== Amsi notification report success: True ===

=== Amsi notification report content ===
<System.Object>.new()
=== Amsi notification report success: True ===
PS C:\>
$utilType = [PSObject].Assembly.GetType($utilTypeName)

=== Amsi notification report content ===
<System.Reflection.RuntimeAssembly>.GetType(<System.Management.Automation.AmsiUtils>)
=== Amsi notification report success: True ===
PS C:\>
exit

PR Context

PowerShell 7.3 added a new feature that will log any .NET method calls and provide it to the IAntimalwareProvider2.Notify call. As an example running the following will result in the string below being provided to the AMSI provider to check before running

$foo = 'bar'
[Console]::WriteLine($foo)
<System.Console>.WriteLine(<bar>)

This allows antimalware providers registered with AMSI to be able to detect the runtime values provided to .NET methods even if they are obfuscated in the code and missed by any static analysis of the scriptblock text being run.

A bug in the implementation means that the provided args in the LogMemberInvocation method that builds the string to provide to AMSI are the ones before they are casted when actually invoking them.

For any .NET method that accepts a string we can now provide a custom class with a ToString method that returns the value we want to provide to the .NET call but the AMSI notify string value will just be the class name. With this we can now bypass the checks that may be in place to stop things like the below which would typically be picked up by AMSI due to the presence of the keyword AmsiUtils in the string.

[PSObject].Assembly.GetType("System.Management.Automation.AmsiUtils")

Repro Steps

Run the following PowerShell script

class Foo {
    [string] ToString() {
        return "System.Management.Automation." + ([char[]]@(0x41, 0x6D, 0x73, 0x69, 0x55, 0x74, 0x69, 0x6C, 0x73) -join "")
    }
}
$utilTypeName = [Foo]::new()
$utilType = [PSObject].Assembly.GetType($utilTypeName)

The type name string provided at runtime to GetType is System.Management.Automation.AmsiUtils but AMSI will see the following in the member invocation log, masking the true intention of the call.

<System.Reflection.RuntimeAssembly>.GetType(<Foo>)

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 1 out of 1 changed files in this pull request and generated no comments.

@daxian-dbw
Copy link
Member Author

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 4 pipeline(s).

@daxian-dbw daxian-dbw requested a review from TravisEz13 January 23, 2025 19:01
@daxian-dbw daxian-dbw self-assigned this Jan 23, 2025
@microsoft-github-policy-service microsoft-github-policy-service bot added the Review - Needed The PR is being reviewed label Jan 31, 2025
@daxian-dbw

This comment was marked as outdated.

@azure-pipelines

This comment was marked as outdated.

@microsoft-github-policy-service microsoft-github-policy-service bot removed the Review - Needed The PR is being reviewed label Feb 13, 2025
@daxian-dbw

This comment was marked as outdated.

@azure-pipelines

This comment was marked as outdated.

@daxian-dbw daxian-dbw closed this Feb 13, 2025
@daxian-dbw daxian-dbw reopened this Feb 13, 2025
@daxian-dbw
Copy link
Member Author

/azp run PowerShell-CI-macos, PowerShell-CI-Linux, PowerShell-CI-Windows, PowerShell-CI-static-analysis, PSResourceGet ACR, PowerShell-Windows-Packaging-CI

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@daxian-dbw
Copy link
Member Author

Superseded by #25022

@daxian-dbw daxian-dbw closed this Feb 15, 2025
@microsoft-github-policy-service
Copy link
Contributor

microsoft-github-policy-service bot commented Feb 15, 2025

📣 Hey @daxian-dbw, how did we do? We would love to hear your feedback with the link below! 🗣️

🔗 https://aka.ms/PSRepoFeedback

@daxian-dbw daxian-dbw deleted the memberInvokeLog branch February 15, 2025 00:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant