James Cao
DreamHack - I LOVE XSS!

DreamHack - I LOVE XSS! Web Challenge Writeup

Room / Challenge: I LOVE XSS! (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: I LOVE XSS! (web) Link: https://dreamhack.io/wargame/challenges/2061 Level: 2 Date: 25-11-2025 Goal Leveraging XSS Scriptin to get the flag. My Solution The app has a banned list for XSS Scripting: banlist = ["`","'","alert(","fetch(","replace(","[","]","javascript","@","!","%","location","href","window","eval"] Also in sanitizer.py it do allow <script> tag however no any attributes is allowed: import bleach ALLOWED_TAGS = ['script'] #I only love script tags! ALLOWED_ATTRIBUTES = {} ALLOWED_PROTOCOLS = ['http', 'https'] def sanitize_input(user_input: str) -> str: return bleach.clean( user_input, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRIBUTES, protocols=ALLOWED_PROTOCOLS, strip=True, strip_comments=True ) Initially, I create a payload: ...

November 25, 2025 · 1 min
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
TryHackMe OWASP Juice Shop

TryHackMe - OWASP Juice Shop Room Walkthrough

Overview Room URL: https://tryhackme.com/room/owaspjuiceshop Difficulty: Easy Time to complete: 120 Walkthrough 1. Open for business! No answer needed! 2. Let’s go on an adventure! Question #1: What’s the Administrator’s email address? => Answer: admin@juice-sh.op Question #2: What parameter is used for searching? => Answer: q Question #3: What show does Jim reference in his review? => Answer: Star Trek 3. Inject the juice Question #1: Log into the administrator account! Using Burp Suite, change the email value to: "' OR 1=1 --" and forward ...

November 21, 2025 · 2 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