ZeroDays CTF 2024 RE - 4

The fourth challenge is named ‘acup.exe’.

Figure 1: Illustrates the executable file ‘acup.exe

Upon analysis with Detect-It-Easy, it is revealed that the file is packed using PyInstaller.

Figure 2: Analysis with Detect-It-Easy reveals the utilization of PyInstaller

PyInstaller is a popular tool used to convert Python scripts into standalone executable files. It bundles the Python interpreter and all necessary dependencies into a single executable, making it easier to distribute Python applications to users who do not have Python installed.

To unpack and retrieve the compiled Python bytecode, pyinstxtractor.py available here, is utilized.

After downloading and executing the pyinstxtractor.py file according to the provided readme instructions, probable entry points and extracted Python bytecode files are obtained.

Figure 3: Output of pyinstxtractor.py displaying potential entry points

Figure 4: Directory generated by pyinstxtractor.py

Figure 5: Python bytecode (.pyc) file extracted by pyinstxtractor.py

We analyze the generated acup.pyc file, a probable entry point, using the same method as described in ZeroDays CTF 2024 RE - 1, employing pycdc.exe.

Figure 6: Represents the file selected for analysis

Figure 7: Illustrates the decompilation output of the ‘acup.pyc’ file using pycdc.exe

The generated output is provided below.

# Source Generated with Decompyle++
# File: acup.pyc (Python 3.10)

Warning: Stack history is not empty!
Warning: block stack is not empty!
import base64
import time
from tkinter import Tk as xg
from arc4 import ARC4 as pe

def ebg46(x):
    d = abs(13) * -1
    c = ''.join((lambda .0: [ chr(97 + i) for i in .0 ])(range(26)))
    t = c[d:] + c[:d]

    rc = lambda z = None: if c.find(z) > -1:
t[c.find(z)]
    return None((lambda .0 = None: for c in .0:
rc(c))(x))

gbbe = xg()
gbbe.withdraw()
zhgnq = gbbe.clipboard_get()
if ebg46(zhgnq.lower()) != 'zvfpuvrs_znantrq':
    time.sleep(5)
    zhgnq = gbbe.clipboard_get()
    if not ebg46(zhgnq.lower()) != 'zvfpuvrs_znantrq':
        rfno = base64.b64encode(str.encode(zhgnq.lower()))
        rfno = rfno.decode('utf-8')
        k = str.encode(''.join([
            rfno[7],
            rfno[15],
            chr(ord(rfno[0]) + 1),
            rfno[-3].lower()]))
        _pe = pe(k)
        erucvp = '5465f059dbae33e458a46ae1fd8f8b89b13f2429d62a132546a4787d6f1c58b5a36cc19fcdce577a7a66'
        prq = _pe.decrypt(bytes.fromhex(erucvp))
        print(prq.decode('utf-8'))
        return None

The code employs obfuscation techniques such as unintelligible variable names, base64 encoding, and decryption logic to unveil the final flag.

To comprehend the provided code, we'll dissect it section by section and examine the following code.

import base64
import time
from tkinter import Tk as xg

gbbe = xg()
gbbe.withdraw()
zhgnq = gbbe.clipboard_get()
if ebg46(zhgnq.lower()) != 'zvfpuvrs_znantrq':
    time.sleep(5)
    zhgnq = gbbe.clipboard_get()
    if not ebg46(zhgnq.lower()) != 'zvfpuvrs_znantrq':
        rfno = base64.b64encode(str.encode(zhgnq.lower()))
        rfno = rfno.decode('utf-8')
        k = str.encode(''.join([
            rfno[7],
            rfno[15],
            chr(ord(rfno[0]) + 1),
            rfno[-3].lower()]))

The code retrieves clipboard data, ideally input by the user, processes it through the ebg46() function, and compares its lowercase version to "zvfpuvrs_znantrq". If unequal, the program sleeps and retrieves clipboard data again, repeating the process once again to check if they match. If matched, the variable rfno is created by converting the lowercase clipboard data to bytes, base64 encoding it, and then UTF-8 decoding it. Subsequently, variable k is formed using parts of the rfno variable.

Let's examine the ebg46() function in detail.

def ebg46(x):
    d = abs(13) * -1
    c = ''.join((lambda .0: [ chr(97 + i) for i in .0 ])(range(26)))
    t = c[d:] + c[:d]

    rc = lambda z = None: if c.find(z) > -1:
t[c.find(z)]
    return None((lambda .0 = None: for c in .0:
rc(c))(x))

The given function, ebg46(), appears to be a simple Caesar cipher implementation with a fixed shift of 13 characters (commonly known as ROT13). It first generates a lowercase alphabet string c using a lambda function and shifts it by 13 characters to create the cipher key t. Then, it defines a lambda function rc to perform the character substitution based on the shift. Finally, it applies the substitution to each character in the input string x using another lambda function and returns the resulting string.

Based on the observed pattern, it can be inferred that applying the ROT13 cipher to the expected output "zvfpuvrs_znantrq" results in the original string. This is due to the property of ROT13, where applying the cipher twice (ROT13(ROT13(x))) returns the original input string x.

Utilizing CyberChef, we can execute the operation on the expected string, “zvfpuvrs_znantrq”, to obtain the input string required for the second condition to match. However, since there is no scenario where both if conditions in the previous code are met, this challenge cannot be resolved by executing the program. Instead, it can only be resolved through static analysis of the code.

Figure 8: Illustrates the expected input string for the second condition in the code

As displayed above, the expected input for the second condition to match is "mischief_managed".

Now, let's comprehend the final section of the code.

import base64
import time
from tkinter import Tk as xg
from arc4 import ARC4 as pe

       rfno = base64.b64encode(str.encode(zhgnq.lower()))
       rfno = rfno.decode('utf-8')
       k = str.encode(''.join([
            rfno[7],
            rfno[15],
            chr(ord(rfno[0]) + 1),
            rfno[-3].lower()]))
        _pe = pe(k)
        erucvp = '5465f059dbae33e458a46ae1fd8f8b89b13f2429d62a132546a4787d6f1c58b5a36cc19fcdce577a7a66'
        prq = _pe.decrypt(bytes.fromhex(erucvp))
        print(prq.decode('utf-8'))
        return None

Firstly, let's calculate the value of the variable rfno, which is "bWlzY2hpZWZfbWFuYWdlZA==".

Figure 9: Illustrates the base64 encoding of the expected input string

Now, the variable k can be generated, resulting in “puca”.

To understand the arc4 import and its usage, we examine an example of its usage, sourced from here.
The arc4 package is a Python implementation of the ARCFOUR (RC4) cipher, prized for its compactness and exceptional speed, as indicated on the referenced page above.

>>> from arc4 import ARC4
>>> arc4 = ARC4(b'key')
>>> cipher = arc4.encrypt(b'some plain text to encrypt')

Upon comparing this with the preceding code, it's evident that the variable k is utilized as the key. Next, the variable erucvp is converted from hexadecimal, decrypted using RC4, and decoded as UTF-8 to form the flag string.

These operations can be replicated in CyberChef to manually retrieve the flag.

Figure 10: Displays the output flag

I have also written a Python script to generate the decoded text utilized to form the key and the key itself.

import base64

def decode_caesar_cipher(ciphertext, shift):
    plaintext = ""
    for char in ciphertext:
        if char.isalpha():
            shifted_char = chr(((ord(char.lower()) - ord('a') - shift) % 26) + ord('a'))
            if char.isupper():
                shifted_char = shifted_char.upper()
            plaintext += shifted_char
        else:
            plaintext += char
    return plaintext

ciphertext = "zvfpuvrs_znantrq"
shift_amount = 13
original_text = decode_caesar_cipher(ciphertext, shift_amount)
print("Decoded text:", original_text)

zhgnq = original_text
rfno = base64.b64encode(str.encode(zhgnq.lower()))
rfno = rfno.decode('utf-8')
k = str.encode(''.join([
            rfno[7],
            rfno[15],
            chr(ord(rfno[0]) + 1),
            rfno[-3].lower()]))
print(k)

The acquired flag is as follows -

Flag - zerodays{lord_what_fools_these_mortals_be}
Saptarshi Laha

I'm a passionate Threat Intelligence Analyst based in Ireland, delving deep into the fascinating realms of Reverse Engineering and Malware Analysis. With a keen eye for dissecting malicious code and navigating Capture The Flag challenges, I guide you through virtual mazes of cryptographic puzzles and real-world malware samples, sharing insights and strategies for navigating the cybersecurity landscape.

https://BinHex.Ninja
Previous
Previous

ZeroDays CTF 2024 RE - 5

Next
Next

ZeroDays CTF 2024 RE - 3