Flare-On 2020 - How not to solve an easy reverse challenge

How not to solve an easy reverse challenge

I decided to give a look to the Flare CTF 2020, it consists of 11 RE tasks on 6 weeks, each tasks rewarding 1 points. This post will describe how I struggled to flag the 2nd task of this CTF.

This task wasn’t supposed to be hard I guess for people used to PE reverse challenge, with packer, IAT rebuild and other related sutff. However, I came from the Unix world and I’m really more used to ELF reverse task.

So let’s have fun of me now.

browsing UPX source code

We start with the following information

λ ackira ~/nextcloud/CTF/flare2020/2  » file garbage.exe
garbage.exe: PE32 executable (console) Intel 80386, for MS Windows, UPX compressed

λ ackira ~/nextcloud/CTF/flare2020/2  » cat Message.txt
One of our team members developed a Flare-On challenge but accidentally deleted it. We recovered it using extreme digital forensic techniques but it seems to be corrupted. We would fix it but we are too busy solving today's most important information security threats affecting our global economy. You should be able to get it working again, reverse engineer it, and acquire the flag.

We know that the PE file is packed with UPX, a well known packer which is quite easy to depack. In order to achieve this, we just have to run the following command :

λ ackira ~/nextcloud/CTF/flare2020/2  upx -d garbage.exe -o unpacked.exe

                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2020
UPX git-d7ba31+ Markus Oberhumer, Laszlo Molnar & John Reiser   Jan 23rd 2020

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
upx: garbage.exe: OverlayException: invalid overlay size; file is possibly corrupt

Unpacked 1 file: 0 ok, 1 error.

We get an error explaining the overlay size is not valid. Let’s grab the source and see where this error could be thrown.

git clone https://github.com/upx/upx.git
cd upx
grep -irn "invalid overlay size;"

Binary file src/upx.out matches
Binary file src/packer.o matches
src/packer.cpp:577:        throw OverlayException("invalid overlay size; file is possibly corrupt");

The code look like this

void Packer::checkOverlay(unsigned overlay)
{
    if ((int)overlay < 0 || (off_t)overlay > file_size)
        throw OverlayException("invalid overlay size; file is possibly corrupt");
    if (overlay == 0)
        return;
    info("Found overlay: %d bytes", overlay);
    if (opt->overlay == opt->SKIP_OVERLAY)
        throw OverlayException("file has overlay -- skipped; try '--overlay=copy'");
}

The value of the overlay is just passed as an argument to a function, lets run upx in gdb in order to get the call stack. But first make sure string demangling is enable in gdb, it could help as it a cpp source code.

gef➤  set print demangle on
gef➤ run
gef➤  info func checkOverlay
All functions matching regular expression "checkOverlay":

Non-debugging symbols:
0x000055555557b800  Packer::checkOverlay(unsigned int) [clone .cold]
0x00005555555d75d0  Packer::checkOverlay(unsigned int)
gef➤  b *0x00005555555d75d0
Breakpoint 2 at 0x5555555d75d0
gef➤  r -d ../../garbage.exe -o unpacked.exe
[...]

gef➤  bt
#0  0x00005555555d75d0 in Packer::checkOverlay(unsigned int) ()
#1  0x00005555555e9645 in void PeFile::unpack0<PeFile32::pe_header_t, LE32, unsigned int>(OutputFile*, PeFile32::pe_header_t const&, PeFile32::pe_header_t&, unsigned int, bool) ()
#2  0x00005555555d8155 in Packer::doUnpack(OutputFile*) ()
#3  0x00005555555f0c9b in do_one_file(char const*, char*) ()
#4  0x00005555555f0e87 in do_files(int, int, char**) ()
#5  0x000055555557c14c in main ()

The function in charge of calling checkOverlay seems to be PeFile::unpack... let’s find it code

λ ackira ~/nextcloud/CTF/flare2020/2/upx/src [master●]  » grep -irn PeFile::unpack
pefile.cpp:2883:void PeFile::unpack0(OutputFile *fo, const ht &ih, ht &oh,
void PeFile::unpack0(OutputFile *fo, const ht &ih, ht &oh,
                     ord_mask_t ord_mask, bool set_oft)
{
    //infoHeader("[Processing %s, format %s, %d sections]", fn_basename(fi->getName()), getName(), objs);

    handleStub(fi,fo,pe_offset);
    if (ih.filealign == 0)
        throwCantUnpack("unexpected value in the PE header");

    const unsigned iobjs = ih.objects;
    const unsigned overlay = file_size - ALIGN_UP(isection[iobjs - 1].rawdataptr
                                                  + isection[iobjs - 1].size,
                                                  ih.filealign);
    checkOverlay(overlay);
    
    [...]
}

The overlay seems to be computed thanks to the ALIGN_UP function which takes argument from the isection structure. This struct give the idea of holding information about an ELF sections.

I will use github powerful code browsing feature to find it definition in pefile.h

__packed_struct(pe_section_t)
char    name[8];
LE32    vsize;
LE32    vaddr;
LE32    size;
LE32    rawdataptr;
char    _[12];
LE32    flags;
__packed_struct_end()

First there is the name of the struct, then some information as it virtual address and size, it size on disk and a pointer to it address on disk (other information are useless for us). Let’s be lazy and print the section name by modifying UPX source code

const unsigned iobjs = ih.objects;
const unsigned overlay = file_size - ALIGN_UP(isection[iobjs - 1].rawdataptr
+ isection[iobjs - 1].size, ih.filealign);

printf("[NAME] : %s\n", isection[iobjs-1].name);

checkOverlay(overlay);
λ 2 ackira ~/nextcloud/CTF/flare2020/2/upx [master●]  » make all
/usr/bin/make -C src all
make[1]: Entering directory '/home/switch/nextcloud/CTF/flare2020/2/upx/src'
Updating .depend
g++ '-DUPX_VERSION_GITREV="d7ba31cab8ce+"' -O2 -fno-delete-null-pointer-checks -fno-strict-aliasing -fwrapv -funsigned-char -Wall -W -Wcast-align -Wcast-qual -Wmissing-declarations -Wpointer-arith -Wshadow -Wvla -Wwrite-strings -Werror -o help.o -c help.cpp
g++ '-DUPX_VERSION_GITREV="d7ba31cab8ce+"' -O2 -fno-delete-null-pointer-checks -fno-strict-aliasing -fwrapv -funsigned-char -Wall -W -Wcast-align -Wcast-qual -Wmissing-declarations -Wpointer-arith -Wshadow -Wvla -Wwrite-strings -Werror -o pefile.o -c pefile.cpp
g++ -O2 -fno-delete-null-pointer-checks -fno-strict-aliasing -fwrapv -funsigned-char -Wall -W -Wcast-align -Wcast-qual -Wmissing-declarations -Wpointer-arith -Wshadow -Wvla -Wwrite-strings -Werror -o upx.out c_file.o c_init.o c_none.o c_screen.o compress.o compress_lzma.o compress_ucl.o compress_zlib.o except.o file.o filter.o filteri.o help.o lefile.o linker.o main.o mem.o msg.o p_armpe.o p_com.o p_djgpp2.o p_exe.o p_lx_elf.o p_lx_exc.o p_lx_interp.o p_lx_sh.o p_mach.o p_ps1.o p_sys.o p_tmt.o p_tos.o p_unix.o p_vmlinx.o p_vmlinz.o p_w32pe.o p_w64pep.o p_wcle.o packer.o packer_c.o packer_f.o packhead.o packmast.o pefile.o s_djgpp2.o s_object.o s_vcsa.o s_win32.o snprintf.o stdcxx.o ui.o util.o work.o -lucl -lz
./../src/stub/scripts/check_whitespace_git.sh ./..
make[1]: Leaving directory '/home/switch/nextcloud/CTF/flare2020/2/upx/src'
/usr/bin/make -C doc all
make[1]: Entering directory '/home/switch/nextcloud/CTF/flare2020/2/upx/doc'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/home/switch/nextcloud/CTF/flare2020/2/upx/doc'


λ ackira ~/nextcloud/CTF/flare2020/2/upx [master●]  » ./src/upx.out -d ../garbage.exe -d ../unpacked.exe
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2020
UPX git-d7ba31+ Markus Oberhumer, Laszlo Molnar & John Reiser   Jan 23rd 2020

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
[NAME] : .rsrc
upx.out: ../garbage.exe: OverlayException: invalid overlay size; file is possibly corrupt
upx.out: ../unpacked.exe: FileNotFoundException: ../unpacked.exe: No such file or directory

Unpacked 1 file: 0 ok, 1 error.

WARNING: this is an unstable beta version - use for testing only! Really.

So the name of the section is .rsrc which mean resources. We can find this section with an hex viewer.

┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
00000000│ 4d 5a 90 00 03 00 00 00 ┊ 04 00 00 00 ff ff 00 00 │MZ×0•000┊•000××00│
00000010│ b8 00 00 00 00 00 00 00 ┊ 40 00 00 00 00 00 00 00 │×0000000┊@0000000│
00000020│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
00000030│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 f8 00 00 00 │00000000┊0000×000│
00000040│ 0e 1f ba 0e 00 b4 09 cd ┊ 21 b8 01 4c cd 21 54 68 │••×•0×_×┊!וL×!Th│
00000050│ 69 73 20 70 72 6f 67 72 ┊ 61 6d 20 63 61 6e 6e 6f │is progr┊am canno│
00000060│ 74 20 62 65 20 72 75 6e ┊ 20 69 6e 20 44 4f 53 20 │t be run┊ in DOS │
00000070│ 6d 6f 64 65 2e 0d 0d 0a ┊ 24 00 00 00 00 00 00 00 │mode.___┊$0000000│
00000080│ 54 78 9d 12 10 19 f3 41 ┊ 10 19 f3 41 10 19 f3 41 │Txו••×A┊••×A••×A│
00000090│ 4b 71 f0 40 1a 19 f3 41 ┊ 4b 71 f6 40 9a 19 f3 41 │Kq×@••×A┊Kq×@ו×A│
000000a0│ 4b 71 f7 40 02 19 f3 41 ┊ 91 72 f6 40 35 19 f3 41 │Kq×@••×A┊×r×@5•×A│
000000b0│ 91 72 f7 40 01 19 f3 41 ┊ 91 72 f0 40 01 19 f3 41 │×r×@••×A┊×r×@••×A│
[...]
000001f0│ 55 50 58 30 00 00 00 00 ┊ 00 e0 00 00 00 10 00 00 │UPX00000┊0×000•00│
00000200│ 00 00 00 00 00 04 00 00 ┊ 00 00 00 00 00 00 00 00 │00000•00┊00000000│
00000210│ 00 00 00 00 80 00 00 e0 ┊ 55 50 58 31 00 00 00 00 │0000×00×┊UPX10000│
00000220│ 00 a0 00 00 00 f0 00 00 ┊ 00 9a 00 00 00 04 00 00 │0×000×00┊0×000•00│
00000230│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 40 00 00 e0 │00000000┊0000@00×│
00000240│ 2e 72 73 72 63 00 00 00 ┊ 00 10 00 00 00 90 01 00 │.rsrc000┊0•000ו0│
00000250│ 00 04 00 00 00 9e 00 00 ┊ 00 00 00 00 00 00 00 00 │0•000×00┊00000000│
00000260│ 00 00 00 00 40 00 00 c0 ┊ 00 00 00 00 00 00 00 00 │0000@00×┊00000000│
00000270│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
[...]

As indicated in the struct definition the first 8 char are the name, then 3 dword and next the pointer on disk for this section data. So it should begin at 0x240 with .rsrc and the pointer should be 00 9e 00 00 = 0x9e00.

Checking it on disk now.

image-20200926231521078

The xml resources is truncated, we can find the complete xml document on the MSDN. It was my only idea at this point, I had no clue if this will be accepted for the given binary.

<?xml version="1.0" encoding="utf-8" standalone="yes"?>  
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">  
  <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>  
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">  
    <security>  
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">  
        <requestedExecutionLevel level="asInvoker"/>  
      </requestedPrivileges>  
    </security>  
  </trustInfo>  
</assembly>  

So I just wrote a tiny python script to append the end of the xml to the binary and tried to uncompress it again.

λ ackira ~/nextcloud/CTF/flare2020/2 python solv.py -o packed.exe

λ 1 ackira ~/nextcloud/CTF/flare2020/2  » upx -d packed.exe -o unpacked.exe
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2020
UPX git-d7ba31+ Markus Oberhumer, Laszlo Molnar & John Reiser   Jan 23rd 2020

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
upx: garbage.exe: OverlayException: invalid overlay size; file is possibly corrupt

The file is style corrupted, lets check the size of the overlay


                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2020
UPX git-d7ba31+ Markus Oberhumer, Laszlo Molnar & John Reiser   Jan 23rd 2020

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
[SECTION SIZE] 1024
[SECTION PTR] 40448
[ALIGN] 41472
[FILE SIZE] 40740
[OVERLAY] 4294966564
upx.out: ../../packed.exe: OverlayException: invalid overlay size; file is possibly corrupt

Unpacked 1 file: 0 ok, 1 error.

So the overlay size is way greater than the file size which is incorrect. After checking the size of the section I found that .rsrc should size 0x400 (offset 0x250 on disk). Let’s pad it with 0.

λ ackira ~/nextcloud/CTF/flare2020/2  » upx -d packed.exe -o unpacked.exe
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2020
UPX git-d7ba31+ Markus Oberhumer, Laszlo Molnar & John Reiser   Jan 23rd 2020

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
     79364 <-     41476   52.26%    win32/pe     unpacked.exe

Unpacked 1 file.

It seems to be unpacked, lets run it in windows env.

image-20200926235629784

After few researches on Google I got some reference to the XML declaration to the end of the file. I decided to check the Event Viewer of windows, especially in Windows Logs > Application and saw this :

image-20200926235910415

The XML syntax is wrong, however I copied it from a trusted source and compared it with other binaries. This had to work unless I’m forgot that it needed \r\n as return char and not only \n.

So the final script is

#!/usr/bin/python3

input = open("garbage.exe", "rb").read()

poc = b"""y>\r
 <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">\r
  <requestedExecutionLevel level="asInvoker"/>\r
 </requestedPrivileges>\r
</security>\r
</trustInfo>\r
</assembly>\r
"""

open("packed.exe", "wb").write(input + poc + b"\x00" * (1024 - 288 - len(poc)) )

Now it should wor…

image-20200927000301116

F. This error is not a good omen, the specified DLL is not … specified. I tried to run the compressed file instead and get nothing by this execution.

A team mate told me to look after IAT rebuilding as often the IAT is not present after dumping the packed file when started through it loader. However I uncompressed the packed file with UPX utility so everything should be present.

I tried to run the loader and to dump the file then, but I couldn’t debug it in x32dbg or windbg as I find out later that the loader was still corrupt.

At this point I had no more idea and accepted to do it without running it.

emulation with unicorn

As I at least successfully dumped the .text section I could reverse it the static way. Loading it in Binary Ninja I found out 3 interesting function :

  • sub_401000 : do some crypto
  • sub_401045 : do some crypto too
  • sub_40106b : which load some base64 look alike code, call the 2 previous functions with the weird base64 stuff and also seems to make call to windows function like CreateFileA and WriteFileA

IDA seems to to recognize call to the win api unlike Binary Ninja and I have no clue why at the moment.

As I can’t run the whole binary I decided to emulate the 3 functions thanks to the unicorn framework. This chall should be an easy one as it’s only the 2nd challenge, this is why I thought I just could emulate 3 functions and get the flag without providing input.

The first step is to map the 3 functions in memory, then to set the base and stack pointer accordingly. But we need to find the offset of theses function on disk. For this I used bgrep (binary grep) and passed their opcode as argument.

For example for the function sub_40106b :

λ ackira ~/nextcloud/CTF/flare2020/2  » bgrep 558bec81 packed.exe
packed.exe: 0000046b
packed.exe: 000008a5
packed.exe: 00000d06
packed.exe: 00003f84
packed.exe: 000046ed
packed.exe: 00004bcb
packed.exe: 00004e50
packed.exe: 00007ad2
packed.exe: 00008714
packed.exe: 0000b873
λ ackira ~/nextcloud/CTF/flare2020/2  » bgrep 558bec81ec3c packed.exe
packed.exe: 0000046b

Then we can specify it size and offset in a python script :

from switch import hx
from unicorn import *
from unicorn.x86_const import *

mu = Uc(UC_ARCH_X86, UC_MODE_32)

unpacked = open("packed.exe", "rb").read()

sub_40106b = unpacked[0x000046b: 0x0000046b + 0x1b0]
sub_401000 = unpacked[0x0000400: 0x0000400 + 0x45]
sub_401045 = unpacked[0x0000445: 0x0000445 + 38]

BASE = 0x0401000
STACK_ADDR = 0x0500000

mu.mem_map(BASE, 1024 * 1024 * 2)

mu.mem_write(BASE + 0x000000 , sub_401000)
mu.mem_write(BASE + 0x0000045 , sub_401045)
mu.mem_write(BASE + 0x000006b , sub_40106b)

mu.mem_write(0x04119F8, b"nPTnaGLkIqdcQwvieFQKGcTGOTbfMjDNmvibfBDdFBhoPaBbtfQuuGWYomtqTFqvBSKdUMmciqKSGZaosWCSoZlcIlyQpOwkcAgw")
mu.mem_write(0x00411A60, b"KglPFOsQDxBPXmclOpmsdLDEPMRWbMDzwhDGOyqAkVMRvnBeIkpZIhFznwVylfjrkqprBPAdPuaiVoVugQAlyOQQtxBNsTdPZgDH")

mu.reg_write(UC_X86_REG_EBP, STACK_ADDR)
mu.reg_write(UC_X86_REG_ESP, STACK_ADDR)

def hook_code(mu, address, size, user_data):

    if mu.reg_read(UC_X86_REG_EIP) == BASE:
        print(green("crypto_1 called"))

    print("[ 0x%x ] size = 0x%x" % (address, size))
    print("> IP = 0x%x" % mu.reg_read(UC_X86_REG_EIP))
    print(
        "> SP = 0x%x    BP = 0x%x"
        % (mu.reg_read(UC_X86_REG_ESP), mu.reg_read(UC_X86_REG_EBP))
    )
    print(
        "> RAX = 0x%x    RBX = 0x%x    RCX = 0x%x    RDX = 0x%x"
        % (
            mu.reg_read(UC_X86_REG_EAX),
            mu.reg_read(UC_X86_REG_EBX),
            mu.reg_read(UC_X86_REG_ECX),
            mu.reg_read(UC_X86_REG_EDX),
        )
    )
    print(
        "> RSI = 0x%x    RDI = 0x%x"
        % (mu.reg_read(UC_X86_REG_ESI), mu.reg_read(UC_X86_REG_EDI))
    )
    print("[STACK] ")
    eip = mu.reg_read(UC_X86_REG_EIP)
    esp = mu.reg_read(UC_X86_REG_ESP)

    for i in range(0, 8):
        print("{0} : {1}" .format(hex((esp - (i * 8))), hx(mu.mem_read(esp - (i * 8), 4))))
    print("")

    if eip == 0x0401150: # after call to crypto_1
        read_string(mu, mu.reg_read(UC_X86_REG_ECX))
        input()

def read_string(mu, addr):
    data = b""
    tmp = b""
    inc = 0
    while tmp != b"\x00":
        tmp = mu.mem_read(addr + inc, 1)
        data += tmp
        inc += 1
    print(data)


mu.hook_add(UC_HOOK_CODE, hook_code)
mu.emu_start(BASE + 0x000006b, BASE + 0x000006b + len(sub_40106b) + 2000)

I wanted to set a breakpoint right after the call to the function I called crypto_1 (sub_401000) to see the data pointed by value in ECX . This function seems to do some xor and store the pointer to the value in ECX !

sub_401000:
00401000  55                 push    ebp {__saved_ebp}
00401001  8bec               mov     ebp, esp {__saved_ebp}
00401003  8b4508             mov     eax, dword [ebp+0x8 {arg2}]
00401006  8b550c             mov     edx, dword [ebp+0xc {arg3}]
00401009  56                 push    esi {__saved_esi}
0040100a  8bf1               mov     esi, ecx
0040100c  57                 push    edi {__saved_edi}
0040100d  33ff               xor     edi, edi  {0x0}
0040100f  8906               mov     dword [esi], eax
00401011  8b4510             mov     eax, dword [ebp+0x10 {arg4}]
00401014  895604             mov     dword [esi+0x4], edx
00401017  894608             mov     dword [esi+0x8], eax
0040101a  c7460c66000000     mov     dword [esi+0xc], 0x66
00401021  85d2               test    edx, edx
00401023  7418               je      0x40103d

00401025  33d2               xor     edx, edx  {0x0}
00401027  8b0e               mov     ecx, dword [esi]
00401029  8bc7               mov     eax, edi
0040102b  f7760c             div     dword [esi+0xc]
0040102e  8b4608             mov     eax, dword [esi+0x8]
00401031  8a0402             mov     al, byte [edx+eax]
00401034  300439             xor     byte [ecx+edi], al
00401037  47                 inc     edi
00401038  3b7e04             cmp     edi, dword [esi+0x4]
0040103b  72e8               jb      0x401025

0040103d  5f                 pop     edi {__saved_edi}
0040103e  8bc6               mov     eax, esi
00401040  5e                 pop     esi {__saved_esi}
00401041  5d                 pop     ebp {__saved_ebp}
00401042  c21000             retn    0x10 {__return_addr}

Then I ran it from sub_40106b until it breaks or crash. And it brake !

[ 0x401150 ] size = 0x1
> IP = 0x401150
> SP = 0x4ffeb4    BP = 0x4ffffc
> RAX = 0x4ffec4    RBX = 0x0    RCX = 0x4fffe4    RDX = 0x13
> RSI = 0x411ac6    RDI = 0x4fffa2
[STACK]
0x4ffeb4 : b'00000000'
0x4ffeac : b'3cff4f00'
0x4ffea4 : b'e4ff4f00'
0x4ffe9c : b'fcff4f00'
0x4ffe94 : b'a2ff4f00'
0x4ffe8c : b'00000000'
0x4ffe84 : b'00000000'
0x4ffe7c : b'00000000'

b'sink_the_tanker.vbs\x00'

As I supposed the decoded value seems interesting and few instruction later there is a call to CreateFileA.

Resuming the execution I landed in the following crash, where EIP is the call to CreateFileA exactly.

[...]
[ 0x401166 ] size = 0x6
> IP = 0x401166
> SP = 0x4ffe98    BP = 0x4ffffc
> RAX = 0x4ffec4    RBX = 0x0    RCX = 0x4fffe4    RDX = 0x13
> RSI = 0x411ac6    RDI = 0x4fffa2
[STACK]
0x4ffe98 : b'e4ff4f00'
0x4ffe90 : b'00000000'
0x4ffe88 : b'00000000'
0x4ffe80 : b'00000000'
0x4ffe78 : b'00000000'
0x4ffe70 : b'00000000'
0x4ffe68 : b'00000000'
0x4ffe60 : b'00000000'

Traceback (most recent call last):
  File "solv.py", line 118, in <module>
    mu.emu_start(BASE + 0x000006b, BASE + 0x000006b + len(sub_40106b) + 2000)
  File "/home/switch/.local/lib/python3.8/site-packages/unicorn/unicorn.py", line 317, in emu_start
    raise UcError(status)
unicorn.unicorn.UcError: Invalid memory fetch (UC_ERR_FETCH_UNMAPPED)

As I can’t emulate the code right now (you should use Qiling for this kind of whole emulation of the win api) and I just decided to return a pointer to the RAM and hope it will not be used too early.

mu.mem_write(0x0040d00c, asm("push 0x040116C; ret;")) # IAT CreateFileA, just nop this call 

At 0x040119D there is another call to crypto_1, let’s find out what is in ECX this time by setting a breakpoint too and running it again.


[ 0x40119d ] size = 0x1
> IP = 0x40119d
> SP = 0x4ffe98    BP = 0x4ffffc
> RAX = 0x4ffec4    RBX = 0x0    RCX = 0x4fffa4    RDX = 0x3c
> RSI = 0x4ffec4    RDI = 0x4fffa2
[STACK]
0x4ffe98 : b'e4ff4f00'
0x4ffe90 : b'd4fe4f00'
0x4ffe88 : b'a4ff4f00'
0x4ffe80 : b'fcff4f00'
0x4ffe78 : b'a2ff4f00'
0x4ffe70 : b'00000000'
0x4ffe68 : b'00000000'
0x4ffe60 : b'00000000'

b'MsgBox("Congrats! Your key is: C0rruptGarbag3@flare-on.com")\x00'

Hell yeah, the flag just here. I was so disappointed of my self that I stopped it and quit everything. In real life I shouldn’t have worked as I just found a random string and I still don’t know the whole behavior of the the binary.

This sucks but still happy to flag it!

bonus - flosss

A teammate told me about floss a tool from fireeye : https://github.com/fireeye/flare-floss

image-20200927142012748
$ ~/downloads/floss -p XORPlugin packed.exe

[...]
FLOSS decoded 2 strings
sink_the_tanker.vbs
MsgBox("Congrats! Your key is: C0rruptGarbag3@flare-on.com")

FLOSS extracted 6 stackstrings
KglPFOsQDxBPXmclOpmsdLDEPMRWbMDzwhDGOyqAkVMRvnBeIkpZIhFznwVylfjrkqprBPAdPuaiVoVugQAlyOQQtxBNsTdPZgDH
##3,
ineIntel
ZBAA
t,*-B
nPTnaGLkIqdcQwvieFQKGcTGOTbfMjDNmvibfBDdFBhoPaBbtfQuuGWYomtqTFqvBSKdUMmciqKSGZaosWCSoZlcIlyQpOwkcAgw

Finished execution after 4.617804 seconds

.rsrc