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
we are given an article as a file
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 !!!
we are given a .pcap file
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
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:
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)**