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;
"Found overlay: %d bytes", overlay);
info(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)
"unexpected value in the PE header");
throwCantUnpack(
const unsigned iobjs = ih.objects;
const unsigned overlay = file_size - ALIGN_UP(isection[iobjs - 1].rawdataptr
1].size,
+ isection[iobjs -
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
pe_section_t)
__packed_struct(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
1].size, ih.filealign);
+ isection[iobjs -
"[NAME] : %s\n", isection[iobjs-1].name);
printf(
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.
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.
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 :
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()
= b"""y>\r
poc <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…
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 cryptosub_401045
: do some crypto toosub_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 likeCreateFileA
andWriteFileA
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 *
= Uc(UC_ARCH_X86, UC_MODE_32)
mu
= open("packed.exe", "rb").read()
unpacked
= unpacked[0x000046b: 0x0000046b + 0x1b0]
sub_40106b = unpacked[0x0000400: 0x0000400 + 0x45]
sub_401000 = unpacked[0x0000445: 0x0000445 + 38]
sub_401045
= 0x0401000
BASE = 0x0500000
STACK_ADDR
1024 * 1024 * 2)
mu.mem_map(BASE,
+ 0x000000 , sub_401000)
mu.mem_write(BASE + 0x0000045 , sub_401045)
mu.mem_write(BASE + 0x000006b , sub_40106b)
mu.mem_write(BASE
0x04119F8, b"nPTnaGLkIqdcQwvieFQKGcTGOTbfMjDNmvibfBDdFBhoPaBbtfQuuGWYomtqTFqvBSKdUMmciqKSGZaosWCSoZlcIlyQpOwkcAgw")
mu.mem_write(0x00411A60, b"KglPFOsQDxBPXmclOpmsdLDEPMRWbMDzwhDGOyqAkVMRvnBeIkpZIhFznwVylfjrkqprBPAdPuaiVoVugQAlyOQQtxBNsTdPZgDH")
mu.mem_write(
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] ")
= mu.reg_read(UC_X86_REG_EIP)
eip = mu.reg_read(UC_X86_REG_ESP)
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):
= b""
data = b""
tmp = 0
inc while tmp != b"\x00":
= mu.mem_read(addr + inc, 1)
tmp += tmp
data += 1
inc print(data)
mu.hook_add(UC_HOOK_CODE, hook_code)+ 0x000006b, BASE + 0x000006b + len(sub_40106b) + 2000) mu.emu_start(BASE
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}
mov esi, ecx
0040100a 8bf1 57 push edi {__saved_edi}
0040100c xor edi, edi {0x0}
0040100d 33ff 8906 mov dword [esi], eax
0040100f 00401011 8b4510 mov eax, dword [ebp+0x10 {arg4}]
00401014 895604 mov dword [esi+0x4], edx
00401017 894608 mov dword [esi+0x8], eax
mov dword [esi+0xc], 0x66
0040101a c7460c66000000 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
div dword [esi+0xc]
0040102b f7760c mov eax, dword [esi+0x8]
0040102e 8b4608 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]
72e8 jb 0x401025
0040103b
pop edi {__saved_edi}
0040103d 5f mov eax, esi
0040103e 8bc6 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.
0x0040d00c, asm("push 0x040116C; ret;")) # IAT CreateFileA, just nop this call mu.mem_write(
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
~/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
- https://github.com/upx/upx/blob/d7ba31cab8ce8d95d2c10e88d2ec787ac52005ef/src/pefile.h
- http://xtx.free.fr/archives/liens/xtx%20cracking%20progressif/html/Pages/Chapitre%2009/01%20-%20UPX.html
- https://docs.microsoft.com/fr-fr/dotnet/visual-basic/reference/command-line-compiler/win32manifest
- https://techtalk.pcmatic.com/2017/11/29/unpacking-malware-part-2-reconstructing-import-address-table/
- http://www.capi-ears.com/tutoriel-informatique/unpacking-packer-decrypter-programme/
- http://www.capi-ears.com/wp-content/uploads/2013/11/OllyDbg-EP-EntryPoint-full-screen.png
- http://deamonftp.free.fr/deamoncrack/Tuts/Crisanar/cours6.htm