2

I would like to run command mkdir C:\Temp\555 in Powershell via a Base64-encoded string.

This code should work

$4 = "bWtkaXIgQzpcVGVtcFw1NTU="

$code = [Text.Encoding]::Utf8.GetString([Convert]::FromBase64String($4))

powershell $code

but if i convert $code to ASCII, how can I run it like that, because this one won't work:

$4 = "bWtkaXIgQzpcVGVtcFw1NTU="

$code = [char]091+[char]084+[char]101+[char]120+[char]116+[char]046+[char]069+[char]110+[char]099+[char]111+[char]100+[char]105+[char]110+[char]103+[char]093+[char]058+[char]058+[char]085+[char]116+[char]102+[char]056+[char]046+[char]071+[char]101+[char]116+[char]083+[char]116+[char]114+[char]105+[char]110+[char]103+[char]040+[char]091+[char]067+[char]111+[char]110+[char]118+[char]101+[char]114+[char]116+[char]093+[char]058+[char]058+[char]070+[char]114+[char]111+[char]109+[char]066+[char]097+[char]115+[char]101+[char]054+[char]052+[char]083+[char]116+[char]114+[char]105+[char]110+[char]103+[char]040+[char]036+[char]052+[char]041+[char]041

powershell $code

because the last code doesn't work while the first one does, any help please?

0

2 Answers 2

4

Note: This answer addresses common use cases first; for a discussion of what you tried, see the bottom section.


Note: If you just want to execute commands stored in a string from inside a PowerShell session, without needing to run commands in a child process, use Invoke-Expression, but do note that Invoke-Expression should generally be avoided:

Note: For illustrative purposes, I'm substituting command Get-Date -UFormat "%s" for your original command, mkdir "C:\Temp\555". This Get-Date command prints the current point in time as a Unix timestamp, i.e., an integer denoting the seconds since 1 Jan 1970, midnight UTC; e.g., 1565229614 for Wednesday, August 7, 2019 10:00:13 PM ETD.

$cmd = 'Get-Date -UFormat "%s"'
Invoke-Expression $cmd  # Execute the string $cmd as code.

If you control construction of the commands, it's better to store pieces of code as script blocks ({ ... }) inside a PowerShell session:

$cmd = { Get-Date -UFormat "%s" }  # create a script block
& $cmd                             # execute the script block

How to pass a complex command to a PowerShell child process via its CLI, using the -EncodedCommand parameter:


  • From outside PowerShell, use the -EncodedCommand parameter, which requires a Base64 encoding of the bytes of a UTF-16LE-encoded string ("Unicode"-encoded), not of a UTF-8-encoded one:
# Get the Base64-encoding of the bytes that make up the UTF-16LE
# ("Unicode") encoding of string 'Get-Date -UFormat "%s"'
# Assigns the following to $base64Cmd: 
#   'RwBlAHQALQBEAGEAdABlACAALQBVAEYAbwByAG0AYQB0ACAAIgAlAHMAIgA='
$base64Cmd = 
  [System.Convert]::ToBase64String(
     [System.Text.Encoding]::Unicode.GetBytes(
       'Get-Date -UFormat "%s"'
     )
  )

powershell -EncodedCommand $base64Cmd  # executes: Get-Date -UFormat "%s"

Note: The above assumes Windows PowerShell. If you're using PowerShell Core, use pwsh instead of powershell.
Specifying the System. component in PowerShell type literals explicitly is optional; e.g., [System.Text.Encoding] can be shortened to [Text.Encoding].


  • From inside PowerShell, there's no need to pass a Base64-encoded string via
    -EncodedCommand explicitly, because PowerShell does that implicitly when you simply pass the code to execute as a script block({ ... }):
powershell { Get-Date -UFormat "%s" }   # -command (-c) parameter implied

Note:

  • You cannot refer to the caller's variables in a script block, but you can pass arguments to it via the -args parameter; e.g.:
    powershell { Get-Date -UFormat $args[0] } -args '%s'.

Behind the scenes, the appropriate Base64 encoding is performed, and the encoded command is passed to -EncodedCommand, as follows:

# The command to execute:
$cmd = 'Get-Date -UFormat "%s"'

# Obtain a Base64-encoded version of the command from the bytes of its
# UTF-16LE encoding.
$base64Cmd = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($cmd))

# Pass the result to the -EncodedCommand parameter:
powershell -EncodedCommand $base64Cmd -inputFormat xml -outputFormat xml

Note:

  • -inputFormat xml -outputFormat xml are automatically added when you pass a script block to the (possibly positionally implied) -Command / -c parameter.

  • They trigger CLIXML serializing of the arguments passed to as well as the output from the CLI call.

  • This serialization is the same as the one used by PowerShell's remoting / background-job infrastructure and has two benefits:

    • PowerShell's output streams are preserved (whereas passing a string -Command merges all output into the single standard stdout stream).
    • Arguments and output are passed and received as objects (rather than as text only, as happens when you pass a string -Command), albeit with the same limitations on type fidelity as in remoting - see this answer for more information.
  • The equivalent of using -args to pass arguments to a script block is to pass an explicitly Base64-encoded argument list to -encodedArguments (-ea):

    • This parameter - undocumented as of this writing - additionally requires serializing the argument list to XML (CLIXML format) before Base64-encoding the result, as demonstrated in this answer.

As for what you tried:

  • Your 1st command works, because you're passing a decoded plain-text version of the mkdir command to powershell.exe, which implicitly binds to the -Command parameter and is executed as a command in the new PowerShell session.
    As an aside: pwsh, PowerShell Core's CLI, now defaults to -File, so -Command (or -c) would have to be used explicitly.

  • Your 2nd command does not work, because $code now contains the plain text of the Base64-decoding command from your 1st command.
    That command references variable $4, which the new PowerShell instance you're creating knows nothing about.

However, instead of trying to defer the decoding of the Base64-encoded mkdir command to the new PowerShell session, it makes much more sense to pass the Base64-encoded command directly to the new session (if a new session is even needed, see above), via -EncodedCommand.

However, -EncodedCommand requires a Base64 encoding based on UTF-16LE, not UTF-8 - see above for how to produce such an encoding explicitly (if needed).

If you're given a UTF-8-based Base64 encoding, you can translate it into a UTF-16LE-based one as follows:

# UTF-8-based Base64-encoding
$4 = "bWtkaXIgQzpcVGVtcFw1NTU="

# Decode to plain text.
$plainTextCode = [Text.Encoding]::Utf8.GetString([Convert]::FromBase64String($4))

# Re-encode to Base64 via UTF-16 ("Unicode"):
$utf16Base64Command = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($plainTextCode))

# Pass to powershell.exe via -EncodedCommand
powershell -EncodedCommand $code
Sign up to request clarification or add additional context in comments.

Comments

0

A string can be executed as a script through the CommandInvocationIntrinsics class method InvokeScript.

powershell $executioncontext.InvokeCommand.InvokeScript($code)

See InvokeScript Method for more information.

1 Comment

$executioncontext.InvokeCommand.InvokeScript($code) is a more obscure way of calling Invoke-Expression $code (strictly speaking, & { Invoke-Expression $code }, but that the child scope makes no difference here) except that errors aren't surfaced. It is worth noting that a much more sensible way to pass the Base64-encoded mkdir command is to directly pass it to powershell -EncodedCommand; however, for that to work the Base64 encoding would have to be based on UTF16-LE, not on UTF-8.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.