# CSCBE Challenge Write-up – Sufbo

The Sufbo challenge was tackled during the Cyber Security Challenge qualifiers and proved to be very difficult to solve. This write-up gives you a possible way of solving it!

Credits

All challenges of the Cyber Security Challenge are created by security professionals from many different organisations. The Sufbo challenge in particular was created by Adriaan Dens, one of our distinguished challenge contributors, from Proximus. Adriaan & Proximus have contributed multiple challenges over the years and they tend to be pretty hard to solve ;).

The challenge

And you thought Assembly was hard to read? Try this!

The solution

The challenge consists out of a heavily obfuscated piece of perl code. We can start by cleaning up the code which improves the readability by a small bit:

print"Flag: ";
chomp(\$_=&lt;&gt;);
\$[=0;
die if y///c!=32;
chomp(@_=&lt;DATA&gt;);
\$/=join'',map{chr(ord\$_^\$=)}split//,pack"H*",shift(@_).shift(@_);

while(\$,=substr\$_,8*\$-,8) {
(\$@,\$*,\$#,\$x,\$y,\$z,\$!,\$.,\$,) = (unpack("N*",\$/.\$,),0,2**31*(sqrt(5)-1),(1&lt;&lt;32)-1);
map {
\$!+=\$.;
\$!&amp;=\$,;
\$y+=(((((\$z&lt;&lt;4)&amp;\$,)+\$@)&amp;\$,)^((\$z+\$!)&amp;\$,)^(((\$z&gt;&gt;5)+\$*)&amp;\$,));
\$y&amp;=\$,;
\$z+=(((((\$y&lt;&lt;4)&amp;\$,)+\$#)&amp;\$,)^((\$y+\$!)&amp;\$,)^(((\$y&gt;&gt;5)+\$x)&amp;\$,));
\$z&amp;=\$,;
\$"=pack("N*",\$y,\$z);
\$/=\$"x2
}0..31;
die if\$"ne pack"H*",\$_[\$-];
\$-++;
}
print "OK\n"

__DATA__
6c594e50630d4f63
7d515d4655525b1d
7872575285c742da
15c670798094a00b
54f08c6b937ed1f2
6810afed7372cd76

This code might still not mean a lot to the average non-perl-speaking-person. Let’s take a look at the same code, but with some inline comments:

print"Flag: "; # Prints "Flag: " to STDIN
chomp(\$_=&lt;&gt;); # Reads in the input into the variable \$_
\$[=0; # Changes the starting index of an array to 0 (It's a useless command actually)
die if y///c!=32; # y///c is a Perl golfing idiom that is similar to length(\$_), so the length of your input has to be a string of length 32.
chomp(@_=&lt;DATA&gt;); # Store the data below (under __DATA__) in the array @_
\$/=join'',map{chr(ord\$_^\$=)}split//,pack"H*",shift(@_).shift(@_); # Shift the first two elements of @_, "unhexify" the strings, split them per character, XOR with \$= (default value is 60), and join the characters back in the variable \$/.
#
while(\$,=substr\$_,8*\$-,8) { # While there are 8 characters left in the input do:
(\$@,\$*,\$#,\$x,\$y,\$z,\$!,\$.,\$,) = (unpack("N*",\$/.\$,),0,2**31*(sqrt(5)-1),(1&lt;&lt;32)-1); # Convert the variable \$/ (unknown) and \$, (our input) to unsigned numbers, assign 0 to \$!, assign 2**31*(sqrt(5)-1) to \$/ and assign (1&lt;&lt;32)-1 to \$,.
map { # Use map to loop 32 times (see below)
\$!+=\$.; # Add \$. to \$!
\$!&amp;=\$,; # Bitwise AND \$! with \$,
\$y+=(((((\$z&lt;&lt;4)&amp;\$,)+\$@)&amp;\$,)^((\$z+\$!)&amp;\$,)^(((\$z&gt;&gt;5)+\$*)&amp;\$,)); # Some bitwise operations added to \$y
\$y&amp;=\$,; # Bitwise AND \$y with \$,
\$z+=(((((\$y&lt;&lt;4)&amp;\$,)+\$#)&amp;\$,)^((\$y+\$!)&amp;\$,)^(((\$y&gt;&gt;5)+\$x)&amp;\$,)); # Some bitwise operations added to \$z
\$z&amp;=\$,; # Bitwise AND \$z with \$,
\$"=pack("N*",\$y,\$z); # Convert the unsigned numbers back to string representation
\$/=\$"x2 # Set \$/ to two times \$"
}0..31; # Use map to loop 32 times
die if\$"ne pack"H*",\$_[\$-]; # Die if \$" is not equal to the "unhexified" element (\$- contains the index) in @_
\$-++; # Increase the variable \$-
} #
print "OK\n" # Printed if you have the key
#
__DATA__ # Starting the DATA block (kinda like a here document)
6c594e50630d4f63 # This part was used for \$/ in line 6.
7d515d4655525b1d # This part was used for \$/ in line 6.
7872575285c742da # This part was used to compare with the input on line 20
15c670798094a00b # This part was used to compare with the input on line 20
54f08c6b937ed1f2 # This part was used to compare with the input on line 20
6810afed7372cd76 # This part was used to compare with the input on line 20

So now we more or less know what each line does but we still miss context on a higher level (what it is doing). As always in reverse engineering, you try to find some “known parts” which allow you to understand the code a lot faster. These parts are usually strings, metadata, fixed numbers or familiar code blocks.

In our case, we have 2 fixed numbers: $2**31*(sqrt(5)-1)$ and $(1<<32)-1$. In this representation they don’t mean much but if we convert them to hex numbers we get 0x9e3779b9 and 0xffffffff respectively.

Hmm, interesting! Seems like we’ve got a Perl implementation of Tiny Encryption Algorithm (TEA) on our hands here!

More specifically, the while loop block in the code is the actual TEA implementation, which decrypts the second half of the __DATA__ section using the first half as the key.

Retrieving the key can be done using the following perl one-liner:

perl -E 'say join"",map{chr(ord\$_^\$=)}split//,pack"H*","6c594e50630d4f637d515d4655525b1d"

Which yields us “Perl_1s_Amazing!” as the key.

So now we have they key and the data to be decrypted. Let’s be lazy and copy the reference code listed on the wikipedia page we found earlier.

#include&lt;stdint.h&gt;
#include&lt;stdio.h&gt;

void decrypt (uint32_t* v, uint32_t* k) {
uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i; /* set up */
uint32_t delta=0x9e3779b9; /* a key schedule constant */
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
for (i=0; i&lt;32; i++) { /* basic cycle start */
v1 -= ((v0&lt;&lt;4) + k2) ^ (v0 + sum) ^ ((v0&gt;&gt;5) + k3);
v0 -= ((v1&lt;&lt;4) + k0) ^ (v1 + sum) ^ ((v1&gt;&gt;5) + k1);
sum -= delta;
} /* end cycle */
v[0]=v0; v[1]=v1;
printf("%x%x", v0, v1);
}

void main() {
/* Our cipher chunks, found in the __DATA__ block of the Perl code */
uint32_t c0[] = { 0x78725752, 0x85c742da };
uint32_t c1[] = { 0x15c67079, 0x8094a00b };
uint32_t c2[] = { 0x54f08c6b, 0x937ed1f2 };
uint32_t c3[] = { 0x6810afed, 0x7372cd76 };

/* The used keys for encrypting */
uint32_t k[] = { 0x5065726c, 0x5f31735f, 0x416d617a, 0x696e6721 }; /* Original key: Perl_1s_Amazing! */
uint32_t k0[] = { 0x78725752, 0x85c742da, 0x78725752, 0x85c742da }; /* c0 . c0 */
uint32_t k1[] = { 0x15c67079, 0x8094a00b, 0x15c67079, 0x8094a00b }; /* c1 . c1 */
uint32_t k2[] = { 0x54f08c6b, 0x937ed1f2, 0x54f08c6b, 0x937ed1f2 }; /* c2 . c2 */

/* Decrypting the chunks */
decrypt(c0,k);
decrypt(c1,k0);
decrypt(c2,k1);
decrypt(c3,k2);
}
\$ gcc --std=c99 solution.c
\$ ./a.out
43534342457b5065726c31735772317465306e6365526534646e33766552727d
\$ ./a.out | perl -nle 'print pack("H*", \$_)'
&gt;&gt;&gt; CSCBE{Perl1sWr1te0nceRe4dn3veRr}

There we go! CSCBE{Perl1sWr1te0nceRe4dn3veRr} was the flag.

# CSCBE Challenge Write-up – Trace Me

This is the first post in a series of write-ups on some of the challenges that were tackled by students during our Cyber Security Challenge Belgium this month.

Credits

All challenges of the Cyber Security Challenge Belgium are created by security professionals from many different organisations. The TraceMe challenge in particular was created by Vasileios Friligkos, one of our distinguished challenge contributors.

The challenge

At your day job, per your recommendation and after many requests, you recently activated host based monitoring using Sysmon.

Perfect! You are now going to have a visibility on each host of your IT system giving you perfect awareness and detection capabilities that will be able to thwart even the most persistent attackers…
Before you can finish your thoughts, you get interrupted by a phone call:
“Steve”, (yes, this is you) says an irritated voice on the other side of the line.
– “Yes…”, replies Steve (yep, still you).
“Your awesome monitoring system did not work, we got an infection.”
– “But there are no detection rules implemented yet, it’s normal that we didn’t… “, you start explaining when you get interrupted.
“At least, tell me you can identify how the infection occurred!”
Eh, yes sure I can…

And by that, the irritated voice (who by the way is your boss) hangs up and sends you one file with the Sysmon log data of the infected host.

Can you identify the benign (non malicious) process that was abused and was ultimately responsible for the infection?
Can you also identify the IP from where the second stage was downloaded (the first connection made by the malware)?

If so, you will be able to save your reputation and also get the points for this challenge by submitting the SHA1 of the abused, benign process (Uppercase) + the IP where the second stage is hosted.

Good luck Steve!

The solution

Evtx is the Windows event file format which makes sense since Sysmon writes to the “Applications and Services Logs/Microsoft/Windows/Sysmon/Operational” event folder as indicated here: https://technet.microsoft.com/en-us/sysinternals/sysmon

There are many ways to start interacting with these events, there is even an official Windows log parser that can query event log data.
If we go this way, we have to download the LogParser and run the following command to extract all logs in csv format:

\$> LogParser.exe -i:EVT -o:csv "SELECT * from sysmon.evtx" > sysmon.csv

This gives us a .csv file with 3.021 log lines of different sizes and types.
By checking the description of Sysmon on the MS site we see that the following types of events can be logged:

• Event ID 1: Process creation
• Event ID 2: A process changed a file creation time
• Event ID 3: Network connection
• Event ID 4: Sysmon service state changed
• Event ID 5: Process terminated
• Event ID 6: Driver loaded
• Event ID 7: Image loaded
• Event ID 8: CreateRemoteThread
• Event ID 9: RawAccessRead
• Event ID 10: ProcessAccess
• Event ID 11: FileCreate
• Event ID 12: RegistryEvent (Object create and delete)
• Event ID 13: RegistryEvent (Value Set)
• Event ID 14: RegistryEvent (Key and Value Rename)
• Event ID 15: FileCreateStreamHash
• Event ID 255: Error

Ok, many interesting events that we could use. In the file, we see that we have events of the following types 1, 2, 3, 5 and 6.
Since we do not have any initial information to start investigating and then pivot until the initial infection, we need to search for abnormal or at least unusual behaviour.

For example, we see that we have only one event ID 6 but by investigating the name of the driver and its SHA we realise that it concerns a legitimate driver.

Since there are not so many logs, we could use excel to try and make some sense by colouring for example the log lines based on the event id.

If we zoom out and simply scroll over the logs, we see that there is a very important network activity at some moment:

By simply investigating, we see that there are many UDP requests to port 6892 by a “roaming.exe” process found in “C:\Users\TestPC\AppData\” and with destination adjacent IPs in the same subnet:

This looks surely suspicious and we could take this lead for our investigation but let’s say that we don’t go this way (Steve doesn’t like excel) and we prefer to put our ninja awk skills into use!

Some parsing is necessary since the comma is a field separator but also found inside the fields and there is much useless information that we can dump.
In this case, let’s choose to substitute the field separator by the pipe ( “|” ) in order to be able to use awk easily, let’s also separate the process creation events (event id 1 – file sysmon_process_creation.csv) and the connections events (event id 3 – file sysmon_connections.csv).

For process creation, we keep the following fields:

EventID|PID|Process|Command|Directory|User|SHA1|PPID|ParentProcess|ParentProcessCommand

Let’s filter the data and search for some unusual execution locations or uncommon process names:

awk -F "|" '{ print "Process:"\$3 }' sysmon_process_creation.csv | sort | uniq -c | sort -rn

We see two executables from the %AppData% directory:

• “Roaming.ExE”
• “OneDrive.exe”

We can pull their SHA1’s and check online whether they are legitimate. Doing so does not reveal clearly if any of them is malicious.

If we try to see the parent processes:

• “Roaming.ExE” -> powershell and roaming.exe
• “OneDrive.exe” -> explorer

Hmm, powershell could be something worth investigating, let’s show also the parent process full command:

Ok, this surely looks bad: powershell launched a hidden download of an executable which was also executed at the end of the command.
So, at last, we have our investigation lead: roaming.exe

For information, we could have used the connections log file to help us spot outliers.
By sorting and counting unique occurrences (similar as for process creation logs) of processes and target IPs we do not have a clear result because we have too many chrome.exe processes reaching to multiple IPs

awk -F "|" '{ printf "Process: %-90s DST:%s:%s\n",\$3,\$13,\$15 }' sysmon_connections.csv | sort | uniq -c | sort -rn

But if we ignore the destination IP and focus only on the destination port, then we should have a clearer view:

awk -F "|" '{ printf "Process: %-90s DST_Port:%s\n",\$3,\$15 }' sysmon_connections.csv | sort | uniq -c | sort -rn

Roaming.exe communicated 1.088 times over port 6892 (on UDP) which when looking online directly leads to Cerber malware.

In both cases, we have roaming.exe which looks malicious and by following its parent process PID we can trace the activities and the initial infection:

• Roaming.exe PID: 1868 was created by powershell.exe PID: 2076
• Powershell.exe PID: 2096 was created by cmd.exe PID: 2152

(We notice that there are two processes with the same PID: 2152 – “cmd.exe” and “Acrobat Reader DC\Reader\reader_sl.exe”; keep in mind that PID’s can be reused)

• Cmd.exe PID: 2152 was created by winword.exe PID: 2232

The parent of winword.exe is explorer.exe which is legitimate and therefore, we can deduce that winword.exe was abused (probably by a macro) and resulted in executing a cmd.exe command that launched a powershell command to fetch the second stage malware (probably cerber according to OSINT).

Therefore, the first part to the solution is the SHA1 of winword.exe:

• CE3538D04AB531F0526C4C6B1917A7BE6FF59938

For the second part, we need to identify the IP of the site from which the second stage was downloaded.
From the powershell command we know that the URL is: footarepu[.]top but instead of resolving the domain name (since it might have changed since the infection), we can find the IP in the sysmon_connections.csv since we have the PID and process name of all the connections.
Searching for powershell.exe PID: 2076 we find one contacted IP over port 80:

• 35.165.86.173

which is the second part of the solution.

Flag: CE3538D04AB531F0526C4C6B1917A7BE6FF59938_35.165.86.173

Good job Steve!