James Cao
DreamHack - web-deserialize-python

DreamHack - web-deserialize-python Web Challenge Write-up

Room / Challenge: web-deserialize-python (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: web-deserialize-python (web) Link: https://dreamhack.io/wargame/challenges/40 Level: 2 Date: 13-11-2025 Goal Leveraging insecure deserialization to retrieve the flag. My Solution The vulnerability is in the /check-session route: @app.route('/check_session', methods=['GET', 'POST']) def check_session(): if request.method == 'GET': return render_template('check_session.html') elif request.method == 'POST': session = request.form.get('session', '') info = pickle.loads(base64.b64decode(session)) return render_template('check_session.html', info=info) The server will loads whatever we pass to the session data: info = pickle.loads(base64.b64decode(session)) The vulnerability here is the pickle. Therefore, we can create a malicious payload through Python: ...

November 13, 2025 · 1 min
DreamHack - baby-sqlite

DreamHack - baby-sqlite Web Challenge Write-up

Room / Challenge: baby-sqlite (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: baby-sqlite (web) Link: https://dreamhack.io/wargame/challenges/1 Level: 2 Date: 13-11-2025 Goal Leveraging SQL Injection to bypass the check and get the flag. My Solution The app has the /login route: @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'GET': return render_template('login.html') uid = request.form.get('uid', '').lower() upw = request.form.get('upw', '').lower() level = request.form.get('level', '9').lower() sqli_filter = ['[', ']', ',', 'admin', 'select', '\'', '"', '\t', '\n', '\r', '\x08', '\x09', '\x00', '\x0b', '\x0d', ' '] for x in sqli_filter: if uid.find(x) != -1: return 'No Hack!' if upw.find(x) != -1: return 'No Hack!' if level.find(x) != -1: return 'No Hack!' with app.app_context(): conn = get_db() query = f"SELECT uid FROM users WHERE uid='{uid}' and upw='{upw}' and level={level};" try: req = conn.execute(query) result = req.fetchone() if result is not None: uid = result[0] if uid == 'admin': return FLAG except: return 'Error!' return 'Good!' So here we have to login as admin to get the flag: ...

November 13, 2025 · 2 min
DreamHack - sql injection bypass waf Advanced

DreamHack - sql injection bypass waf Advanced Web Challenge Write-up

Room / Challenge: sql injection bypass WAF Advanced (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: sql injection bypass WAF Advanced (web) Link: https://dreamhack.io/wargame/challenges/416 Level: 2 Date: 12-11-2025 Goal Bypass WAF and use SQL Injection to get the flag My Solution The app.py has this WAF check: keywords = ['union', 'select', 'from', 'and', 'or', 'admin', ' ', '*', '/', '\n', '\r', '\t', '\x0b', '\x0c', '-', '+'] def check_WAF(data): for keyword in keywords: if keyword in data.lower(): return True return False It is harder for us to make requests that we want to do some injection. This is what it looked like when we are blocked by WAF: ...

November 12, 2025 · 2 min
DreamHack - login-1

DreamHack - login-1 Web Challenge Write-up

Room / Challenge: login-1 (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: login-1 (web) Link: https://dreamhack.io/wargame/challenges/47 Level: 2 Date: 10-11-2025 Goal Login as the account with admin rights to get the flag. My Solution The app.py has one route that we have to focus on /reset-password: @app.route('/forgot_password', methods=['GET', 'POST']) def forgot_password(): if request.method == 'GET': return render_template('forgot.html') else: userid = request.form.get("userid") newpassword = request.form.get("newpassword") backupCode = request.form.get("backupCode", type=int) conn = get_db() cur = conn.cursor() user = cur.execute('SELECT * FROM user WHERE id = ?', (userid,)).fetchone() if user: # security for brute force Attack. time.sleep(1) if user['resetCount'] == MAXRESETCOUNT: return "<script>alert('reset Count Exceed.');history.back(-1);</script>" if user['backupCode'] == backupCode: newbackupCode = makeBackupcode() updateSQL = "UPDATE user set pw = ?, backupCode = ?, resetCount = 0 where idx = ?" cur.execute(updateSQL, (hashlib.sha256(newpassword.encode()).hexdigest(), newbackupCode, str(user['idx']))) msg = f"<b>Password Change Success.</b><br/>New BackupCode : {newbackupCode}" else: updateSQL = "UPDATE user set resetCount = resetCount+1 where idx = ?" cur.execute(updateSQL, (str(user['idx']))) msg = f"Wrong BackupCode !<br/><b>Left Count : </b> {(MAXRESETCOUNT-1)-user['resetCount']}" conn.commit() return render_template("index.html", msg=msg) return "<script>alert('User Not Found.');history.back(-1);</script>"; Firstly, this code may appears safely without exploitation can be made however the time.sleep(1) is the key where Race Condition come in play. There are 100 possible backup code from 0 -> 100. Therefore, it can be brute-forced. In order to bypass the the max reset count check we can make 100 requests at a time with different backup code. From this we can change the password. ...

November 10, 2025 · 2 min
DreamHack - funjs

DreamHack - funjs Web Challenge Write-up

Room / Challenge: funjs (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: funjs (web) Link: https://dreamhack.io/wargame/challenges/116 Level: 2 Date: 10-11-2025 Goal Examining the index.html and solving the flag. My Solution The web app is the index.html. I will comment the moveBox function to test more comfortably: function init() { box = document.getElementById("formbox"); {{ /* setInterval(moveBox,1000); */ }} } We have to submit the correct flag in order to get the flag, if not we will received NOP!: ...

November 10, 2025 · 2 min
DreamHack - blind-command

DreamHack - blind-command Web Challenge Write-up

Room / Challenge: blind-command (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: blind-command (web) Link: https://dreamhack.io/wargame/challenges/73 Level: 2 Date: 10-11-2025 Goal My Solution The app is simple with just one app.py: #!/usr/bin/env python3 from flask import Flask, request import os app = Flask(__name__) @app.route('/' , methods=['GET']) def index(): cmd = request.args.get('cmd', '') if not cmd: return "?cmd=[cmd]" if request.method == 'GET': '' else: os.system(cmd) return cmd app.run(host='0.0.0.0', port=8000) It is only one route that we can leverage. It needs us to have cmd arguments which is executed by os.system(cmd) however there is a check: ...

November 10, 2025 · 1 min
DreamHack - web-ssrf

DreamHack - web-ssrf Web Challenge Write-up

Room / Challenge: web-ssrf (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: web-ssrf (web) Link: https://dreamhack.io/wargame/challenges/75 Level: 2 Date: 10-11-2025 Goal Using image viewer service to capture the flag. My Solution You can download and examine the source code here The source code has one main app.py which is the file runs the application, there is a route that we have to pay attention to /img_viewer: @app.route("/img_viewer", methods=["GET", "POST"]) def img_viewer(): if request.method == "GET": return render_template("img_viewer.html") elif request.method == "POST": url = request.form.get("url", "") urlp = urlparse(url) if url[0] == "/": url = "http://localhost:8000" + url elif ("localhost" in urlp.netloc) or ("127.0.0.1" in urlp.netloc): data = open("error.png", "rb").read() img = base64.b64encode(data).decode("utf8") return render_template("img_viewer.html", img=img) try: data = requests.get(url, timeout=3).content img = base64.b64encode(data).decode("utf8") except: data = open("error.png", "rb").read() img = base64.b64encode(data).decode("utf8") return render_template("img_viewer.html", img=img) Firstly, I tried several payloads to get the flag.txt content like /flag.txt, /static/../flag.txt, … however all of them returns this base64 encoded: ...

November 10, 2025 · 2 min
TryHackMe Putting It All Together Room

TryHackMe - Putting It All Together Room Walkthrough

Overview Room URL: https://tryhackme.com/room/puttingitalltogether Difficulty: Easy Time to complete: 15 Walkthrough 1. Putting It All Together No answer needed! 2. Other Components What can be used to host static files and speed up a clients visit to a website? => Answer: CDN What does a load balancer perform to make sure a host is still alive? => Answer: health check What can be used to help against the hacking of a website? ...

November 9, 2025 · 1 min
TryHackMe How Websites Work Room

TryHackMe - How Websites Work Room Walkthrough

Overview Room URL: https://tryhackme.com/room/howwebsiteswork Difficulty: Easy Time to complete: 25 Walkthrough 1. How websites work What term best describes the component of a web application rendered by your browser? => Answer: Front End 2. HTML One of the images on the cat website is broken - fix it, and the image will reveal the hidden text answer! Change the <img src='img/cat-2'> to <img src='img/cat-2.jpg'>: => Answer: HTMLHERO Add a dog image to the page by adding another img tag (<img>) on line 11. The dog image location is img/dog-1.png. What is the text in the dog image? ...

November 9, 2025 · 1 min
TryHackMe Linux Strength Training Room

TryHackMe - Linux Strength Training Room Walkthrough

Overview Room URL: https://tryhackme.com/room/linuxstrengthtraining Difficulty: Easy Time to complete: 45 Walkthrough 1. Intro No answer needed! 2. Finding your way around linux - overview What is the correct option for finding files based on group => Answer: -group What is format for finding a file with the user named Francis and with a size of 52 kilobytes in the directory /home/francis/ => Answer: find /home/francis -type f -user Francis -size 52k ...

November 9, 2025 · 6 min