Author Archives: bautersj

How CSCBE’s “Modbusted” challenge came to be

About the CSCBE

The Cyber Security Challenge Belgium (CSCBE) is a typical Capture-The-Flag (CTF) competition aimed at students from universities and colleges all over Belgium. All of the CSCBE’s challenges are created by security professionals from many different organisations.  The “Modbusted” challenge was created by Jonas B, one of NVISO’s employees.

First, some statistics about the Modbusted challenge during this year’s CSCBE qualifiers. The challenge was rated as quite easy, with a score of 40 points. It was solved by 43 teams, with the first solve by team “Riskture”, about 1 hour and 6 minutes into the CSCBE 2018. Unsurprisingly, all 8 teams that made it to the finals were able to solve it.

Before diving into the details of how it was created, we’ll give you the chance to try out the challenge yourself:

Oh no, we’ve discovered some weird behaviour in a crucial part of our nuclear power plant! In order to investigate the problem, we attached some monitoring equipment to our devices. Could you investigate the captured traffic? Hopefully we’re not compromised …

You can download the PCAP file here: CSCBE_Modbusted

After you’ve solved it, or if you just want to see how the PCAP was created, read on below!

Intro to Modbusted

Another fitting title for this blog post would have been “PCAP manipulation 101”. For this challenge, we didn’t want to generate a PCAP from scratch, since that would probably have required multiple VMs to simulate machines and additional effort to generate some realistically-looking traffic. That’s why we went for an existing PCAP containing sample Modbus traffic, more precisely DigitalBond’s Modbus test data part 1.

This traffic will serve as the legitimate traffic. In the PCAP, we can discern some master devices (for example a human-machine interface), with IPs ending in .57 and .9 that are querying slave devices with IPs .8 and .3 (for example PLCs) using Modbus TCP.

Generating the data exfiltration traffic

The malicious traffic will consist of some kind of encoded data exfiltration from an infected master system towards a fake slave device, which simulates a physical device attached to the network that further communicates with the adversary using a 4G connection. So for this part, two virtual machines were in fact needed to generate the traffic.

For the slave machine simulation, there is a Java application called “ModbusPal” that is able to emulate a Modbus slave. To communicate with the slave, the master can use “mbtget”, a command line Modbus client written in Perl. For the data exfiltration part, we wrote a short script to convert the flag letter per letter into its ASCII code and send that code as Modbus’ unit identifier.


for i in $(seq 1 ${#word}); do 
code=$(echo $(printf "%d" "'${word:i-1:1}")); 
mbtget -r1 -u $code -n 1

The unit identifier specifies the slave address, but this is often not used, since the slave is usually identified by its IP address. Only in case multiple devices are connected to a single IP, will there be different unit identifiers. As a result, communication containing a lot of different unit identifiers should arouse suspicion.

Will it blend?

Okay, we have simulated data exfiltration using the Modbus TCP protocol. However, the traffic in our malicious PCAP looks nowhere near like the traffic in the original PCAP. So, can we make it blend in?

First of all, while running the script and capturing the Modbus exfiltration traffic, there is also some undesired noise generated by the virtual machines. To get rid of this, we can use a tool called “editcap”, which allows us to delete certain entries or split the PCAP. For example, to delete the first 2 ARP entries in the image below, we run the command below:



editcap Malicious_traffic1.pcap Malicious_traffic2.pcap 1 2

Next up is changing the source and destination addresses. We can do this using the “tcprewrite” tool. However, unless you only have unidirectional traffic, we’ll need some extra processing to specify the bidirectional traffic flows. That’s where “tcpprep” comes into play.

Using tcpprep, it’s possible to determine the communication flows in the PCAP and thus mark servers and clients to correctly substitute information. Here, we use the port mode, which is applied when you want to use the source and destination port of TCP/UDP packets to classify hosts. So, by running the following command, we can create a cache file to be used with the tcprewrite command.

tcpprep --port Malicious_traffic2.pcap --cachefile=csc.cache

You can view the information stored in the cachefile by using:

tcpprep --print-info csc.cache

This command also tells us that it marked traffic from the master as “primary” and traffic from the slave as “secondary”. Keeping this in mind, we’ll use the following command to rewrite the layer 3 address information and assign the correct IP addresses. As a result, the master gets the same IP as in the legitimate traffic PCAP, and the slave gets another IP in the same subnet:

tcprewrite --endpoints= --cachefile=csc.cache --infile=Malicious_traffic2.pcap --outfile=Malicious_traffic3.pcap

It’s nice that we managed to simulate the layer 3 addresses, but one look at the MAC addresses and you immediately notice the malicious traffic. So, for that reason we also need to change the layer 2 addressing info:

tcprewrite --enet-smac=00:20:78:00:62:0d,00:90:E8:1A:C3:37 --enet-dmac=00:90:E8:1A:C3:37, 00:20:78:00:62:0d --cachefile=csc.cache --infile=Malicious_traffic3.pcap --outfile=Malicious_traffic4.pcap

For the master, we obviously chose the same MAC address as the original traffic (00:20:78:00:62:0d). To make the slave less suspicious, we couldn’t keep it at its current value, giving away the VM environment. Instead of picking a random value, we went for a MAC address belonging to MOXA (00:90:E8:…), an industrial device provider. With these actions completed, our data exfiltration traffic blends in with the legitimate traffic.

Traffic merge

Now we have two PCAPs, one with legitimate traffic, and one with malicous traffic. To finish things up, we have to merge them into one file. Thanks to “tcpreplay“, we can retransmit a PCAP’s traffic over an interface, ànd specify the sending rate.

Since the original PCAP has traffic spread over a period of nearly half an hour, we’ll speed things up a little and set the sending rate to 2 packets per second. For the malicious traffic, we have a lot more packets, so to fit the same time frame, we need to set the rate to 5 packets per second.


Running both of the tcpreplay commands with Wireshark listening on the interface produces the PCAP that we want!

Jonas is a senior security consultant at NVISO, where his main focus is infrastructure assessments. Next to defensive tasks, such as architecture reviews, he also likes to get offensive and perform network pentesting and red teaming. You can find Jonas on LinkedIn.

“We are happy to share our knowledge with the community and thus hope you enjoyed the content provided on this article! If you’re interested in receiving dedicated support to help you improve your cyber security capabilities, please don’t hesitate to have a look at the services NVISO offers! We can help you define a cyber security strategy / roadmap, but can also offer highly technical services such as security assessments, security monitoring, threat hunting & incident response ( Want to learn similar techniques? Have a look at the SEC599 SANS class, which was co-authored by NVISO’s experts! (”

Analyzing obfuscated scripts using nothing but a text editor

In this blog post, we will perform an analysis on some obfuscated scripts that we received. These files were already detected by automated scanners but as these are mainly malware droppers, we figured it could be interesting to do some manual analysis to determine where the actual malware is hosted.

The first sample we will investigate is a .wsf file. This type of file is a Windows script file and can contain various scripting languages. In this case, we’re dealing with an obfuscated VBScript.


Due to the obfuscation, it’s impossible to see on first sight what this script is trying to accomplish. We could run it in a virtual machine and dynamically monitor actions taken by the script such as network connections or processes started, but first we’d like to have an idea of the code.

Some script code structures that are interesting to look for are functions that execute commands, such as:

  • Execute
  • Eval
  • ExecuteGlobal
  • Exec

In the script above, we can identify some “eval” statements and “executeglobal”. When the script is started, it will first run “InItIalIZe()”, followed by “ExecGlOBal()”. Let’s take a look at the contents of the Initialize function.


The first statement assigns a large encoded string to a parameter. Not much we can do there. The second statement performs a split on the encoded string using “chr(EvAl(31080/740))”. If you calculate 31080/740 from the top of your head, you get 42. Unsurprisingly, using “chr()”, this is translated to “*”, which is used as the splitting character in the obfuscated string. Don’t panic.

In the next step, a loop runs through all the integers from the string above, translates them to characters and appends them to one large string. This string is then passed as the parameter to the “ExecuteGlobal” command.


We can piggyback on the deobfuscation that the malicious script is already performing by itself. Instead of letting it execute the deobfuscated code in the “ExecuteGlobal” command, we replace this command by something that will print the parameter to output. For Windows scripting, we can do this using “Wscript.echo”.


Using a command prompt and the “wscript” command, we can run the modified script. Our printing command shows us the decoded output in a pop-up window, which is too small to contain the entire code:


Copy pasting the entire text is not feasible either. Luckily, there’s an alternative: “cscript”. Using “csript file.wsf > deobfuscated.txt” we can print the deobfuscated string to a new file.

The result is similar to what we started with. We will follow the same approach again and replace the “execute” with a new printing statement and save the script as .vbs. Alternatively, we could surround the script with a “” and “// <![CDATA[
” tag and specify the scripting language in the “” tag. Then we could save and run the script as .wsf, similarly to the original script.


Now we get something that is actually readable. Nice! On first sight, we can see the script runs some persistence commands, including: writing the script to startup folders and adding some registry keys. This is interesting but these are not the juicy details we were looking for. We’ve added the persistence code below, along with the relevant parameters.


What follows is a loop that looks a lot more interesting. We can see that it makes a POST call using host and port parameters that are defined at the start of the script. The user-agent is set using pre-defined parameters as well (client & auth). This might allow the malware authors to determine what version of their malware is trying to communicate back to them. This is also a reason why user-agent string monitoring can be interesting for security analysts. Anomalies such as these can easily be detected.


Based on the result of this POST call, the script continues its execution through one of three functions. All of them make use of the function “D()”, that is defined a bit further in the code, presumably for deobfuscation of the POST response. The two quotes functions seem to be unused (for now).


If we want to further analyse this script, we need to have a response from the command and control server. The time to actually run the script has come!

Running the script

When executing the script and sniffing the traffic using Wireshark, we can see the POST call flying by, containing the user-agent set by the script. The response is another encoded string, prefixed with “s0”. We can use Wireshark’s “Export Objects” functionality to extract the response. If we go back to the script, we can see a case statement for this string. Looks like “Func0” will be executed on our newly obtained string! Time to let the script do the deobfuscation by replacing the execution command in “Func0” and pasting the obfuscated C&C response in our script! Note that we snipped most of the obfuscated string for the image below. Don’t forget to quit the loop or it’ll keep on running.


This final script we receive is not obfuscated at all. Looks like this malware is fingerprinting our computer. Adding an additional “echo” command would show us the information contained in the “ret” parameter. We can give you a small hint: it’s our computer and user name, OS, anti-virus,.. being sent through a POST call to the C&C. We have also observed this call in the traffic capture from before, so we could export the contents through Wireshark as well. There’s a sample of the AV fingerprinting code below.


Unfortunately, we won’t be able to figure out what the other functions (Func1 & Func2) do, since the command and control server did not send us any other command. Some possible causes could be that it decided our system is not interesting based on the information it received or it might just have been in an information gathering phase. If you want more information on this malware, look for Houdini!