Intro
We have a VM image with this challenge. This challenge has so many layers. This writing will be quite a long one. Let’s open the image and see what it’s going on.
Write-Up
When we start the VM, we see that it’s an openSUSE based VM. We choose “Start bootloader from a read-only snapshot” and see that it has
- openSUSE Leap 15.2 (5.3,18-(pl52,75,2021 -08-26T14:58,post,zypp(zypper))
- openSUSE Leap 15.2 (5,3.18-p152,75,2021-08-26T14:58 pre,zypp(zypper))
- openSUSE Leap 15.2 (5.3.18-p152.75,2021-06-07T14:26,post, zypp(zypper);
- openSUSE Leap 15,2 (5.3,18-p 152.75,2021 -06-07T14:26,pre,zypp(zypper))
These boot entries show that we are dealing with something related to zypper
We log in to the VM with root and flare, start our long journey.
We have following files in root directory
.bash_history .bash_profile .bashrc .gnupg .viminfo Documents bin
-
.bash_history
#1622565496 ip a #1623075831 zypper refresh #1623075992 zypper in --no-confirm openssh #1629999465 poweroff
So this file confirms that we are indeed working with
zypper
-
.bash_profile
export NUMBER1=2 export NUMBER2=3 export NUMBER3=37
- .bashrc
alias FLARE="echo 'The 13th byte of the password is 0x35'"
- .viminfo
> /tmp/crontab.uM22lg
* 1629990116 0
" 1 23
We see that vim is used to edit the crontab.
crontab -l
* * * * * /usr/lib/zyppe
Let’s transfer this file from VM to our computer and analyze it. We can transfer this file out of the VM with the scp command.
scp root@VM_IP://usr/lib/zyppe ~/Desktop/
It scans all the files in the Documents directory, and then encrypts the file and adds the .broken
suffix to the filename.
The proc is something like below
push rbp
mov rbp, rsp
sub rsp, 3F0h
mov [rbp+var_468], rdi
mov rax, 'terces A'
mov rdx, 'l on si '
mov [rbp+var_60], rax
mov [rbp+var_58], rdx
mov rax, ' a regno'
mov rdx, 'o terces'
mov [rbp+var_50], rax
mov [rbp+var_48], rdx
mov rax, 'emos ecn'
mov rdx, 'wonk eno'
mov [rbp+var_40], rax
mov [rbp+var_38], rdx
I am going to use something fancy to break this encryption. If we decompile this file with Ghidra we get the following result
void encrypt(char *param_1)
{
uint uVar1;
int iVar2;
int aiStack1128 [256];
undefined8 local_68;
undefined8 local_60;
undefined8 local_58;
undefined8 local_50;
undefined8 local_48;
undefined8 local_40;
undefined4 local_38;
undefined local_34;
int local_2c;
int local_28;
int local_24;
int local_20;
int local_1c;
int local_18;
int local_14;
int local_10;
int local_c;
local_68 = 0x7465726365732041;
local_60 = 0x6c206f6e20736920;
local_58 = 0x2061207265676e6f;
local_50 = 0x6f20746572636573;
local_48 = 0x656d6f732065636e;
local_40 = 0x776f6e6b20656e6f;
local_38 = 0x74692073;
local_34 = 0;
for (local_c = 0; local_c < 0x100; local_c = local_c + 1) {
aiStack1128[local_c] = local_c;
}
local_10 = 0;
for (local_14 = 0; local_14 < 0x100; local_14 = local_14 + 1) {
iVar2 = aiStack1128[local_14] + local_10 +
(int)*(char *)((long)&local_68 + (long)(local_14 % 0x34));
uVar1 = (uint)(iVar2 >> 0x1f) >> 0x18;
local_10 = (iVar2 + uVar1 & 0xff) - uVar1;
local_24 = aiStack1128[local_14];
aiStack1128[local_14] = aiStack1128[local_10];
aiStack1128[local_10] = local_24;
}
local_18 = 0;
local_10 = 0;
local_1c = 0;
for (local_20 = 0; local_20 < 0x400; local_20 = local_20 + 1) {
uVar1 = (uint)(local_18 + 1 >> 0x1f) >> 0x18;
local_18 = (local_18 + 1 + uVar1 & 0xff) - uVar1;
uVar1 = (uint)(aiStack1128[local_18] + local_10 >> 0x1f) >> 0x18;
local_10 = (aiStack1128[local_18] + local_10 + uVar1 & 0xff) - uVar1;
local_28 = aiStack1128[local_18];
aiStack1128[local_18] = aiStack1128[local_10];
aiStack1128[local_10] = local_28;
uVar1 = (uint)(aiStack1128[local_18] + aiStack1128[local_10] >> 0x1f) >> 0x18;
local_2c = aiStack1128
[(int)((aiStack1128[local_18] + aiStack1128[local_10] + uVar1 & 0xff) - uVar1)];
param_1[local_20] = param_1[local_20] ^ (byte)local_1c ^ (byte)local_2c;
local_1c = local_2c;
}
return;
}
So it basically creates a table at aiStack1128
and uses that table to encrypt the file. I don’t wanna reverse the algorithm for the table creation at all. Because the last step,
param_1[local_20] = param_1[local_20] ^ (byte)local_1c ^ (byte)local_2c;
is the important step. If we can extract the table at this point, we can easily recover the table and decrypt the file. This line refers to below assembly code.
0040194d 31 ce XOR ESI,ECX
0040194f 89 f2 MOV EDX,ESI
00401951 88 10 MOV byte ptr [RAX],DL
So if we can record cl
at 0040194d, we can recover the table. Let’s write a Unicorn script for this.
from os import write
from unicorn import *
from unicorn.x86_const import *
mask_table = bytearray()
def code_hook(emu, address, size, user_data):
if (address == 0x40194D): # if the hook address 0040194d XOR ESI,ECX, note cl
ecx = emu.reg_read(UC_X86_REG_CL)
mask_table.append(ecx)
return
mu = Uc(UC_ARCH_X86,UC_MODE_64)
BASE = 0x400000
STACK_ADDR = 0x0
STACK_SIZE = 1024*1024
start_addr = 0x401707 # start of encrypt
end_addr = 0x0401964 # end of encrypt
source = '0234567890' # dummy string to test the algorithm
source_addr = 0x555555756000
mu.mem_map(BASE, 1024*1024)
mu.mem_map(STACK_ADDR, STACK_SIZE)
mu.mem_write(BASE,open('./zyppe', 'rb').read()) #open our file
mu.reg_write(UC_X86_REG_RSP, STACK_ADDR + STACK_SIZE - 1) #create the stack
mu.mem_map(source_addr, 0x1000, 0o3) #set the permisson for read and write
mu.mem_write(source_addr, bytes(source,'utf-8')) #read it
mu.reg_write(UC_X86_REG_RDI, source_addr) #point register to source address
mu.reg_write(UC_X86_REG_RAX, source_addr)
mu.hook_add(UC_HOOK_CODE, code_hook) # add breakpoint for every step
mu.emu_start(start_addr, end_addr)
final = ''.join('0x{:02x}, '.format(x) for x in mask_table)
print(final)
f = open('./mask.bin', "wb")
f.write(mask_table)
f.close()
If we run this script, we will have our mask.bin in the current directory. Let’s use that mask.bin and decrypt all the files in the Documents directory. Here is another script for that.
import glob
import os
def readFile(filename):
with open(filename,'rb') as f:
bytes = f.read()
f.close()
return bytes
def writeFile(filename,bytes):
with open(filename,'wb') as f:
f.write(bytes)
f.close()
def decrypt(filename,mask):
src = readFile(filename)
bytes = bytearray()
v2 = 0
for i in range(len(src)):
byte = src[i]
byte ^= mask[i] ^ v2
v2 = mask[i]
bytes.append(byte)
original_name = os.path.basename(filename).replace('.broken','')
print("Decrypted %s" % original_name)
writeFile('./Decrypted/' + original_name,bytes)
mask = readFile('./mask.bin')
for filename in glob.iglob('./Documents/' + '*.broken', recursive=True):
decrypt(filename,mask)
We have decrypted all the files and let’s check each file one by one. Fasten your seat belt, it’ll be a long ride.
shopping_list.txt
/
[U]don noodles
[S]trawberries
[R]eese's
/
[B]anana chips
[I]ce Cream
[N]atillas
/
[D]onuts
[O]melettes
[T]acos
So we have a file at /usr/bin/dot which asks for a password.
unagi.txt
The 1st byte of the password is 0x45
ugali.txt
Ugali with Sausages or Spaghetti is tasty. It doesn’t matter if you rotate it left or right, it is still tasty! You should try to come up with a great recipe using CyberChef.
So we know that we should try to decrypt Sausages
and Spaghetti
with CyberChef.
sausages.txt
Rotate left and we get
The 2st byte of the password is 0x34
spaghetti.txt
Rotate left and we get
In the FLARE language “spaghetti” is “c3BhZ2hldHRp”.
strawberries.txt
Rotate left and we get
In the FLARE team we like to speak in code. You should learn our language, otherwise you want be able to speak with us when you escape (if you manage to escape!). For example, instead of “strawberries” we say “c3RyYXdiZXJyaWVz”.
udon_noddles.txt
“ugali”, “unagi” and “udon noodles” are delicious. What a coincidence that all of them start by “u”!
raisins.txt
VGhlIDNyZCBieXRlIG9mIHRoZSBwYXNzd29yZCBpcy4uIGl0IGlzIGEgam9rZSwgd2UgZG9uJ3QgbGlrZSByYWlzaW5zIQo=
If we base64 decode this string we get
The 3rd byte of the password is.. it is a joke, we don’t like raisins!
rasberries.txt
The 3rd byte of the password is: 0x51
reeses.txt
V2UgTE9WRSAiUmVlc2UncyIsIHRoZXkgYXJlIGdyZWF0IGZvciBldmVyeXRoaW5nISBUaGV5IGFyZSBhbWF6aW5nIGluIGljZS1jcmVhbSBhbmQgdGhleSBldmVuIHdvcmsgYXMgYSBrZXkgZm9yIFhPUiBlbmNvZGluZy4K
If we base64 decode this string we get
We LOVE “Reese’s”, they are great for everything! They are amazing in ice-cream and they even work as a key for XOR encoding.
So we know that some files are xored with Reese's
backberries.txt
Xor decrypted result
If you are not good in maths, the only thing that can save you is to be a bash expert. Otherwise you will be locked here forever HA HA HA!
banana_chips.txt
Xor decrypted result
Are you good at maths? We love maths at FLARE! We use this formula a lot to decode bytes: “ENCODED_BYTE + 27 + NUMBER1 * NUMBER2 - NUMBER3”
We have another encryption. Remember the bashprofile file so our encryption algorithm is basically `ENCODEDBYTE + 27 + 2 * 3 - 37`
blue_cheese.txt
The 4th byte of the password is: 0x35
ice_cream.txt
If we decrypt this file with the above algorithm, we get
If this challenge is too difficult and you want to give up or just in case you got hungry, what about baking some muffins? Try this recipe: 0 - Cinnamon 1 - Butter 150gr 2 - Lemon 1/2 3 - Eggs 3 4 - Sugar 150gr 5 - Flour 250gr 6 - Milk 30gr 7 - Icing sugar 10gr 8 - Apple 100gr 9 - Raspberries 100gr
Mix 0 to 9 and bake for 30 minutes at 180
ice_coffee.txt
Decrypt the file, we get this
The only problem with RC4 is that you need a key. The FLARE team normally uses this number: “SREFBE” (as an UTF-8 string). If you have no idea what that means, you should give up and bake some muffins.
So we know that our next algorithm is RC4 and we will use SREFBE
as key. But there is another trick in here. We will use the muffin recipe to convert this key. Our key is 493513
instant_noodles.txt
The 5th byte of the password is: 0xMS This one was quite puzzling. It didn’t make any sense at all. Remember the recipe above. M —> Milk So it is 6 and S —> Sugar So it is 4 this means The 5th byte of the password is 0x64
nachos.txt
If we decrypt this file with RC4 by using 493513
as key, we get
In the FLARE team we really like Felix Delastelle algorithms, specially the one which combines the Polybius square with transposition, and uses fractionation to achieve diffusion.
When we google this we get another cipher called Bifid Cipher
natillas.txt
If we decrypt this file with RC4 by using 493513
as key, we get
Do you know natillas? In Spain, this term refers to a custard dish made with milk and KEYWORD, similar to other European creams as crème anglaise. In Colombia, the delicacy does not include KEYWORD, and is called natilla. If we google this sentence we find out that our keyword is EGGS
.
nutella.txt
The 6th byte of the password is: 0x36
donuts.txt
We use Bifid Cipher Decode with EGGS
as key and we get Did you know that Giovan Battista Bellaso loved microwaves?
This text says that we have a Bellaso chipper(Viginere) and our key is microwaves
.
dumplings.txt
Are you missing something? You should search for it better! It’s hidden, but not really. This one says that we have a hidden file somewhere
.daiquiris.txt
The 7th byte of the password is: 0x66
oats.txt
We use Viginere and decode it. We get
You should follow the FLARE team in Twitter. They post a bunch of interesting stuff and have great conversation on Twitter! https://twitter.com/anamma_06 https://twitter.com/MalwareMechanic
omelettes.txt
You should follow the FLARE team in Twitter. Otherwise they may get angry and not let you leave even if you get the flag. https://twitter.com/anamma_06 https://twitter.com/osardar1 https://twitter.com/MalwareMechanic
We check the above Twitter addresses and see the conversation between them. This conversation shows that for AES encryption we should use the following parameters
- KEY: Sheep should sleep in a shed15.2
- IV: PIZZA00000000000
oranges.txt
The 8th byte of the password is: 0x60
tacos.txt
Decrypt with AES and we get
Woow! It seems you are very very close to get the flag! Be careful when converting decimal and hexadecimal values to ASCII and hurry up before we run out of tacos!
tiramisu.txt
Decrypt with AES and we get
The 9th byte of the password is the atomic number of the element moscovium The 10th byte of the password is the bell number preceding 203 The 12th byte of the password is the largest known number to be the sum of two primes in exactly two different ways The 14th (and last byte) of the password is the sum of the number of participants from Spain, Singapore and Indonesia that finished the FLARE-ON 7, FLARE-ON 6 or FLARE-ON 5
tomatoes.txt
Decrypt with AES and we get
It seems you are close to escape… We are preparing the tomatoes to throw at you when you open the door! It is only a joke… The 11th byte of the password is the number of unique words in /etc/Quijote.txt The 13th byte of the password is revealed by the FLARE alias
If we combine all those values, we get the following.
- The 1st byte of the password is 0x45
- The 2st byte of the password is 0x34
- The 3rd byte of the password is: 0x51
- The 4th byte of the password is: 0x35
- The 5th byte of the password is: 0x64
- The 6th byte of the password is: 0x36
- The 7th byte of the password is: 0x66
- The 8th byte of the password is: 0x60
- The 9th byte of the password is: 0x73
- The 10th byte of the password is: 0x34
- The 11th byte of the password is: 0x6c
- The 12th byte of the password is: 0x44
- The 13th byte of the password is: 0x35
- The 14th byte of the password is: 0x73
Our password
- Hex: 45 34 51 35 64 36 66 60 73 34 6c 44 35 49
- ASCII: E4Q5d6f`s4lD5I
If we run dot
and use the above password we finally get our flag
H4Ck3r_e5c4P3D@flare-on.com
Flare-On 2021 Write-ups