KEMBAR78
Move .NET method invocation logging to after the needed type conversion is done for method arguments by daxian-dbw · Pull Request #25022 · PowerShell/PowerShell · GitHub
Skip to content

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>)

@daxian-dbw daxian-dbw added the CL-General Indicates that a PR should be marked as a general cmdlet change in the Change Log label Feb 15, 2025
@iSazonov

This comment was marked as off-topic.

@daxian-dbw

This comment was marked as off-topic.

@iSazonov

This comment was marked as off-topic.

@trackd
Copy link

trackd commented Feb 22, 2025

Does this address the perf issues with amsi logging?

#24459 for example, theres a few others

@daxian-dbw
Copy link
Member Author

@iSazonov Let's not sidetrack this PR and keep the perf discussion in those specific issues.
@trackd No, this PR has nothing to do with the perf issue.

@microsoft-github-policy-service microsoft-github-policy-service bot added the Review - Needed The PR is being reviewed label Mar 4, 2025
@daxian-dbw
Copy link
Member Author

daxian-dbw commented Mar 13, 2025

@SeeminglyScience and @TravisEz13 The comment #25022 (review) was addressed. The PR is ready for another pass of review from both of you. Thanks!

Copy link
Collaborator

@SeeminglyScience SeeminglyScience left a comment

Choose a reason for hiding this comment

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

LGTM with one optional suggestion

Copy link
Collaborator

@SeeminglyScience SeeminglyScience left a comment

Choose a reason for hiding this comment

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

LGTM!

@daxian-dbw daxian-dbw merged commit a6005f3 into PowerShell:master Mar 18, 2025
34 checks passed
@daxian-dbw daxian-dbw deleted the amsilog branch March 18, 2025 18:43
@daxian-dbw
Copy link
Member Author

/backport to release/v7.4

@microsoft-github-policy-service
Copy link
Contributor

microsoft-github-policy-service bot commented Mar 18, 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
Copy link
Member Author

/backport to release/v7.5

@github-actions
Copy link
Contributor

github-actions bot commented Mar 18, 2025

Started backporting to release/v7.4: https://github.com/PowerShell/PowerShell/actions/runs/13931074040

GitHub
PowerShell for every system! Contribute to PowerShell/PowerShell development by creating an account on GitHub.

@github-actions
Copy link
Contributor

@daxian-dbw backporting to "release/v7.4" failed, the patch most likely resulted in conflicts:

$ git am --3way --empty=keep --ignore-whitespace --keep-non-patch changes.patch

Applying: Make sure the arguments used with .NET method logging are after the needed type conversion
Using index info to reconstruct a base tree...
M	src/System.Management.Automation/engine/runtime/Binding/Binders.cs
Falling back to patching base and 3-way merge...
Auto-merging src/System.Management.Automation/engine/runtime/Binding/Binders.cs
CONFLICT (content): Merge conflict in src/System.Management.Automation/engine/runtime/Binding/Binders.cs
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip" instead.
hint: To restore the original branch and stop patching, run "git am --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Patch failed at 0001 Make sure the arguments used with .NET method logging are after the needed type conversion
Error: The process '/usr/bin/git' failed with exit code 128

Please backport manually!

@github-actions
Copy link
Contributor

github-actions bot commented Mar 18, 2025

Started backporting to release/v7.5: https://github.com/PowerShell/PowerShell/actions/runs/13931076169

GitHub
PowerShell for every system! Contribute to PowerShell/PowerShell development by creating an account on GitHub.

@github-actions
Copy link
Contributor

@daxian-dbw backporting to "release/v7.5" failed, the patch most likely resulted in conflicts:

$ git am --3way --empty=keep --ignore-whitespace --keep-non-patch changes.patch

Applying: Make sure the arguments used with .NET method logging are after the needed type conversion
Using index info to reconstruct a base tree...
M	src/System.Management.Automation/engine/runtime/Binding/Binders.cs
Falling back to patching base and 3-way merge...
Auto-merging src/System.Management.Automation/engine/runtime/Binding/Binders.cs
CONFLICT (content): Merge conflict in src/System.Management.Automation/engine/runtime/Binding/Binders.cs
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip" instead.
hint: To restore the original branch and stop patching, run "git am --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Patch failed at 0001 Make sure the arguments used with .NET method logging are after the needed type conversion
Error: The process '/usr/bin/git' failed with exit code 128

Please backport manually!

@daxian-dbw daxian-dbw added CL-Engine Indicates that a PR should be marked as an engine change in the Change Log and removed CL-General Indicates that a PR should be marked as a general cmdlet change in the Change Log labels Mar 24, 2025
pwshBot pushed a commit to pwshBot/PowerShell that referenced this pull request Apr 11, 2025
pwshBot pushed a commit to pwshBot/PowerShell that referenced this pull request Apr 14, 2025
@TravisEz13
Copy link
Member

@PowerShell/powershell-maintainers triage decision - More time in the 7.5 release before considering for 7.4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backport-7.4.x-Migrated Backport-7.5.x-Migrated CL-Engine Indicates that a PR should be marked as an engine change in the Change Log

Projects

Development

Successfully merging this pull request may close these issues.

7 participants