AeroCTF. Reverse. 1000 and 1 night & Elderly File
@ Rakovsky Stanislav | Tuesday, Mar 3, 2020 | 6 minutes read | Update at Tuesday, Mar 3, 2020

reverse, 100 points > Elderly File

We found some old encoded file and a program for encoding.

It seems that the solution will take the same amount of time.

Understand, it may turn out to decode the file.

*Download link*

@revker

eldery_file.7z

There are encoder ELF file and file.enc.

Let’s open encoder in IDA.

We can see MEIPASS strings in the binary.

    sub_403590(&s1);
    v3 = (const char *)sub_404AA0("_MEIPASS2", v20);
    j__unsetenv("_MEIPASS2");
    v5 = &s1;

MEIPASS is a magic for Pyinstaller files.

We have pyinstxtractor for this sort of binaries, bit it cannot work with ELF.

[*] Processing encoder
[*] Error : Unsupported pyinstaller version or not a pyinstaller archive

Hopefully, we can see that pyinstxtractor just cannot detect overlay.

Binwalk suggests us the start of ZLIB archives - 0x7a50

> binwalk encoder

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             ELF, 64-bit LSB executable, AMD x86-64, version 1 (SYSV)
31312         0x7A50          Zlib compressed data, best compression
31483         0x7AFB          Zlib compressed data, best compression
...

Let’s help pyinstxtractor.py to detect the overlay. I’ll use my diff-version of pyinstxtractor (it solves some overlay fixes) in EXE-files.

And make additional change:

        self.overlayPos = 0x7a50 # pe.get_overlay_data_start_offset()

The result is:

[*] Processing encoder
[*] Okay, two magic numbers, all right
[*] Pyinstaller version: 2.0
[*] Python version: 27
[*] Length of package: 4065226 bytes
[*] Found 25 files in CArchive
[*] Beginning extraction...please standby
[+] Possible entry point: pyiboot01_bootstrap
[+] Possible entry point: encoder
[!] Warning: The script is running in a different python version than the one used to build the executable
    Run this script in Python27 to prevent extraction errors(if any) during unmarshalling
[*] Found 194 files in PYZ archive
[*] Successfully extracted pyinstaller archive: encoder

You can now use a python decompiler on the pyc files within the extracted directory

Yay, let’s use uncompyle6 on encoder, but before this we need to recover the magic of .pyc. For python 2.7 it is 03 F3 0D 0A 00 00 00 00.

# uncompyle6 version 3.3.5
# Python bytecode 2.7 (62211)
# Decompiled from: Python 3.5.2 (default, Oct  8 2019, 13:06:37)
# [GCC 5.4.0 20160609]
# Embedded file name: encoder.py
import lzss, sys
if __name__ == '__main__':
    if len(sys.argv) > 1:
        file_path = sys.argv[1]
    else:
        print 'Usage: ' + sys.argv[0] + ' <file-path>'
        sys.exit(-1)
    lzss.encode_file(file_path, file_path + '.enc')
# okay decompiling encoder.pyc

Fmm, it’s not a crypto, but compression. We got lzss.so file while decompressing the encoder, let’s use them:

import lzss
lzss.decode_file("..//file.enc", "..//file.dec")

Opening file.dec, and…

:100000007F454C4601010100000000000000000097
:100010000300030001000000501000003400000045
:100020005838000000000000340020000B002800B9
:100030001E001D0006000000340000003400000017
:1000400034000000600100006001000004000000B6
:10005000040000000300000094010000940100006F
:1000600094010000130000001300000004000000D1
:10007000010000000100000000000000000000007E
:100080000000000098030000980300000400000036
...

It’s Intel HEX.

Rename file.dec -> file.hex, converting to regular file using 010. The file is another ELF with hardcoded flag:

Aero{33d8b218a9961657b74c5036fe44527a02ce03c4da34f8a1cda5f2188c23a1b5}

reverse, 100 points > 1000 and 1 night

I wrote all this manually in 1000 and 1 night.

It seems that the solution will take the same amount of time.

After connecting to the server, it will ask you for a token for a binary.

You need to enter the correct tokens for the requested files and you will receive a flag.

nc tasks.aeroctf.com 44324

*Download link*

@revker

files.zip

We have literally 1001 file, they differs only on constants of check function:

_BOOL8 __fastcall check(_BYTE *a1)
{
  __int64 s2; // [rsp+10h] [rbp-30h]
  __int64 v3; // [rsp+18h] [rbp-28h]
  __int64 v4; // [rsp+20h] [rbp-20h]
  __int64 v5; // [rsp+28h] [rbp-18h]
  int i; // [rsp+3Ch] [rbp-4h]

  s2 = 0x2673231825237276LL;
  v3 = 0x2373262077182774LL;
  v4 = 0x2025271873772577LL;
  v5 = 0x2375232776217420LL;
  for ( i = 0; i <= 31; ++i )
    a1[i] = ((a1[i] + 7) ^ 0x17) - 8;
  return memcmp(a1, &s2, 0x20uLL) == 0;
}

Okay, let’s parse them. I use yara engine + mkyara plugin for this kind of stuff:

rule parse
{
  strings:
    $chunk_1 = {
      55
      48 89 E5
      48 83 EC 40
      48 89 7D C8
      48 B8 ?? ?? ?? ?? ?? ?? ?? ?? ; qw1 14-21
      48 BA ?? ?? ?? ?? ?? ?? ?? ?? ; qw2 24-31
      48 89 45 D0
      48 89 55 D8
      48 B8 ?? ?? ?? ?? ?? ?? ?? ?? ; qw3 ; 42-49
      48 BA ?? ?? ?? ?? ?? ?? ?? ?? ; qw4 ; 52-69
      48 89 45 ??
      48 89 55 ??
      C7 45 FC 00 00 00 00
      EB ??
      8B 45 ??
      48 63 D0
      48 8B 45 ??
      48 01 D0
      0F B6 00
      83 C0 ?? ; add ; -10
      83 F0 ?? ; xor ; -7
      8D 48 ?? ; sub ; -4
      8B 45 FC
    }
  condition:
    any of them
}

There is my jupyter notebook to solve this task:

#%%

import yara
import os

#%%

rule = yara.compile("1001.yar")
files = os.listdir("./files")

# TESTING
# do not take ida files
files = [i for i in files if len(i)==32]
# checking the rule. We can suppose that not all the files are similar, tasks sounds like "thousand AND one"
for i in files:
    matches = rule.match("./files/"+i)
    if not matches:
        print("Bad", i)

#%%

matches[0].strings
#[(4772,
#  '$chunk_1',
#  b'UH\x89\xe5H\x83\[email protected]\x89}\xc8H\xb8nn\x1e\x19\x1d\x1fm\x1fH\xbak \x1f\x1e\x1bB nH\x89E\xd0H\x89U\xd8H\xb8n\x1e\x1eAo\x1a jH\xbaml\x1dl\x1cl\x1clH\x89E\xe0H\x89U\xe8\xc7E\xfc\x00\x00\x00\x00\xeb.\x8bE\xfcHc\xd0H\x8bE\xc8H\x01\xd0\x0f\xb6\x00\x83\xc0\x08\x83\xf0\x10\x8dH\xf1\x8bE\xfc')]
#%%

def parse_vars_from_file(file): # parsing the rule output
    match = rule.match(file)[0].strings[0][2]
    qw1 = match[14:22]
    qw1 += match[24:32]
    qw1 += match[42:50]
    qw1 += match[52:70]
    add, xor, sub = match[-10], match[-7], 0x100 - match[-4]
    return qw1, add, xor, sub

def repair(qw1, add, xor, sub): # solving
    qw2 = bytearray(qw1)[:32]
    for i in range(len(qw2)):
        qw2[i] = (qw2[i] + sub)%0x100
        qw2[i] ^= xor
        qw2[i] = (0x100+ qw2[i] - add)%0x100
    return qw2



#%%
# test
r = repair(*parse_vars_from_file("./files/00ac8ed3b4327bdd4ebbebcb2ba10a00"))
# bytearray(b'bf5305e2d10a82e5a3ae01388d7b15c5')

#%%

import nclib
import time


#%%

nc = nclib.Netcat(('tasks.aeroctf.com', 44324), verbose = True)
while 1:
    time.sleep(0.1)
    mes = nc.recv().decode()
    s = mes[mes.find("<")+1:mes.find(">")]
    nc.send(
        repair(*parse_vars_from_file(f"./files/{s}"))+b"\n"
    )

======== Receiving 4096B ========
<< b'Enter valid token to binary with name <88ae6372cfdc5df69a976e893f4d554b>'
<< b''
======== Sending (33) ========
>> bytearray(b'e7d56cb4e9a723a988d25fd292d904ce')
>> bytearray(b'')
======== Receiving 4096B ========
<< b'Token: Enter valid token to binary with name <f57a2f557b098c43f11ab969efe1504b>'
<< b'Token: '
======== Sending (33) ========
>> bytearray(b'601bd2b40f963ab0be1c87a293d3d9e3')
>> bytearray(b'')
======== Receiving 4096B ========
<< b'Enter valid token to binary with name <71ad16ad2c4d81f348082ff6c4b20768>'
<< b'Token: '
======== Sending (33) ========
>> bytearray(b'6ca1d1718a78c97acf989b921658967f')
>> bytearray(b'')
======== Receiving 4096B ========
<< b'Enter valid token to binary with name <da0d1111d2dc5d489242e60ebcbaf988>'
<< b'Token: '
======== Sending (33) ========
>> bytearray(b'7bb06410c0be38160fd529f030f5b662')
>> bytearray(b'')
======== Receiving 4096B ========
<< b'Enter valid token to binary with name <e7f8a7fb0b77bcb3b283af5be021448f>'
<< b'Token: '
...
======== Sending (33) ========
>> bytearray(b'c328d7fa246eefc047c409c9ce306f53')
>> bytearray(b'')
======== Receiving 4096B ========
<< b'Flag: Aero{0f9e7ddd2be70f58b86f8f6589e17f182fc21c71437c2d9923fefa7ae281712b}'
<< b''
---------------------------------------------------------------------------

Error                                     Traceback (most recent call last)

<ipython-input-54-98f036eef290> in <module>
      5     s = mes[mes.find("<")+1:mes.find(">")]
      6     nc.send(
----> 7         repair(*parse_vars_from_file(f"./files/{s}"))+b"\n"
      8     )
      9 

<ipython-input-35-22cdf82dd799> in parse_vars_from_file(file)
      1 
      2 def parse_vars_from_file(file):
----> 3     match = rule.match(file)[0].strings[0][2]
      4     qw1 = match[14:22]
      5     qw1 += match[24:32]

Error: could not open file "./files/Flag: Aero{0f9e7ddd2be70f58b86f8f6589e17f182fc21c71437c2d9923fefa7ae281712b}"

The flag: Aero{0f9e7ddd2be70f58b86f8f6589e17f182fc21c71437c2d9923fefa7ae281712b}

Cats!

Under construction

Wow! Flipable!

Hello from another side of the Moon!

Looking for a flag? Okay, take this:
LFXXKIDBOJSSA43XMVSXIIC6LYQCAIA=

About me

Under construction. You can try to contact me and fill this field… haha… ha…