PowerShell RC4

Every language needs an RC4 implementation. Despite its insecurities, RC4 is widely used due to its simple algorithm and the minimal amount of code it takes to implement it. Some people have even tried to fit implementations into single tweets. It’s commonly used by malware due to its low overhead, and I’m actually shocked that RosettaCode doesn’t have an entry for RC4.

The only PowerShell implementation I’m aware of is Remko Weijnen’s code here, and as far as I know .NET doesn’t include an RC4 implementation that we can take advantage of. This post will cover a ‘proper’-(esque) implementation of RC4, a practical ‘minimized’ version, and a version that I collaborated with some PowerShell madmen on in the quest to get it under 140 characters for a tweet.

RC4 Background

Read the Wikipedia page if you’re actually curious.

Proper Implementation

Without further ado:

function ConvertTo-Rc4ByteStream {
<#
.SYNOPSIS
Converts an input byte array to a RC4 cipher stream using the specified key.
Author: @harmj0y
License: BSD 3-Clause
Required Dependencies: None
Optional Dependencies: None
.PARAMETER InputObject
The input byte array to encrypt with the RC4 cipher.
.PARAMETER Key
The byte array of the RC4 key to use.
.EXAMPLE
$Enc = [System.Text.Encoding]::ASCII
$Data = $Enc.GetBytes('This is a test! This is only a test.')
$Key = $Enc.GetBytes('SECRET')
($Data | ConvertTo-Rc4ByteStream -Key $Key | ForEach-Object { "{0:X2}" -f $_ }) -join ' '
.LINK
https://en.wikipedia.org/wiki/RC4
http://www.remkoweijnen.nl/blog/2013/04/05/rc4-encryption-in-powershell/
#>
[CmdletBinding()]
Param (
[Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True)]
[ValidateNotNullOrEmpty()]
[Byte[]]
$InputObject,
[Parameter(Position = 1, Mandatory = $True)]
[ValidateNotNullOrEmpty()]
[Byte[]]
$Key
)
begin {
# key-scheduling algorithm
[Byte[]] $S = 0..255
$J = 0
0..255 | ForEach-Object {
$J = ($J + $S[$_] + $Key[$_ % $Key.Length]) % 256
$S[$_], $S[$J] = $S[$J], $S[$_]
}
$I = $J = 0
}
process {
# pseudo-random generation algorithm (PRGA) combined with XOR logic
ForEach($Byte in $InputObject) {
$I = ($I + 1) % 256
$J = ($J + $S[$I]) % 256
$S[$I], $S[$J] = $S[$J], $S[$I]
$Byte -bxor $S[($S[$I] + $S[$J]) % 256]
}
}
}
# minimized RC4 function
$R={$D,$K=$Args;$S=0..255;0..255|%{$J=($J+$S[$_]+$K[$_%$K.Length])%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_bxor$S[($S[$I]+$S[$H])%256]}}
$Enc = [System.Text.Encoding]::ASCII
$UEnc = [System.Text.Encoding]::UNICODE
$Data = $Enc.GetBytes('This is a test! This is only a test.')
$Key = $Enc.GetBytes('SecretPassword')
($Data | ConvertTo-Rc4ByteStream Key $Key | ForEach-Object { "{0:X2}" -f $_ }) -join ' '
(& $R $data $key | ForEach-Object { "{0:X2}" -f $_ }) -join ' '
$Enc = [System.Text.Encoding]::ASCII
$D = $Enc.GetBytes('This is a test! This is only a test.')
$K = $Enc.GetBytes('SecretPassword')
# *almost* in a single tweet
-join[Char[]]([Text.Encoding]::Unicode|% *es '匤〽⸮㔲㬵⠤匤簩笥䨤⠽䨤␫孓弤⭝䬤⑛╟䬤䌮畯瑮⥝㈥㘵␻孓弤ⱝ匤⑛嵊␽孓䨤ⱝ匤⑛嵟㭽䐤╼⑻㵉⬫䤤㈥㘵␻㵈␨⭈匤⑛嵉┩㔲㬶匤⑛嵉␬孓䠤㵝匤⑛嵈␬孓䤤㭝弤戭潸⑲孓␨孓䤤⭝匤⑛嵈┩㔲崶⁽')|IEX

view raw
RC4.ps1
hosted with ❤ by GitHub

Yes, there’s not a completely proper PRGA implementation, but this was partly to take advantage of PowerShell’s pipelining. This was the approach that made the most sense to me. It requires byte arrays for the -InputObject and -Key, so here’s how you use it:

Note that it can accept an -InputObject byte array on the pipeline, or by calling the parameter, your choice.

Minimization Take 1

The minimization idea came about from doing training prep, where we wanted a practical RC4 implementation for decrypting, or executing, malware communications. For malware staging, space and (some) obfuscation matters, so proper script ‘etiquette’ can mostly be thrown out the window. The optimization for this function is heavily due to @lee_holmes, @mattifestation, @secabstraction, and @tifkin_, which will come to properly crazy fruition in the last section.

Here’s the code:

For the optimization, we’re taking advantage of a few things- I’ll try to outline a few here as it was an interesting thought exercise:

  • Uninitialized variables are assumed to be $Null/0 – $J in the KSA, $I and $H (replacing $J) in the PRGA.
  • We obviously dropped pipeline support, and used nameless/lambda functions (@mattifestation‘s idea) to cut down a bit more.
  • Spaces? Who need spaces?

PowerShell has its own form of Lambda functions – anonymous script blocks that can serve as functions without a formal name. There’s more information on PowerShell and Lambda functions from @mattifestation. These functions can be invoked with the call operator (&) like the following:

Getting Weird With PowerShell

So how can we cut this down even more to fit into a tweet? Matt had the great idea of converting the ASCII representation of our logic to bytes and then repacking those bytes as UNICODE. Since an ASCII character is encoded as 8 bits/1 byte and UNICODE is encoded with 16 bits/2 bytes, we can pack two ASCII characters into a single UNICODE encoding. Here’s how we can accomplish this in PowerShell:

So we can cut our script down to len(script)/2 + len(decoding logic). Unfortunately, we were only able to get the logic down to 141 characters with piping to IEX, so I tweeted out the version that just echoed the V3+ algorithm (if anyone can shave another few characters off, let us know). Before running, $D needs to be initialized as the data array and $K initialized as the key array. The weird % *es bit is a PowerShell v3.0+ shortcut for ForEach-Object -MemberName *es, which returns the GetBytes() method for the [Text.Encoding]::Unicode instantiation. This is a quick way to call [System.Text.Encoding]::Unicode.GetBytes(‘UNICODE’).

The UNICODE packing technique seems like it might have additional use cases for offensive obfuscation, but this is an exercise left to the reader.

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.