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

I am available for new work
Interested? Feel free to reach