// k3ng
  • 👋
  • 2025
    • Cyber Jawara National 2024
      • Whale
      • Grayscale
      • Log4Shell
  • 2024
    • HTB University CTF 2024: Binary Badlands
      • Apolo
      • Freedom
      • Frontier Exposed
      • Wanter Alive
      • Armaxis
    • TSA Cyber Champion 2024
      • 101 - Forensics
      • eavesdropped
      • 101 - Web Exploitation
    • Cyber Jawara International 2024
      • prepare the tools
      • Sleeper
      • P2PWannabe
    • CTF Hology 7.0
      • give me
      • Books Gallery
    • TCP1P CTF 2024
      • doxxed
      • Lost Progress
    • Gemastik 2024 Finals
      • kode-viewer
Powered by GitBook
On this page
  • Challenge Description
  • Flag
  • Analysis
  • Solution
  1. 2024
  2. TSA Cyber Champion 2024

eavesdropped

Last updated 6 months ago

Challenge Description

Recently, I discovered that a malicious actor has been using my proxy to obtain specific information from a particular server. Could you help me figure out what the attacker did?

Author: nagi

Flag

TSA{c0mmand_4nd_control_0ver_mitmprox1es_c1ee2e623a}


Analysis

We are given a file containing some sort of log of a network traffic. From the contents, we can see some sort of git clone operation happening and also some HTTP requests at the end of the log.

Since Git uses Zlib as their compression algorithm, we can use binwalk to extract all Git objects.

After decompressing the extracted Zlib files, there is one file that contains byte-compiled Python executable. After decompiling the file, we can see a client.py file from a tool called trevorc2.

The trevorc2 tool is used to mask command execution using a cloned website. From the decompiled code, the payload itself will be stored on the nonce with the format nonce="(.+?)" and will be decrypted using AES with the result of XORing the reversed CIPHER variable with the __author__ variable as the key. The resulting payload will contain the hostname and the command itself with the format hostname::::command. After executing the command, the output will be encrypted again using the same algorithm and key and will be hidden on the query parameter.

We parsed the nonces and decrypt using the previously analyzed decryption flow to get the commands used.

From the output, we can see a flag.png is being archived using a password with some kind of format. The first part of the password is the hostname, the second part is a random password from the first 64 passwords from the rockyou.txt wordlist. The last part is a number between 1 and 65537. Then, the Base64 representation of the flag.zip is outputted.

Solution

Before getting the zip file, we need to create a custom wordlist from the analyzed format. The hostname is retrieved from the first part of the parsed nonces.

from itertools import product

rockyou = open("/usr/share/wordlists/rockyou.txt", "rb").read().splitlines()[:64]
hostname = "phionify"
number = range(1, 65538)

a = [[hostname], [x.decode('utf-8') for x in rockyou], [str(x) for x in number]]

with open("wordlist.txt", "w") as f:
    for x in product(*a):
        f.write(x[0] + "_" + x[1] + "_" + x[2] + "\n")

After creating a new wordlist, we can get the flag.zip by parsing the last query parameter on the log file.

import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES
from collections import Counter
import pwn
import re

class AESCipher(object):
    """
    A classical AES Cipher. Can use any size of data and any size of password thanks to padding.
    Also ensure the coherence and the type of the data with a unicode to byte converter.
    """

    def __init__(self, key):
        self.bs = 16
        self.key = hashlib.sha256(AESCipher.str_to_bytes(key)).digest()

    @staticmethod
    def str_to_bytes(data):
        u_type = type(b''.decode('utf8'))
        if isinstance(data, u_type):
            return data.encode('utf8')
        return data

    def _pad(self, s):
        return s + (self.bs - len(s) % self.bs) * AESCipher.str_to_bytes(chr(self.bs - len(s) % self.bs))

    @staticmethod
    def _unpad(s):
        return s[:-ord(s[len(s) - 1:])]

    def encrypt(self, raw):
        raw = self._pad(AESCipher.str_to_bytes(raw))
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return base64.b64encode(iv + cipher.encrypt(raw)).decode('utf-8')

    def decrypt(self, enc):
        enc = base64.b64decode(enc)
        iv = enc[:AES.block_size]
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')

ROOT_PATH_QUERY = '/'
SITE_PATH_QUERY = '/search'
QUERY_STRING = 'q='
CIPHER = 'zxtWV82qG1Zj0DhyjSCYsB7iSkJNvRf4zO9sxSAqL6'
__author__ = 'Dave Kennedy (@HackingDave)'
cipher = AESCipher(key=pwn.xor(CIPHER[::-1], __author__))

with open("flows", "rb") as f:
    flows = f.read()
    ctr = Counter(re.findall(rb'search\?q=(.+?)\,', flows)).most_common()[-1]
    data = base64.b64decode(ctr[0])
    data = cipher.decrypt(data)
    data = data.split('::::')[1]
    data = eval(data).decode('utf-8')
    data = data.replace('\n', '')
    with open("flag.zip", "wb") as f:
        f.write(base64.b64decode(data))

Then we can brute-force the zip file using john.

After opening the zip file, the flag will be stored as a QR code.

Git clone message
HTTP request on the file
Binwalk output
Trevorc2 client.py
AES cipher definition
Command execution code
Output of the parsed nonces
John to brute-force the zip password
Scanning the QR code