James Cao
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
TryHackMe Input Manipulation & Prompt Injection Room

TryHackMe - Input Manipulation & Prompt Injection Room Walkthrough

Overview Room URL: https://tryhackme.com/room/inputmanipulationpromptinjection Difficulty: Easy Time to complete: 45 Walkthrough 1. Introduction No answer needed! 2. System Prompt Leakage What do we call the exposure of hidden system instructions? => Answer: leakage 3. Jailbreaking What evasive technique replaces or alters characters to bypass naive keyword filters? => Answer: Obfuscation 4. Prompt Injection Which injection type smuggles instructions via uploaded documents, web pages, or plugins? => Answer: Indirect Which injection type places malicious instructions directly in the user input? ...

November 19, 2025 · 1 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
DreamHack - crawling

DreamHack - crawling Web Challenge Write-up

Room / Challenge: crawling (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: crawling (web) Link: https://dreamhack.io/wargame/challenges/274 Level: 2 Date: 18-11-2025 Goal Leveraging the crawling service to get access to /admin page and get the flag. My Solution The web app is simple with the crawling logic, the main logic is in this code: def check_get(url): ip = lookup(urlparse(url).netloc.split(':')[0]) if ip == False or ip =='0.0.0.0': return "Not a valid URL." res=requests.get(url) if check_global(ip) == False: return "Can you access my admin page~?" for i in res.text.split('>'): if 'referer' in i: ref_host = urlparse(res.headers.get('refer')).netloc.split(':')[0] if ref_host == 'localhost': return False if ref_host == '127.0.0.1': return False res=requests.get(url) return res.text It doesn’t allow us to have ip address to 0.0.0.0, also there is a check of IP: ...

November 18, 2025 · 2 min
DreamHack - Tomcat Manager

DreamHack - Tomcat Manager Web Challenge Write-up

Room / Challenge: tomcat-manager (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: tomcat-manager (web) Link: https://dreamhack.io/wargame/challenges/248 Level: 2 Date: 17-11-2025 Goal Examining the code and leverage LFI and RCE to capture the flag. My Solution The source code has the ROOT.war besides Dockerfile and tomcat-users.xml. The tomcat-users.xml content: <?xml version="1.0" encoding="UTF-8"?> <tomcat-users xmlns="http://tomcat.apache.org/xml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd" version="1.0"> <role rolename="manager-gui"/> <role rolename="manager-script"/> <role rolename="manager-jmx"/> <role rolename="manager-status"/> <role rolename="admin-gui"/> <role rolename="admin-script"/> <user username="tomcat" password="[**SECRET**]" roles="manager-gui,manager-script,manager-jmx,manager-status,admin-gui,admin-script" /> </tomcat-users> It suggests that we have to find the password for user tomcat to get access to the code, let’s examine the source in ROOT.war. I use JD-GUI: ...

November 17, 2025 · 2 min
DreamHack - Relative Path Overwrite

DreamHack - Relative Path Overwrite Web Challenge Write-up

Room / Challenge: Relative Path Overwrite (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: Relative Path Overwrite (web) Link: https://dreamhack.io/wargame/challenges/439 Level: 2 Date: 17-11-2025 Goal Injecting XSS and leveraging Relative Path to get the flag. 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') { for (var i = 0; i < filter.length; i++) { if (param.toLowerCase().includes(filter[i])) { param = "nope !!"; break; } } } param_elem.innerHTML = param; </script> However it has a blacklists in filter.js: ...

November 17, 2025 · 1 min
DreamHack - easy-login

DreamHack - easy-login Web Challenge Write-up

Room / Challenge: easy-login (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: easy-login (web) Link: https://dreamhack.io/wargame/challenges/1213 Level: 2 Date: 17-11-2025 Goal Login as admin to get the flag. My Solution This is the PHP code in index.php which validates our credentials: <?php function generatePassword($length) { $characters = '0123456789abcdef'; $charactersLength = strlen($characters); $pw = ''; for ($i = 0; $i < $length; $i++) { $pw .= $characters[random_int(0, $charactersLength - 1)]; } return $pw; } function generateOTP() { return 'P' . str_pad(strval(random_int(0, 999999)), 6, "0", STR_PAD_LEFT); } $admin_pw = generatePassword(32); $otp = generateOTP(); function login() { if (!isset($_POST['cred'])) { echo "Please login..."; return; } if (!($cred = base64_decode($_POST['cred']))) { echo "Cred error"; return; } if (!($cred = json_decode($cred, true))) { echo "Cred error"; return; } if (!(isset($cred['id']) && isset($cred['pw']) && isset($cred['otp']))) { echo "Cred error"; return; } if ($cred['id'] != 'admin') { echo "Hello," . $cred['id']; return; } if ($cred['otp'] != $GLOBALS['otp']) { echo "OTP fail"; return; } if (!strcmp($cred['pw'], $GLOBALS['admin_pw'])) { require_once('flag.php'); echo "Hello, admin! get the flag: " . $flag; return; } echo "Password fail"; return; } ?> The problem is this every new request will be compared to new password and OTP so it is impossible to guess/brute-force the creds: ...

November 17, 2025 · 2 min
DreamHack - weblog-1

DreamHack - weblog-1 Web Challenge Write-up

Room / Challenge: weblog-1 (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: weblog-1 (web) Link: https://dreamhack.io/wargame/challenges/71 Level: 2 Date: 14-11-2025 Goal Examining the source code and access.log to answer questions then get the flag. My Solution This challenge is a web challenge with a mix with forensics challenge. First question: Please enter the password for the admin account that was stolen by the attacker. In the access.log scroll down we can see a bunch of requests of blind SQL Injection: ...

November 14, 2025 · 3 min
DreamHack - Client Side Template Injection

DreamHack - Client Side Template Injection Web Challenge Write-up

Room / Challenge: Client Side Template Injection (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: Client Side Template Injection (web) Link: https://dreamhack.io/wargame/challenges/437 Level: 2 Date: 14-11-2025 Goal Bypassing CSP rules and get the flag with XSS. My Solution This challenge is similar to CSP Bypass and DOM XSS, however the CSP policy is different: @app.after_request def add_header(response): global nonce response.headers['Content-Security-Policy'] = f"default-src 'self'; img-src https://dreamhack.io; style-src 'self' 'unsafe-inline'; script-src 'nonce-{nonce}' 'unsafe-eval' https://ajax.googleapis.com; object-src 'none'" nonce = os.urandom(16).hex() return response The app accepts script from https://ajax.googleapis.com, this is a huge security vulnerability we can check it in CSP Evaluator ...

November 14, 2025 · 1 min
DreamHack - file-csp-1

DreamHack - file-csp-1 Web Challenge Write-up

Room / Challenge: file-csp-1 (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: file-csp-1 (web) Link: https://dreamhack.io/wargame/challenges/36 Level: 2 Date: 14-11-2025 Goal Crafted the correct CSP satisfying the needs to get the flag. My Solution There are 3 routes in this challenge /test, /live and /verify. The /verify is the route we need to satisfy to get the flag: @APP.route('/verify', methods=['GET', 'POST']) def verify_csp(): global CSP if request.method == 'POST': csp = request.form.get('csp') try: options = webdriver.ChromeOptions() for _ in ['headless', 'window-size=1920x1080', 'disable-gpu', 'no-sandbox', 'disable-dev-shm-usage']: options.add_argument(_) driver = webdriver.Chrome('/chromedriver', options=options) driver.implicitly_wait(3) driver.set_page_load_timeout(3) driver.get(f'http://localhost:8000/live?csp={quote(csp)}') try: a = driver.execute_script('return a()'); except: a = 'error' try: b = driver.execute_script('return b()'); except: b = 'error' try: c = driver.execute_script('return c()'); except Exception as e: c = 'error' c = e try: d = driver.execute_script('return $(document)'); except: d = 'error' if a == 'error' and b == 'error' and c == 'c' and d != 'error': return FLAG return f'Try again!, {a}, {b}, {c}, {d}' except Exception as e: return f'An error occured!, {e}' return render_template('verify.html') The /test and /live is where we can use to test our payloads. The app requirements is we have to crafted the correct CSP policy which satisfy the needs in the csp.html: ...

November 14, 2025 · 3 min