James Cao
UMass CTF 2026 Web Writeups

UMass CTF 2026 Writeups

Brick by Brick Goal Find the hidden admin dashboard to get the flag. My Solution The home page doesn’t have any links, or any hints: Therefore I did some basic emuneration and found a existed robots.txt file with this content: User-agent: * Disallow: /internal-docs/assembly-guide.txt Disallow: /internal-docs/it-onboarding.txt Disallow: /internal-docs/q3-report.txt # NOTE: Maintenance in progress. # Unauthorized crawling of /internal-docs/ is prohibited. Seems like the maintenance source code is hosted public, taking a look at three internal docs files. I found a breakthrough solution for the flag. ...

April 16, 2026 · 15 min
HackTheBox Facts Cover

HackTheBox Facts Machine Walkthrough

Reconnaissance & Enumeration Running simple nmap command: nmap -v -sV <MACHINE_IP> Two simple http and ssh services. Access to the website http://facts.htb. After an amount of time of enumeration, I found an /admin/login where I check the source and found the technology behind the app: Camaleon CMS. Found a CVE that we can leverage of it: CVE-2024-46987: a Path Traversal bug in Camaleon CMS 2.8.0 < 2.8.2 (work on 2.9.0). It allows authenticated users to read sensitive server files via the MediaController. Intended for authorized security auditing and educational research only. github.com/Goultarde/CVE-2024-46987 ...

April 16, 2026 · 1 min
HackTheBox Silentium Cover

HackTheBox Silentium Machine Walkthrough

Reconnaissance & Enumeration Doing simple nmap command: nmap -v -sV <MACHINE_IP> The server has 2 ports http and ssh, visit the website I couldn’t get anything useful the only thing is I found 3 leadership users, which may be used to gain access through ssh: I brute-force the domain in order to see I got anything useful: ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u http://silentium.htb -H "Host: FUZZ.silentium.htb" -fs 8753 > result.txt cat result.txt | grep "Status: 200" ...

April 15, 2026 · 2 min
Pre-Authentication RCE in KoaCook Order Online System

Case Study: Pre-Authentication RCE in KoaCook Order Online System (CVE-2025-55182)

Executive Summary While performing a white-box security audit of a legacy internal application, I identified a critical, pre-authentication Remote Code Execution (RCE) vulnerability stemming from the unsafe deserialization of payloads within the React Server Components (RSC) transport protocol, commonly referred to as the Flight protocol. This vulnerability is tracked as CVE-2025-55182, colloquially known as React2Shell. I have responsibly disclosed the finding and patched the application by upgrading the vulnerable dependencies (react, react-dom, and next). This post details the technical mechanics of the exploit, provides a sanitized Proof of Concept (PoC), and illustrates the remediation path. ...

March 21, 2026 · 3 min
VSL CTF 2026 Web Writeups

VSL CTF 2026 Web Writeups

mrGraph Goal Leveraging the GraphQL Injection to get the flag in the database. My Solution The new intern made a website, there must be vulnerability. Examining sources of available pages, we can find a route that we can take advantage of /api/graphql (visible in /users route). In order to know all the schemas existed in the database, run this in the Console tab of browser: fetch('/api/graphql', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: ` query { __schema { types { name fields { name } } } }`, }), }) .then(response => response.json()) .then(data => { console.log(JSON.stringify(data, null, 2)); }); Got a really suspicious postPassword field: ...

January 29, 2026 · 9 min
TryHackMe Solar, exploiting log4j

TryHackMe - Solar, exploiting log4j Room Walkthrough

Overview Room URL: https://tryhackme.com/room/solar Difficulty: Medium Time to complete: 60 Walkthrough 1. CVE-2021-44228 Introduction No answer needed! 2. Reconnaissance What service is running on port 8983? (Just the name of the software) nmap -sV -p 8983 <MACHINE_IP> => Answer: Apache Solr 3. Discovery Take a close look at the first page visible when navigating to http://MACHINE_IP:8983. You should be able to see clear indicators that log4j is in use within the application for logging activity. What is the -Dsolr.log.dir argument set to, displayed on the front page? ...

December 18, 2025 · 3 min
WannaGame Championship CTF 2025 longtrip Web Writeup

WannaGame Championship CTF 2025 longtrip Web Writeup

Room / Challenge: longtrip (Web) Metadata Author: jameskaois CTF: WannaGame Championship CTF 2025 Challenge: longtrip (web) Target / URL: https://ctf.cnsc.com.vn/games/1/challenges?challenge=24 Points: 947 Solved: 6 Date: 10-12-2025 Goal Enjoying the longtrip and get the flag. My Solution The challenge comes with no source code, visit the home page: Doing some enumeration steps with dirsearch, found /login page, login with admin:1234 as in description, found a XML Parser tool, tried using the template to see how it works: ...

December 16, 2025 · 6 min
WannaGame Championship CTF 2025 CTFguideline Web Writeup

WannaGame Championship CTF 2025 CTFguideline Web Writeup

Room / Challenge: CTFguideline (Web) Metadata Author: jameskaois CTF: WannaGame Championship CTF 2025 Challenge: CTFguideline (web) Target / URL: https://ctf.cnsc.com.vn/games/1/challenges?challenge=23 Points: 967 Solved: 5 Date: 09-12-2025 Goal Leveraging the Race Condition XSS in the home page, submit to the bot to get the flag. My Solution Visit the home page and view the page source we should find the script.js which is the main script for the page: /* ============================================================ Load config ============================================================ */ var config; fetch('config.json') .then(resp => resp.json()) .then(async resp => { config = resp; document.body.style.backgroundColor = config.background; document.body.style.color = config.text_color; await loadContentsList(config); if (location.hash.length > 0) (config.skin = 'Pane'), initPane(config); else { const url = new URL(location.href); if (url.searchParams.get('doc')) (config.skin = 'Modern'), initModern(config); else (config.skin = 'Default'), initDefault(config); } }); if (location.hash.length > 0) { sanitizeHash(); window.onhashchange = () => loadPaneContent(config); } /* ============================================================ Build navigation list ============================================================ */ async function loadContentsList(config) { const nav = document.getElementById('nav'); nav.innerHTML = ''; config.contents.forEach(file => { const name = file.replace('.html', ''); nav.innerHTML += ` <li><a href="#" class="nav-item" data-file="${file}">${name}</a></li> `; }); attachNavHandlers(config); } /* ============================================================ Navigation Handlers ============================================================ */ function attachNavHandlers(config) { document.querySelectorAll('.nav-item').forEach(item => { item.addEventListener('click', e => { e.preventDefault(); const file = item.getAttribute('data-file'); if (config.skin === 'Pane') { location.hash = file; // Pane uses hash } else if (config.skin === 'Default') { loadDefault(file); } else if (config.skin === 'Modern') { const url = new URL(location.href); url.searchParams.set('doc', file); location.href = url.toString(); } }); }); } /* ============================================================ Pane Theme — 3 Panel View ============================================================ */ function initPane(config) { hideAllViewers(); document.body.classList.add('pane'); document.getElementById('viewerLeft').style.display = 'block'; document.getElementById('viewerCenter').style.display = 'block'; document.getElementById('viewerRight').style.display = 'block'; loadPaneContent(config); } function sanitizeHash() { currentHash = location.hash.substring(1); if (currentHash) { var decodedHash = decodeURIComponent(currentHash); var sanitizedHash = decodedHash.replace(/(javascript:|data:|[<>])/gi, ''); if (decodedHash != sanitizedHash) { document.location.hash = encodeURI(sanitizedHash); } } } function loadPaneContent(config) { sanitizeHash(); let file = location.hash.substring(1) || config.default; if (file) { document.getElementById( 'viewerLeft', ).innerHTML = `<h2>Overview</h2><p>You selected: ${encodeURIComponent(file)}</p>`; url = new URL(location.href); if (config) { if (!config.load_remote) { file = 'contents/' + file; } else { if (!file.startsWith('http://') && !file.startsWith('https://')) file = url.origin + url.pathname + '/contents' + file; } } document.getElementById('viewerCenter').contentWindow.location.replace(decodeURI(file)); document.getElementById('viewerRight').innerHTML = `<h2>Extras</h2><p>Copyright.</p>`; } } /* ============================================================ Default Theme ============================================================ */ function initDefault(config) { hideAllViewers(); document.body.classList.add('default'); document.getElementById('viewer').style.display = 'block'; loadDefault(config.default); } async function loadDefault(file) { const text = await fetch('contents/' + file).then(r => r.text()); document.getElementById('viewer').innerHTML = text; } /* ============================================================ Modern Theme ============================================================ */ function initModern(config) { hideAllViewers(); document.body.classList.add('modern'); document.getElementById('viewer').style.display = 'block'; const params = new URLSearchParams(location.search); const doc = params.get('doc') || config.default; loadModern(doc); } async function loadModern(file) { const text = await fetch('contents/' + file).then(r => r.text()); document.getElementById('viewer').innerHTML = `<div class="modern-card">${text}</div>`; } /* ============================================================ Helpers ============================================================ */ function hideAllViewers() { document.querySelectorAll('#viewer, .pane-panel').forEach(el => { el.style.display = 'none'; }); } Also, from this we can find the config.json: ...

December 16, 2025 · 4 min
WannaGame Championship CTF 2025 Trust Web Writeup

WannaGame Championship CTF 2025 Trust Web Writeup

Room / Challenge: Trust (Web) Metadata Author: jameskaois CTF: WannaGame Championship CTF 2025 Challenge: Trust (web) Target / URL: https://ctf.cnsc.com.vn/games/1/challenges?challenge=20 Points: 500 Solved: 61 Date: 10-12-2025 Goal Bypassing the vulnerability chain with 3 main vulnerabilities to get the flag. My Solution This app is vulnerable with a vulnerability chain: # Vulnerability Purpose 1 CVE-2025-23419 Bypassing the client certificate of hidden service 2 Signature Bypass Bypass the signature check to upload exploit plugin 3 Zip Slip Achieve RCE to read the flag CVE-2025-23419 SSL Session Reuse This vulnerability can be easily noticed by the comment in the nginx.conf: ...

December 16, 2025 · 5 min
DreamHack - spring-view

DreamHack - spring-view Web Challenge Writeup

Room / Challenge: spring-view (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: spring-view (web) Link: https://dreamhack.io/wargame/challenges/99 Level: 4 Date: 28-11-2025 Goal Decompile the app.jar and leveraging SSTI to get the flag. My Solution Using Java Decompiler to decompile the app.jar we received this source code In UserController.class is where we have to examine to find the vulnerability: public class UserController { Logger log = LoggerFactory.getLogger(com.dreamhack.spring.UserController.class); @GetMapping({"/"}) public String index(@RequestParam(value = "lang", required = false) String lang, Model model, HttpServletRequest request, HttpServletResponse response) { if (lang != null) { response.addCookie(new Cookie("lang", lang)); return "redirect:/"; } Cookie cookie_lang = WebUtils.getCookie(request, "lang"); if (cookie_lang == null) response.addCookie(new Cookie("lang", "en")); model.addAttribute("message", "Spring World !"); return "index"; } @GetMapping({"/welcome"}) public String welcome(@CookieValue(value = "lang", defaultValue = "en") String lang) { return lang + "/welcome"; } @GetMapping({"/signup"}) public String signup(@CookieValue(value = "lang", defaultValue = "en") String lang) { return lang + "/underconstruction"; } @GetMapping({"/signin"}) public String signin(@CookieValue(value = "lang", defaultValue = "en") String lang) { return lang + "/underconstruction"; } } We can see here all three routes /welcome, /signup and /signin all used the cookie lang value to render the template, here we can think of SSTI vulnerability. ...

November 28, 2025 · 2 min