Flare-On 8 Challenge 10 - wizardcult
We are given a PCAP to start off with this challenge.
PCAP Analysis
Looking at the TCP streams, a command injection seems to be going on.
We can then see a binary named induct being requested and downloaded onto the machine.
We can extract the binary out for analysis.
Binary Analysis
The binary is a golang binary and luckily for us, the symbols are not stripped.
Looking at main_main
, we can see that it creates a new IRC client, attempting to connect to wizardcult.flare-on.com
. It also registers 2 functions to run upon 2 events, main_main_func1
and main_main_func2
.
main_main_func1
is triggered upon successfully connecting to the IRC server while main_main_func2
is triggered upon the PRIVMSG event.
A check is done in main_main_func2
to check if the targeted user is dung3onm4st3r13
.
wizardcult_comms_ProcessDMMessage
is then called to parse the message sent to the user.
Analyzing wizardcult_comms_ProcessDMMessage
The function checks if the message contains the string what is your quest?
and replies with My quest is to retrieve the Big Pterodactyl Fossil
.
If the message contains you have learnt how to create the Potion of
, it will parse the potion name, and perform a lookup of the ingredients via wizardcult_tables_GetBytesFromTable
.
The format of the message can be seen from the PCAP traffic.
Analyzing wizardcult_tables_GetBytesFromTable
The function removes all instances of and
in the message and calls wizardcult_tables_Lookup
in a loop.
The function is called with each word in the list of keywords (in this case, ingredients) parsed from the message, and the specified table (in this case, wizardcult_tables_Ingredients
). The return values are then placed into a slice.
Analyzing wizardcult_tables_Lookup
The function simplies take the word passed in as argument and iterates over each word in the specified table, comparing them via a memequal
. If the words are the same, the index of the word in the table is returned.
Program Loading into VM
The slice of bytes built by wizardcult_tables_GetBytesFromTable
is then fed into wizardcult_vm_LoadProgram
.
Within wizardcult_vm_LoadProgram
, the bytes are being decoded into a vm_Program
object, and some components of the VM is being set up.
.
We can view how the VM program looks like decoded with the help of degob.
At the start of wizardcult_vm_LoadProgram
, encoding_gob___ptr_Decoder__Decode
is called with the address of vm_Program
, which transform a binary blob into a set of well-defined nested structures. Using degob
, the process can be reversed, and we can obtain the opcodes for each potion, revealing that each potion is actually a VM program.
From here, we can go on to trace and follow the VM’s program exection.
However, there is an alternative method to solve this.
Potions
In main_main_func1
we can see that the Potion Of Acid Resistance
is mapped to the dungeon of Graf's Infernal Disco
.
It seems that when this potion is used, the wizardcult_potion_CommandPotion
function will be executed.
Similarly the Potion of Water Breathing
is mapped to the dungeon of The Sunken Crypt
, which will execute wizardcult_potion_ReadFile
.
We now need to investigate how the commands to execute/file to read are passed into these functions.
Looking back at wizardcult_comms_ProcessDMMessage
, we see that when the message contains “you enter the dungeon
”, it splits the descriptions of the dungeon and calls wizardcult_tables_GetBytesFromTable
to perform a lookup using the DungeonDescriptions
table.
We have previously established that the wizardcult_tables_GetBytesFromTable
function returns a slice of indexes, from a slice of words, according to the selected table. We can perform the lookup ourselves by extracting the DungeonDescriptions
table.
The table is a slice
, where the first value is a pointer to the first element in an array of golang strings, with the next 2 int64
values being the capacity and length of the slice
. (Note: the string
and slice
structs has to be created manaully)
We can go to the string element of the array, and declare an string
array of size 0x2e8. We can then export the array out into a file from IDA.
We can now extract the 1st chunk of dungeon descriptions from the PCAP, and perform a lookup of their indexes using the extracted DungeonDescriptions
table.
Decoding the resulting hex bytes would reveal ls /mages_tower
.
This suggests that the C2 server is:
- Instructing command parameters by giving descriptions of a dungeon
- Executing commands based on dungeon name
- Binary performs a lookup of the respective potion to use, based on the dungeon name to execute the commands
- The VM is used to obfuscate/encrypt the data before it is sent back to the server as spell casting text
Decoding the 2nd chunk of dungeon descriptions from the PCAP reveals: /mages_tower/cool_wizard_meme.png
. It is likely that the 2ng huge blob of spell casts is our ciphertext for the PNG holding the flag.
The C2 server can be mimicked by creating an IRC bot. The bot can be found on my Github
Our goal is to find out the mapping of each of plaintext to its ciphertext, generating a reverse lookup table, and retrieve the original PNG.
Breaking The Encryption
After getting the bot out, I generated a 300 byte null-byte file and instructed the binary to read the file.
Even though the file is made up of the same bytes, the ciphertext only repeats every 8 lines. It is also noticed that the spells and damage changes. It suggests the following:
- The spell represents a byte
- The damage represents 2 bytes, delimited by ‘
d
’ - A 24-byte polyalphabetic cipher
Due to the polyalphabetic nature of the cipher, we would need to generate a lookup table of each byte at each of the 24 positions.
|
|
The ciphertext returned by the binary can be cleaned up to be a lookup table.
Getting the flag now is just a matter of reverse mapping.
The lookups and script to solve can be found on my Github