OALabs and Eli Salem inspired my first ever write-up on reverse engineering. Thank you for your contribution to the RE community and all your hard work!
You can check the reference materials at the end of this blog post.
SquirrelWaffle (sounds pretty yummy, no offense for squirrel lovers!) loader usually comes with Qakbot or Cobalt Strike flavors.
Today we will be analyzing the SquirrelWaffle loader with Cobalt Strike.
Let’s analyze the packed sample first.
The initial infection happens via Word document with embedded macro:
Let’s do a quick analysis on the Word doc using olevba:
We have noticed some promising C2 domains and DLLs above. The malicious DLLs are then executed via rundll32.exe:
We also see the Sleep function (not even longer than my nap) before the execution:
The DLLs are being downloaded using the PowerShell System.Net.WebClient method:
A large amount of random strings indicates that this is a packed loader:
How else can you detect that the sample is packed? Try running it in Detect-It-Easy (DiE), here is the comparison of the packed (on the left) and unpacked sample (on the right).
The packed sample has an entropy of 78% and the unpacked sample has an entropy of 46%:
Another weird thing is that you will see interesting names in the exports section instead of ‘ldr’:
To unpack this sample, you can either:
- Use https://www.unpac.me/ — an excellent resource for unpacking most malware samples, saves you a lot of time!
- Manually unpack it via x32dbg. Eli Salem did a thorough walkthrough of how to unpack it manually in the blog post I have mentioned in the references.
There is no preference, and I used both of the methods. If you are new to x32dbg or reversing world like me, I highly recommend practicing unpacking the sample manually.
The manual way of unpacking the sample:
We will need to put a breakpoint on VirtualAlloc, this is when we can see if the malware is allocating space in memory of a new process or creating new regions to stuff the code during the unpacking process
I run until the first VirtualAlloc call, then I will run to the user code. This is where I am seeing another call to the VirtualAlloc:
The instruction rep movsb > transfers the shellcode to the allocated memory
We can see the jump to EAX and if you follow the EAX dump, you should see the hex: E8 00 00 00 00 which means +0 bytes from the next instruction of the shellcode:
Click F9 (run) twice and then execute until return, then step over. You should see another call to VirtualAlloc [ebx+2113BC], click “Follow in Dump” in EAX. You won’t see anything at this point being written to the Dump section.
But it’s not the end of the world, let’s put the breakpoint for “Write” at the first row of the empty hex to observe the contents that will be written there and keep pressing “Run” (F9), until….
Until you see this…
Let’s now put another breakpoint on ‘leave’ and run the debugger one more time:
Doing some Googling on the M8Z header, I have stumbled across OALabs again :D According to OALabs: https://github.com/herrcore/aplib-ripper, MZ becomes M8Z after compressing the PE file using aPlib.
Let’s now see how aPlib will get decompressed and magically turn into MZ. Remove the breakpoints set before and put another breakpoint for “Access” on the first hex row:
Click “Run” or F9. This is how the aPlib decompression looks like:
Let’s follow the EDI registry in dump (this is where our unpacked payload will be decompressed) and set the breakpoint on the third return instruction.
Click “Run” and you should see our MZ header.
You should be able to extract the MZ file at the end of the unpacking process by highlighting the rest of the MZ content, right-click > Binary > Save to a File.
I didn’t have to tweak the Section Headers since the APIs are resolved correctly (Phew…):
The next step would be extracting the configuration from the payload.
We can do it in three ways:
- Debugging it via x32dbg
- Write a Python script
- Use CyberChef
If you choose the first method, you would need to get to the Entry Point of the payload and search for reference strings: APPDATA or TEMP, go to either of the functions and change the EIP register to point to the address of the entry point of APPDATA or TEMP and start the execution from there.
While debugging the payload, you will notice that the IPs are populated after the highlighted “XOR key” string. How do we know it’s a XOR encryption, and how can we test it? Hang tight with me until the end of this blog post.
Further running the payload, you will notice the function memcpy writing the C2 domains:
So, let’s come back to our question of how we determined the XOR key and how we can quickly test the theory.
I will use the commercial version of IDA, but you can test it out with Ghidra or whichever floats your boat.
There is one call (sub_583B50) in the ‘ldr’ export function:
We are seeing our familiar APPDATA and TEMP references:
Let’s open the Pseudocode view with F5:
We see the string that we mentioned above again.
Let’s check the sub_5819B0 function:
We will notice the for loop, and XOR decryption algorithm with a11 being the key length, v14 — pointer, v16 — key, p_Block is the data. The mod (%) and the key length is also a giveaway that this might be a XOR:
Let’s test it out in CyberChef.
We can copy the cute strings as Hex by using an awesome script provided by OALabs: https://github.com/OALabs/hexcopy-ida/blob/main/hexcopy.py. For it to work 1000% — use this script with IDA running on Python 3.
And it works. Kudos to OALabs again!
You can also test out on your own with the other XOR keys by xref-ing the sub_5819B0 function.
Let’s get the domains out:
You can also be creative and practice your Python skills by writing a script to extract the configurations using the XOR decryption algorithm we have noticed above.
OALabs has a great video tutorial on how to write the configuration extractor in Python (check out the reference section).
Conclusion:
I hope you enjoyed my humble write-up. And if you have any recommendations, comments, or concerns, please don’t hesitate to reach out.
References:
- The Squirrel Strikes Back: Analysis of the newly emerged cobalt-strike loader “SquirrelWaffle” by Eli Salem: https://elis531989.medium.com/the-squirrel-strikes-back-analysis-of-the-newly-emerged-cobalt-strike-loader-squirrelwaffle-937b73dbd9f9
- Live Coding A Squirrelwaffle Malware Config Extractor by OALabs: https://www.youtube.com/watch?v=9X2P7aFKSw0
- My modified version of the SquirrelWaffle config extractor: https://github.com/RussianPanda95/SquirrelWaffle/blob/main/squirrelconfig.py
- SquirrelWaffle Loader with Cobalt Strike (Sample): https://www.malware-traffic-analysis.net/2021/09/17/index.html