openECSC 2024 - CTF Writeup

openECSC 2024 - CTF Writeup

General Remarks

CTF Page Link: https://open.ecsc2024.it/

openECSC has mostly medium to difficult challenges as it is designed to scout the best participants to battle in the grand finals in Italy, and ultimately at the International Cybersecurity Championships.

The challenges had varying categories: web, crypto, misc, pwn,rev .

Round 1

Cable Fish (Misc)

Challenge Context

Welcome to our platform! Here you can do traffic analysis via TCP, isn't it awesome?

Just specify your filter, we will sanitize it (we want to make sure no sensitive data will be leaked) and you will be given the traffic!

This is a remote challenge, you can connect with:

nccablefish.challs.open.ecsc2024.it38005

Additional Files (1 - python source code)

#!/usr/bin/env python3

import os
import subprocess
import time

flag = os.getenv('FLAG', 'openECSC{redacted}')

def filter_traffic(filter):
    filter = filter[:50]
    sanitized_filter = f'(({filter}) and (not frame contains "flag_placeholder"))'
    p1 = subprocess.Popen(['tshark', '-r', '/home/user/capture.pcapng', '-Y', sanitized_filter, '-T', 'fields', '-e', 'tcp.payload'], shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout, stderr = p1.communicate()

    if stderr != b'':
        return sanitized_filter, stderr.decode(), 'err'

    res = []
    for line in stdout.split(b'\n'):
        if line != b'':
            p2 = subprocess.Popen(['xxd', '-r', '-p'], shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=open(os.devnull, 'wb'))
            stdout, _ = p2.communicate(input=line)
            p3 = subprocess.Popen(['xxd', '-c', '32'], shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=open(os.devnull, 'wb'))
            stdout, _ = p3.communicate(input=stdout)
            res.append(stdout.decode().replace('flag_placeholder', flag))

    return sanitized_filter, res, 'ok'

if __name__ == '__main__':
    banner = '''
                                            ,@@
                                        @@@/  @@
                                     @@      &@
                                   @@        @*
                                 ,@          @*
                                ,@           @@
                                @,            @.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@              @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@


                         ,,        ,,                    ,,           ,,
       .g8"""bgd        *MM      `7MM         `7MM"""YMM db         `7MM
     .dP'     `M         MM        MM           MM    `7              MM
     dM'       ` ,6"Yb.  MM,dMMb.  MM  .gP"Ya   MM   d `7MM  ,pP"Ybd  MMpMMMb.
     MM         8)   MM  MM    `Mb MM ,M'   Yb  MM""MM   MM  8I   `"  MM    MM
     MM.         ,pm9MM  MM     M8 MM 8M""""""  MM   Y   MM  `YMMMa.  MM    MM
     `Mb.     ,'8M   MM  MM.   ,M9 MM YM.    ,  MM       MM  L.   I8  MM    MM
       `"bmmmd' `Moo9^Yo.P^YbmdP'.JMML.`Mbmmd'.JMML.   .JMML.M9mmmP'.JMML  JMML.

'''
    print(banner)
    filter = input('Please, specify your filter: ')
    print("Loading, please wait...")
    sanitized_filter, res, status = filter_traffic(filter)
    print(f'Evaluating the filter: {sanitized_filter}')
    print()
    output = ''
    if status == 'err':
        output = f'ERROR: {res}'
    else:
        for line in res:
            for l in line.strip().split('\n'):
                output += l[91:]
    print(output)
    print('\nEnd of the results. Bye!')

Solution

  • This challenge gave the participant a netcat endpoint where it can display packets of a network capture if a user enters a filter.

  • I did some quick code analysis with ChatGPT to get the basic idea of the functions within the code, and it appeared that there contained some blacklisted packets that contained the flag.

  • After some trial and error with code injection (in the form of filters), I managed to create a payload that revealed the blacklisted packets with the flag.

Fileshare (Web)

Challenge Context

You can now share your files for free thanks to our new disruptive technology!

Site: https://fileshare.challs.open.ecsc2024.it

Additional Files (1 - zip file with backend source code)

Solution

  • This challenge presented a web interface that allowed users to upload files.

  • As part of the challenge, participants received a zipped file of the server-side source code as clues.

  • After scrutinizing the files, the first hint I noticed was this:

  • The code checks the Content-Type of an uploaded file, and if it contains the letter ‘’h’’ the Content-Type is set to ‘’text/plain’’ – otherwise it uploads the file.

  • This functionality posses a security threat as it implies that any file (including one with a payload) can be uploaded as long as the Content-Type is either blank or does not include the letter ‘’h’’.

  • Next step was to look for a method to invoke the code if it’s uploaded, and the solution was buried under the support page:

  • The report functionality invoked a bot that potentially opens a file (payload) after being summoned for a report ticket feature – if the bot opens the payload them a flag it’s stored on its browser.

  • After crafting the below payload, I managed to fetch the flag from the bot’s cookies and send it to my webhook endpoint:

  • My first attempt involved manual fetching of the flag cookie using Ngrok for my webhook endpoint, but it did not yield fruitful results.

Protoless WAF (Web)

Challenge Context

I found this cool parser for protobuf that does not require a .proto file. I'm using it in a WAF to prevent admin access to my server, I hope it works as expected.

Site: http://protolesswaf.challs.open.ecsc2024.it

Additional Files (1 - zip file with backend source code)

Solution

  • This web challenge was designed for participants to bypass a Web Application Firewall (WAF) to gain the admin flag.

  • Below are the challenge files and how I analyzed them individually:

  • app_pb2.py: contains the generated code for the protobuf message ‘GetFlag’.

  • app.proto: defines the protobuf message ‘GetFlag’.

  • protoless.py: provides functions to parse protobuf messages without a ‘a .proto’ file.

  • server.py: this is a flask server that receives protobuf messages containing username and checks if it’s ‘admin’ to return the flag.

  • waf.py: another flask server acting as a WAF, intercepting requests to the ‘server.py’ and preventing access if the username is “admin”.

  • I figured the exploit would require me to craft protobuf message with a field having a wrong type followed by the same field with the correct ‘.proto’ type.

  • This way the WAF will accept the field with the wrong type, but the server will interpret the the field according to the correct ‘.proto’ type, potentially allowing access to the admin flag.

  • I tried a few scripts but eventually gave up:

Round 2

Blind Maze (Misc)

Challenge Context

Welcome to the blind maze, you move without knowing the outcome and maybe you will reach the end, good luck.

A previous winner left us a strange file. Maybe it will help you.

Site: http://blindmaze.challs.open.ecsc2024.it

Additional Files (1 - pcap file)

Solution

  • This challenge presented the participants with a blind maze (labyrinth) in the form of a web application.

  • I instantly noticed that the pcap file provided was the trick to solving the challenges – as it contained moves of a previous player who completed it.

  • The trick was to extract the moves, then replicate them to get to the flag.

  • I used a script to extract the moves, then saved them to a text file.

  • I then created another script that would establish a session with the game and try these moves continuously until the flag is retrieved.

  • I was still making changes to the script to bypass failed attempts, but I haven’t managed to refine the script.