Last updated: 23 Sep 24 11:11:18 (UTC)

Midnight CTF

Midnight CTF

Me and a group of Friends from school participated at MidnightCTF.

It was from 27 april 22:00
to 28 april 9:00

I didn't flag a lot during this CTF,
but i still kept the write-up of some of my attempts.

They are accessible below.
It was from 27 april 22:00
to 28 april 9:00

I didn't flag a lot during this CTF,
but i still kept the write-up of some of my attempts.

They are accessible below.

we still manage to qualify for the final (that i wont be able to attend since i’m in Toronto for my formation)

We classed 9/~70 in the Student’s scoreboard.

Steganography

ca1e686c6131da5fe8bce7eda0f80259.png

we are given an article as a file

3395ed7aa10a010e4a7b3ed801d3dbe7.png

i tried adding the uppercase & lowercase letters, scanning the barcode, looking at bytes etc and found nothing.

so i went back on the image and starting looking for other ways.

the text in white looked kind of random, so i thought about it beeing binary code.

uppercase letters beeing ones & lowercase letters beeing zeroes.

after using automated tools we successfully extract the text & then we run a script to do the replacing (after making sure the extracted text is correct & removing all spaces/ponctuation)

para="mEcaNIsMeDesecURiTeSoUsanDroiDResTAUReRLeSsymbOleSDeDebOgaGEapARtIRdEBInalrESCOMpIleSsTatiQUementAUToMaTisERleXPlOlTatIonDeVULNErABiLiTelORsdUnTeStDINTRuSIonrEMoTEdOWNLoADeXEcUtIOnavEcjAVaUTiliSAtiOnEtPrOTECTiONWindowSFeLIVEcDDInVeStIGAtiOnINfORMATiQUewinDoWSPeLAseCUrITEDeSReSeaUxBLUEToO"


def extract_binary(text):
    binary = ""
    for char in para:
        if char.isalpha():  # Check if character is a letter
            if char.isupper():
                binary += '1'
            elif char.islower():
                binary += '0'
        # Skip spaces and dots (no else needed because we ignore them)
    return binary

# Example usage:
binary_output = extract_binary(para)
print(binary_output)
para="mEcaNIsMeDesecURiTeSoUsanDroiDResTAUReRLeSsymbOleSDeDebOgaGEapARtIRdEBInalrESCOMpIleSsTatiQUementAUToMaTisERleXPlOlTatIonDeVULNErABiLiTelORsdUnTeStDINTRuSIonrEMoTEdOWNLoADeXEcUtIOnavEcjAVaUTiliSAtiOnEtPrOTECTiONWindowSFeLIVEcDDInVeStIGAtiOnINfORMATiQUewinDoWSPeLAseCUrITEDeSReSeaUxBLUEToO"


def extract_binary(text):
    binary = ""
    for char in para:
        if char.isalpha():  # Check if character is a letter
            if char.isupper():
                binary += '1'
            elif char.islower():
                binary += '0'
        # Skip spaces and dots (no else needed because we ignore them)
    return binary

# Example usage:
binary_output = extract_binary(para)
print(binary_output)
01001101 01000011 01010100 01000110 01111011 01000010 01101001 00110011 01101110 01011111 01001010 00110000 01110101 00110011 01110010 01011111 01101010 01100101 01011111 01100011 01101111 01101101 01100010 01101100 01100101 01011111 01010000 01101111 01110101 01110010 01011111 01100001 01110110 01101111 01101001 01111101
01001101 01000011 01010100 01000110 01111011 01000010 01101001 00110011 01101110 01011111 01001010 00110000 01110101 00110011 01110010 01011111 01101010 01100101 01011111 01100011 01101111 01101101 01100010 01101100 01100101 01011111 01010000 01101111 01110101 01110010 01011111 01100001 01110110 01101111 01101001 01111101

once decoded :

MCTF{Bi3n_J0u3r_je_comble_Pour_avoi}
MCTF{Bi3n_J0u3r_je_comble_Pour_avoi}

Network


Why Encoding !!!

e58d61adeb4561379d4aeed0839d05e4.png

we are given a .pcap file

60739e770442e527ace979b4bfe85bf2.png

we just need to extract the data of the packets.

we can either use tshark or do it manually since its just 9 paquets

$ echo "TUNURntXSFktRU5DT0RJTkctVEhJUyEhIX0=" | base64 -d

MCTF{WHY-ENCODING-THIS!!!}%
$ echo "TUNURntXSFktRU5DT0RJTkctVEhJUyEhIX0=" | base64 -d

MCTF{WHY-ENCODING-THIS!!!}%

 

 

 

 

this one was not flagged

Web

CacheCache

afcf6549789ef99c8cec34077d564555.png

we have source code & a website :

from flask import Flask, request
from flask_caching import Cache
import random
import string
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from os import environ
from time import sleep

app = Flask(__name__)
config = {
    "DEBUG": False,
    "CACHE_TYPE": "SimpleCache",
    "CACHE_DEFAULT_TIMEOUT": 300
}
app.config.from_mapping(config)
cache = Cache(app)

def make_key():
    cachekey = request.args.get("cachekey")
    return cachekey if cachekey else ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8))

@app.route('/script/main.js', methods=["GET"])
def script():
    return "setTimeout(function(){document.getElementById('quote').innerText = 'Welcome to my personal website !';},1500);"

@app.route('/visit', methods=["GET","POST"])
def visit():
    if request.method == "GET":
        return f'''
            <!DOCTYPE HTML>
                <html>
                    <head>
                        <title>Show me your quote!</title>
                    </head>
                    <body>
                        <form method="POST" action="/visit">
                            <input type="text" placeholder="http://superquote.fr/myquote" name="url" id="url"/>
                            <br/>
                            <button action="submit">Send your quote!</button>
                        </form>
                    </body>
                </html>
        '''
    elif request.method == "POST":
        if(request.form.get("url") and request.form.get("url").startswith("http://") or request.form.get("url").startswith("https://")):
            chrome_options = Options()
            chrome_options.add_argument("--headless")
            chrome_options.add_argument("--incognito")
            chrome_options.add_argument("--no-sandbox")
            chrome_options.add_argument("--disable-gpu")
            chrome_options.add_argument("--disable-jit")
            chrome_options.add_argument("--disable-wasm")
            chrome_options.add_argument("--disable-dev-shm-usage")
            chrome_options.add_argument("--ignore-certificate-errors")
            chrome_options.binary_location = "/usr/bin/chromium-browser"
            service = Service("/usr/bin/chromedriver")
            driver = webdriver.Chrome(service=service, options=chrome_options)
            driver.set_page_load_timeout(3)
            driver.get("http://127.0.0.1:5000")
            driver.add_cookie({
                "name": "flag",
                "value": environ.get("FLAG"),
                "path": "/",
                "httpOnly": False,
                "samesite": "Strict",
                "domain": "127.0.0.1"
            })
            try:
                driver.get(request.form.get("url"))
            except: 
                pass
            sleep(3)
            driver.close()
            return "That's a good quote !"
        else:
            return "'url' parameter not given or is incorrect"
    else:
        return 'Method not allowed.'

@app.route('/')
@cache.cached(timeout=60, make_cache_key=make_key)
def index():
    return f'''
    <!DOCTYPE HTML>
    <head>
        <script src="http://{request.headers.get('Host')}/script/main.js"></script>
    </head>
    <body>
        The today random quote is : <p id="quote"></p>
    </body>
    '''

app.run(host='0.0.0.0', port=5000)
from flask import Flask, request
from flask_caching import Cache
import random
import string
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from os import environ
from time import sleep

app = Flask(__name__)
config = {
    "DEBUG": False,
    "CACHE_TYPE": "SimpleCache",
    "CACHE_DEFAULT_TIMEOUT": 300
}
app.config.from_mapping(config)
cache = Cache(app)

def make_key():
    cachekey = request.args.get("cachekey")
    return cachekey if cachekey else ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8))

@app.route('/script/main.js', methods=["GET"])
def script():
    return "setTimeout(function(){document.getElementById('quote').innerText = 'Welcome to my personal website !';},1500);"

@app.route('/visit', methods=["GET","POST"])
def visit():
    if request.method == "GET":
        return f'''
            <!DOCTYPE HTML>
                <html>
                    <head>
                        <title>Show me your quote!</title>
                    </head>
                    <body>
                        <form method="POST" action="/visit">
                            <input type="text" placeholder="http://superquote.fr/myquote" name="url" id="url"/>
                            <br/>
                            <button action="submit">Send your quote!</button>
                        </form>
                    </body>
                </html>
        '''
    elif request.method == "POST":
        if(request.form.get("url") and request.form.get("url").startswith("http://") or request.form.get("url").startswith("https://")):
            chrome_options = Options()
            chrome_options.add_argument("--headless")
            chrome_options.add_argument("--incognito")
            chrome_options.add_argument("--no-sandbox")
            chrome_options.add_argument("--disable-gpu")
            chrome_options.add_argument("--disable-jit")
            chrome_options.add_argument("--disable-wasm")
            chrome_options.add_argument("--disable-dev-shm-usage")
            chrome_options.add_argument("--ignore-certificate-errors")
            chrome_options.binary_location = "/usr/bin/chromium-browser"
            service = Service("/usr/bin/chromedriver")
            driver = webdriver.Chrome(service=service, options=chrome_options)
            driver.set_page_load_timeout(3)
            driver.get("http://127.0.0.1:5000")
            driver.add_cookie({
                "name": "flag",
                "value": environ.get("FLAG"),
                "path": "/",
                "httpOnly": False,
                "samesite": "Strict",
                "domain": "127.0.0.1"
            })
            try:
                driver.get(request.form.get("url"))
            except: 
                pass
            sleep(3)
            driver.close()
            return "That's a good quote !"
        else:
            return "'url' parameter not given or is incorrect"
    else:
        return 'Method not allowed.'

@app.route('/')
@cache.cached(timeout=60, make_cache_key=make_key)
def index():
    return f'''
    <!DOCTYPE HTML>
    <head>
        <script src="http://{request.headers.get('Host')}/script/main.js"></script>
    </head>
    <body>
        The today random quote is : <p id="quote"></p>
    </body>
    '''

app.run(host='0.0.0.0', port=5000)

we see that there is a page giving us a way to send a request, once the request is sent it’ll curl the website given:

a9481a65477ae828bd9c9ebe233546b4.png

i tried XSS injection but it did not work at first, lets look at the code in /script/main.js

setTimeout(function(){document.getElementById('quote').innerText = 'Welcome to my personal website !';},1500);
setTimeout(function(){document.getElementById('quote').innerText = 'Welcome to my personal website !';},1500);

just this.

**The solution was web cache poisonning, by sending the curl command to our website while hosting a payload to steal the cookie on the /script/main.js of our own server (basically doing an XSS through our own website)**
**The solution was web cache poisonning, by sending the curl command to our website while hosting a payload to steal the cookie on the /script/main.js of our own server (basically doing an XSS through our own website)**