Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
396 views
in Technique[技术] by (71.8m points)

.net core - Are you able to use PtrToStringAuto to decrypt a secure string in Powershell 7 on macOS?

I have had no success in getting the following code snippet to output "Hello World!" in PS7

$string = $("Hello World!" | ConvertTo-SecureString -AsPlainText -Force)
[System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($string))

The above code is an example of decrypting a secure string without specifying a length.

This same code works in PS6 and PS5 to fully decrypt the Secure String, but does not work in PS7. The only way around this I have found is to use PtrToStringBSTR. Then it works as expected across all versions of PS for this use case.

I raised an issue at the Powershell repo on Github, but haven't had any responses. I'm honestly just looking for some confirmation that the behavior is the same for others.

https://github.com/PowerShell/PowerShell/issues/11953

I would think something like this would be a breaking change for a lot of code being ported to PS7.

Here is what I have found so far:

Documentation

https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.ptrtostringauto?view=netframework-4.8

According to the documentation, when specifying an integer, PtrToStringAuto:

Allocates a managed String and copies the specified number of characters from a string stored in unmanaged memory into it.

Specifying an int of 11 Returns "Hello", this is because every other char returned is Null. In this case, you must specify an int of 23 to return the complete string "Hello World!" using this method. I have stored the output in a variable to demonstrate this.

$String = $("Hello World!" | ConvertTo-SecureString -AsPlainText -Force)
[System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($string), 23)

$String[0] Returns H
$String[1] Returns NULL
$String[2] Returns E
$String[3] Returns NULL
etc.... 

If no integer is specified, PtrToStringAuto:

Allocates a managed String and copies all characters up to the first null character from a string stored in unmanaged memory into it.

I believe this suggests that either the Secure String is being stored with NULL values, whereas in PS6 it was not, or that the behavior of the PtrToStringAuto function has changed, and now adheres to the behavior the documentation describes above.

This is only an issue on macOS; however, using PtrToStringBSTR in place of PtrToStringAuto to decrypt the Secure String works as expected across windows and macOS.

This seems related: https://stackoverflow.com/a/11022662/4257163

I also do not see anywhere that a change was made.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Note that [securestring] is not recommended for new code anymore.

While on Windows secure strings offer limited protection - by storing the string encrypted in memory - via the DPAPI - and by shortening the window during which the plain-text representation is held in memory, no encryption at all is used on Unix-like platforms.[1]


The only way around this I have found is to use PtrToStringBSTR.

That is not only a way around the problem, PtrToStringBSTR is the method that should have been used to begin with, given that the input string is a BSTR.[2]

Do note that converting a secure string to and from a regular [string] instance defeats the very purpose of using [securestring] to begin with: you'll end up with a plain-text representation of your sensitive data in your process' memory whose lifetime you cannot control.

If you really want to do this, a simpler, cross-platform-compatible approach is:

[System.Net.NetworkCredential]::new('dummy', $string).Password

[1] This is especially problematic when you save a secure string in a file, via ConvertFrom-SecureString or Export-CliXml - see this answer.

[2] The Auto in PtrToStringAuto() means that the unmanaged input string is assumed to use a platform-appropriate character encoding, whereas BSTR is a "Unicode" (UTF-16) string on all platforms. On Windows, an unmanaged string is assumed to have UTF-16 encoding (which is why the code works), whereas on Unix-like platforms it is UTF-8 since .NET Core 3.0 (PowerShell [Core] 7.0 is based on .NET Core 3.1), which explains your symptoms: the NUL chars. in the BSTR instance's UTF-16 code units are interpreted as characters in their own right when (mis)interpreted as UTF-8. Note that .NET Core 2.x (which is what PowerShell [Core] 6.x is based on) (inappropriately) defaulted to UTF-16, which this PR fixed, amounting to a breaking change.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...