James Cao
DreamHack - Test site

DreamHack - Test site Web Challenge Writeup

Room / Challenge: Test site (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: Test site (web) Link: https://dreamhack.io/wargame/challenges/2064 Level: 2 Date: 25-11-2025 Goal My Solution To get the flag in /flag we have to be the admin to get access to /admin however we’re still not the admin now, and the setcookie and readcookie is hidden: # def setcookie(id_str): # The code for this function is hidden # def readcookie(cookie_str): # The code for this function is hidden We can just blind testing to get admin role. There is a vulnerable in /logintest route: ...

November 25, 2025 · 2 min
DreamHack - Special Letter Translator

DreamHack - Special Letter Translator Web Challenge Writeup

Goal Gaining privileges and leveraging RCE to get the flag. My Solution Based on the description and the source code the first thing we have to do is gain VIP role, the SECRET_KEY in app.py: SECRET_KEY = "SECRET_KEY" Try logged in as guest with password guest check this is the wrong secret key, then we have to brute-force the secret key in order to create our own JWT token, my brute-force Python script: ...

November 24, 2025 · 2 min
Patriot CTF 2025 - Web Challenges Writeups

Patriot CTF 2025 - Web Challenges Writeups

I’m a player in web, so all my writeups about web challenges in Patriot CTF 2025, since the challs are closed after the event so there aren’t any images of challs. These are all 5 web challs in the event: Connection Tester Goal Leveraging SQLi and Command Injection to get the flag. My Solution The app has a login form and there isn’t any credentials given, so tried some simple SQL Injection to bypass the login, normally admin account existed so inject: ...

November 24, 2025 · 5 min
DreamHack - Black-Hacker-Company

DreamHack - Black-Hacker-Company Web Challenge Write-up

Room / Challenge: Black-Hacker-Company (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: Black-Hacker-Company (web) Link: https://dreamhack.io/wargame/challenges/1834 Level: 2 Date: 22-11-2025 Goal Bypass filter and leveraging SSRF to get the flag. My Solution The app.py is simple with three routes: @app.route('/user-page', methods=['GET']) def user(): url = request.args.get('url') if not url: return jsonify({"swap": "Write URL"}), 400 if not any(allow in url for allow in ALLOW_HOST): return jsonify({"swap": "URL not allowed"}), 403 for bad in BLACK_LIST: if bad in url.lower(): return jsonify({"swap": "BAN LIST"}), 403 try: result = subprocess.run( ["curl", "-s", url], text=True, capture_output=True, check=True ) return jsonify({"response": result.stdout}) except subprocess.CalledProcessError as e: return jsonify({"swap": "Error!~", "details": str(e)}), 500 @app.route('/access-token', methods=['GET']) def admin(): if request.remote_addr in ["127.0.0.1"]: password = request.args.get("password") if password: if password == PASSWORD: return jsonify({"server": TOKEN}), 200 else: return jsonify({"server": "Nop~ Password Wrong><"}), 403 else: return jsonify({"server": "Write Password!"}), 400 else: return jsonify({"server": "Only Localhost Can Access : )"}), 403 @app.route('/admin', methods=['GET']) def check(): if request.args.get("token") == TOKEN: return "<h1>dotori-company : $#@&*(@#&*(@)) BeePPP.. </h1>" + FLAG else: return jsonify({"server": "you are not admin..."}), 403 The /access-token will check our given password and if correct it will returned token which will be used to get the flag in /admin. However, /access-token just accepts request from 127.0.0.1 IP so we have to use the /user-page. The filter: ...

November 22, 2025 · 2 min
DreamHack - Not-only

DreamHack - Not-only Web Challenge Write-up

Room / Challenge: Not-only (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: Not-only (web) Link: https://dreamhack.io/wargame/challenges/1619 Level: 2 Date: 21-11-2025 Goal Find the correct user and brute-force the password to get the flag. My Solution Based on the description: Find a user with admin rights! The user's password is a flag. The password format is a string containing numbers, uppercase and lowercase letters, and special characters. { } How many admin users are there? The flag format is DH{} We have some details. We have to: ...

November 21, 2025 · 2 min
DreamHack - crack crack crack it

DreamHack - [wargame.kr] crack crack crack it Web Challenge Write-up

Room / Challenge: [wargame.kr] crack crack crack it (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: [wargame.kr] crack crack crack it (web) Link: https://dreamhack.io/wargame/challenges/330 Level: 2 Date: 21-11-2025 Goal Brute-force the password and get the flag. My Solution Based on the description: oops, i forgot my password!! somebody help me T_T (i remember that my password begin with 'G4HeulB' and the other chars is composed of number, lower alphabet only..) notice: this .htaccess file is different each other IP Address. authentication have to be same ip plz, We can create our password generator with Python and use John-the-ripper to brute-force the password: ...

November 21, 2025 · 1 min
DreamHack - Hello, go!

DreamHack - Hello, Go! Web Challenge Write-up

Room / Challenge: Hello, go! (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: Hello, go! (web) Link: https://dreamhack.io/wargame/challenges/1999 Level: 2 Date: 20-11-2025 Goal Leveraging SSTI in Go template and get the flag. My Solution The app.go has a vulnerable code in how it renders our input: t, err := template.New("page").Parse( fmt.Sprintf(` <html> <body> <h1>Hello, %s!</h1> </body> </html>`, name)) The server gets our input and render it to the template, then it executes: ...

November 20, 2025 · 1 min
DreamHack - development-env

DreamHack - development-env Web Challenge Write-up

Room / Challenge: development-env (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: development-env (web) Link: https://dreamhack.io/wargame/challenges/783 Level: 2 Date: 20-11-2025 Goal The development environment source code is deployed in production, leverage this to get the flag. My Solution The source code has 2 main routes /validate, /: app.get('/', async (req, res) => { try { let token = req.cookies.auth || ''; const payloadData = await cryptolib.readJWT(token, 'FAKE_KEY'); if (payloadData) { userflag = payloadData['uid'] == 'admin' ? flag : 'You are not admin'; res.render('main', { username: payloadData['uid'], flag: userflag }); } else { res.render('login'); } } catch (e) { if (isDevelopmentEnv) { res.json(JSON.parse(parsetrace(e, { sources: true }).json())); } else { res.json({ message: 'error' }); } } }); app.post('/validate', async (req, res) => { try { let contentType = req.header('Content-Type').split(';')[0]; if ( ['multipart/form-data', 'application/x-www-form-urlencoded'].indexOf(contentType) === -1 ) { throw new Error('content type not supported'); } else { let bodyKeys = Object.keys(req.body); if (bodyKeys.indexOf('id') === -1 || bodyKeys.indexOf('pw') === -1) { throw new Error('missing required parameter'); } else { if ( typeof database[req.body['id']] !== 'undefined' && database[req.body['id']] === req.body['pw'] ) { if ( req.get('User-Agent').indexOf('MSIE') > -1 || req.get('User-Agent').indexOf('Trident') > -1 ) throw new Error('IE is not supported'); jwt = await cryptolib.generateJWT(req.body['id'], 'FAKE_KEY'); res.cookie('auth', jwt, { maxAge: 30000, }).send("<script>alert('success');document.location.href='/'</script>"); } else { res.json({ message: 'error', detail: 'invalid id or password' }); } } } } catch (e) { if (isDevelopmentEnv) { res.status(500).json({ message: 'devError', detail: JSON.parse(parsetrace(e, { sources: true }).json()), }); } else { res.json({ message: 'error', detail: e }); } } }); Based on the description, it is a hint that the development source code is published to production. We can find that vulnerable code: ...

November 20, 2025 · 3 min
DreamHack - Are you admin?

DreamHack - Are you admin? Web Challenge Write-up

Room / Challenge: are you admin? (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: are you admin? (web) Link: https://dreamhack.io/wargame/challenges/1922 Level: 2 Date: 19-11-2025 Goal XSS Scripting takes in place to get the flag. My Solution The app is vulnerable to XSS Scripting: @app.route("/intro", methods=["GET"]) def intro(): name = request.args.get("name") detail = request.args.get("detail") return render_template("intro.html", name=name, detail=detail) @app.route("/report", methods=["GET", "POST"]) def report(): if request.method == "POST": path = request.form.get("path") if not path: return render_template("report.html", msg="fail") else: parsed_path = urlparse(path) params = parse_qs(parsed_path.query) name = params.get("name", [None])[0] detail = params.get("detail", [None])[0] if access_page(name, detail): return render_template("report.html", message="Success") else: return render_template("report.html", message="fail") else: return render_template("report.html") @app.route("/whoami", methods=["GET"]) def whoami(): user_info = "" authorization = request.headers.get('Authorization') if authorization: user_info = b64decode(authorization.split('Basic ')[1].encode()).decode() else: user_info = "guest:guest" id = user_info.split(":")[0] password = user_info.split(":")[1] if ((id == 'admin') and (password == '[**REDACTED**]')): message = FLAG return render_template('whoami.html',id=id, message=message) else: message = "You are guest" return render_template('whoami.html',id=id, message=message) There are 3 routes in the app, /intro is where we use to inject Javascript, /report is to report the URL to the bot, /whoami is where we can use to make the bot to get the flag for us. ...

November 19, 2025 · 2 min
DreamHack - Relative Path Overwrite Advanced

DreamHack - Relative Path Overwrite Advanced Web Challenge Write-up

Room / Challenge: Relative Path Overwrite Advanced (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: Relative Path Overwrite Advanced (web) Link: https://dreamhack.io/wargame/challenges/440 Level: 2 Date: 18-11-2025 Goal Leveraging Relative Path Overwrite and XSS Scripting to force the bot sending the flag to you. My Solution The app has the vuln.php where we can inject XSS Scripting: <script src="filter.js"></script> <pre id=param></pre> <script> var param_elem = document.getElementById("param"); var url = new URL(window.location.href); var param = url.searchParams.get("param"); if (typeof filter === 'undefined') { param = "nope !!"; } else { for (var i = 0; i < filter.length; i++) { if (param.toLowerCase().includes(filter[i])) { param = "nope !!"; break; } } } param_elem.innerHTML = param; </script> However it needs the filter to not undefined in order to run our param, currently filter is undefined by default: Here is the progress we have to do to exploit the app, first we have to set a filter=[] for the vuln page to prevent the nope !! of filter === 'undefined', then force the bot to make a request to our server with the document.cookie. ...

November 18, 2025 · 2 min