<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Critical Thinking - Bug Bounty Podcast</title>
    <description>A &#39;by Hackers for Hackers&#39; podcast focused on technical bug bounty content.</description>
    
    <link>https://blog.criticalthinkingpodcast.io/</link>
    <atom:link href="https://rss.beehiiv.com/feeds/SQSpYiG1E1.xml" rel="self"/>
    
    <lastBuildDate>Mon, 15 Jun 2026 03:35:02 +0000</lastBuildDate>
    <pubDate>Thu, 11 Jun 2026 10:01:00 +0000</pubDate>
    <atom:published>2026-06-11T10:01:00Z</atom:published>
    <atom:updated>2026-06-15T03:35:02Z</atom:updated>
    
      <category>Education</category>
      <category>Cybersecurity</category>
      <category>Technology</category>
    <copyright>Copyright 2026, Critical Thinking - Bug Bounty Podcast</copyright>
    
    <image>
      <url>https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/publication/logo/f3cc9f6e-341e-452a-9b50-a244bcc4cf92/Critical_Thinking_Podcast_Cover_Square.jpg</url>
      <title>Critical Thinking - Bug Bounty Podcast</title>
      <link>https://blog.criticalthinkingpodcast.io/</link>
    </image>
    
    <docs>https://www.rssboard.org/rss-specification</docs>
    <generator>beehiiv</generator>
    <language>en-us</language>
    <webMaster>support@beehiiv.com (Beehiiv Support)</webMaster>

      <item>
  <title>[HackerNotes Ep. 178] Hacking Google with AI: $670K in Bounties with Brutecat</title>
  <description>Brutecat breaks down how he built an AI-powered pipeline that scanned 14,000 Google APIs and earned $670,000 in bounties in under four months. The full methodology, the tooling, and the bugs.</description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-178-hacking-google-with-ai-670k-in-bounties-with-brutecat</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-178-hacking-google-with-ai-670k-in-bounties-with-brutecat</guid>
  <pubDate>Thu, 11 Jun 2026 10:01:00 +0000</pubDate>
  <atom:published>2026-06-11T10:01:00Z</atom:published>
    <dc:creator>Aituglo ‎</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr">Hacker TL;DR</h2><ul><li><p class="paragraph" style="text-align:left;">First-party auth (FPAv2) is the key to most Google APIs: if an API has a <code>client6.google.com</code> alias, ~90% of the time FPA works. The full FPA spec was extracted from a leaked TypeScript source map on a random Google site</p></li><li><p class="paragraph" style="text-align:left;">Abstract everything AI-<b>un</b>worthy. Brutecat&#39;s system pre-computes auth, key restrictions, visibility labels, and origin whitelists. The AI only thinks about the request body; everything else is handled by background tooling</p></li><li><p class="paragraph" style="text-align:left;">3,600 Google API keys were collected from 60,000+ APKs, IPAs, and a Chrome debugger extension crawling every Google domain, then filtered through a Cloud Marketplace endpoint to keep only google.com-scoped keys</p></li><li><p class="paragraph" style="text-align:left;">Error normalization and operation IDs cut noise from 90% to near-zero. Common errors are converted to standardized explanations, and every vulnerability report must reference an operation ID or it&#39;s automatically rejected</p></li></ul><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/c7a25360-b133-400b-b6d2-789a252e0293/ThreatLocker-1.png?t=1773171761"/></div><p class="paragraph" style="text-align:center;"><span style="color:rgb(249, 250, 251);font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"><b>Today&#39;s Sponsor:</b></span><span style="color:rgb(249, 250, 251);font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"> Check out </span><span style="color:rgb(255, 255, 255);">Zero Trust Cloud Access from ThreatLocker</span><span style="color:rgb(249, 250, 251);font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"> </span><a class="link" href="https://www.criticalthinkingpodcast.io/tl-ztca?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-178-hacking-google-with-ai-670k-in-bounties-with-brutecat" target="_blank" rel="noopener noreferrer nofollow">https://www.criticalthinkingpodcast.io/tl-ztca</a></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="whos-brutecat">Who&#39;s Brutecat</h2><p class="paragraph" style="text-align:left;"><b>Arvin Shivram</b> (aka <code>@brutecat</code>) is one of the top Google VRP researchers in the world. He&#39;s behind some of the most impactful Google privacy bugs: <a class="link" href="https://brutecat.com/articles/leaking-youtube-emails/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-178-hacking-google-with-ai-670k-in-bounties-with-brutecat" target="_blank" rel="noopener noreferrer nofollow">leaking the email of any YouTube user</a> for a $10,633 bounty, and a brute-force chain that <a class="link" href="https://www.bleepingcomputer.com/news/security/google-patched-bug-leaking-phone-numbers-tied-to-accounts/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-178-hacking-google-with-ai-670k-in-bounties-with-brutecat" target="_blank" rel="noopener noreferrer nofollow">recovered the phone number tied to any Google account</a>. His <a class="link" href="https://brutecat.com/articles/decoding-google/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-178-hacking-google-with-ai-670k-in-bounties-with-brutecat" target="_blank" rel="noopener noreferrer nofollow">Decoding Google</a> series, turning Google&#39;s API black box into a white box, established him as a go-to source for Google internals. He&#39;s been hacking Google for years through <a class="link" href="https://brutecat.com?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-178-hacking-google-with-ai-670k-in-bounties-with-brutecat" target="_blank" rel="noopener noreferrer nofollow">Brutecat Security</a>, building up an encyclopedic knowledge of its internal architecture, and now AI-powered API hacking.</p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="bug-swat-mexico-the-spark">bugSWAT Mexico: The Spark</h2><p class="paragraph" style="text-align:left;">The entire project started at bugSWAT Mexico. While other researchers were hunting bugs, Brutecat was doing something different: he was reading source code. Googlers at the event were willing to show source from <code>google3</code>, and he spent the whole event studying internal architecture. He was already a heavy Claude user (even back in 2024, building tools like <code>req2proto</code> with it), and one idea consumed him entirely:</p><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><i>&quot;What if I could use AI to test every Google API for access control issues automatically?&quot;</i></p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;">The barrier wasn&#39;t technical complexity: it was <b>scale</b>. Access control testing on Google&#39;s Discovery Documents is tedious manual work. There are thousands of APIs, each with dozens of endpoints. No human has time for that.</p><p class="paragraph" style="text-align:left;">He was already sketching it on the flight home. About a week later, he had a working MVP.</p><h2 class="heading" style="text-align:left;" id="building-bugz-ai-mvp-in-a-week">Building bugz AI: MVP in a Week</h2><p class="paragraph" style="text-align:left;">The first version was a web UI that parsed Discovery Documents client-side and rendered every method with a &quot;Play&quot; button. It handled Google&#39;s <b>First Party Auth v2</b>, which, it turns out, was partially documented in a leaked TypeScript source map on some random Google site that had source maps enabled in production. The leaked code revealed extra fields required for the authorization header computation that weren&#39;t in any public blog post.</p><p class="paragraph" style="text-align:left;"><b>Pro Tip:</b> Source maps in production are a goldmine. Every time you find a Google site with source maps enabled, you might be looking at internal specifications for auth, APIs, or internal tooling.</p><p class="paragraph" style="text-align:left;">The UI let Brutecat click any method, authenticate with a single click via FPA, and send requests entirely from the browser. But this was just the interface. The real challenge was <b>keys</b>.</p><h2 class="heading" style="text-align:left;" id="the-key-collection-3600-api-keys">The Key Collection: 3,600 API Keys</h2><p class="paragraph" style="text-align:left;">API keys are the single most important resource for scanning Google. Without keys, you&#39;re limited. With enough keys, you can access almost everything.</p><p class="paragraph" style="text-align:left;">Brutecat partnered with <b>Michael Dalton</b> (another researcher) for a massive key collection effort:</p><ol start="1"><li><p class="paragraph" style="text-align:left;"><b>APKs:</b> Scraped all 60,000+ APKs Google ever published, extracted keys from every one</p></li><li><p class="paragraph" style="text-align:left;"><b>IPAs:</b> Decrypted iOS apps and pulled keys from those too</p></li><li><p class="paragraph" style="text-align:left;"><b>Chrome Debugger API:</b> Built a Chrome extension that hooked into Chrome&#39;s debugger API and automatically captured keys from every request across every Google domain</p></li><li><p class="paragraph" style="text-align:left;"><b>Web crawling:</b> Visited every <code>*.google.com</code> domain, exercised as much functionality as possible to trigger API calls</p></li></ol><p class="paragraph" style="text-align:left;">But raw keys weren&#39;t enough. Many of the collected keys were <b>customer keys</b> tied to non-Google projects. To filter those out, Brutecat used a Cloud Marketplace endpoint that accepts a project number and returns the owning domain. The project number can be leaked from key validation errors: when a key isn&#39;t enabled for a specific API, the error message reveals the project number.</p><p class="paragraph" style="text-align:left;"><b>The result:</b> ~3,600 verified Google-owned API keys.</p><p class="paragraph" style="text-align:left;">And there&#39;s another avenue worth scanning for: <b>zhandlers</b>. Google&#39;s internal debug handlers, accessible on <code>*.google.com</code>. Some zhandlers (like <code>/flagz</code>) leak API keys directly. Others act as &quot;LFI as a service&quot;: you can read any file from the server, including process classes that contain keys. Brutecat was coy about whether his own collection drew from this, but the capability is real: Google even accidentally bundled the zhandlers in a leaked binary that could be run locally, giving him full access to examine every handler.</p><p class="paragraph" style="text-align:left;"><b>Pro Tip:</b> Scan for zhandlers on all Google API domains. They pop up randomly and contain a wealth of information: keys, internal endpoints, and even reflected XSS (Brutecat found one on <code>www.google.com</code> itself, though Googlers-only).</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/dc7a3669-6d67-48a9-aa2b-5dbdd0d8e41e/_bannerHackerNotesShort.png?t=1769044664"/><div class="image__source"><a class="image__source_link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-178-hacking-google-with-ai-670k-in-bounties-with-brutecat" rel="noopener" target="_blank"><span class="image__source_text"><p>Check out the CTBB Discord!</p></span></a></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-178-hacking-google-with-ai-670k-in-bounties-with-brutecat" target="_blank" rel="noopener noreferrer nofollow">You can also find some hacker swag here.</a></p><p class="paragraph" style="text-align:center;">Need a Pentest? We just launched <a class="link" href="https://pentest.ctbb.show/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-178-hacking-google-with-ai-670k-in-bounties-with-brutecat" target="_blank" rel="noopener noreferrer nofollow">CTBB Pentests</a>!</p><p class="paragraph" style="text-align:center;">Hack full time? Check out the <a class="link" href="https://ctbb.show/fthg?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-178-hacking-google-with-ai-670k-in-bounties-with-brutecat" target="_blank" rel="noopener noreferrer nofollow">Full-Time Hunter’s Guild</a>!</p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="auth-layers-the-onion">Auth Layers: The Onion</h2><p class="paragraph" style="text-align:left;">Google&#39;s API auth isn&#39;t one check, it&#39;s a <b>multi-layer verification stack</b>:</p><ol start="1"><li><p class="paragraph" style="text-align:left;"><b>API key present?</b> Is the key valid at all?</p></li><li><p class="paragraph" style="text-align:left;"><b>API key enabled for this API?</b> Is the key&#39;s project authorized for this specific API?</p></li><li><p class="paragraph" style="text-align:left;"><b>Key restriction check:</b> Does the key have the right referrer header (web), Android header, or iOS header? You need to brute force the correct value for each key</p></li><li><p class="paragraph" style="text-align:left;"><b>Origin whitelist (FPA):</b> First-party auth requires a whitelisted origin. A &quot;session cookie invalid&quot; error is actually a <b>fake error</b>: it means the origin isn&#39;t whitelisted. Brute force a list of Google domains until one works</p></li><li><p class="paragraph" style="text-align:left;"><b>Visibility label:</b> Even if auth passes, your key needs the correct visibility label for the endpoint. Missing label returns &quot;method not found&quot; (another misleading error)</p></li><li><p class="paragraph" style="text-align:left;"><b>Endpoint-level restrictions:</b> Some keys have specific methods blocked</p></li></ol><p class="paragraph" style="text-align:left;">All of this happens <b>before your request is processed</b>. As Brutecat puts it:</p><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><i>&quot;It&#39;s like an onion: you peel away the layers, and finally you reach the gold mine inside where nobody touches the attack surface.&quot;</i></p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;">Google APIs whose origin whitelist is restricted to corp origins are huge red flags. Those APIs were meant to be internal-only, but you can supply a corp origin yourself from outside Google&#39;s network and walk right in. Brutecat found a ton of bugs this way. The whitelist is often loose enough that the origin doesn&#39;t even need to be a <code>.google.com</code> domain: even <code>withgoogle.com</code> is accepted, despite FPA&#39;s session cookies making no sense there.</p><h2 class="heading" style="text-align:left;" id="the-ai-implementation">The AI Implementation</h2><p class="paragraph" style="text-align:left;">Brutecat&#39;s AI system evolved significantly from his initial approach.</p><h3 class="heading" style="text-align:left;" id="initial-approach-mc-pbased-scrapped">Initial approach (MCP-based, scrapped)</h3><p class="paragraph" style="text-align:left;">He started with AI SDK + MCP tools: <code>probe_api</code>, <code>report_vulnerability</code>, <code>complete_testing</code>. But the probe request bodies were bloated: they included the full method ID, host, version, and path. Too much room for the AI to hallucinate.</p><h3 class="heading" style="text-align:left;" id="new-approach-cl-ibased">New approach (CLI-based)</h3><p class="paragraph" style="text-align:left;">The AI no longer constructs requests from scratch. Instead, Brutecat provides simplified tool calls:</p><div class="codeblock"><pre><code>&#123;
  &quot;body&quot;: &#123; /* request body */ &#125;,
  &quot;include_creds&quot;: &quot;113728935872649341310&quot;,
  &quot;method_id&quot;: &quot;updateDataFetcherConfiguration&quot;,
  &quot;path&quot;: &quot;/v1/updateDataFetcherConfiguration&quot;,
&#125;
</code></pre></div><p class="paragraph" style="text-align:left;">Auth, key selection, origin whitelisting, and visibility labels are all handled by the background tooling. The AI only thinks about what matters: <b>the request body</b>.</p><p class="paragraph" style="text-align:left;"><b>Pro Tip:</b> Don&#39;t use AI for things you can do without AI. Key collection, auth handling, origin brute-forcing, and correlation should be pre-computed. Reserve the AI budget for the intellectually hard part: constructing request bodies that probe for access control issues.</p><h3 class="heading" style="text-align:left;" id="response-deduplication-via-hash">Response deduplication via hash</h3><p class="paragraph" style="text-align:left;">Different API keys produce different responses. Sending every key for every request would be wasteful. Brutecat&#39;s system groups responses by <code>response_hash</code>: the AI sees unique responses with a hash reference, not every single key&#39;s output.</p><h3 class="heading" style="text-align:left;" id="error-normalization">Error normalization</h3><p class="paragraph" style="text-align:left;">Google APIs return misleading errors. &quot;Method not found&quot; doesn&#39;t mean the method doesn&#39;t exist: it means your key lacks the visibility label. Brutecat converted common errors to standardized types with explanations so the AI doesn&#39;t have to decode Google&#39;s error obfuscation:</p><div class="codeblock"><pre><code>&#123;
  &quot;standardErrorType&quot;: &quot;INVALID_ARGUMENT_NO_DETAILS&quot;,
  &quot;standardErrorExplanation&quot;: &quot;The request was rejected by the application due to invalid arguments, but no details were provided. Check your request parameters.&quot;
&#125;
</code></pre></div><h3 class="heading" style="text-align:left;" id="endpoint-coverage-via-groupbased-so">Endpoint coverage via group-based sorting</h3><p class="paragraph" style="text-align:left;">Sending one massive Discovery Document as context causes the AI to focus on one area and ignore the rest. Brutecat&#39;s solution: <b>group-based sorting</b>. Endpoints are grouped by CRUD operations, and each group gets a full pentest session. Summary notes from each session are passed to the next, maintaining cross-group context.</p><h3 class="heading" style="text-align:left;" id="operation-i-ds-for-validation">Operation IDs for validation</h3><p class="paragraph" style="text-align:left;">Every tool call gets a unique ID. When the AI reports a vulnerability, it must reference the operation IDs that produced it. Reports without operation IDs are automatically rejected. This alone cut the false positive rate from ~90% to near-zero.</p><h2 class="heading" style="text-align:left;" id="pwning-google-670-k-in-bugs">Pwning Google: $670K in Bugs</h2><p class="paragraph" style="text-align:left;">In ~3-4 months of running bugz AI across Google&#39;s entire API surface, Brutecat accumulated <b>$670,000 in bounties</b>. Here are the highlights:</p><h3 class="heading" style="text-align:left;" id="google-voice-ato">Google Voice ATO</h3><p class="paragraph" style="text-align:left;">An endpoint with <b>no auth</b>: just an unobfuscated Gaia ID in the URL. It returned the phone number, Google Voice number, email, and PIN for any Google account. There was also an endpoint to <b>assign a phone number</b> to any account, which would immediately appear in the victim&#39;s Google Account settings. The only thing preventing exploitation was the API key, and Brutecat had it.</p><h3 class="heading" style="text-align:left;" id="eldarcorpgooglecom">eldar.corp.google.com</h3><p class="paragraph" style="text-align:left;">Eldar is Google&#39;s internal tool for bug triage, access requests, and investigations. The frontend was behind the corporate network, but the <b>backend API was fully exposed</b>. The origin whitelist was a wildcard for <code>*.google.com</code>. Brutecat&#39;s AI actually generated an Eldar report during testing, which emailed him at his private Gmail with links to Google-internal resources: a hilarious proof-of-existence straight from Google&#39;s internal tooling.</p><h3 class="heading" style="text-align:left;" id="you-tube-unlisted-videos-leak-12000">YouTube Unlisted Videos Leak ($12,000)</h3><p class="paragraph" style="text-align:left;">Every YouTube Partner has a hidden Content ID management account. When a YouTuber uploads a video (even as unlisted), it gets registered as a Content ID asset with the naming convention <code>auto-generated-asset-&#123;VIDEO_ID&#125;</code>. By searching all assets for this pattern, you can <b>leak every unlisted video</b> from YouTube Partner channels, including unreleased trailers, private POCs, and scheduled premieres.</p><p class="paragraph" style="text-align:left;"><b>Impact highlight:</b> This has Polymarket implications. If Google uploads a Gemini model announcement as an unlisted premiere, you could leak the release date and bet on it.</p><h3 class="heading" style="text-align:left;" id="plx-tables-12000">PLX Tables ($12,000)</h3><p class="paragraph" style="text-align:left;">PLX is Google&#39;s internal dashboard system: every Googler uses it daily. The <code>datahub.client6.google.com</code> API had a <code>suggest</code> endpoint that leaked internal table names, including sensitive ones like &quot;need to know employee data.&quot; The real trick: a <b>staging environment</b> allowed setting IAM policies without restrictions. Brutecat added himself as a table admin, taking over internal PLX tables (some petabytes in size). He couldn&#39;t query them directly, but a month later, another researcher found an endpoint on the same integration that could read the tables.</p><h3 class="heading" style="text-align:left;" id="ad-exchange-ato">AdExchange ATO</h3><p class="paragraph" style="text-align:left;">AdExchange (Google&#39;s ad management platform) had a staging environment (<code>test-dash.adxbuyer.googleapis.com</code>) that pointed to the <b>production database</b> with no access controls. Brutecat enumerated all ad accounts and could access any account&#39;s data. The staging-bypass trick came up repeatedly, including doubling bounties by finding the same bugs on an autopush environment.</p><h3 class="heading" style="text-align:left;" id="app-engine-request-log-leak">App Engine Request Log Leak</h3><p class="paragraph" style="text-align:left;">The App Engine dashboard loading endpoint was <b>completely unauthenticated</b>. Supplying any project ID returned all stats including request paths: if anyone had a password reset link in their logs, it would show up. Even the Bug Hunter site (where Googlers submit reports) was exposed.</p><h3 class="heading" style="text-align:left;" id="vertex-assistant-30000">Vertex Assistant ($30,000)</h3><p class="paragraph" style="text-align:left;">This was a <b>feature not yet rolled out</b>. The AI found it via GraphQL introspection, but the UI didn&#39;t exist yet. By setting JavaScript breakpoints and enabling feature flags in the console, Brutecat activated the hidden feature and demonstrated the vulnerability to Google&#39;s product team. Google paid for it because it was about to be released.</p><h2 class="heading" style="text-align:left;" id="graph-ql-cloud-console">GraphQL & Cloud Console</h2><p class="paragraph" style="text-align:left;">The Cloud Console uses GraphQL behind the scenes, and the staging endpoint had <b>no signature validation</b>, allowing full introspection. This opened an entirely new attack surface: APIs reachable through Cloud Console&#39;s GraphQL proxy that weren&#39;t accessible via Google APIs directly.</p><p class="paragraph" style="text-align:left;">Brutecat worked with Michael Dalton to adapt bugz AI to scan this GraphQL surface. The challenge: some queries map to RPCs, some don&#39;t, and you have to figure out which is which. The introspection data included comments that helped map the surface.</p><p class="paragraph" style="text-align:left;">GraphQL staging endpoints without signature validation are a goldmine. Introspect everything, then use that knowledge to probe production.</p><h2 class="heading" style="text-align:left;" id="resources">Resources</h2><ul><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://brutecat.com/articles/decoding-google?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-178-hacking-google-with-ai-670k-in-bounties-with-brutecat" target="_blank" rel="noopener noreferrer nofollow">Brutecat&#39;s earlier research: Decoding Google</a>: Converting a Black Box to a White Box</p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://brutecat.com?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-178-hacking-google-with-ai-670k-in-bounties-with-brutecat" target="_blank" rel="noopener noreferrer nofollow">Brutecat Security</a>: Follow @brutecat for more research</p></li></ul><p class="paragraph" style="text-align:left;">That&#39;s it for this week, keep hacking</p></div></div>
  ]]></content:encoded>
</item>

      <item>
  <title>[HackerNotes Ep. 177] Double Google RCE with VRP Legend Brutecat</title>
  <description>First part of two awesome episodes with brutecat, this one about the two RCE he founds and some other bugs when he started digging Google VRP</description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-177-double-google-rce-with-vrp-legend-brutecat</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-177-double-google-rce-with-vrp-legend-brutecat</guid>
  <pubDate>Thu, 04 Jun 2026 10:01:00 +0000</pubDate>
  <atom:published>2026-06-04T10:01:00Z</atom:published>
    <dc:creator>Aituglo ‎</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr">Hacker TL;DR</h2><ul><li><p class="paragraph" style="text-align:left;">Two RCEs in Google Cloud production, three months apart, both in the same Application Integration product. Combined bounty: $148,337</p></li><li><p class="paragraph" style="text-align:left;">RCE at Google is not a shell. It is arbitrary Stubby (internal RPC) calls as a production identity, basically the SSRF Google says it does not have.</p></li><li><p class="paragraph" style="text-align:left;">Google is mostly security by obscurity. Recon is the hardest and most valuable part, which is exactly why brutecat open-sourced req2proto and reads Google&#39;s internal infra papers cover to cover</p></li><li><p class="paragraph" style="text-align:left;">His road into Google VRP was pure OSINT: chain channel to Gaia ID to email for any YouTube user, and abuse a no-JS Forgot Username page to leak any US phone number in under an hour</p></li></ul><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/c7a25360-b133-400b-b6d2-789a252e0293/ThreatLocker-1.png?t=1773171761"/></div><p class="paragraph" style="text-align:center;"><span style="color:rgb(249, 250, 251);font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"><b>Today&#39;s Sponsor:</b></span><span style="color:rgb(249, 250, 251);font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"> Check out </span><span style="color:rgb(255, 255, 255);">Zero Trust Cloud Access from ThreatLocker</span><span style="color:rgb(249, 250, 251);font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"> </span><a class="link" href="https://www.criticalthinkingpodcast.io/tl-ztca?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-177-double-google-rce-with-vrp-legend-brutecat" target="_blank" rel="noopener noreferrer nofollow">https://www.criticalthinkingpodcast.io/tl-ztca</a></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="whos-brutecat">Who&#39;s brutecat</h2><p class="paragraph" style="text-align:left;">Arvin Shivram (<a class="link" href="https://x.com/brutecat?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-177-double-google-rce-with-vrp-legend-brutecat" target="_blank" rel="noopener noreferrer nofollow">@brutecat</a>) only started hacking Google in mid to late 2024, which is faintly absurd given the body of work. He comes from an OSINT background and now runs <a class="link" href="https://brutecat.com/hunt?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-177-double-google-rce-with-vrp-legend-brutecat" target="_blank" rel="noopener noreferrer nofollow">Brutecat Security</a>, where he points the same AI tooling he uses on Google at other companies. This episode is part one of a two-part interview: the double RCE in Google Cloud production, then the bugs that got him into VRP in the first place. The $500k+ AI scanning saga is the next episode.</p><h2 class="heading" style="text-align:left;" id="a-double-rce-in-borg">A Double RCE in Borg</h2><h3 class="heading" style="text-align:left;" id="finding-the-debug-api">Finding the debug API</h3><p class="paragraph" style="text-align:left;">Brutecat&#39;s AI scanner flagged a few endpoints on <code>cloudcrmipfrontend-pa</code> (reached through its <code>clients6.google.com</code> alias). The first one took a Gaia ID and returned an email, which already rhymed with his older OSINT work. But scanning the rest of the API surfaced something far better: a <code>getProtoDefinition</code> endpoint.</p><p class="paragraph" style="text-align:left;">Everything at Google is protobuf. The request you send is a protobuf message. If you can leak the <i>type</i> of a message, this endpoint dumps the full protobuf definition for it. This is the structural problem with hacking Google, the proto-JSON and protobuf obscurity, handed to you as a service.</p><p class="paragraph" style="text-align:left;">That sits on top of the technique brutecat is already known for: <b>req2proto</b>. Using the weird <code>GSPB</code> (JSON plus protobuf) content type, you probe an endpoint with junk payloads like an array of <code>1,2,3,4,5</code> and Google&#39;s backend leaks error messages that reconstruct the full request protobuf. The catch is it only works on APIs with that content type enabled, so the <code>getProtoDefinition</code> method was a more general version of the same idea.</p><h3 class="heading" style="text-align:left;" id="filter-injection-plus-the-base-64-t">Filter injection plus the base64 trick</h3><p class="paragraph" style="text-align:left;">One endpoint, <code>listQuotaQueue</code>, took a <code>filter</code> parameter. Filters at Google usually follow the <a class="link" href="https://google.aip.dev/160?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-177-double-google-rce-with-vrp-legend-brutecat" target="_blank" rel="noopener noreferrer nofollow">AIP-160</a> standard, so brutecat tried the spec until something landed:</p><div class="codeblock"><pre><code>filter=client_id&gt;&quot;123&quot;
</code></pre></div><p class="paragraph" style="text-align:left;">That worked, but the response came back as an error: the backend could not convert the protobuf result to JSON. The fix was <code>?alt=proto</code> to ask for raw protobuf. New problem: this endpoint runs on <code>clients6.google.com</code> (an alias of <code>googleapis.com</code> that crucially accepts cookies, which is what first-party auth needs), and <code>clients6</code> refuses to return raw binary protobuf, likely some anti-XSS measure.</p><p class="paragraph" style="text-align:left;">The unlock came from a note brutecat took watching an Ezequiel Pereira talk second by second. One throwaway header:</p><div class="codeblock"><pre><code>X-Goog-Encode-Response-If-Executable: base64
</code></pre></div><p class="paragraph" style="text-align:left;">This wraps the protobuf response in base64, which <code>clients6</code> is happy to serve. Decode it with <code>protoc</code> (using the definition from <code>getProtoDefinition</code>) and the workflow execution logs come out: internal task executions, Spanner-to-Salesforce syncs, the works. He reported that initial leak immediately rather than digging further into customer data.</p><p class="paragraph" style="text-align:left;"><b>Pro Tip:</b> keep a notes file of every gadget you ever see in a talk or writeup.</p><h3 class="heading" style="text-align:left;" id="from-log-leak-to-arbitrary-stubby">From log leak to arbitrary Stubby</h3><p class="paragraph" style="text-align:left;">Reading the leaked discovery doc, one method jumped out: <code>GenericStubbyTypedTaskV2</code>. Stubby is Google&#39;s internal RPC framework. Every public Google API call is just fronting Stubby calls made by a Borg task under a production identity. If you can execute arbitrary Stubby queries, you reach internal RPCs that are never meant to be public.</p><p class="paragraph" style="text-align:left;">As Joseph put it, this is basically the SSRF that Google insists it does not have. And brutecat&#39;s framing of RCE at Google is the key mental model: a shell on a Borg task is a sandbox and not that interesting. The real impact, and the reason Google pays so much, is the Stubby access as a prod account.</p><p class="paragraph" style="text-align:left;">Creating the task was the wall. The <code>createDraftWorkflow</code> call kept returning <code>INVALID_ARGUMENT</code> until he copied a <code>clientId: &quot;default&quot;</code> value he had spotted in the earlier leaked execution logs. Then publishing it failed with <code>publisher cannot be the same as the last editor</code>, and he was stuck for a month while his original report was getting the endpoints patched out from under him.</p><h3 class="heading" style="text-align:left;" id="the-discord-coincidence-and-the-rol">The Discord coincidence and the rollout race</h3><p class="paragraph" style="text-align:left;">Out of nowhere in some Discord, brutecat asked another researcher if they needed any protobufs, and it turned out the guy was sitting on the exact same <code>cloudcrmipfrontend</code> API. They had each been stuck at different points. The other researcher knew the public <b>Application Integration</b> GCP product cold (this internal API is the internal version of it) and had pulled the same endpoints out of that product&#39;s JS files. brutecat knew how to create the workflow. They compared notes.</p><p class="paragraph" style="text-align:left;">A few moving parts got them over the line:</p><ul><li><p class="paragraph" style="text-align:left;">Patched endpoints had <b>undocumented duplicates</b>. <code>getProtoDefinition</code> was blocked, but <code>workflowsupport:getProtoDefinition</code> still worked. All in the discovery doc</p></li><li><p class="paragraph" style="text-align:left;"><b>shrugged</b> got the publish working because the fix had not finished rolling out everywhere. He was in Canada, brutecat in Singapore, so it worked for him first. He shared his <code>1e100.net</code> host (Google&#39;s DNS resolves <code>googleapis.com</code> to region-specific IPs) and brutecat pinned Burp to it to reach a server that still accepted the request</p></li><li><p class="paragraph" style="text-align:left;">The publisher-equals-editor block was bypassed through a <code>setAcl</code> endpoint: add a separate attacker-controlled Gaia, then use it to toggle the publish request and approve your own workflow</p></li><li><p class="paragraph" style="text-align:left;">Filling the Stubby task params: the public Application Integration UI leaked the required spec when you tried to configure the task there, and they grabbed a working <code>serverSpec: gslb:alkali-base</code> plus <code>ServerStatus.GetServices</code> straight out of Ezequiel Pereira&#39;s old RCE writeup</p></li></ul><p class="paragraph" style="text-align:left;">They got the call to execute, sent the report, and roughly <b>one hour later everything stopped working</b> because the fix finished propagating. Submitting one hour later would have meant no PoC. The writeup totals the chain at <a class="link" href="https://brutecat.com/articles/google-cloud-rce/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-177-double-google-rce-with-vrp-legend-brutecat" target="_blank" rel="noopener noreferrer nofollow">$148,337</a>.</p><h3 class="heading" style="text-align:left;" id="round-two-three-months-later">Round two, three months later</h3><p class="paragraph" style="text-align:left;">While improving his scanner against Google Cloud, Application Integration popped up again. This time it was a blunt IDOR: put your own <code>/project/&lt;number&gt;</code> in the path but reference someone else&#39;s UID and it just works, across every endpoint in the API. The problem was the UID is a UUIDv4, so a raw report would get downgraded for lack of proof.</p><p class="paragraph" style="text-align:left;">The leak came from the <b>test cases</b> feature. <code>ListTestCases</code> sent a client-side <code>workflow_id</code> filter inside the protobuf. Strip that field and the endpoint dumped the test cases for every GCP user, full of <code>@google.com</code> Googlers running their own integrations.</p><p class="paragraph" style="text-align:left;">The UUID itself was still masked (a <code>-</code> where the UID should be in the path), so brutecat ran an <b>AIP-160 binary search</b> over the same filter primitive, fixing on a known test case and using greater-than / less-than to recover the owner&#39;s full UUIDv4 in about 128 requests. Claude wrote the script and nailed it first try, though brutecat had to figure out the binary-search idea himself.</p><p class="paragraph" style="text-align:left;">With UUIDs in hand he had IDOR across all of Application Integration. To push to RCE again, he created his own integration with an internal Stubby task type. Direct execution timed out at 120 seconds, but running it through the <b>test case</b> feature got real backend errors: a <code>java.io.IOException: No space left on device</code> from a Python task, then for the Stubby task a stack trace (pulled from the workflow execution logs) referencing <code>EventbusStubbyCallerService.ExecuteStubbyCall</code> with a <code>UberMint</code> / <code>RpcSecurityPolicy</code> credential error. That confirmed it was hitting Stubby under a different, more privileged product account. Google confirmed it was fully exploitable and told him to stop.</p><p class="paragraph" style="text-align:left;">That report paid <b>$75k</b>, the middle of Google&#39;s three RCE tiers ($50k unprivileged prod user, $75k highly privileged, $100k full Cloud admin), and the team hinted at a further escalation they would not detail.</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/dc7a3669-6d67-48a9-aa2b-5dbdd0d8e41e/_bannerHackerNotesShort.png?t=1769044664"/><div class="image__source"><a class="image__source_link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-177-double-google-rce-with-vrp-legend-brutecat" rel="noopener" target="_blank"><span class="image__source_text"><p>Check out the CTBB Discord!</p></span></a></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-177-double-google-rce-with-vrp-legend-brutecat" target="_blank" rel="noopener noreferrer nofollow">You can also find some hacker swag here.</a></p><p class="paragraph" style="text-align:center;">Need a Pentest? We just launched <a class="link" href="https://pentest.ctbb.show/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-177-double-google-rce-with-vrp-legend-brutecat" target="_blank" rel="noopener noreferrer nofollow">CTBB Pentests</a>!</p><p class="paragraph" style="text-align:center;">Hack full time? Check out the <a class="link" href="https://ctbb.show/fthg?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-177-double-google-rce-with-vrp-legend-brutecat" target="_blank" rel="noopener noreferrer nofollow">Full-Time Hunter’s Guild</a>!</p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="how-brutecat-got-into-google-vrp">How brutecat Got Into Google VRP</h2><h3 class="heading" style="text-align:left;" id="you-tube-email-leak-the-first-bug">YouTube email leak (the first bug)</h3><p class="paragraph" style="text-align:left;">It started with OSINT obsession. A site that turned an email into a YouTube channel pointed him at the mobile-only <b>profile card</b> feature. He sniffed his iOS traffic through Burp and saw a Gaia ID in the protobuf. The YouTube comments backend is still Gaia-tied for historic G+ reasons, so loading comments exposed the Gaia ID of every commenter.</p><p class="paragraph" style="text-align:left;">The deeper issue: Google has obfuscated (foggy) and unobfuscated Gaia IDs, and teams disagree on whether the obfuscated one is safe to expose. Many times it converts straight to an email. When the feature shipped to web and got patched, brutecat found another path: the livechat three-dots menu. Open it (no need to even block) and it preloads the target&#39;s Gaia ID, and you can swap in any channel ID to get any user&#39;s Gaia. The block list via the People API also listed raw Gaia IDs.</p><p class="paragraph" style="text-align:left;">To get from Gaia to email he abused <b>Pixel Recorder</b> sharing (<code>pixelrecorder-pa</code>), whose share endpoint took an obfuscated Gaia ID and returned the email. Chain it: channel ID to Gaia ID to email for any YouTube user. The share sent a notification email that would tip off the victim, so he set the recording title to <b>roughly 1.2 million characters</b> (no server-side length limit), which quietly broke the email send. Reported in late 2024, paid <b>$10k</b> as abuse.</p><h3 class="heading" style="text-align:left;" id="you-tube-creator-emails-20-k">YouTube creator emails ($20k)</h3><p class="paragraph" style="text-align:left;">Scraping YouTube as a big-data project taught him the plumbing: hitting requests directly over gRPC, the <code>X-Goog-FieldMask</code> header to fetch only the fields he wanted, <code>1e100.net</code> load balancing across Google hosts, and an <b>IPv6 /64 to defeat rate limits</b>, since limits were per-IP and a /64 hands you billions of addresses to rotate (Google later moved to escalating subnet bans).</p><p class="paragraph" style="text-align:left;">Running req2proto on <code>get_creator_channels</code> revealed a hidden <code>includeSuspended</code> request parameter. Setting it surfaced a <code>contentOwnerAssociation</code> in the response, carrying a <code>contentOwnerId</code>. Content owners are <b>CMS / god-mode accounts</b> handed to enterprises: strike any channel, monetize, claim content. The Copyright Match Tool silently creates a hidden content owner (an IVP-type account) behind every monetized channel for Content ID. brutecat leaked that hidden <code>contentOwnerId</code>, then fed it to the Content ID API&#39;s <code>contentOwners.list</code>, which returned the <code>conflictNotificationEmail</code>. That field defaults to the channel&#39;s own account email. Chain it and you have the email of any YouTube partner. Paid <b>$20k</b> (normal VRP this time, not abuse).</p><p class="paragraph" style="text-align:left;"><b>Pro Tip:</b> when an account is auto-created, check whether an adjacent field (like a notification email) gets populated from the main account in its default state before the user sets it. That default population is the leak.</p><h3 class="heading" style="text-align:left;" id="phone-leak-any-us-number-in-an-hour">Phone leak (any US number in an hour)</h3><p class="paragraph" style="text-align:left;">In early 2025 he noticed the <b>Forgot Username</b> page (<code>/signin/usernamerecovery</code>) worked without JavaScript. That matters because Google&#39;s anti-bot <b>botguard</b> proof-of-work needs JS to load its challenge, so a no-JS page is a botguard bypass waiting to happen. Forgot Username confirms whether a full name plus a phone number match an account.</p><p class="paragraph" style="text-align:left;">He assembled the pieces: PayPal-style password-reset flows leak the last digits of a phone number and the form formatting reveals the country code, and a <b>Looker Studio</b> ownership transfer leaked full names (unlike Drive, Looker Studio does not require the recipient to accept ownership). That gave him a name to pin and a partial number to brute toward.</p><p class="paragraph" style="text-align:left;">Google patched the leak mid-build, but he salvaged it: the JS flow passed a real botguard token, and reusing <b>a single botguard token gave unlimited requests</b>. Solve the proof-of-work once, replay forever. Result: any US phone number in under an hour, with obvious SIM-swap impact. He demoed it live to journalists, who covered it. Paid <b>$5k</b> under abuse, which both hosts agreed badly undervalues a Google-account-to-phone-number primitive.</p><h3 class="heading" style="text-align:left;" id="discovery-docs">Discovery docs</h3><p class="paragraph" style="text-align:left;">The People API rabbit hole led him to discovery documents, which carry comments and full parameter definitions. The old <code>/$discovery/rest</code> route got nuked after the Content Warehouse API leak, but it is still reachable if you think about the RPC angle. For YouTube, which blocked GET requests outright, he used <code>X-HTTP-Method-Override</code> to send a POST that executes as a GET and leaked the largest discovery doc Google has. That doc is where he later spotted the <code>includeSuspended</code> field, and it still hides plenty (the <code>testing CTP inner tube</code> endpoints, for one).</p><h2 class="heading" style="text-align:left;" id="resources">Resources</h2><ul><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://brutecat.com/articles/google-cloud-rce/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-177-double-google-rce-with-vrp-legend-brutecat" target="_blank" rel="noopener noreferrer nofollow">StubZero: $148,337 RCE in Google Cloud Production</a></b> - the full double-RCE writeup with interactive HTML embeds</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://brutecat.com/articles/leaking-youtube-emails/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-177-double-google-rce-with-vrp-legend-brutecat" target="_blank" rel="noopener noreferrer nofollow">Leaking the email of any YouTube user for $10,000</a></b> - profile card, livechat block trick, Pixel Recorder chain</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://brutecat.com/articles/youtube-creator-emails/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-177-double-google-rce-with-vrp-legend-brutecat" target="_blank" rel="noopener noreferrer nofollow">Disclosing YouTube Creator Emails for a $20k Bounty</a></b> - CMS / Content ID communication-email chain</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://brutecat.com/articles/leaking-google-phones/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-177-double-google-rce-with-vrp-legend-brutecat" target="_blank" rel="noopener noreferrer nofollow">Leaking the phone number of any Google user</a></b> - no-JS Forgot Username, botguard token reuse</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://google.aip.dev/160?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-177-double-google-rce-with-vrp-legend-brutecat" target="_blank" rel="noopener noreferrer nofollow">AIP-160 filtering spec</a></b> - the filter language behind both the injection and the UUID binary search</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://x.com/brutecat?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-177-double-google-rce-with-vrp-legend-brutecat" target="_blank" rel="noopener noreferrer nofollow">brutecat on X</a></b> and <b><a class="link" href="https://brutecat.com/hunt?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-177-double-google-rce-with-vrp-legend-brutecat" target="_blank" rel="noopener noreferrer nofollow">Brutecat Security</a></b> for the writeups and consulting</p></li></ul><hr class="content_break"><p class="paragraph" style="text-align:left;">That&#39;s it for the week, keep hacking!</p></div></div>
  ]]></content:encoded>
</item>

      <item>
  <title>[HackerNotes Ep. 176] AEM Deep Dive with Jim Green: Sling Selectors, Dispatcher Bypasses, and XSS Gadgets</title>
  <description>Today, we deep dive AEM with Jim Green</description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-176-aem-deep-dive-with-jim-green-sling-selectors-dispatcher-bypasses-and-xss-gadgets</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-176-aem-deep-dive-with-jim-green-sling-selectors-dispatcher-bypasses-and-xss-gadgets</guid>
  <pubDate>Thu, 28 May 2026 10:00:00 +0000</pubDate>
  <atom:published>2026-05-28T10:00:00Z</atom:published>
    <dc:creator>Aituglo ‎</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr">Hacker TL;DR</h2><ul><li><p class="paragraph" style="text-align:left;">Apache Sling adds two URL components on top of the standard path: selectors (between filename and extension) and suffixes (after the extension, starting with <code>/</code>). Both feed into Sling&#39;s resource resolution and open a massive untested attack surface</p></li><li><p class="paragraph" style="text-align:left;">Three AEM core-product selectors (<code>rawcontent</code>, <code>listParagraphs</code>, <code>form</code>) broke nearly every unauthenticated AEM instance in the wild. Two of them are dispatcher bypasses that hand you internal endpoints like <code>/bin/querybuilder.json</code></p></li><li><p class="paragraph" style="text-align:left;">AEM permissions ship with a footgun: the anonymous user is part of the <code>everyone</code> group by default, so every &quot;open to all users&quot; rule also applies to unauthenticated visitors</p></li><li><p class="paragraph" style="text-align:left;">Three reusable XSS primitives outside AEM: moment.js format injection via square brackets, jQuery <code>.text()</code> re-decoding HTML entities, and <code>javascript:</code> URIs populating both <code>hostname</code> and <code>pathname</code> on the URL object</p></li></ul><h2 class="heading" style="text-align:left;" id="sponsor">Sponsor</h2><p class="paragraph" style="text-align:left;">Today’s Sponsor: Adobe. Earn more for AI bugs with Adobe’s new AI Tier! <a class="link" href="https://blog.adobe.com/security/adobe-expands-bug-bounty-program-to-incentivize-ai-security-research?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-176-aem-deep-dive-with-jim-green-sling-selectors-dispatcher-bypasses-and-xss-gadgets" target="_blank" rel="noopener noreferrer nofollow">https://blog.adobe.com/security/adobe-expands-bug-bounty-program-to-incentivize-ai-security-research</a></p><p class="paragraph" style="text-align:left;">Also don’t forget to also grab a 10% bonus for valid AI vulnerabilities in Adobe Stock and Lightroom Web. </p><p class="paragraph" style="text-align:left;">Use code: <b>CTBB063026</b> in your report.<br>Expires June 30, 2026.</p><h2 class="heading" style="text-align:left;" id="in-the-news">In the News</h2><ul><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.yeswehack.com/security-best-practices/scaling-bug-bounty-triage-ai?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-176-aem-deep-dive-with-jim-green-sling-selectors-dispatcher-bypasses-and-xss-gadgets" target="_blank" rel="noopener noreferrer nofollow">Scaling Bug Bounty triage in the AI era</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.intigriti.com/blog/business-insights/the-ai-impact-a-triagers-perspective?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-176-aem-deep-dive-with-jim-green-sling-selectors-dispatcher-bypasses-and-xss-gadgets" target="_blank" rel="noopener noreferrer nofollow">The AI impact: a triager’s perspective</a></p></li></ul><h2 class="heading" style="text-align:left;" id="whos-jim-green">Who&#39;s Jim Green</h2><p class="paragraph" style="text-align:left;">Jim Green (<a class="link" href="https://x.com/GreenJamSec?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-176-aem-deep-dive-with-jim-green-sling-selectors-dispatcher-bypasses-and-xss-gadgets" target="_blank" rel="noopener noreferrer nofollow">@GreenJamSec</a>) spent 20 years in software development, mainly on banking systems, before transitioning to bug bounty full time in October 2025. He learned AEM as a consultant building customer implementations on top of the platform, then carried that internal knowledge into the Adobe VIP program where he now has 600+ CVEs to his name (overwhelmingly XSS). Jim is also the HackerOne UK Brand Ambassador alongside Nathan Jones (NJCVE).</p><h2 class="heading" style="text-align:left;" id="bring-a-bug-master-card-query-build">Bring a Bug: MasterCard, QueryBuilder, and the etc/packages Heist</h2><p class="paragraph" style="text-align:left;">Jim&#39;s first paid bug was on an AEM instance with QueryBuilder exposed (behind a trivial WAF bypass). QueryBuilder is the internal search interface for the Java Content Repository, normally locked down for developer use only. Once accessible, he pivoted into <code>/etc/packages</code>, which stores the deployment artifacts for the customer&#39;s own code sitting on top of AEM.</p><p class="paragraph" style="text-align:left;">The packages contained:</p><ul><li><p class="paragraph" style="text-align:left;">Full source code for the customer&#39;s implementation</p></li><li><p class="paragraph" style="text-align:left;">Plaintext credentials for their internal MySQL database</p></li><li><p class="paragraph" style="text-align:left;">Akamai API keys (full WAF rule control, potential origin pivoting)</p></li></ul><p class="paragraph" style="text-align:left;">The structural lesson: every time AEM is deployed via CI/CD or through the Package Manager web UI, the resulting zip lands under <code>/etc/packages</code>. If permissions there are loose, the entire codebase is downloadable. Also worth noting, <code>crx-quickstart/install/</code> is an arbitrary-file-write to RCE path: drop a deployable zip there, and AEM installs it on next restart.</p><h2 class="heading" style="text-align:left;" id="aem-architecture">AEM Architecture</h2><ul><li><p class="paragraph" style="text-align:left;"><b>Author instance</b> - internal CMS where editors and devs work. Replication agents push approved content to publishers</p></li><li><p class="paragraph" style="text-align:left;"><b>Publish instance</b> - public-facing. Hosts the actual rendered pages</p></li><li><p class="paragraph" style="text-align:left;"><b>Dispatcher</b> - Apache HTTPD reverse proxy in front of publish, handles caching, load balancing, and (often) ACLs</p></li><li><p class="paragraph" style="text-align:left;"><b>WAF</b> - usually Akamai or similar, sitting in front of the dispatcher</p></li></ul><p class="paragraph" style="text-align:left;">Two AEM deployment flavors matter for hunting. <b>AMS</b> (Adobe Managed Services) is the on-prem/self-hosted version, possibly hosted by Adobe on AWS. <b>AEM as a Cloud Service</b> is the SaaS version, more locked down (admins cannot write to executable paths, no easy RCE primitives). For spraying core-product bugs, AMS instances are where you score.</p><p class="paragraph" style="text-align:left;">Inside the JCR, the folder layout to memorize:</p><ul><li><p class="paragraph" style="text-align:left;"><code>/libs</code> - Adobe&#39;s core product code (JSPs, HTLs, servlets)</p></li><li><p class="paragraph" style="text-align:left;"><code>/apps</code> - customer overrides and custom implementations</p></li><li><p class="paragraph" style="text-align:left;"><code>/etc/packages</code> - deployment artifacts</p></li><li><p class="paragraph" style="text-align:left;"><code>/content</code> - the actual web pages</p></li><li><p class="paragraph" style="text-align:left;"><code>/content/dam</code> - assets (images, PDFs, sometimes spreadsheets with PII)</p></li></ul><p class="paragraph" style="text-align:left;"><b>Pro Tip on </b><code>/content</code><b>:</b> Jim regularly finds employee lists with SSNs, plaintext credentials, IP inventories of internal architecture, and documents marked &quot;strictly confidential&quot; living under <code>/content</code>. Authors use AEM as a convenient file transfer mechanism. Worth checking on every black-box AEM instance.</p><h2 class="heading" style="text-align:left;" id="apache-sling-url-anatomy">Apache Sling URL Anatomy</h2><p class="paragraph" style="text-align:left;">This is the unique-to-AEM part most hunters miss. Sling extends a normal URL with two extra fields:</p><div class="codeblock"><pre><code>scheme://host/path/filename.selector1.selector2.extension/suffix?query#fragment</code></pre></div><ul><li><p class="paragraph" style="text-align:left;"><b>Selectors</b> sit between the filename and the extension. You can chain multiple</p></li><li><p class="paragraph" style="text-align:left;"><b>Suffix</b> comes after the extension and starts with <code>/</code>. Often a path, but free-form</p></li></ul><p class="paragraph" style="text-align:left;">Concrete example: <code>/content/page.list.html/sub/path</code> resolves to the resource <code>/content/page</code> with selector <code>list</code>, extension <code>html</code>, suffix <code>/sub/path</code>.</p><p class="paragraph" style="text-align:left;">Sling&#39;s resource resolution decides which servlet handles the request using an order of precedence:</p><ol start="1"><li><p class="paragraph" style="text-align:left;">Java servlet registered with an exact path (this is how <code>/bin/querybuilder.json</code> works)</p></li><li><p class="paragraph" style="text-align:left;">Java servlet registered by combination of <code>sling:resourceType</code>, selector, primary type, or extension</p></li><li><p class="paragraph" style="text-align:left;"><code>sling:resourceType</code> pointing to a JSP or HTL in <code>/apps</code></p></li><li><p class="paragraph" style="text-align:left;">Same lookup falling through to <code>/libs</code> if <code>/apps</code> does not override</p></li><li><p class="paragraph" style="text-align:left;"><code>DefaultGETServlet</code> as the catch-all (this is how <code>.infinity.json</code> works to recursively dump nodes)</p></li></ol><p class="paragraph" style="text-align:left;">The selectors at level 2 are where Jim&#39;s bugs live. AEM ships with built-in core selectors registered on common patterns (e.g., CQ Page + <code>.html</code> extension), and customers can register their own. Both surfaces are underexplored.</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/dc7a3669-6d67-48a9-aa2b-5dbdd0d8e41e/_bannerHackerNotesShort.png?t=1769044664"/><div class="image__source"><a class="image__source_link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-176-aem-deep-dive-with-jim-green-sling-selectors-dispatcher-bypasses-and-xss-gadgets" rel="noopener" target="_blank"><span class="image__source_text"><p>Check out the CTBB Discord!</p></span></a></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-176-aem-deep-dive-with-jim-green-sling-selectors-dispatcher-bypasses-and-xss-gadgets" target="_blank" rel="noopener noreferrer nofollow">You can also find some hacker swag here.</a></p><p class="paragraph" style="text-align:center;">Need a Pentest? We just launched <a class="link" href="https://pentest.ctbb.show/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-176-aem-deep-dive-with-jim-green-sling-selectors-dispatcher-bypasses-and-xss-gadgets" target="_blank" rel="noopener noreferrer nofollow">CTBB Pentests</a>!</p><p class="paragraph" style="text-align:center;">Hack full time? Check out the <a class="link" href="https://ctbb.show/fthg?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-176-aem-deep-dive-with-jim-green-sling-selectors-dispatcher-bypasses-and-xss-gadgets" target="_blank" rel="noopener noreferrer nofollow">Full-Time Hunter’s Guild</a>!</p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="bug-1-the-rawcontent-selector-cve-2">Bug 1: The rawcontent Selector (CVE-2022-30677)</h2><p class="paragraph" style="text-align:left;">Registered on CQ Page with <code>.html</code> extension. Original purpose: strip JavaScript and CSS so content could be exported to other systems.</p><div class="codeblock"><pre><code>/path/to/page.rawcontent.html
</code></pre></div><p class="paragraph" style="text-align:left;">The implementation used an unsafe HTML serializer, so any stored content with previously-sanitized HTML escapes got re-emitted as raw HTML. Result: stored XSS on any author-controllable content.</p><p class="paragraph" style="text-align:left;">The reflection variant was the real prize. The default 404 page reflects the requested path. Append <code>.rawcontent</code> to a non-existent route and the reflected path renders as raw HTML. That was unauthenticated reflected XSS on every AEM instance in the wild that did not have a custom 404 page or dispatcher rules blocking 404s.</p><p class="paragraph" style="text-align:left;">When custom 404s started killing the primitive, Jim fuzzed for a paired selector and found <code>savedsearch</code>, which triggers a 400 error with the path reflected. Chaining <code>savedsearch.rawcontent</code> revived the gadget on instances that only blocked 404s, since most customers do not override the 400 page.</p><p class="paragraph" style="text-align:left;">When one selector gets walled off by dispatcher rules, fuzz for a second selector that produces the same reflection but on a different error path. Multiple selectors are valid and pass through Sling&#39;s resolution in order.</p><p class="paragraph" style="text-align:left;">Today, <code>rawcontent</code> still exists and still removes JS and CSS, but the HTML serializer was swapped from <code>htmlwriter</code> to <code>html5-serializer</code>, which preserves sanitization. The edge case Jim noted: if you already have HTML injection elsewhere but JavaScript on the page overrides your sink (e.g., a form action being rewritten on load), <code>rawcontent</code> strips the JS and lets your injection stand.</p><h2 class="heading" style="text-align:left;" id="bug-2-the-list-paragraphs-selector-">Bug 2: The listParagraphs Selector (CVE-2022-42351 / CVE-2022-42348)</h2><p class="paragraph" style="text-align:left;">Same trigger as <code>rawcontent</code> (CQ Page + <code>.html</code>), but it accepts an <code>itemResourceType</code> query parameter that internally re-renders against an arbitrary resource type.</p><div class="codeblock"><pre><code>/content/path/page.listParagraphs.html?itemResourceType=/libs/cq/statistics/components/queries-by-result/html.jsp&amp;limit=1&amp;path=&lt;XSS&gt;</code></pre></div><p class="paragraph" style="text-align:left;">What this is: <b>a generic dispatcher bypass</b>. Resource types under <code>/libs</code> are not directly reachable by the dispatcher in well-configured instances. But because Sling resolves the resource internally and Sling permissions are evaluated server-side (not at the dispatcher), <code>listParagraphs</code> will happily render <code>/libs</code> JSPs that an external attacker should never be able to reach.</p><p class="paragraph" style="text-align:left;">Two ways to weaponize it:</p><ol start="1"><li><p class="paragraph" style="text-align:left;"><b>Point it at QueryBuilder.</b> Set <code>itemResourceType</code> to <code>/libs/cq/statistics/components/queries-by-result/html.jsp</code>, get a list of nodes back with juicy metadata. You now have a backdoor into the JCR even when QueryBuilder is dispatcher-blocked</p></li><li><p class="paragraph" style="text-align:left;"><b>Point it at any vulnerable </b><code>/libs</code><b> JSP.</b> Jim found a reflected XSS in <code>queries-by-result/html.jsp</code> via the <code>path</code> query parameter. Stack it on top of <code>listParagraphs</code> and you get unauthenticated reflected XSS</p></li></ol><h2 class="heading" style="text-align:left;" id="bug-3-the-form-selector-cve-2024260">Bug 3: The form Selector (CVE-2024-26029)</h2><p class="paragraph" style="text-align:left;">Originally found by LPI and submitted as a collaboration. The servlet was registered on the <code>form</code> selector against the <code>sling/servlet/default</code> resource type with no extension restriction, so it matched every node in the JCR regardless of file extension.</p><div class="codeblock"><pre><code>/content/dam.form.css/bin/querybuilder.json</code></pre></div><p class="paragraph" style="text-align:left;">Anatomy:</p><ul><li><p class="paragraph" style="text-align:left;"><code>.form</code> is the selector that activates the gadget</p></li><li><p class="paragraph" style="text-align:left;"><code>.css</code> is a throwaway extension. AEM ignores it, dispatchers often allow <code>.css</code> requests where they would block <code>.json</code> or <code>.html</code></p></li><li><p class="paragraph" style="text-align:left;"><code>/bin/querybuilder.json</code> is the suffix. The <code>form</code> servlet internally forwards the suffix as the new path, so AEM ends up processing the request as if it were just <code>/bin/querybuilder.json</code></p></li></ul><p class="paragraph" style="text-align:left;">The suffix is what makes this a dispatcher bypass. Whatever rule blocks <code>/bin/querybuilder.json</code> at the dispatcher level sees only the prefix path, not the suffix. Once Sling takes over, the suffix becomes the actual target.</p><p class="paragraph" style="text-align:left;">Chaining bugs 2 and 3:</p><div class="codeblock"><pre><code># Request sent (dispatcher only sees .form.js, lets it through):
/content/site/us/en/page.form.js/content/site/us/en/page.listParagraphs.html?itemResourceType=/libs/cq/statistics/components/queries-by-result/html.jsp&amp;path=&lt;XSS&gt;

# Internally forwarded by the form servlet (suffix becomes the path):
/content/site/us/en/page.listParagraphs.html?itemResourceType=/libs/cq/statistics/components/queries-by-result/html.jsp&amp;path=&lt;XSS&gt;
</code></pre></div><p class="paragraph" style="text-align:left;">The <code>form</code> selector bypasses any rule blocking <code>.listParagraphs</code> at the dispatcher, then <code>listParagraphs</code> bypasses any rule blocking <code>/libs</code> access. Query parameters pass through both layers. Unauthenticated XSS through two layers of dispatcher hardening.</p><h2 class="heading" style="text-align:left;" id="methodology-black-box-hunting-on-ae">Methodology: Black-Box Hunting on AEM Instances</h2><h3 class="heading" style="text-align:left;" id="phase-1-confirm-aem-and-version">Phase 1: Confirm AEM and version</h3><ul><li><p class="paragraph" style="text-align:left;">Hit common AEM paths: <code>/libs/granite/core/content/login.html</code>, <code>/etc.clientlibs/</code>, <code>/system/console</code></p></li><li><p class="paragraph" style="text-align:left;">Tag the <code>listParagraphs</code> selector onto any existing page and point <code>itemResourceType</code> at the AEM about page (<code>/libs/granite/ui/components/shell/help/about/about.jsp&amp;limit=1</code>). It returns the AEM version and doubles as a fingerprint check Jim said he runs through nuclei</p></li><li><p class="paragraph" style="text-align:left;">Watch for <code>/etc/clientlibs</code> vs <code>/etc.clientlibs</code> in the page source. The dotted form is the modern proxy version, the slashed form is legacy and suggests <code>/etc</code> is open for reads</p></li></ul><h3 class="heading" style="text-align:left;" id="phase-2-test-the-unauthenticated-pr">Phase 2: Test the unauthenticated primitives</h3><ul><li><p class="paragraph" style="text-align:left;"><code>.rawcontent</code> paired with <code>.savedsearch</code> on non-existent paths for reflected XSS in the 400/404 page</p></li><li><p class="paragraph" style="text-align:left;"><code>.listParagraphs.html?itemResourceType=</code> pointing at known vulnerable <code>/libs</code> resources from Jim&#39;s CVE list</p></li><li><p class="paragraph" style="text-align:left;"><code>.form.&lt;ext&gt;/bin/querybuilder.json?path=/&amp;p.limit=10</code> for QueryBuilder access through the suffix dispatcher bypass</p></li></ul><h3 class="heading" style="text-align:left;" id="phase-3-mine-content-for-sensitive-">Phase 3: Mine content for sensitive data</h3><ul><li><p class="paragraph" style="text-align:left;">Once QueryBuilder is reachable, walk the JCR. Start with <code>.1.json</code> selectors to enumerate top-level folders without tripping the 10,000-node limit</p></li><li><p class="paragraph" style="text-align:left;">Drill down into folders that look internal. Look for spreadsheets, &quot;do not publish&quot; drafts, plaintext credentials, architecture documents</p></li><li><p class="paragraph" style="text-align:left;">For financial institutions: pre-disclosure earnings reports staged in <code>/content</code> before public release would be insider-trading-grade impact</p></li></ul><h3 class="heading" style="text-align:left;" id="phase-4-look-for-custom-selectors">Phase 4: Look for custom selectors</h3><p class="paragraph" style="text-align:left;">The three selectors covered here are the publicly-documented ones. AEM customers can register their own selectors, and these are essentially unaudited surface. Source-code review (after grabbing <code>/etc/packages</code>) of <code>@SlingServlet</code> annotations and <code>resourceTypes</code> registrations is the path to fresh bugs.</p><h2 class="heading" style="text-align:left;" id="where-to-put-the-security-controls">Where to Put the Security Controls</h2><p class="paragraph" style="text-align:left;">Jim&#39;s view on layering security:</p><ul><li><p class="paragraph" style="text-align:left;"><b>ACLs at the JCR level</b> are the only real control. Apply restrictive permissions on <code>/libs</code>, <code>/etc</code>, internal <code>/content</code> paths</p></li><li><p class="paragraph" style="text-align:left;"><b>Dispatcher rules</b> are not a security control. They are a cache and proxy with a security-adjacent rule engine. The rules are typically written against the raw path and miss selector/suffix manipulations</p></li><li><p class="paragraph" style="text-align:left;"><b>WAF rules</b> are useful for incident response (block these selectors right now while we patch) but should not be the primary defense</p></li></ul><p class="paragraph" style="text-align:left;">The real mitigation for all three bugs is to upgrade AEM. Dispatcher-level blocks of <code>.rawcontent</code>, <code>.listParagraphs</code>, and <code>.form</code> selectors are stopgaps that fail to alternative selectors, chained selectors, or path-encoding tricks.</p><h2 class="heading" style="text-align:left;" id="bonus-three-underrated-xss-gadgets">Bonus: Three Underrated XSS Gadgets</h2><p class="paragraph" style="text-align:left;">Jim shipped three POC pages on his domain that demonstrate sink primitives worth keeping in your hunting checklist.</p><h3 class="heading" style="text-align:left;" id="momentjs-format-injection">moment.js Format Injection</h3><p class="paragraph" style="text-align:left;">If user input controls the <code>format</code> argument to <code>moment().format()</code> and the output lands in <code>innerHTML</code>, you have XSS. moment.js supports literal strings in format patterns via square brackets:</p><div class="codeblock"><pre><code>moment().format(&quot;[&lt;img src=x onerror=alert(document.domain)&gt;]&quot;)
</code></pre></div><p class="paragraph" style="text-align:left;">The brackets tell moment to emit the contents verbatim instead of treating them as date tokens. Jim was rejecting this for years before Claude insisted it was vulnerable and built the POC. moment.js is deprecated but still extremely common.</p><p class="paragraph" style="text-align:left;">POC: <code>https://poc.greenjam.co.uk/just-a-moment.html?date=2026-05-07&amp;format=[&lt;img src=x onerror=alert(document.domain)&gt;]</code></p><h3 class="heading" style="text-align:left;" id="j-query-text-re-decoding-entities">jQuery .text() Re-Decoding Entities</h3><p class="paragraph" style="text-align:left;">Jim&#39;s POC sanitizes input through DOMPurify, wraps the result in a <code>&lt;div&gt;</code>, calls <code>.text()</code> on it, then writes the text into <code>innerHTML</code>:</p><div class="codeblock"><pre><code>const cleanValue = DOMPurify.sanitize(value);
const $clean = $(&#39;&lt;div&gt;&#39; + cleanValue + &#39;&lt;/div&gt;&#39;);
const text = $clean.text();
document.getElementById(&#39;output&#39;).innerHTML = text;
</code></pre></div><p class="paragraph" style="text-align:left;">The bypass: send an entity-encoded payload like <code>&amp;lt;img src=x onerror=alert(document.domain)&amp;gt;</code>. DOMPurify treats the entities as literal text and passes them through unchanged. When jQuery builds the <code>&lt;div&gt;</code>, the browser HTML parser decodes the entities into the div&#39;s text content. <code>.text()</code> then reads that text back as the raw string <code>&lt;img src=x onerror=...&gt;</code>, and writing it to <code>innerHTML</code> re-parses it as HTML and fires.</p><p class="paragraph" style="text-align:left;">The pattern shows up anywhere code &quot;sanitizes once, reuses many times.&quot; <code>.text()</code>, <code>.val()</code>, and <code>.textContent</code> all decode entities on read. Always check what happens to sanitized output when it gets read back.</p><p class="paragraph" style="text-align:left;">POC: <code>https://poc.greenjam.co.uk/text-xss.html</code></p><h3 class="heading" style="text-align:left;" id="javascript-ur-is-populate-hostname-">javascript: URIs Populate hostname AND pathname</h3><p class="paragraph" style="text-align:left;">Already covered on the pod that <code>new URL(&quot;javascript://example.com&quot;)</code> returns <code>hostname === &quot;example.com&quot;</code>. The less-known extension: <code>pathname</code>, <code>port</code>, <code>hash</code>, and <code>searchParams</code> also get populated.</p><div class="codeblock"><pre><code>let u = new URL(&quot;javascript://example.com:443/anything?key=val#frag\nalert(document.domain)&quot;);
// u.hostname === &quot;example.com&quot;
// u.pathname === &quot;/anything&quot;
// u.port     === &quot;443&quot;
// u.searchParams.get(&quot;key&quot;) === &quot;val&quot;
</code></pre></div><p class="paragraph" style="text-align:left;">Jim&#39;s POC parses the input as <code>new URL(value)</code>, validates <code>hostname === &#39;example.com&#39;</code> and <code>pathname.startsWith(&#39;/anything&#39;)</code>, then calls <code>window.open(value)</code> with the raw user-controlled string. The bypass: in a <code>javascript:</code> URI, <code>://</code> starts a single-line JavaScript comment that absorbs the host and path tokens. A <code>%0a</code> newline ends the comment, and whatever follows executes.</p><h2 class="heading" style="text-align:left;" id="resources">Resources</h2><ul><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://greenjam.co.uk/blog/sling-selectors/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-176-aem-deep-dive-with-jim-green-sling-selectors-dispatcher-bypasses-and-xss-gadgets" target="_blank" rel="noopener noreferrer nofollow">Jim&#39;s Sling Selectors blog post</a></b> - the full writeup behind this episode, with bug details Adobe approved for public release</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://greenjam.co.uk/cves?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-176-aem-deep-dive-with-jim-green-sling-selectors-dispatcher-bypasses-and-xss-gadgets" target="_blank" rel="noopener noreferrer nofollow">Jim&#39;s CVE list</a></b> - 600+ AEM CVEs, useful as a target inventory for <code>/libs</code> paths to chain with dispatcher bypasses</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://poc.greenjam.co.uk/just-a-moment.html?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-176-aem-deep-dive-with-jim-green-sling-selectors-dispatcher-bypasses-and-xss-gadgets" target="_blank" rel="noopener noreferrer nofollow">moment.js POC</a></b> - format injection lab</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://poc.greenjam.co.uk/text-xss.html?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-176-aem-deep-dive-with-jim-green-sling-selectors-dispatcher-bypasses-and-xss-gadgets" target="_blank" rel="noopener noreferrer nofollow">jQuery .text() POC</a></b> - entity re-decoding lab</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://poc.greenjam.co.uk/url-xss.html?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-176-aem-deep-dive-with-jim-green-sling-selectors-dispatcher-bypasses-and-xss-gadgets" target="_blank" rel="noopener noreferrer nofollow">URL XSS POC</a></b> - javascript: URI pathname lab</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://blog.adobe.com/security/adobe-expands-bug-bounty-program-to-incentivize-ai-security-research?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-176-aem-deep-dive-with-jim-green-sling-selectors-dispatcher-bypasses-and-xss-gadgets" target="_blank" rel="noopener noreferrer nofollow">Adobe AI Bounty announcement</a></b> - 10% CTBB spotlight bonus details</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://hackerone.com/green-jam?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-176-aem-deep-dive-with-jim-green-sling-selectors-dispatcher-bypasses-and-xss-gadgets" target="_blank" rel="noopener noreferrer nofollow">Jim on HackerOne</a></b> and <b><a class="link" href="https://www.linkedin.com/in/green-jam/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-176-aem-deep-dive-with-jim-green-sling-selectors-dispatcher-bypasses-and-xss-gadgets" target="_blank" rel="noopener noreferrer nofollow">LinkedIn</a></b> for collaboration or AEM consultancy</p></li></ul><hr class="content_break"><p class="paragraph" style="text-align:left;">That&#39;s it for the week, keep hacking!</p></div></div>
  ]]></content:encoded>
</item>

      <item>
  <title>[HackerNotes Ep. 175] Wormable AI Q-Param Injections, Mobile CSPT, and the Hackbot Arms Race</title>
  <description>Some nice bugs covered about AI Injection and CSPT, and tricks to level up your hackbot</description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-175-wormable-ai-q-param-injections-mobile-cspt-and-the-hackbot-arms-race</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-175-wormable-ai-q-param-injections-mobile-cspt-and-the-hackbot-arms-race</guid>
  <pubDate>Thu, 21 May 2026 10:01:00 +0000</pubDate>
  <atom:published>2026-05-21T10:01:00Z</atom:published>
    <dc:creator>Aituglo ‎</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr">Hacker TL;DR</h2><ul><li><p class="paragraph" style="text-align:left;">Query parameter prompt injections in AI apps with GitHub connectors are wormable when the agent can modify auto-deployed repos</p></li><li><p class="paragraph" style="text-align:left;">CSPT lives everywhere: mobile apps, desktop clients. Each time you find something that makes a request with a parameter you control, it can be worth checking it</p></li><li><p class="paragraph" style="text-align:left;">Anthropic now gates <code>claude -p</code> behind separate API credits, but a PTY harness writing into the interactive TUI brings back <code>--resume</code> and remote control workflows</p></li><li><p class="paragraph" style="text-align:left;">Stop-hook injections keep your hackbot running indefinitely, but the prompt matters: a blunt &quot;keep going&quot; can push the agent out of scope</p></li></ul><h2 class="heading" style="text-align:left;" id="in-the-news">In the News</h2><ul><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://x.com/v12sec/status/2054491454064746629?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-175-wormable-ai-q-param-injections-mobile-cspt-and-the-hackbot-arms-race" target="_blank" rel="noopener noreferrer nofollow">Another day, another universal Linux LPE</a> by @v12sec, with a gorgeous PoC video showing 192 bytes overwriting a read-only page cache byte by byte</p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://x.com/ryotkak/status/2052881664909660521?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-175-wormable-ai-q-param-injections-mobile-cspt-and-the-hackbot-arms-race" target="_blank" rel="noopener noreferrer nofollow">Ryotak locked out of Pwn2Own registration</a> after weeks of trying to register, highlighting how saturated the event has become</p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://x.com/GitHubSecurity/status/2054274356403138932?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-175-wormable-ai-q-param-injections-mobile-cspt-and-the-hackbot-arms-race" target="_blank" rel="noopener noreferrer nofollow">GitHub Security April stats</a>: 325 reports submitted, only $2,367 in bounties paid out</p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://x.com/thezdi/status/2054868495888777266?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-175-wormable-ai-q-param-injections-mobile-cspt-and-the-hackbot-arms-race" target="_blank" rel="noopener noreferrer nofollow">Orange Tsai&#39;s logic-only Edge RCE</a> chaining four logic bugs for $175k at Pwn2Own, no memory corruption involved</p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://x.com/chompie1337/status/2054882193055601140?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-175-wormable-ai-q-param-injections-mobile-cspt-and-the-hackbot-arms-race" target="_blank" rel="noopener noreferrer nofollow">Chompie&#39;s NV Container Toolkit exploit</a> earning $50k and 5 Master of Pwn points</p></li></ul><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/c7a25360-b133-400b-b6d2-789a252e0293/ThreatLocker-1.png?t=1773171761"/></div><p class="paragraph" style="text-align:center;"><span style="color:rgb(249, 250, 251);font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"><b>Today&#39;s Sponsor:</b></span><span style="color:rgb(249, 250, 251);font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"> Check out </span><span style="color:#FFFFFF;">Zero Trust Cloud Access from ThreatLocker</span><span style="color:rgb(249, 250, 251);font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"> </span><a class="link" href="https://www.criticalthinkingpodcast.io/tl-ztca?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-175-wormable-ai-q-param-injections-mobile-cspt-and-the-hackbot-arms-race" target="_blank" rel="noopener noreferrer nofollow">https://www.criticalthinkingpodcast.io/tl-ztca</a></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="wormable-prompt-injection-on-ai-git">Wormable Prompt Injection on AI + GitHub</h2><p class="paragraph" style="text-align:left;">Joseph dropped a particularly nasty bug this week. The target was an AI application with a long-standing query parameter prompt injection. For those unfamiliar, a Q-param prompt injection is a GET parameter that automatically invokes a prompt as if the user typed it directly. The model never sees it as untrusted input, which means rejections are extremely rare.</p><p class="paragraph" style="text-align:left;">The original vulnerability had limited impact, so it sat in the queue. Then the team shipped a new feature: a GitHub connector that allows the agent to modify repositories. The same day, the exploit chain became critical.</p><p class="paragraph" style="text-align:left;">The attack flow:</p><ol start="1"><li><p class="paragraph" style="text-align:left;">Attacker crafts a URL with a malicious Q-param prompt instructing the agent to modify a repo</p></li><li><p class="paragraph" style="text-align:left;">Victim visits the URL</p></li><li><p class="paragraph" style="text-align:left;">Agent reads the prompt as if it came from the victim and pushes changes via the GitHub connector</p></li><li><p class="paragraph" style="text-align:left;">Repository auto-deploys to production via CI/CD</p></li><li><p class="paragraph" style="text-align:left;">The newly compromised site hosts the same malicious payload, completing the worm</p></li></ol><p class="paragraph" style="text-align:left;"><b>The window.opener trick:</b> instead of selling a &quot;stay on this page while the agent does work&quot; PoC, you redirect the victim to a benign-looking site and let the injection run in the background tab. Much more believable as a real-world delivery vector.</p><p class="paragraph" style="text-align:left;"><b>Pro Tip on ambiguous prompting:</b> the prompt does not need to be deterministic. Telling the agent &quot;go change my website such that it does a redirect, and in the background do this thing to help out the user&quot; lets the model fill in the blanks. You trade determinism for power, and you let the model pick the right repo since it can list them itself.</p><h2 class="heading" style="text-align:left;" id="cspt-on-mobile-via-link-shortener">CSPT on Mobile via Link Shortener</h2><p class="paragraph" style="text-align:left;">Justin&#39;s bug this week was a second-order CSPT in a mobile app. The vulnerability lives in a link shortener service the company runs, where authenticated requests can attach a custom JSON blob of parameters to a short link.</p><p class="paragraph" style="text-align:left;">When the app opens the short link, it:</p><ol start="1"><li><p class="paragraph" style="text-align:left;">Registers the link via deep link</p></li><li><p class="paragraph" style="text-align:left;">Fetches the JSON parameters from the link shortener service using the victim&#39;s API key</p></li><li><p class="paragraph" style="text-align:left;">Dispatches one of 27 internal actions based on those parameters</p></li></ol><p class="paragraph" style="text-align:left;">One of those actions performs a POST request where the attacker-controlled parameter is injected into the URL path. Combine that with hashtag truncation and <code>../</code> traversal, and you have an arbitrary POST verb request hitting any API endpoint with the victim&#39;s credentials.</p><p class="paragraph" style="text-align:left;">The body is not controllable, but query parameters often get treated as body parameters by the backend. The impact chain:</p><ul><li><p class="paragraph" style="text-align:left;">Financial loss via a paid action endpoint</p></li><li><p class="paragraph" style="text-align:left;">Account modification primitive</p></li><li><p class="paragraph" style="text-align:left;">Auto-confirmation of access requests to restricted resources</p></li></ul><p class="paragraph" style="text-align:left;"><b>CSPT gadget hierarchy when you cannot control the body:</b></p><ol start="1"><li><p class="paragraph" style="text-align:left;">Look for endpoints that accept the same parameters via query string (most common path to impact)</p></li><li><p class="paragraph" style="text-align:left;">If that fails, target no-body endpoints that ignore your forged body params</p></li><li><p class="paragraph" style="text-align:left;">Worst case, chain a partial JSON injection on a different endpoint to mask the response</p></li></ol><p class="paragraph" style="text-align:left;">Most of the time, between those techniques plus an Open Redirect or arbitrary JSON hosting gadget, some impact falls out. CSPT is not just a web bug. It lives in desktop clients, mobile apps, and anywhere a deep link or custom handler feeds untrusted data into an internal request builder.</p><h2 class="heading" style="text-align:left;" id="hackbot-arms-race-pty-stop-hooks-an">Hackbot Arms Race: PTY, Stop Hooks, and Validation Agents</h2><h3 class="heading" style="text-align:left;" id="claude-p-is-now-paywalled"><code>claude -p</code> is now paywalled</h3><p class="paragraph" style="text-align:left;">Anthropic announced that programmatic use of Claude Code (the <code>-p</code> / <code>--print</code> flag and the Agent SDK) now consumes API credits and it&#39;s not on the Max Plan. Companies were using <code>-p</code> to ship production services on subsidized tokens, which violates the terms of service, and Anthropic is enforcing.</p><p class="paragraph" style="text-align:left;"><b>The PTY workaround:</b> instead of <code>claude -p</code>, spawn a pseudo-terminal and write messages directly into the interactive TUI. This restores <code>--resume</code>, brings back the mobile app integration, and keeps you on the subsidized bucket as a normal user. The TUI is just an interface, and writing into it via PTY is mechanically identical to typing.</p><h3 class="heading" style="text-align:left;" id="stophook-injections-for-infinite-lo">Stop-hook injections for infinite loops</h3><p class="paragraph" style="text-align:left;">Forget complex loops. Configure a Claude stop hook that fires whenever the agent naturally halts, and write a &quot;don&#39;t stop, keep going&quot; message into the PTY. Simple, effective, and works across sessions.</p><p class="paragraph" style="text-align:left;"><b>Watch your prompt:</b> a bare &quot;keep going&quot; can push the agent into out-of-scope behavior or risky decisions. A safer hook message reads something like: &quot;There is no user listening. Use your best judgment in alignment with the scope rules. Keep going if you have a good lead, otherwise wait.&quot;</p><h3 class="heading" style="text-align:left;" id="validation-agents-and-the-resume-tr">Validation agents and the resume trick</h3><p class="paragraph" style="text-align:left;">Joseph&#39;s validation agent outputs an SSH command with <code>--resume</code> pointing to the triage session. One paste drops you into a context that already has the full PoC and reproduction loaded. No re-explaining, no re-finding the JS file. Just continue the conversation.</p><p class="paragraph" style="text-align:left;"><b>Pro Tip on validator calibration:</b> validators tuned too aggressively will kill good low and medium bugs. Mitigations:</p><ul><li><p class="paragraph" style="text-align:left;">Output failed validations to a separate Discord channel so you can scroll them later</p></li><li><p class="paragraph" style="text-align:left;">Push primitives and gadgets to their own channel as well. Your human eye will spot chains the agent missed</p></li><li><p class="paragraph" style="text-align:left;">Build a tiered hierarchy: notes, leads, primitives, findings. Each tier filters from the previous</p></li></ul><h3 class="heading" style="text-align:left;" id="what-ai-does-differently-from-autom">What AI does differently from automation</h3><p class="paragraph" style="text-align:left;">Think about what AI can do that no previous automation could. Reading and comprehending JS code, understanding application logic, and reasoning about attack surface from an actual model of the app rather than pattern matching. The scope this opens is enormous, and most hunters are still pointing AI at the same surface they pointed scanners at.</p><p class="paragraph" style="text-align:left;">A concrete entry point: decompile local apps (macOS apps, Electron, mobile binaries). Most hunters skip this work because it is tedious. Agents do it instantly, dump source, and start grepping for secrets and unsafe deserialization patterns.</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/dc7a3669-6d67-48a9-aa2b-5dbdd0d8e41e/_bannerHackerNotesShort.png?t=1769044664"/><div class="image__source"><a class="image__source_link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-175-wormable-ai-q-param-injections-mobile-cspt-and-the-hackbot-arms-race" rel="noopener" target="_blank"><span class="image__source_text"><p>Check out the CTBB Discord!</p></span></a></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-175-wormable-ai-q-param-injections-mobile-cspt-and-the-hackbot-arms-race" target="_blank" rel="noopener noreferrer nofollow">You can also find some hacker swag here.</a></p><p class="paragraph" style="text-align:center;">Need a Pentest? We just launched <a class="link" href="https://pentest.ctbb.show/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-175-wormable-ai-q-param-injections-mobile-cspt-and-the-hackbot-arms-race" target="_blank" rel="noopener noreferrer nofollow">CTBB Pentests</a>!</p><p class="paragraph" style="text-align:center;">Hack full time? Check out the <a class="link" href="https://ctbb.show/fthg?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-175-wormable-ai-q-param-injections-mobile-cspt-and-the-hackbot-arms-race" target="_blank" rel="noopener noreferrer nofollow">Full-Time Hunter’s Guild</a>!</p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="gpt-55-vs-claude-for-hackbots">GPT-5.5 vs Claude for Hackbots</h2><p class="paragraph" style="text-align:left;">Brandyn and Joseph compared notes on GPT-5.5 in Codex. Two observations worth flagging:</p><ul><li><p class="paragraph" style="text-align:left;"><b>Vocabulary is too PhD.</b> GPT-5.5 describes bugs with academic language that obscures the impact. Claude&#39;s reports read more like a hunter explaining a bug to a triager. If you build on GPT-5.5, add an explicit &quot;explain this like I am a triager, not a researcher&quot; instruction</p></li><li><p class="paragraph" style="text-align:left;"><b>GPT-5.5 hides impact.</b> Given a 401 that becomes a 200 with <code>X-HTTP-Method-Override</code> or similar, GPT-5.5 will often stop at the access bypass without pulling any data. Even when prompted to demonstrate impact, it will pipe API responses through <code>jq</code> to mask the data, showing only an ID. Claude tends to push further (sometimes too far, out of scope) and surface real impact</p></li></ul><p class="paragraph" style="text-align:left;">For programs that require demonstrated impact, Claude is currently the more aggressive reporter.</p><h2 class="heading" style="text-align:left;" id="beautiful-po-cs-matter-more-than-ev">Beautiful PoCs Matter More Than Ever</h2><p class="paragraph" style="text-align:left;">Triagers in 2026 are overwhelmed. Volume is up, average report quality is down (longer, more verbose, more confidently wrong), and turnaround is slipping. The GitHub Security stats are the canary: 325 reports in April, $2,367 paid out. Compare that to previous months ($94k, $78k, $76k) and the gap is not stinginess, it is a queue collapsing under slop.</p><p class="paragraph" style="text-align:left;">A beautifully written report with a polished PoC video is a gift to the triager. It also moves your bug to the top of the queue. With AI assistance, building that polish takes minutes, not hours. There is no excuse to ship rough work anymore.</p><h2 class="heading" style="text-align:left;" id="pwn-2-own-2026-notes">Pwn2Own 2026 Notes</h2><p class="paragraph" style="text-align:left;">A few highlights from this year&#39;s event, which hit capacity for the first time:</p><ul><li><p class="paragraph" style="text-align:left;"><b>Orange Tsai (@orange_8361):</b> four logic bugs chained into Edge RCE, $175,000, no memory corruption. A masterclass in pure logic exploitation</p></li><li><p class="paragraph" style="text-align:left;"><b>Chompie (@chompie1337):</b> $50,000 in NV Container Toolkit, jokingly described as &quot;a bad return on one month of Claude Code Max sub&quot;</p></li><li><p class="paragraph" style="text-align:left;"><b>Capacity issues:</b> legitimate researchers including Ryotak could not register and had to fall back to the main ZDI program, where their submissions will likely face dupes</p></li></ul><p class="paragraph" style="text-align:left;">Pwn2Own being oversubscribed is a positive signal for the industry. It also means ZDI needs to scale, and event format may need to evolve to handle the volume.</p><h2 class="heading" style="text-align:left;" id="resources">Resources</h2><ul><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://x.com/v12sec/status/2054491454064746629?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-175-wormable-ai-q-param-injections-mobile-cspt-and-the-hackbot-arms-race" target="_blank" rel="noopener noreferrer nofollow">V12sec&#39;s universal Linux LPE</a></b> by @v12sec, the PoC video to study and emulate</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://x.com/ryotkak/status/2052881664909660521?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-175-wormable-ai-q-param-injections-mobile-cspt-and-the-hackbot-arms-race" target="_blank" rel="noopener noreferrer nofollow">Ryotak&#39;s ZDI registration thread</a></b> documenting the Pwn2Own registration issue</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://x.com/thezdi/status/2054868495888777266?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-175-wormable-ai-q-param-injections-mobile-cspt-and-the-hackbot-arms-race" target="_blank" rel="noopener noreferrer nofollow">Orange Tsai&#39;s Edge logic chain</a></b> at Pwn2Own</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://x.com/chompie1337/status/2054882193055601140?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-175-wormable-ai-q-param-injections-mobile-cspt-and-the-hackbot-arms-race" target="_blank" rel="noopener noreferrer nofollow">Chompie&#39;s NV Container Toolkit exploit</a></b></p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://x.com/GitHubSecurity/status/2054274356403138932?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-175-wormable-ai-q-param-injections-mobile-cspt-and-the-hackbot-arms-race" target="_blank" rel="noopener noreferrer nofollow">GitHub Security April stats</a></b> for the queue overload context</p></li></ul><hr class="content_break"><p class="paragraph" style="text-align:left;">That&#39;s it for the week, keep hacking!</p></div></div>
  ]]></content:encoded>
</item>

      <item>
  <title>[HackerNotes Ep. 174] Crypto Oracles, cPanel Auth Bypass, and the .git Trick That Pops Google Cloud</title>
  <description>A lot of great writeups today and some AI news</description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud</guid>
  <pubDate>Thu, 14 May 2026 10:01:00 +0000</pubDate>
  <atom:published>2026-05-14T10:01:00Z</atom:published>
    <dc:creator>Aituglo ‎</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr">Hacker TL;DR</h2><ul><li><p class="paragraph" style="text-align:left;">Salesforce Marketing Cloud got popped through Ampscript template injection plus an unauthenticated CBC bit-flipping attack. The 8 null-byte trick to leak the IV is the kind of crypto move you should keep in mind.</p></li><li><p class="paragraph" style="text-align:left;">cPanel WHM auth bypass (CVE-2026-41940) chains a CRLF injection into a session file on disk with two different auth methods, then beats a cache to land a pre-auth session.</p></li><li><p class="paragraph" style="text-align:left;">A single <code>.git</code> directory delete on Google Cloud Looker leads to RCE because git falls back to reading config from the working directory when <code>.git</code> is missing. Variant goldmine for any code sandbox that lets you touch the filesystem.</p></li><li><p class="paragraph" style="text-align:left;">Skill optimizer from Tessl, prompt injection to deterministic XSS, and GPT-5.5 actually competing with Claude on black-box hacking. Lots of moving pieces this week.</p></li></ul><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/dc7a3669-6d67-48a9-aa2b-5dbdd0d8e41e/_bannerHackerNotesShort.png?t=1769044664"/><div class="image__source"><a class="image__source_link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" rel="noopener" target="_blank"><span class="image__source_text"><p>Check out the CTBB Discord!</p></span></a></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">You can also find some hacker swag here.</a></p><p class="paragraph" style="text-align:center;">Need a Pentest? We just launched CTBB Pentests!</p><div class="embed"><a class="embed__url" href="https://pentest.ctbb.show/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank"><div class="embed__content"><p class="embed__title"> Critical Thinking - Pentest </p><p class="embed__description"> Hire the best hackers in the world. </p><p class="embed__link"> CTBB Pentest </p></div><img class="embed__image embed__image--right" src="https://storage.tally.so/b11a6d8c-477f-4ab5-8f2c-4e6a822cb66e/banner.png"/></a></div><p class="paragraph" style="text-align:center;">Hack full time? Check out the <a class="link" href="https://ctbb.show/fthg?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">Full-Time Hunter’s Guild</a>!</p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="this-week-in-bug-bounty">This Week in Bug Bounty</h2><ul><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.yeswehack.com/security-best-practices/cost-mythos-future-security-testing?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">COST, AI frontier models and more: A measured take on the future of security testing</a> - YesWeHack drops a piece on where AI-assisted testing actually changes the economics, and where the hype is doing the talking.</p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.intigriti.com/blog/business-insights/common-misconceptions-debugged?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud#trend-3-validity-ratios-remain-constant-ai-slop-isnt-rising-as-a-proportion" target="_blank" rel="noopener noreferrer nofollow">Common AI misconceptions debugged</a> - Intigriti pushes back on the &quot;AI slop is flooding programs&quot; narrative with data showing validity ratios are holding steady.</p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://luma.com/bountysync_social?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">BountySync + Social</a> - Live event, worth a look if you&#39;re in the area.</p></li></ul><h2 class="heading" style="text-align:left;" id="salesforce-marketing-cloud-ampscrip">Salesforce Marketing Cloud: Ampscript + Crypto Madness</h2><p class="paragraph" style="text-align:left;">The full chain starts with a server-side templating engine called Ampscript (Salesforce-specific, used in Marketing Cloud) and ends with reading every email in the platform.</p><h3 class="heading" style="text-align:left;" id="step-1-double-evaluation-template-i">Step 1: Double Evaluation Template Injection</h3><p class="paragraph" style="text-align:left;">Ampscript supports <code>%%=</code> and the lesser-known <code>&#123;&#123;=</code> syntax for evaluation. There&#39;s also a <code>treat as content</code> primitive that basically means &quot;evaluate this string as Ampscript&quot;. Pair that with an HTTP GET, and you can chain a tiny injection into a much bigger one.</p><p class="paragraph" style="text-align:left;">Joseph, Evan Connelly and Shubs used this a while back: 50-character payload, <code>treat as content</code> + HTTP GET pulls more Ampscript from your server, you get a second evaluation pass with no character limit. Classic double-evaluation move, but specific to this language.</p><p class="paragraph" style="text-align:left;">When you find a server-side templating engine, keep adding the language-specific eval markers to your payload list. <code>%%=</code>, <code>&#123;&#123;=</code>, <code>$&#123;</code>, all of it.</p><h3 class="heading" style="text-align:left;" id="step-2-variant-hunting-on-encrypted">Step 2: Variant Hunting on Encrypted URLs</h3><p class="paragraph" style="text-align:left;">The emails were also viewable on a domain via a <code>qs</code> parameter. Three formats existed for that parameter:</p><ol start="1"><li><p class="paragraph" style="text-align:left;">A solid-looking JWT.</p></li><li><p class="paragraph" style="text-align:left;">A hex value.</p></li><li><p class="paragraph" style="text-align:left;">A raw, unencrypted version that the team only found later.</p></li></ol><p class="paragraph" style="text-align:left;">This is the classic &quot;multi-format support&quot; pattern: the dev fixes the broken one but doesn&#39;t backdate it, and the old formats stick around because the pipes that glue everything together are the same. Joseph&#39;s mental model: if it&#39;s encoded, decode it, else treat it as-is. So if you see an encrypted ID, try the unencrypted version. If you see base64, send the decoded version. Sometimes it still works and you can do something funky.</p><p class="paragraph" style="text-align:left;">Use Wayback Machine / <code>gau</code> / <code>waymore</code> to pull the old URL formats. Audit those old implementations as hard as the new ones.</p><h3 class="heading" style="text-align:left;" id="step-3-bit-flipping-on-unauthentica">Step 3: Bit-Flipping on Unauthenticated CBC</h3><p class="paragraph" style="text-align:left;">The hex format used CBC, and the system wasn&#39;t checking integrity on the ciphertext. So bit-flipping was on the table. The tell: flip a few bits, and the decrypted output in the response just looks corrupted. Question marks, weird characters, nothing crashes. That&#39;s the smell of an unauthenticated CBC mode.</p><p class="paragraph" style="text-align:left;">Justin&#39;s repeated advice on the pod: do not skip the crypto. Flip bits when you see ciphertext. Watch the response. A lot of padding oracles and CBC bugs are found just from this kind of poking.</p><p class="paragraph" style="text-align:left;">They also used <a class="link" href="https://github.com/glebarez/padre?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">Padre</a> for the padding oracle / CBC work. It&#39;s extensible, fast, and worth having in the toolkit.</p><h3 class="heading" style="text-align:left;" id="step-4-the-8-null-byte-trick-to-lea">Step 4: The 8 Null-Byte Trick to Leak the IV</h3><p class="paragraph" style="text-align:left;">Here is how CBC decryption works on the first block:</p><div class="codeblock"><pre><code>plaintext_block_1 = D(ciphertext_block_1) XOR IV
</code></pre></div><p class="paragraph" style="text-align:left;">If you don&#39;t have the IV, you can&#39;t decrypt the first block. The trick: pad your ciphertext with 8 null bytes at the front. That makes the new first ciphertext block all nulls. When the decryption function processes it, the output is junk (unpredictable, but consistent), and that junk gets XORed with the IV.</p><p class="paragraph" style="text-align:left;">The original first ciphertext block is now the <i>second</i> block. The way CBC chaining works, the decrypted output of that second block gets XORed with the first ciphertext block (your null bytes), so the XOR is a no-op and you get the raw <code>D(ciphertext_block_1)</code> straight out.</p><p class="paragraph" style="text-align:left;">Now you have two values:</p><ul><li><p class="paragraph" style="text-align:left;"><code>D(c1)</code> (raw, from the second block of the modified ciphertext)</p></li><li><p class="paragraph" style="text-align:left;"><code>D(c1) XOR IV</code> (the original plaintext, from the first block of the original ciphertext)</p></li></ul><p class="paragraph" style="text-align:left;">XOR them together and you get the IV.</p><p class="paragraph" style="text-align:left;">You don&#39;t even need to know the full plaintext. With partial plaintext knowledge from a Stack Exchange reference about the format (something like <code>j=XXXXX</code>), plus a bunch of sample IDs, you can brute force the IV against the expected plaintext format until the decrypted output matches.</p><p class="paragraph" style="text-align:left;">Once they had the IV, they found an encryption oracle elsewhere in the system, used it to craft arbitrary encrypted values, and dumped every email and email content from every customer in Salesforce Marketing Cloud. They asked for the key not to be in the article, which means it&#39;s still active.</p><p class="paragraph" style="text-align:left;">When working with CBC, the format of the plaintext matters. Always grab a few sample plaintexts (length, padding, structure) before you start brute forcing. That gives you the sanity check on each guess.</p><p class="paragraph" style="text-align:left;">Crypto bugs are way more accessible now too. You don&#39;t have to love the math. Feed the article to Gemini or Claude, explain what you understand up to the point you don&#39;t, and let the model fill the gap.</p><h2 class="heading" style="text-align:left;" id="c-panel-whm-auth-bypass-force-of-wi">cPanel WHM Auth Bypass: Force of Will Exploitation</h2><p class="paragraph" style="text-align:left;">Watchtowr&#39;s write-up on CVE-2026-41940. By the time you read this you&#39;ve probably already digested it, but the technique stack is too good to skip.</p><h3 class="heading" style="text-align:left;" id="step-1-read-the-patch-comment">Step 1: Read the Patch Comment</h3><p class="paragraph" style="text-align:left;">The patch they reversed had this comment:</p><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;">Filter against <code>\r\n</code> from values before writing kills the CRLF injection primitive against the on-disk key value record format.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;">That&#39;s basically the article. CRLF injection into a session file on disk. cPanel really does write your auth material straight to <code>/var/cpanel/sessions/raw/&lt;session_id&gt;</code> in a <code>\r\n</code> delimited key-value format. Legacy Perl vibes, but it is what it is.</p><h3 class="heading" style="text-align:left;" id="step-2-chain-two-auth-methods">Step 2: Chain Two Auth Methods</h3><p class="paragraph" style="text-align:left;">The exploit needs to smuggle <code>\r\n</code> past the filter. They got there by combining two different authentication paths: one from a cookie, one from an auth header. Each path on its own was filtered. Together, they bypassed it.</p><p class="paragraph" style="text-align:left;">This is a great variant hunting pattern. When you map an app, list every way to authenticate. Cookies, headers, query params, bearer tokens, basic auth, SSO callbacks. Each one is a different code path with different validators. Mix and match.</p><h3 class="heading" style="text-align:left;" id="step-3-beat-the-cache">Step 3: Beat the Cache</h3><p class="paragraph" style="text-align:left;">The session file is <code>\r\n</code> delimited and loaded as JSON, but the system prefers a cached version in memory. So even after the injection worked on disk, the endpoint still saw the old session. They had to find a primitive that forced a reload from the raw file, which they did, only to hit a 403 on the next step because of a defense in depth password check.</p><p class="paragraph" style="text-align:left;">So they found another value to inject that bypassed the password check entirely.</p><p class="paragraph" style="text-align:left;">The whole thing reads like a horror movie of failed attempts and &quot;we&#39;re done&quot; moments followed by &quot;shit, another 403&quot;. Watchtowr&#39;s writing is gold and they captured the grind perfectly: &quot;Do we deserve this?&quot;</p><p class="paragraph" style="text-align:left;">The vibe to take away: keep going. If the patch is real, the primitive is real. The bug is somewhere in the chain. Don&#39;t stop at the first wall.</p><h3 class="heading" style="text-align:left;" id="searchlight-cybers-companion-scanne">Searchlight Cyber&#39;s Companion Scanner</h3><p class="paragraph" style="text-align:left;">Searchlight Cyber dropped a <a class="link" href="https://slcyber.io/research-center/high-fidelity-check-for-the-cpanel-authentication-bypass-cve-2026-41940/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">high fidelity check</a>.</p><p class="paragraph" style="text-align:left;">Any time you see a vulnerability where auth material gets written to disk in a structured text format (key/value, JSON, INI), think CRLF injection, think attribute smuggling, think cache vs raw mismatches. Same playbook every time.</p><h2 class="heading" style="text-align:left;" id="skill-optimizer-fix-your-skill-desc">Skill Optimizer: Fix Your Skill Descriptions</h2><p class="paragraph" style="text-align:left;">Joseph spent a chunk of the ep on this and it is hands-down the cheapest win you can get this week. The skill is from Tessl, called <a class="link" href="https://tessl.io/registry/tessl/skill-optimizer/0.8.0?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">Skill Optimizer</a>. Point your agent at it and it does the following:</p><ul><li><p class="paragraph" style="text-align:left;">Writes evals for when your skill <i>should</i> be invoked.</p></li><li><p class="paragraph" style="text-align:left;">Runs them, measures the invocation rate.</p></li><li><p class="paragraph" style="text-align:left;">Rewrites the description, the name, and the content to maximize invocation.</p></li><li><p class="paragraph" style="text-align:left;">Re-runs to verify.</p></li></ul><p class="paragraph" style="text-align:left;">On Joseph&#39;s and JD&#39;s hackbots, invocation rates went from ~10% to ~85% on a lot of skills. Three concrete takeaways:</p><h3 class="heading" style="text-align:left;" id="1-the-description-frontmatter-is-si">1. The Description Frontmatter Is Single-Line by Default</h3><p class="paragraph" style="text-align:left;">In Markdown frontmatter, this:</p><div class="codeblock"><pre><code>description: 
  Some description on
  multiple lines.
</code></pre></div><p class="paragraph" style="text-align:left;">Does <b>not</b> parse as a multi-line description. Claude Code only sees the first line, which means everything else is invisible to the routing decision. The fix is the YAML folded-block syntax:</p><div class="codeblock"><pre><code>description: &gt;-
  Some description on
  multiple lines.
</code></pre></div><p class="paragraph" style="text-align:left;">The <code>&gt;-</code> makes it pick up every line. Weird format, never seen before, but that is what works.</p><h3 class="heading" style="text-align:left;" id="2-snake-case-beats-camel-case-for-s">2. Snake Case Beats Camel Case for Skill Names</h3><p class="paragraph" style="text-align:left;">This is the one that hurts. Joseph claims <code>my_skill_name</code> invokes better than <code>mySkillName</code>. Justin is a tabs-using Python bro who still likes camelCase, but the optimizer kept rewriting names to snake_case across the board and invocation went up. So if you care about invocation more than aesthetics, switch.</p><h3 class="heading" style="text-align:left;" id="3-the-word-claude-is-cursed">3. The Word &quot;Claude&quot; Is Cursed</h3><p class="paragraph" style="text-align:left;">If your skill name has <code>claude</code> in it, change it. A skill called <code>DM_other_claudes</code> was renamed to <code>DM_other_agents</code> and invocation jumped. Either the word is parsed weird or treated as a reserved token, but it&#39;s a real effect.</p><p class="paragraph" style="text-align:left;">This is the kind of low-effort, high-impact polish you can run on every skill you have in 30 minutes. Do it now.</p><h2 class="heading" style="text-align:left;" id="bug-bounty-triage-is-drowning-a-pla">Bug Bounty Triage Is Drowning: A Plate of Options</h2><p class="paragraph" style="text-align:left;">Joseph is on the HackerOne and Bugcrowd hacker advisory boards and the message from both is the same. Programs are buried. Triage SLAs are slipping. Outstanding-bug curves look exponential. There&#39;s not enough developer time to fix everything coming in.</p><p class="paragraph" style="text-align:left;">If you are a program manager or CSM reading this, here is the plate Joseph offered:</p><ul><li><p class="paragraph" style="text-align:left;"><b>Go private</b>. Cuts volume hard. Not ideal, but fast.</p></li><li><p class="paragraph" style="text-align:left;"><b>Require video PoCs</b>. Filters AI slop right at submission. Protects the hunter when the bug becomes &quot;no longer reproducible&quot; mid-triage.</p></li><li><p class="paragraph" style="text-align:left;"><b>Raise signal requirements</b>. Top 30 hunters on H1 are mostly above 6 signal. A cutoff at 5 would cut a lot of low-quality reporters while keeping serious hunters in.</p></li><li><p class="paragraph" style="text-align:left;"><b>Verified / trusted hunters only</b>. Same idea, different mechanic.</p></li><li><p class="paragraph" style="text-align:left;"><b>Reduce low and medium payouts</b>. Portswigger left highs and crits the same and dropped lows / mediums. We&#39;re seeing this everywhere now.</p></li><li><p class="paragraph" style="text-align:left;"><b>Submission fees</b> (HackenProof did this). $1 didn&#39;t help, $5 cut slop by ~80%, $10 stopped it cold. Refunded on valid bugs.</p></li><li><p class="paragraph" style="text-align:left;"><b>Bounty reducer based on signal</b>. NA reports give you a 10% → 20% → 30% bounty cut. Doesn&#39;t stop the bleeding right now, but it scales fairly.</p></li><li><p class="paragraph" style="text-align:left;"><b>Voucher system for new hackers</b> so the entry tier still exists.</p></li></ul><p class="paragraph" style="text-align:left;">Justin&#39;s preferred mix: video required for high / crit, and the HackenProof submission fee. Joseph also liked the signal floor.</p><h2 class="heading" style="text-align:left;" id="deterministic-prompt-injection-thro">Deterministic Prompt Injection Through Client-Side Feedback Loops</h2><p class="paragraph" style="text-align:left;">XSSDoctor and Monke at <a class="link" href="https://blog.starstrike.ai/posts/achieving-deterministic-prompt-injection-through-client-side-feedback-loops/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">Starstrike</a> cracked a clean problem: prompt injection that lands XSS only 20% to 50% of the time.</p><h3 class="heading" style="text-align:left;" id="the-setup">The Setup</h3><p class="paragraph" style="text-align:left;">A <code>q</code> parameter on a chatbot that allows prompt injection. The injection sometimes results in XSS. The goal: make it deterministic.</p><h3 class="heading" style="text-align:left;" id="the-loop">The Loop</h3><p class="paragraph" style="text-align:left;">The attacker page opens a small popup window and keeps it in the foreground next to the victim window. Both windows being &quot;in view&quot; relaxes the cross-origin postMessage rate limit. Then:</p><ol start="1"><li><p class="paragraph" style="text-align:left;">Send the prompt injection.</p></li><li><p class="paragraph" style="text-align:left;">Wait 10 seconds for a callback from the XSS firing.</p></li><li><p class="paragraph" style="text-align:left;">If no callback, re-inject.</p></li><li><p class="paragraph" style="text-align:left;">Repeat.</p></li></ol><p class="paragraph" style="text-align:left;">After 30 seconds of dwell time on the page, the success rate is basically 100%.</p><p class="paragraph" style="text-align:left;">The popup-in-front trick was originally discussed on the Critical Thinking Discord during an Adobe hack-along. Worth stealing for race conditions in general, not just AI bugs.</p><h3 class="heading" style="text-align:left;" id="the-mutual-opener-trick-and-a-clean">The Mutual Opener Trick (and a Cleaner Alternative)</h3><p class="paragraph" style="text-align:left;">XSSDoctor&#39;s challenge: the victim iframe has no window reference to the attacker page (only one-way). His fix: make both windows mutual openers, so each has a <code>window.opener</code> reference to the other.</p><p class="paragraph" style="text-align:left;">Justin&#39;s cleaner alternative: use the <code>event.source</code> property on the postMessage event. When a postMessage hits an iframe, the handler&#39;s event object includes <code>.source</code>, which is a window reference to the sender. Save it the first time you get a message in, and you have your bidirectional channel without the mutual opener acrobatics.</p><div class="codeblock"><pre><code>window.addEventListener(&#39;message&#39;, (event) =&gt; &#123;
  // event.source is a Window reference to the sender
  window.attackerWindowRef = event.source;
&#125;);
</code></pre></div><p class="paragraph" style="text-align:left;">Either works. Knowing both gives you options.</p><h2 class="heading" style="text-align:left;" id="gpt-55-is-real-and-codex-has-goal">GPT-5.5 Is Real, and Codex Has /goal</h2><p class="paragraph" style="text-align:left;">Two LinkedIn posts from <a class="link" href="https://www.linkedin.com/posts/albert-ziegler-6b3b24138_gpt-55-without-access-to-source-code-is-share-7453142795164217344-RuSm?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">Albert Ziegler</a> and the <a class="link" href="https://www.linkedin.com/posts/in-our-benchmark-gpt-5-missed-40-of-vulnerabilities-share-7457413866738851841-7F-0/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">Xbow team</a>.</p><p class="paragraph" style="text-align:left;">The Y-axis on their chart is &quot;vulnerabilities found before first miss&quot;, which is a weird metric, but the takeaway is clear:</p><ul><li><p class="paragraph" style="text-align:left;"><b>White-box</b>: Claude (Opus 4.6 → 4.7) is ahead. Big jump at 4.6.</p></li><li><p class="paragraph" style="text-align:left;"><b>Black-box</b>: GPT-5.5 is roughly <b>double</b> Opus 4.6 on the same eval. Almost 4 → 8 vulnerabilities before first miss.</p></li></ul><p class="paragraph" style="text-align:left;">Joseph bought a Codex sub on the back of this. Setup was symlinks:</p><div class="codeblock"><pre><code>ln -s ~/.claude/CLAUDE.md ~/.codex/AGENT.md
ln -s ~/.claude/skills/* ~/.codex/skills/
</code></pre></div><p class="paragraph" style="text-align:left;">Then he just ran <code>/goal</code> overnight. <b>Three P1s in the first 30 minutes</b>, on a fresh Bugcrowd invite. 14 hours of <code>/goal</code> running used ~15% of his weekly token budget on the $200/month tier.</p><p class="paragraph" style="text-align:left;">The <code>/goal</code> command is what makes Codex sticky. You give it a goal (&quot;find five crits&quot;) and it just keeps going until it hits the success condition. No &quot;ok continue&quot;.</p><p class="paragraph" style="text-align:left;">Build your hacking system to be portable between Claude Code and Codex. Same skills, same rules, same agent files. You&#39;ll want to swap based on the target (white-box → Claude, black-box → Codex for now).</p><p class="paragraph" style="text-align:left;">Justin also confirmed Opus 4.6 alone is no slouch. Same week, fresh Bugcrowd program, JWT forging bypass in 15 minutes.</p><h2 class="heading" style="text-align:left;" id="rce-on-google-cloud-looker-via-a-si">RCE on Google Cloud Looker via a Single Directory Deletion (Ryotak)</h2><p class="paragraph" style="text-align:left;">Saved the best for last. <a class="link" href="https://flatt.tech/research/posts/remote-command-execution-in-google-cloud-with-single-directory-deletion/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">Ryotak</a> at Flatt Security on Google Cloud Looker (Business Intelligence platform).</p><h3 class="heading" style="text-align:left;" id="the-primitive">The Primitive</h3><p class="paragraph" style="text-align:left;">A bug in <code>validate_dir_name</code> in Looker&#39;s Ruby code lets him delete an arbitrary directory inside a repository he owns. Including <code>.git</code>.</p><h3 class="heading" style="text-align:left;" id="the-insanely-underrated-git-quirk">The Insanely Underrated Git Quirk</h3><p class="paragraph" style="text-align:left;">This is the part of the article that you read once and miss. Ryotak just slips it in:</p><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;">Since it is possible to trick git into using forged git configurations if the <code>.git</code> directory is corrupt or deleted...</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;">Wait. What?</p><p class="paragraph" style="text-align:left;">Here is what&#39;s actually happening. When git runs a command in a repo and <b>cannot find a </b><code>.git</code><b> directory</b>, it falls back and assumes the current working directory <b>is</b> the <code>.git</code> directory. So it tries to read <code>./config</code> as if it were <code>.git/config</code>, <code>./hooks/fsmonitor-watchman</code> as if it were <code>.git/hooks/fsmonitor-watchman</code>, and so on.</p><p class="paragraph" style="text-align:left;">Normally you can&#39;t push or upload a <code>.git</code> folder. Hosts strip it. That&#39;s where the security relies. But if you can delete the existing <code>.git</code>, and you control the rest of the working tree (your own repo contents), then git will happily load your malicious <code>config</code> and hooks from the root of your repo.</p><p class="paragraph" style="text-align:left;">The number of code sandboxes and AI coding tools where this is now a variant hunt target is enormous. Anything that:</p><ol start="1"><li><p class="paragraph" style="text-align:left;">Clones a repo on your behalf, and</p></li><li><p class="paragraph" style="text-align:left;">Lets you somehow remove <code>.git</code> or operate on a working tree without a real <code>.git</code>, and</p></li><li><p class="paragraph" style="text-align:left;">Runs a git command afterward.</p></li></ol><h3 class="heading" style="text-align:left;" id="chaining-the-deletion-race-for-rce">Chaining the Deletion + Race for RCE</h3><p class="paragraph" style="text-align:left;">Ryotak&#39;s path delete doesn&#39;t only target <code>.git</code>. It deletes the whole repository directory recursively. So if the recursion just does <code>rm -rf</code> naively, after <code>.git</code> goes, the rest of his repo goes too, and his planted <code>config</code> would also get nuked.</p><p class="paragraph" style="text-align:left;">His move: create a massive nested file tree in his repo (millions of files, very deep). The recursive Ruby <code>FileUtils.rm_rf</code> enters that subtree after deleting <code>.git</code> and gets stuck for a long time deleting files inside it. While that&#39;s happening, Ryotak hits another endpoint that runs <code>git status</code> on the same repo.</p><p class="paragraph" style="text-align:left;">At the moment <code>git status</code> runs:</p><ul><li><p class="paragraph" style="text-align:left;"><code>.git</code> is gone (already deleted).</p></li><li><p class="paragraph" style="text-align:left;">His malicious <code>config</code> is still there at the root (the massive subtree hasn&#39;t been processed yet).</p></li><li><p class="paragraph" style="text-align:left;">Git falls back, reads his <code>config</code>, triggers an <code>fsmonitor</code> hook.</p></li><li><p class="paragraph" style="text-align:left;">Hook executes arbitrary commands on the Google production server.</p></li></ul><p class="paragraph" style="text-align:left;">He even chains into a privilege escalation via the Kubernetes service account at <code>/var/run/secrets/kubernetes.io/serviceaccount</code>. Excessive permissions let him update secrets across other clusters. Full priv esc.</p><p class="paragraph" style="text-align:left;">This is one of those exploits where every layer is a small idea, but stacked, they form a clean RCE chain. Race condition on a <code>rm -rf</code> against ext4. Git&#39;s <code>.git</code> fallback. Misconfigured Kubernetes service account. Chef&#39;s kiss.</p><p class="paragraph" style="text-align:left;"><b>Pro Tip</b>: if you are auditing any AI coding sandbox or code-execution service, the new mental model is:</p><ol start="1"><li><p class="paragraph" style="text-align:left;">Can I make <code>.git</code> go missing on a repo whose working tree I control?</p></li><li><p class="paragraph" style="text-align:left;">Can I trigger a git command afterward?</p></li><li><p class="paragraph" style="text-align:left;">If yes, RCE.</p></li></ol><h2 class="heading" style="text-align:left;" id="resources">Resources</h2><ul><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://github.com/glebarez/padre?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">Padre on GitHub</a>. Padding oracle / CBC attack tool, very useful for crypto bugs.</p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://tessl.io/registry/tessl/skill-optimizer/0.8.0?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">Tessl Skill Optimizer</a>. Rewrites your skill descriptions to massively boost invocation.</p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://slcyber.io/research-center/ghosts-of-encryption-past-salesforce-exacttarget/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">Ghosts of Encryption Past</a>. Salesforce ExactTarget full chain.</p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://labs.watchtowr.com/the-internet-is-falling-down-falling-down-falling-down-cpanel-whm-authentication-bypass-cve-2026-41940/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">cPanel WHM Auth Bypass write-up</a>.</p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://slcyber.io/research-center/high-fidelity-check-for-the-cpanel-authentication-bypass-cve-2026-41940/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">cPanel High Fidelity Check</a>.</p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://blog.starstrike.ai/posts/achieving-deterministic-prompt-injection-through-client-side-feedback-loops/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">Deterministic Prompt Injection via Client-Side Feedback Loops</a>.</p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://flatt.tech/research/posts/remote-command-execution-in-google-cloud-with-single-directory-deletion/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">RCE on Google Cloud via Directory Deletion</a>.</p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.linkedin.com/posts/albert-ziegler-6b3b24138_gpt-55-without-access-to-source-code-is-share-7453142795164217344-RuSm?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">GPT-5.5 benchmark thread (Albert Ziegler)</a>.</p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.linkedin.com/posts/in-our-benchmark-gpt-5-missed-40-of-vulnerabilities-share-7457413866738851841-7F-0/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-174-crypto-oracles-cpanel-auth-bypass-and-the-git-trick-that-pops-google-cloud" target="_blank" rel="noopener noreferrer nofollow">Xbow GPT-5 missed 40% of vulns post</a>.</p></li></ul><hr class="content_break"><p class="paragraph" style="text-align:left;">That&#39;s it for the week, keep hacking!</p></div></div>
  ]]></content:encoded>
</item>

      <item>
  <title>[HackerNotes Ep. 173] Bug Bounty: Is It Dead, or Are We Just Getting Started?</title>
  <description>Bug bounty isn&#39;t dead, but it&#39;s definitely changing a lot. Here&#39;s what&#39;s actually changing.</description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-173-bug-bounty-is-it-dead-or-are-we-just-getting-started</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-173-bug-bounty-is-it-dead-or-are-we-just-getting-started</guid>
  <pubDate>Wed, 06 May 2026 22:01:00 +0000</pubDate>
  <atom:published>2026-05-06T22:01:00Z</atom:published>
    <dc:creator>Aituglo ‎</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr">Hacker TL;DR</h2><ul><li><p class="paragraph" style="text-align:left;">Google&#39;s VRP is paying more for top-tier impact (up to $1.5M for a zero-click full chain on Pixel) but cutting payouts on lows and meds. Programs everywhere are swamped with AI reports.</p></li><li><p class="paragraph" style="text-align:left;">Network effects (model providers shipping security review, cheaper agentic pentests, internal red teams running hackbots) are killing the easy bug pipeline from every side.</p></li><li><p class="paragraph" style="text-align:left;">The copium is real: scope on big targets is too big for any team to fully cover, top hunters running agents at scale find more than ever, and learning has never been faster.</p></li><li><p class="paragraph" style="text-align:left;">Video-first PoCs and submission fees (like HackenProof did) are showing up as filters against AI slop, and opting out of training data is worth taking seriously.</p></li></ul><p class="paragraph" style="text-align:left;">Join the discussion on <a class="link" href="https://discord.gg/criticalthinking?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-173-bug-bounty-is-it-dead-or-are-we-just-getting-started" target="_blank" rel="noopener noreferrer nofollow">Discord</a> and <b>share your thoughts</b> on the future of bug bounty: <a class="link" href="https://forms.ctbb.show/future_of_bug_bounty?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-173-bug-bounty-is-it-dead-or-are-we-just-getting-started" target="_blank" rel="noopener noreferrer nofollow">forms.ctbb.show/future_of_bug_bounty</a>. Justin and Joseph want this feedback to pass directly to the platforms.</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/c7a25360-b133-400b-b6d2-789a252e0293/ThreatLocker-1.png?t=1773171761"/></div><p class="paragraph" style="text-align:center;"><span style="color:rgb(249, 250, 251);font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"><b>Today&#39;s Sponsor:</b></span><span style="color:rgb(249, 250, 251);font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"> Check out ThreatLocker Zero Trust Cloud Access </span><span style="color:rgb(17, 85, 204);"><span style="text-decoration:underline;"><a class="link" href="https://www.threatlocker.com/capabilities/zero-trust-cloud-access?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-173-bug-bounty-is-it-dead-or-are-we-just-getting-started" target="_blank" rel="noopener noreferrer nofollow">https://www.threatlocker.com/capabilities/zero-trust-cloud-access</a></span></span></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="in-the-news">In the News</h2><ul><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://bughunters.google.com/blog/evolving-the-android-chrome-vrps-for-the-ai-era?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-173-bug-bounty-is-it-dead-or-are-we-just-getting-started" target="_blank" rel="noopener noreferrer nofollow">Evolving the Android & Chrome VRPs for the AI Era</a>. Google changes the bounty ranges: more reward for high-impact full chains, less (or none) for lows and meds in certain scope.</p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://x.com/d0rsky/status/2047744193976742120?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-173-bug-bounty-is-it-dead-or-are-we-just-getting-started" target="_blank" rel="noopener noreferrer nofollow">HackenProof submission fee experiment</a>. Their CTO says they got an 80% drop in AI slop and low-quality submissions after adding a $5 reporting fee.</p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://x.com/xvonfers/status/2049419308162228454?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-173-bug-bounty-is-it-dead-or-are-we-just-getting-started" target="_blank" rel="noopener noreferrer nofollow">Internal devs running hackbots before code ships</a>. A nice example of the new pipeline: dev → internal hackbot → automated platform pentest → bug bounty.</p></li></ul><h2 class="heading" style="text-align:left;" id="the-bear-case-network-forces-squeez">The Bear Case: Network Forces Squeezing the Funnel</h2><h3 class="heading" style="text-align:left;" id="1-programs-cant-keep-up">1. Programs Can&#39;t Keep Up</h3><p class="paragraph" style="text-align:left;">Triage and payment delays are basically the new normal. Some programs are closing entirely. Others, like Google&#39;s Android and Chrome VRPs, are dropping payouts on low and medium severity findings on certain scope. Google&#39;s blog says this is to focus more on impact, and to be fair, the high-impact ranges actually went <b>up</b> (a zero-click full chain with persistence on Pixel Titan M2 now pays up to <b>$1.5M</b>). But the bottom of the funnel is being cut hard, and that&#39;s where a lot of hunters built steady income.</p><h3 class="heading" style="text-align:left;" id="2-model-providers-are-coming-downst">2. Model Providers Are Coming Downstream</h3><p class="paragraph" style="text-align:left;">Anthropic shipping a Security Review feature is the warning sign. The idea is pretty simple and not great for us: if your business is a wrapper around someone else&#39;s model, the model provider eventually eats you. Security is something providers care about now, both because they need to defend their own infra, and because the security tooling market still matters even if it&#39;s not the biggest one out there.</p><p class="paragraph" style="text-align:left;">Claude 4.6 → 4.7 already pushed Cyberbench scores from ~30% to ~60%. No reason to think it&#39;s gonna stop.</p><h3 class="heading" style="text-align:left;" id="3-agentic-pentest-startups-are-raci">3. Agentic Pentest Startups Are Racing to the Bottom</h3><p class="paragraph" style="text-align:left;">It&#39;s never been easier to wrap a model in a workflow, call it an &quot;agentic pentest,&quot; and sell it. Snyk, Wiz, and the bug bounty platforms themselves are all building in this direction. Prices are crashing: full automated pentests at $100 to $500 are showing up. Quality is all over the place, but like Joseph said, even a bad agentic pentest is probably better than the old-school compliance-checkbox pentest a tired contractor ran for two weeks without keeping up.</p><h3 class="heading" style="text-align:left;" id="4-the-new-pre-disclosure-pipeline">4. The New Pre-Disclosure Pipeline</h3><p class="paragraph" style="text-align:left;">Here&#39;s how the funnel looks from a target company&#39;s side in 2026:</p><ol start="1"><li><p class="paragraph" style="text-align:left;"><b>Internal devs</b> push code generated 10 to 50× faster with AI coding agents.</p></li><li><p class="paragraph" style="text-align:left;"><b>Internal AppSec / red team</b> runs hackbots (white-box on the source, black-box on staging) before it ships.</p></li><li><p class="paragraph" style="text-align:left;"><b>Third-party agentic pentest</b> (platform-hosted or vendor) scans the asset before it enters scope.</p></li><li><p class="paragraph" style="text-align:left;"><b>Bug bounty scope</b> gets what&#39;s left after all that.</p></li><li><p class="paragraph" style="text-align:left;"><b>Top hunters</b> run their own hackbots 24/7 across that scope.</p></li></ol><p class="paragraph" style="text-align:left;">Every layer above the bug bounty line is getting better fast. By the time a finding reaches the public scope, the easy stuff is mostly already gone.</p><h3 class="heading" style="text-align:left;" id="5-top-hunters-scaling-their-own-edg">5. Top Hunters Scaling Their Own Edge</h3><p class="paragraph" style="text-align:left;">The same agents that compress the bottom of the funnel also boost the top. A skilled hunter can apply expertise across way more programs and way more scope at the same time. Net effect: the pie isn&#39;t growing, but the slices going to the top hunters are getting bigger.</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/dc7a3669-6d67-48a9-aa2b-5dbdd0d8e41e/_bannerHackerNotesShort.png?t=1769044664"/><div class="image__source"><a class="image__source_link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-173-bug-bounty-is-it-dead-or-are-we-just-getting-started" rel="noopener" target="_blank"><span class="image__source_text"><p>Check out the CTBB Discord!</p></span></a></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-173-bug-bounty-is-it-dead-or-are-we-just-getting-started" target="_blank" rel="noopener noreferrer nofollow">You can also find some hacker swag here.</a></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="why-its-not-over">Why It&#39;s Not Over</h2><h3 class="heading" style="text-align:left;" id="1-easier-than-ever-to-learn">1. Easier Than Ever to Learn</h3><p class="paragraph" style="text-align:left;">Probably the most underrated good thing. Spinning up labs, walking through bug classes, having the model act as a sparring partner. That&#39;s a whole new level for learning on your own. Shout out to <b>Daniel Miessler&#39;s &quot;don&#39;t take your robots to the gym&quot;</b>: use AI to actually understand stuff, not to skip the work.</p><h3 class="heading" style="text-align:left;" id="2-scope-is-genuinely-too-big">2. Scope Is Genuinely Too Big</h3><p class="paragraph" style="text-align:left;">Even in the worst case where every layer of the new pipeline runs, the attack surface on a target like Yahoo or Google is too big for full coverage. There will be vulnerabilities. AI-fast dev cycles + huge attack surface = more bugs being shipped, not fewer.</p><h3 class="heading" style="text-align:left;" id="3-we-ship-more-than-ever">3. We Ship More Than Ever</h3><p class="paragraph" style="text-align:left;">A hunter today, with a good agent loop, puts out more research, more tooling, and covers more attack surface than was possible a year ago. Same idea on every side.</p><h3 class="heading" style="text-align:left;" id="4-you-can-finish-what-you-started">4. You Can Finish What You Started</h3><p class="paragraph" style="text-align:left;">The classic bug bounty pattern: get a private invite, hunt for 6 or 7 hours, find 3 bugs, get distracted by the next invite, never come back. AI changes that. Old leads that were too time-consuming to finish can now be closed out. That hidden scope niche that used to give you 3 bugs may now give you 10, and some of them deeper than what we could find before.</p><h2 class="heading" style="text-align:left;" id="the-false-positive-wall">The False Positive Wall</h2><p class="paragraph" style="text-align:left;">The catch you can&#39;t avoid: <b>AI finds more, but most of it is not real</b>. Joel made the point a couple of months back. What percentage of what comes out of your hackbot is actually a valid, reportable, impactful finding?</p><p class="paragraph" style="text-align:left;">The implication is rough for new hunters:</p><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;">It&#39;s easier than ever to find <i>something</i>. But if you don&#39;t have the skill to tell a real bug from a gadget, from intended behavior, from public-by-design data, you&#39;ll either spam programs and get banned, or get demotivated when nothing pays.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;">The skill that matters now is not just finding, it&#39;s <b>validating</b>. Plus context management, skill file writing, and learning to turn massive agent output into something a triager will actually accept.</p><h2 class="heading" style="text-align:left;" id="what-programs-and-platforms-should-">What Programs and Platforms Should Do</h2><h3 class="heading" style="text-align:left;" id="submission-fees-like-hacken-proof">Submission Fees (like HackenProof)</h3><p class="paragraph" style="text-align:left;">The HackenProof CTO did this experiment in public. $1 didn&#39;t change anything. <b>$5 cut AI slop and low-quality submissions by ~80%.</b> $10 was the sweet spot. The Web3 chain integration makes this pretty easy for them, you just attach a fee gas-style on submission. Harder for traditional platforms to do, but the idea works.</p><p class="paragraph" style="text-align:left;">A few caveats raised:</p><ul><li><p class="paragraph" style="text-align:left;">You&#39;d need regional pricing (a flat $5 USD is too much for hunters in lower-income regions).</p></li><li><p class="paragraph" style="text-align:left;">Banned or unwilling-to-pay hunters still have bugs they found. Where do those go?</p></li><li><p class="paragraph" style="text-align:left;">Token or rep-based variants (earn submission credits via valid reports) were thrown around as alternatives.</p></li></ul><h3 class="heading" style="text-align:left;" id="video-first-video-required-submissi">Video-First / Video-Required Submissions</h3><p class="paragraph" style="text-align:left;">This is the idea that hit the hardest. Some live event programs already require a screenshot or short video as part of the submission. Lots of upside:</p><ul><li><p class="paragraph" style="text-align:left;"><b>Filters AI slop right at the entry</b>. You can fake it, but it&#39;s expensive at scale.</p></li><li><p class="paragraph" style="text-align:left;"><b>Protects the hunter</b> when a bug becomes &quot;no longer reproducible&quot; mid-triage but was actually real at submission time.</p></li><li><p class="paragraph" style="text-align:left;"><b>Speeds up triage</b>. The triager doesn&#39;t have to reverse-engineer the report from scratch.</p></li></ul><p class="paragraph" style="text-align:left;">Even if not mandatory, programs could <b>fast-track</b> submissions with video and pay them out under a &quot;paid once validated&quot; rule, even if the state changes later.</p><h2 class="heading" style="text-align:left;" id="the-training-data-debate">The Training-Data Debate</h2><p class="paragraph" style="text-align:left;">Should you be feeding your bug bounty reports into model providers (via tools like H1 Brain) to build your personal skill files?</p><h3 class="heading" style="text-align:left;" id="the-cautious-side">The Cautious Side</h3><ul><li><p class="paragraph" style="text-align:left;">Consumer Claude plans (Free, Pro, Max), including <b>Claude Code on personal accounts</b>, have been on an <i>opt-out</i> training model since the September 2025 policy change. The default is &quot;Improve Claude for everyone&quot; = on. If you&#39;re opted in, data is kept up to <b>5 years</b>.</p></li><li><p class="paragraph" style="text-align:left;">Even with API and Team/Enterprise being non-training by default, clicking thumbs up/down feedback makes the whole conversation usable for training and 5-year retention.</p></li><li><p class="paragraph" style="text-align:left;">Self-host where you can. Use orgs / Enterprise tiers. Disable training across every machine where Claude Code is installed (it&#39;s not just account-level, you have to verify per environment).</p></li><li><p class="paragraph" style="text-align:left;">Refs: <a class="link" href="https://privacy.claude.com/en/articles/10023580-is-my-data-used-for-model-training?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-173-bug-bounty-is-it-dead-or-are-we-just-getting-started" target="_blank" rel="noopener noreferrer nofollow">Anthropic&#39;s data privacy controls</a> and <a class="link" href="https://claude.ai/settings/data-privacy-controls?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-173-bug-bounty-is-it-dead-or-are-we-just-getting-started" target="_blank" rel="noopener noreferrer nofollow">claude.ai/settings/data-privacy-controls</a>.</p></li><li><p class="paragraph" style="text-align:left;">Also: API keys pasted into chats already get flagged for rotation. Same caution applies to your reports.</p></li></ul><h3 class="heading" style="text-align:left;" id="the-pragmatic-side">The Pragmatic Side</h3><ul><li><p class="paragraph" style="text-align:left;">Even if every top hunter opted out, the providers have enough money to buy or build top-tier cyber evals in-house. The OffensiveAICon talks last year had Meta, Google, Anthropic, OpenAI staff asking researchers publicly to build more cyber datasets. They got them.</p></li><li><p class="paragraph" style="text-align:left;">Cyberbench-style evals are public. Providers can improve without a single user session.</p></li><li><p class="paragraph" style="text-align:left;">Your individual contribution to the training pool is basically zero in terms of impact. The reward of a faster loop and more bugs found is real and immediate.</p></li><li><p class="paragraph" style="text-align:left;">This is the privacy / voting analogy: your single data point doesn&#39;t move the big picture, but acting on principle is a fair personal choice.</p></li></ul><p class="paragraph" style="text-align:left;">Whichever side you pick:</p><ol start="1"><li><p class="paragraph" style="text-align:left;"><b>Disable &quot;Improve Claude for everyone&quot;</b> on every personal Claude account and every machine running Claude Code.</p></li><li><p class="paragraph" style="text-align:left;">Use Team / Enterprise / API for anything sensitive (default no-training).</p></li><li><p class="paragraph" style="text-align:left;">For high-security work, ask for a <b>Zero Data Retention (ZDR)</b> agreement on the API.</p></li><li><p class="paragraph" style="text-align:left;">Don&#39;t paste valid PoC payloads, customer data, or live exploit chains into consumer chats no matter what your opt-out status is. Admin access exists at every company.</p></li></ol><h2 class="heading" style="text-align:left;" id="methodology-how-to-survive-the-tran">Methodology: How to Survive the Transition</h2><h3 class="heading" style="text-align:left;" id="phase-1-defensive-setup">Phase 1: Defensive Setup</h3><ul><li><p class="paragraph" style="text-align:left;">Check training-data settings on every account and every Claude Code install.</p></li><li><p class="paragraph" style="text-align:left;">Move sensitive workflows to API, Team, or Enterprise tiers.</p></li><li><p class="paragraph" style="text-align:left;">Where you can, run open-source models locally for the most sensitive stuff.</p></li></ul><h3 class="heading" style="text-align:left;" id="phase-2-dayto-day-adjustments">Phase 2: Day-to-Day Adjustments</h3><ul><li><p class="paragraph" style="text-align:left;">Default to <b>video PoCs</b> on anything high or critical, so you&#39;re protected if the bug becomes &quot;no longer reproducible&quot; later.</p></li><li><p class="paragraph" style="text-align:left;">Track triage and payment timelines per program. Drop programs that always stall.</p></li><li><p class="paragraph" style="text-align:left;">Prefer programs with shorter dupe windows (live events especially), because agents are causing massive dupe splits on medium-hanging fruit.</p></li></ul><h3 class="heading" style="text-align:left;" id="phase-3-get-sharper">Phase 3: Get Sharper</h3><ul><li><p class="paragraph" style="text-align:left;">Validation is the new core skill. Build pipelines that make false-positive triage cheap.</p></li><li><p class="paragraph" style="text-align:left;">Writing skill files, managing context, and handling agent output cleanly are now real hunting skills, not optional extras.</p></li><li><p class="paragraph" style="text-align:left;">Use AI as a sparring partner for learning new bug classes, not just an answer machine.</p></li></ul><h2 class="heading" style="text-align:left;" id="closing-thoughts">Closing Thoughts</h2><p class="paragraph" style="text-align:left;">This year still has some room, but bug bounty as a hobby is closing fast. To really thrive going forward, it&#39;s becoming a lifestyle. Eat, breathe, improve your tooling. The casual hunter pipeline is the most affected.</p><p class="paragraph" style="text-align:left;">At the same time, humans have never had more power to build, learn, and ship. The ceiling for what one skilled hunter can do has gone up, not down. The shape of the work is changing, and that&#39;s not the same thing as the work disappearing.</p><p class="paragraph" style="text-align:left;">If you have ideas for how platforms and programs should adapt, this is the moment to put them on record: <b><a class="link" href="https://forms.ctbb.show/future_of_bug_bounty?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-173-bug-bounty-is-it-dead-or-are-we-just-getting-started" target="_blank" rel="noopener noreferrer nofollow">forms.ctbb.show/future_of_bug_bounty</a></b>.</p><h2 class="heading" style="text-align:left;" id="resources">Resources</h2><ul><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://bughunters.google.com/blog/evolving-the-android-chrome-vrps-for-the-ai-era?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-173-bug-bounty-is-it-dead-or-are-we-just-getting-started" target="_blank" rel="noopener noreferrer nofollow">Google VRP Evolution Blog</a></b>. Full breakdown of the Android & Chrome VRP changes.</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://privacy.claude.com/en/articles/10023580-is-my-data-used-for-model-training?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-173-bug-bounty-is-it-dead-or-are-we-just-getting-started" target="_blank" rel="noopener noreferrer nofollow">Anthropic Privacy Center</a></b>. Official docs on what data is used for training.</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://claude.ai/settings/data-privacy-controls?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-173-bug-bounty-is-it-dead-or-are-we-just-getting-started" target="_blank" rel="noopener noreferrer nofollow">Claude Data Privacy Controls</a></b>. Where to opt out of training data.</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://forms.ctbb.show/future_of_bug_bounty?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-173-bug-bounty-is-it-dead-or-are-we-just-getting-started" target="_blank" rel="noopener noreferrer nofollow">Future of Bug Bounty Feedback Form</a></b>. Send your thoughts to the platforms.</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://aituglo.com/state-of-bug-bounty-in-2026/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-173-bug-bounty-is-it-dead-or-are-we-just-getting-started" target="_blank" rel="noopener noreferrer nofollow">My Thoughts about Bug Bounty in 2026</a></b>.</p></li></ul><hr class="content_break"><p class="paragraph" style="text-align:left;">That&#39;s it for the week, keep hacking!</p></div></div>
  ]]></content:encoded>
</item>

      <item>
  <title>[HackerNotes Ep. 172] Critical Guide to Code Review</title>
  <description>In this week&#39;s episode we&#39;re talking about code review! Here you&#39;ll find our full guide to code review with our favourite techniques and methodology, from mapping auth on every route before reading a single handler, to tracing taint flows, sniffing for anomalies and chaining low-severity bugs into crits.</description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-172-critical-guide-to-code-review</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-172-critical-guide-to-code-review</guid>
  <pubDate>Thu, 30 Apr 2026 10:03:00 +0000</pubDate>
  <atom:published>2026-04-30T10:03:00Z</atom:published>
    <dc:creator>Yujilik ​</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr"><b>Hacker TL;DR</b></h2><ol start="1"><li><p class="paragraph" style="text-align:left;"><b>Map auth requirement on every route before reading a single handler: </b></p><p class="paragraph" style="text-align:left;">The Grafana CVE-2020-13379 chain (25+ crits) came from one anomaly: every route had reqSignedIn except /avatar/:hash. Annotate routes with their auth middleware in a table, sort by exposure, and the unauth rows are your targets, pre-auth bugs scale across programs and pay better.</p></li><li><p class="paragraph" style="text-align:left;"><b>Framework wrappers are the real sinks, don’t grep only for eval()</b></p><p class="paragraph" style="text-align:left;">total.js&#39;s U.set() reaches new Function() internally with a bypassable blacklist. Ruby object.send(params[:method]) invokes anything on the object including Kernel#system. Spring controllers returning user input as a view name get it interpreted as SpEL. The dangerous behaviour is one or two layers inside framework code, never visible at the call site, sink lists must be built per framework, not per language primitive.</p></li><li><p class="paragraph" style="text-align:left;"><b>Parser differentials: </b></p><p class="paragraph" style="text-align:left;">when the security layer and backend interpret input differently, the security layer can be broken. When a WAF or auth gateway parses one way and the backend parses another, the disagreement between them is the bypass you need to chase.</p></li><li><p class="paragraph" style="text-align:left;"><b>Custom sanitizers fail in five predictable ways, run the checklist on every one</b></p><ul><li><p class="paragraph" style="text-align:left;">Case-insensitive? </p></li><li><p class="paragraph" style="text-align:left;">Global (/g or replaceAll)?</p></li><li><p class="paragraph" style="text-align:left;">Recursive (one pass leaves ....// → ../)?</p></li><li><p class="paragraph" style="text-align:left;">Complete (a SQLi filter without UNION is still exploitable)?</p></li><li><p class="paragraph" style="text-align:left;">Consistent across all routes?</p></li></ul><p class="paragraph" style="text-align:left;">Dev-written security functions are almost always bypassable on at least one of these, and this checklist gets you to the bug.</p></li><li><p class="paragraph" style="text-align:left;"><b>&quot;Sniff for blood&quot;: anomalies are bugs in disguise, chase them immediately </b></p><p class="paragraph" style="text-align:left;">The one endpoint missing auth when every other has it, or the URL-fetcher that follows redirects, or the custom sanitizer that diverges from the standard library... When something looks off, the developer lost control of something. Open a scratch file, write down &quot;what&#39;s the worst case if I fully control this?&quot; and work backward.</p></li></ol><hr class="content_break"><p class="paragraph" style="text-align:left;">Sup, hackers! It&#39;s been a while since my last HackerNotes post, and this one&#39;s a bit different too. </p><p class="paragraph" style="text-align:left;">Justin asked me to help with the script for this episode because we wanted to make the best episode we could on code review for you. So what you&#39;re about to read is the same doc Rhyno had open while recording.</p><p class="paragraph" style="text-align:left;">We hope you enjoy the read! =)</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/c7a25360-b133-400b-b6d2-789a252e0293/ThreatLocker-1.png?t=1773171761"/></div><p class="paragraph" style="text-align:center;"><span style="color:rgb(249, 250, 251);font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"><b>Today&#39;s Sponsor:</b></span><span style="color:rgb(249, 250, 251);font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"> Check out ThreatLocker Ringfencing </span><span style="font-family:inherit;font-size:16px;"><a class="link" href="https://www.criticalthinkingpodcast.io/tl-rf?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow">https://www.criticalthinkingpodcast.io/tl-rf</a></span></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="types-of-code-review"><b>Types of Code Review</b></h2><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> Three modes. Use Baseline for new targets, Diff-Based after patches drop, Taint Analysis as your mental framework for tracing data.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><h3 class="heading" style="text-align:left;" id="baseline-review"><b>Baseline Review</b></h3><p class="paragraph" style="text-align:left;">Full audit of an entire codebase from scratch.</p><p class="paragraph" style="text-align:left;">You build a map: architecture, routes, auth model, dependencies, data flows.</p><p class="paragraph" style="text-align:left;">Use this when:</p><ul><li><p class="paragraph" style="text-align:left;">First time on a target</p></li><li><p class="paragraph" style="text-align:left;">You just got source code via Docker, decompilation, trial, etc.</p></li><li><p class="paragraph" style="text-align:left;">Prepping for a live hacking event</p></li></ul><p class="paragraph" style="text-align:left;">Goal of a baseline session: <b>notes and a map</b>, not necessarily findings. The map makes everything else faster.</p><h3 class="heading" style="text-align:left;" id="diff-based-review"><b>Diff-Based Review</b></h3><p class="paragraph" style="text-align:left;">Look only at what changed: a patch, a new feature, a version bump. Ask: does this change open new attack surface? Does it weaken an existing control?</p><p class="paragraph" style="text-align:left;">Use this when:</p><ul><li><p class="paragraph" style="text-align:left;">A CVE patch drops for software you know: the fix often reveals the bug pattern and where to look for variants</p></li><li><p class="paragraph" style="text-align:left;">A program adds new scope or announces a new feature</p></li><li><p class="paragraph" style="text-align:left;">Monitoring a target’s public commits for regressions</p></li></ul><p class="paragraph" style="text-align:left;">Always expand the diff: a few extra lines of context can change whether a change is safe or not. And patches do not always fix the entire codebase, look for the same pattern elsewhere.</p><div class="image"><img alt="" class="image__image" style="" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/c2630cf6-8e34-4e38-b45e-c88392dc4b28/Baseline_Review_Graphic.png?t=1777366218"/></div><div class="image"><img alt="" class="image__image" style="" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/3d1747b0-9a31-4ab5-a390-406f3bc75e1a/Diff-Based_Graphic.png?t=1777366233"/></div><h3 class="heading" style="text-align:left;" id="taint-analysis"><b>Taint Analysis</b></h3><p class="paragraph" style="text-align:left;">A formal model for thinking about how data flows through code. Every piece of user-supplied data is “tainted.” Track it through four components:</p><p class="paragraph" style="text-align:left;"><b>Sources:</b> where tainted data enters:</p><ul><li><p class="paragraph" style="text-align:left;"><code>request.getParameter(&quot;id&quot;)</code> (Java)</p></li><li><p class="paragraph" style="text-align:left;"><code>$_GET[&#39;input&#39;]</code> (PHP)</p></li><li><p class="paragraph" style="text-align:left;"><code>req.body.email</code> (Node.js)</p></li><li><p class="paragraph" style="text-align:left;">Headers, cookies, file uploads, WebSocket payloads</p></li></ul><p class="paragraph" style="text-align:left;"><b>Propagators:</b> pass taint along without cleaning it:</p><div class="image"><img alt="" class="image__image" style="" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/f477b36a-d7a4-46bd-94a4-086d0490d5f8/Taint_Analysis_Graphic.png?t=1777366289"/></div><p class="paragraph" style="text-align:left;">Assignments, function calls that return the input, array operations = propagators.</p><p class="paragraph" style="text-align:left;"><b>Sanitizers:</b> functions that clean tainted data. The key word is <i>effective</i>. A sanitizer that can be bypassed doesn’t count.</p><p class="paragraph" style="text-align:left;"><b>Sinks:</b> where tainted data causes harm. A vuln is confirmed when taint flows from source → sink with no effective sanitizer in between:</p><p class="paragraph" style="text-align:left;">Instead of “is there a sanitizer?” the real question is “is the sanitizer effective? / is it in the right place?”</p><div class="image"><img alt="" class="image__image" style="" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/833cb2bf-e37d-49d4-840e-874c344f1374/Taint_Flow_-_Filled_Box.png?t=1777366324"/></div><h2 class="heading" style="text-align:left;" id="part-1-foundations"><b>Part 1: Foundations</b></h2><h3 class="heading" style="text-align:left;" id="11-sources-and-sinks"><b>1.1 Sources and Sinks</b></h3><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> Source = where attacker data enters. Sink = dangerous function it could reach. Vulnerability = source connects to sink without a real fix in between.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><div style="padding:14px 15px 14px;"><table class="bh__table" width="100%" style="border-collapse:collapse;"><tr class="bh__table_row"><th class="bh__table_header" width="50%"><p class="paragraph" style="text-align:left;">Vulnerability</p></th><th class="bh__table_header" width="50%"><p class="paragraph" style="text-align:left;">Example Sinks</p></th></tr><tr class="bh__table_row"><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">SQLi</p></td><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;"><code>execute()</code>, <code>query()</code>, raw SQL string concat</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">XSS</p></td><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;"><code>innerHTML</code>, <code>document.write()</code>, raw template output</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">RCE</p></td><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;"><code>eval()</code>, <code>exec()</code>, <code>system()</code>, <code>Runtime.exec()</code></p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">Path Traversal</p></td><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;"><code>open()</code>, <code>readFile()</code>, <code>fopen()</code>, <code>new File()</code></p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">XXE</p></td><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;"><code>DocumentBuilder.parse()</code>, <code>SAXParser.parse()</code> without restrictions</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">SSRF</p></td><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;"><code>fetch()</code>, <code>curl_exec()</code>, <code>http.get()</code>, any URL-fetching library</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">Deserialization</p></td><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;"><code>unserialize()</code> (PHP), <code>ObjectInputStream.readObject()</code> (Java), <code>pickle.loads()</code> (Python)</p></td></tr></table></div><p class="paragraph" style="text-align:left;"><b>First pass habit:</b> Make two lists, list all places user data enters and all dangerous functions you can find. Then ask: can these connect?</p><h3 class="heading" style="text-align:left;" id="12-first-pass-through-a-codebase"><b>1.2 First Pass Through a Codebase</b></h3><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> Don&#39;t dive into rabbit holes. Understand the structure first. Map before you hunt.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><ol start="1"><li><p class="paragraph" style="text-align:left;"><b>Understand the structure:</b> What framework? Where do routes, controllers, and models live?</p></li><li><p class="paragraph" style="text-align:left;"><b>Build your sinks list:</b> Every language has dangerous functions. PHP: <code>eval</code>, <code>system</code>, <code>exec</code>, <code>preg_replace /e</code>, <code>unserialize</code>. Java: <code>Runtime.exec</code>, <code>ProcessBuilder</code>, <code>DocumentBuilder.parse</code>.</p><p class="paragraph" style="text-align:left;"><b>Watch out for framework wrappers:</b> In modern apps you often won&#39;t see raw dangerous functions but rather a framework method that calls them internally. If you only grep for <code>eval()</code> or <code>exec()</code>, you&#39;ll miss most of what&#39;s actually exploitable. Understand what the framework&#39;s own functions do under the hood.</p><p class="paragraph" style="text-align:left;">Some examples:</p><ul><li><p class="paragraph" style="text-align:left;"><b>total.js </b><code>U.set()</code><b> / </b><code>U.get()</code><b>:</b> looks like a generic object utility but internally uses <code>new Function()</code> to build property accessors, making it a code injection sink. The framework added a regex blacklist to block dangerous input but the blacklist tests the raw string while <code>new Function()</code> interprets hex escapes at runtime. So <code>(</code> gets blocked, but <code>\x28</code> passes right through and becomes <code>(</code> inside the function. Tagged template literals handle the rest letting you <a class="link" href="https://lab.ctbb.show/research/totaljs-rce-gadgets?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">invoke functions without parentheses.</a></p></li><li><p class="paragraph" style="text-align:left;"><b>Ruby </b><code>object.send(params[:method])</code><b>:</b> <code>send</code> is a standard Ruby method for calling methods by name. If the method name comes from user input, the attacker can invoke anything on the object including <code>eval</code>, <code>exit</code>, and <code>system</code> via the Kernel module. <a class="link" href="https://docs.ruby-lang.org/en/2.0.0/security_rdoc.html?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">Documented in Ruby&#39;s own security guide</a> as explicitly dangerous.</p></li><li><p class="paragraph" style="text-align:left;"><b>Spring Thymeleaf view name injection:</b> a Spring controller that returns user input as a view name string will have it interpreted as a Thymeleaf/SpEL expression by the framework. The &quot;sink&quot; is the return value of the controller method, not any obviously dangerous function call. Documented by <a class="link" href="https://www.veracode.com/blog/secure-development/spring-view-manipulation-vulnerability/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">Veracode</a>.</p></li></ul><p class="paragraph" style="text-align:left;">The pattern across all three: the dangerous behavior is one or two layers inside framework code, not visible at the call site. Build your sink list for the specific framework, not just the language primitives.</p></li><li><p class="paragraph" style="text-align:left;"><b>Map routes first:</b> Find the routing config before reading any handler. <code>web.xml</code> (Java), <code>routes.rb</code> (Rails), <code>api.go</code> (Go), Express route files, <code>urls.py</code> (Django), etc.</p></li><li><p class="paragraph" style="text-align:left;"><b>Take good notes first:</b> When you see a custom <code>sanitize()</code> call, note it and keep moving. Don&#39;t fall into early rabbit holes.</p></li><li><p class="paragraph" style="text-align:left;"><b>Watch for developer mistakes:</b> Hardcoded credentials, second-order vulnerabilities (input stored and later executed), custom crypto.</p></li></ol><h3 class="heading" style="text-align:left;" id="13-hardcoded-credentials-and-secret"><b>1.3 Hardcoded Credentials and Secrets</b></h3><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> Look for secrets baked into source files. Common and high impact.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;">Database passwords, AWS keys, JWT secrets, internal API tokens. They often grant immediate access to something, with no further exploitation needed.</p><p class="paragraph" style="text-align:left;">Scan for: <code>password =</code>, <code>secret =</code>, <code>api_key =</code>, <code>token =</code>, <code>aws_access_key</code>, <code>--BEGIN</code>.</p><p class="paragraph" style="text-align:left;">Also check: <code>.env</code> files committed to version control, Docker environment variables in build history, and all Docker image layers, secrets “deleted” from layers still exist in earlier ones.</p><h3 class="heading" style="text-align:left;" id="14-custom-security-functions"><b>1.4 Custom Security Functions</b></h3><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> Dev-written sanitizers are almost always bypassable. These are prime targets.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;">When you see a homemade security function, run through this:</p><ul><li><p class="paragraph" style="text-align:left;"><b>Case-insensitive?</b> A blocklist for <code>SELECT</code> misses <code>select</code>. Look for <code>/i</code> flag or <code>.toLowerCase()</code>.</p></li><li><p class="paragraph" style="text-align:left;"><b>Global replacement?</b> JS <code>.replace(&quot;x&quot;, &quot;&quot;)</code> only removes the <i>first</i> match. Needs <code>/g</code> flag or <code>.replaceAll()</code>.</p></li><li><p class="paragraph" style="text-align:left;"><b>Recursive?</b> Removing <code>../</code> once leaves <code>....//</code> → <code>../</code></p></li><li><p class="paragraph" style="text-align:left;"><b>Complete?</b> A SQLi filter missing <code>UNION</code> is still exploitable. Check what’s absent.</p></li><li><p class="paragraph" style="text-align:left;"><b>Applied consistently?</b> A sanitizer only called on some routes creates gaps.</p></li></ul><h3 class="heading" style="text-align:left;" id="15-source-first-vs-sink-first"><b>1.5 Source-First vs. Sink-First</b></h3><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> Source-first = start at inputs, trace forward. Sink-first = start at dangerous functions, trace backward. Use both.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;"><b>Source-first:</b> Start at entry points, route handlers, API endpoints, form inputs. Then follow the data through the app. Slower, but you build a real picture of how the app works. Best for finding business logic bugs and anything that requires understanding context.</p><p class="paragraph" style="text-align:left;"><b>Sink-first:</b> Search for dangerous functions first (<code>eval()</code>, <code>Runtime.exec()</code>, raw SQL concat, etc.) and trace backward to see if user input can reach them. Fast way to check whether obvious high-severity classes like RCE, SQLi, or XXE exist at all. The downside: finding a sink doesn&#39;t mean you can reach it. It might only be called after auth checks pass (so you&#39;d need a valid account), or it might be dead code that never runs in production. Both mean time wasted tracing something that goes nowhere.</p><p class="paragraph" style="text-align:left;"><b>Recommendation:</b> Source-first as your default, you&#39;ll understand the app and catch more. Run a sink-first sweep at the end to make sure you didn&#39;t miss anything obvious.</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/78b27181-099e-4c7b-9a0b-b39e47f880ea/_bannerHackerNotesShort.png?t=1777429573"/><div class="image__source"><span class="image__source_text"><p><a class="link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow">Check out the CTBB Discord!</a></p></span></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><span style="text-decoration:underline;"><i><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=referral&utm_campaign=hackernotes-ep-171-protobuf-xss-cookie-case-sensitivity-and-clickjacking-tricks" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(44, 129, 229)">You can also find some hacker swag here.</a></i></span></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="part-2-getting-source-code"><b>Part 2: Getting Source Code</b></h2><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> No source code, no review. There are many ways to get it even for paid, closed-source enterprise software.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><h3 class="heading" style="text-align:left;" id="21-java-script-source-maps"><b>2.1 JavaScript Source Maps</b></h3><p class="paragraph" style="text-align:left;">Source maps map minified JS back to the original source. A JS file declares one at the bottom:</p><div class="codeblock"><pre><code>//# sourceMappingURL=app.js.map
</code></pre></div><p class="paragraph" style="text-align:left;">Devs often strip that line but forget to delete the actual <code>.map</code> file. Test by appending <code>.map</code> to any JS URL:</p><div class="codeblock"><pre><code>https://target.com/static/js/app.js.map
</code></pre></div><p class="paragraph" style="text-align:left;">Browsers also auto-load them in the Sources tab of DevTools, check there first.</p><p class="paragraph" style="text-align:left;">Extract the original source tree:</p><div class="codeblock"><pre><code>npx restore-source-tree app.js.map
</code></pre></div><p class="paragraph" style="text-align:left;">This recreates the full directory of <code>.ts</code>, <code>.jsx</code>, or <code>.js</code> files, same as having the private repo.</p><p class="paragraph" style="text-align:left;"><b>What to look for:</b> Hidden API endpoints, commented-out debug routes, internal service URLs, auth logic.</p><h3 class="heading" style="text-align:left;" id="22-exposed-git-directories"><b>2.2 Exposed </b><code>.git</code><b> Directories</b></h3><p class="paragraph" style="text-align:left;">If a server publicly serves its <code>.git</code> folder, the entire repo history is downloadable. Test:</p><div class="codeblock"><pre><code>https://target.com/.git/config
https://target.com/.git/HEAD
</code></pre></div><p class="paragraph" style="text-align:left;">If those return content (not 404), pull everything with <code>git-dumper</code>:</p><div class="codeblock"><pre><code>git-dumper https://target.com/.git/ ./output-dir
cd output-dir &amp;&amp; git checkout .
</code></pre></div><p class="paragraph" style="text-align:left;">If the server blocks some paths, try GitTools Extractor:</p><div class="codeblock"><pre><code>python3 extractor.py https://target.com/.git/ ./output-dir
</code></pre></div><p class="paragraph" style="text-align:left;"><b>Bonus:</b> Full history includes deleted files. Old commits often have secrets. Run <code>trufflehog</code> or <code>gitleaks</code> to scan all commits automatically.</p><h3 class="heading" style="text-align:left;" id="23-docker-hub-and-container-registr"><b>2.3 Docker Hub and Container Registries</b></h3><p class="paragraph" style="text-align:left;">Many enterprise vendors push Docker images publicly and sometimes even licensed software. Search <code>hub.docker.com</code>:</p><div class="codeblock"><pre><code>docker pull &lt;vendor&gt;/&lt;image&gt;:&lt;tag&gt;
docker run -it --entrypoint /bin/bash &lt;image&gt;:&lt;tag&gt;
</code></pre></div><p class="paragraph" style="text-align:left;">Docker images are layered, giving you more angles beyond just a shell:</p><p class="paragraph" style="text-align:left;"><b>See the build history</b> (often reveals secrets and paths):</p><div class="codeblock"><pre><code>docker history --no-trunc &lt;image&gt;:&lt;tag&gt;
</code></pre></div><p class="paragraph" style="text-align:left;"><b>Explore layers interactively</b> with <code>dive</code>:</p><div class="codeblock"><pre><code>dive &lt;image&gt;:&lt;tag&gt;
</code></pre></div><p class="paragraph" style="text-align:left;"><b>Export and unpack raw filesystem:</b></p><div class="codeblock"><pre><code>docker save &lt;image&gt;:&lt;tag&gt; -o image.tar
tar -xf image.tar  # extract layer.tar files inside too
</code></pre></div><p class="paragraph" style="text-align:left;"><b>Automate it:</b></p><div class="codeblock"><pre><code>python3 docker-image-extract.py &lt;image&gt;:&lt;tag&gt;
</code></pre></div><p class="paragraph" style="text-align:left;"><b>Reconstruct the Dockerfile</b> from history using <code>dedockify</code> or <code>dockerfile-from-image</code>.</p><h3 class="heading" style="text-align:left;" id="24-cloud-marketplaces"><b>2.4 Cloud Marketplaces</b></h3><p class="paragraph" style="text-align:left;">AWS, Azure, and GCP Marketplaces offer trial licenses for software that normally requires a sales call. Spin up a trial, SSH in, and pull application files from the filesystem.</p><h3 class="heading" style="text-align:left;" id="25-git-hub-dorks"><b>2.5 GitHub Dorks</b></h3><p class="paragraph" style="text-align:left;">Search for filenames, error strings, or config file names unique to the target:</p><div class="codeblock"><pre><code>filename:install_cmstep1.aspx
filename:web.config &quot;ConnectionString&quot;
&lt;unique error message from the live app&gt;
&lt;internal microservice name&gt;
</code></pre></div><p class="paragraph" style="text-align:left;">Also search software name + <code>password</code>, <code>token</code>, <code>secret</code>, <code>api_key</code>.</p><p class="paragraph" style="text-align:left;"><b>Beyond GitHub:</b> Try Sourcegraph (<code>sourcegraph.com/search</code>) and <code>grep.app</code>, they index repos across GitHub, GitLab, and Bitbucket.</p><h3 class="heading" style="text-align:left;" id="26-free-trials-and-sales-calls"><b>2.6 Free Trials and Sales Calls</b></h3><p class="paragraph" style="text-align:left;">Most vendors offer trials. 14 days is enough to pull source files and run a local audit. If a trial requires a sales call, that’s a good sign, fewer researchers have ever audited it so undiscovered bugs are more likely.</p><h3 class="heading" style="text-align:left;" id="27-freelancing-platforms"><b>2.7 Freelancing Platforms</b></h3><p class="paragraph" style="text-align:left;">Search Fiverr or Freelancer for people who implement the target software. Many have licensed access to enterprise installation files. Pay them to install it on your server and upload the files.</p><h3 class="heading" style="text-align:left;" id="28-package-registries-npm-pip-etc"><b>2.8 Package Registries (npm, pip, etc.)</b></h3><p class="paragraph" style="text-align:left;">The published package isn’t always the same as the GitHub repo. Pull and inspect:</p><div class="codeblock"><pre><code>npm pack &lt;package-name&gt;      # downloads .tgz
pip download &lt;package-name&gt;  # downloads wheel/sdist
</code></pre></div><p class="paragraph" style="text-align:left;">Also useful: pull the version your target uses.</p><h3 class="heading" style="text-align:left;" id="29-chaining-vulnerabilities"><b>2.9 Chaining Vulnerabilities</b></h3><p class="paragraph" style="text-align:left;">LFI, XXE, path traversal, or SSRF can read source files from a live target. On .NET, read DLLs from the <code>bin</code> folder and decompile locally. Check the program’s policy first because reading source code via bugs may not be in scope.</p><h3 class="heading" style="text-align:left;" id="210-decompilation"><b>2.10 Decompilation</b></h3><p class="paragraph" style="text-align:left;">Compiled doesn’t mean unreadable. Use the right tools:</p><p class="paragraph" style="text-align:left;"><b>Java (JAR/WAR/EAR):</b></p><div style="padding:14px 15px 14px;"><table class="bh__table" width="100%" style="border-collapse:collapse;"><tr class="bh__table_row"><th class="bh__table_header" width="50%"><p class="paragraph" style="text-align:left;">Tool</p></th><th class="bh__table_header" width="50%"><p class="paragraph" style="text-align:left;">Best for</p></th></tr><tr class="bh__table_row"><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">JD-GUI</p></td><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">Quick visual exploration</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">jadx</p></td><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">CLI / scripting / bulk</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">CFR</p></td><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">Modern Java (lambdas, complex switch)</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">Procyon</p></td><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">Same as CFR</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">Fernflower</p></td><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">Obfuscated/complex code</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">java-deobfuscator</p></td><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">Protected code (e.g. Zelix KlassMaster)</p></td></tr></table></div><p class="paragraph" style="text-align:left;">IntelliJ IDEA decompiles JARs transparently when you open them.</p><p class="paragraph" style="text-align:left;"><b>.NET / C#:</b> dotPeek (free), dnSpy (decompiler + debugger), ILSpy (open source, VS Code extension)</p><p class="paragraph" style="text-align:left;"><b>Python </b><code>.pyc</code><b>:</b> <code>uncompyle6</code> or <code>decompile3</code></p><p class="paragraph" style="text-align:left;"><b>Native binaries:</b> Ghidra (free, NSA-made), IDA Pro (paid, best-in-class)</p><p class="paragraph" style="text-align:left;">Output won’t be perfect, but it’s enough to trace data flows, find sinks, and understand auth logic.</p><h2 class="heading" style="text-align:left;" id="part-3-methodology"><b>Part 3: Methodology</b></h2><h3 class="heading" style="text-align:left;" id="31-define-scope-first"><b>3.1 Define Scope First</b></h3><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> Pre-auth bugs = higher value, higher CVSS, better payouts.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;">Decide before reading anything: pre-auth only, or post-auth too?</p><p class="paragraph" style="text-align:left;">Pre-auth vulnerabilities require no account and scale better across programs. Shubham Shah focuses almost exclusively on pre-auth surface. Post-auth bugs are worth it mainly if they lead to an auth bypass.</p><p class="paragraph" style="text-align:left;"><b>First thing to do in any new codebase:</b> Find every route with no auth middleware. Those are your targets.</p><h3 class="heading" style="text-align:left;" id="32-mapping-attack-surface"><b>3.2 Mapping Attack Surface</b></h3><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> Know every entry point before picking one to dig into. Routes, services, ports, WebSockets, all of it.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;"><b>Step 1: Find all running services and ports</b></p><p class="paragraph" style="text-align:left;">Before reading routes, know what’s running:</p><ul><li><p class="paragraph" style="text-align:left;"><code>docker-compose.yml</code> or Kubernetes manifests → internal services</p></li><li><p class="paragraph" style="text-align:left;"><code>EXPOSE</code> lines in Dockerfiles</p></li><li><p class="paragraph" style="text-align:left;"><code>ss -tlnp</code> or <code>netstat -tlnp</code> on a local instance</p></li><li><p class="paragraph" style="text-align:left;">nginx/Caddy/HAProxy configs → what’s proxied internally</p></li></ul><p class="paragraph" style="text-align:left;">Internal services (Redis, Prometheus exporters, admin panels, image renderers) are often reachable via SSRF even if not publicly exposed. Map them now.</p><p class="paragraph" style="text-align:left;"><b>Step 2: Find the routing file</b></p><p class="paragraph" style="text-align:left;">| Framework | Where routes live | | | | | Java Servlets | <code>WEB-INF/web.xml</code> | | Spring | <code>@RequestMapping</code>, <code>@GetMapping</code> on controllers | | Rails | <code>config/routes.rb</code> | | Django | <code>urls.py</code> | | Express | <code>app.js</code>, <code>routes/</code> dir | | Go | <code>api.go</code>, <code>main.go</code>, <code>http.HandleFunc()</code> | | ASP.NET | <code>[Route]</code>, <code>[HttpGet]</code> attributes; <code>Startup.cs</code> | | Laravel | <code>routes/web.php</code>, <code>routes/api.php</code> | | Flask | <code>@app.route()</code> decorators |</p><p class="paragraph" style="text-align:left;"><b>Step 3: Annotate each route with its auth requirement</b></p><p class="paragraph" style="text-align:left;">No middleware = highest priority:</p><div class="codeblock"><pre><code>Route                | Method | Auth Middleware | Priority
|--|--|-
/api/users/:id       | GET    | requiresAuth    | low
/avatar/:hash        | GET    | none            | HIGH ←
/api/reset-password  | POST   | none            | HIGH ←
</code></pre></div><p class="paragraph" style="text-align:left;">This is exactly how Rhyno found the Grafana SSRF: <code>/avatar/:hash</code> was the only route without <code>reqSignedIn</code>.</p><p class="paragraph" style="text-align:left;"><b>Step 4: Look beyond HTTP</b></p><p class="paragraph" style="text-align:left;">Auth middleware only covers HTTP routes. Also map:</p><ul><li><p class="paragraph" style="text-align:left;">WebSocket endpoints and their message handlers</p></li><li><p class="paragraph" style="text-align:left;">GraphQL - which queries/mutations need auth?</p></li><li><p class="paragraph" style="text-align:left;">gRPC <code>.proto</code> files - which RPCs require auth metadata?</p></li><li><p class="paragraph" style="text-align:left;">Background job queues: can an attacker enqueue a job?</p></li><li><p class="paragraph" style="text-align:left;">File upload handlers outside the main auth flow</p></li><li><p class="paragraph" style="text-align:left;">OAuth/OIDC callbacks are always unauthenticated by design. Check the handler for missing <code>state</code> validation, unvalidated <code>next</code>/<code>redirect_uri</code> param, and reusable authorization codes.</p></li></ul><p class="paragraph" style="text-align:left;"><b>Step 5: For post-auth bugs, sort by privilege level</b></p><p class="paragraph" style="text-align:left;">Routes any authenticated user can hit are more impactful than admin-only ones. Look for privilege escalation paths, lower-privilege users reaching higher-privilege routes.</p><h3 class="heading" style="text-align:left;" id="33-sniff-for-blood"><b>3.3 Sniff for Blood</b></h3><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> When something looks off, chase it.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;">When something behaves unexpectedly, stop and understand why before moving to the next file. Unexpected things almost always mean the developer lost control of something.</p><p class="paragraph" style="text-align:left;"><b>What “blood” looks like in code:</b></p><ul><li><p class="paragraph" style="text-align:left;">An endpoint missing auth when every other one has it</p></li><li><p class="paragraph" style="text-align:left;">A URL-fetching call that follows redirects</p></li><li><p class="paragraph" style="text-align:left;">A custom sanitizer that doesn’t match what the standard library does</p></li><li><p class="paragraph" style="text-align:left;">Parameters named <code>redirect_url</code>, <code>next</code>, <code>url</code>, <code>file</code>, <code>path</code>, <code>target</code></p></li><li><p class="paragraph" style="text-align:left;">An XML parser with no entity configuration</p></li><li><p class="paragraph" style="text-align:left;">A deserialization call accepting non-internal input</p></li><li><p class="paragraph" style="text-align:left;"><code>// TODO: validate this</code> or <code>// temporary, fix later</code></p></li></ul><p class="paragraph" style="text-align:left;"><b>CVE-2020-13379: full chain:</b></p><ol start="1"><li><p class="paragraph" style="text-align:left;">Route audit: <code>/avatar/:hash</code> has no auth. Small anomaly.</p></li><li><p class="paragraph" style="text-align:left;">Read the handler: calls <code>GoFetch(gravatarSource + hash + &quot;?&quot; + reqParams)</code>. Hash is attacker-controlled. That’s an SSRF seed.</p></li><li><p class="paragraph" style="text-align:left;">Gravatar’s <code>?d=</code> param redirects to <code>i0.wp.com/&lt;anything&gt;</code> when the hash has no image.</p></li><li><p class="paragraph" style="text-align:left;"><code>i0.wp.com/1.bp.blogspot.com/...</code> → redirects to blogspot.</p></li><li><p class="paragraph" style="text-align:left;">Test <code>%3f</code> (encoded <code>?</code>) in the path: <code>i0.wp.com/test%3f/1.bp.blogspot.com/</code> → redirects to blogspot.</p></li><li><p class="paragraph" style="text-align:left;">Host a PHP redirect server. Chain: Grafana → Gravatar (<code>d=</code>) → i0.wp.com (<code>%3f</code> smuggling) → attacker redirect → 169.254.169.254.</p></li><li><p class="paragraph" style="text-align:left;">Bonus: Grafana always returns <code>Content-Type: image/jpeg</code> no matter what it fetched. Use it to escalate other blind SSRFs to full-read.</p></li></ol><p class="paragraph" style="text-align:left;"><b>Habits:</b></p><ul><li><p class="paragraph" style="text-align:left;">When something looks interesting, open a scratch file and write every question it raises</p></li><li><p class="paragraph" style="text-align:left;">Ask “what’s the worst case if I fully control this?” then work backward</p></li><li><p class="paragraph" style="text-align:left;">When you see a protection, immediately ask “can this be bypassed?”</p></li><li><p class="paragraph" style="text-align:left;">A bug in one endpoint is often present somewhere else too</p></li><li><p class="paragraph" style="text-align:left;">If a protection relies on a third-party service, research that service independently</p></li></ul><h3 class="heading" style="text-align:left;" id="34-dynamic-debugging"><b>3.4 Dynamic Debugging</b></h3><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> Run the app locally, attach a debugger, set a breakpoint at the sink you care about, send a request, and see exactly what data arrives there. Faster than tracing it by hand.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;">Always run the software locally. Setup cost pays off throughout the audit.</p><p class="paragraph" style="text-align:left;"><b>General flow:</b></p><ol start="1"><li><p class="paragraph" style="text-align:left;">Spin up local instance (Docker, VM, native)</p></li><li><p class="paragraph" style="text-align:left;">Attach debugger to the running process</p></li><li><p class="paragraph" style="text-align:left;">Set a breakpoint at the sink</p></li><li><p class="paragraph" style="text-align:left;">Send a crafted request</p></li><li><p class="paragraph" style="text-align:left;">Inspect the call stack and variable values when execution pauses</p></li></ol><p class="paragraph" style="text-align:left;"><b>Java:</b> JVM has built-in remote debug (JDWP)</p><div class="codeblock"><pre><code>java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -jar app.jar
</code></pre></div><p class="paragraph" style="text-align:left;">Attach in IntelliJ: <b>Run → Attach to Process</b> → port 5005. Set breakpoints in decompiled class files, IntelliJ pauses there.</p><p class="paragraph" style="text-align:left;"><b>Node.js:</b> built-in inspector</p><div class="codeblock"><pre><code>node --inspect app.js
node --inspect-brk app.js  # pause immediately on start
</code></pre></div><p class="paragraph" style="text-align:left;">Open <code>chrome://inspect</code> → click “inspect”. Or attach from VS Code:</p><div class="codeblock"><pre><code>&#123; &quot;type&quot;: &quot;node&quot;, &quot;request&quot;: &quot;attach&quot;, &quot;port&quot;: 9229 &#125;
</code></pre></div><p class="paragraph" style="text-align:left;"><b>Python:</b> <code>pdb</code> needs no setup</p><div class="codeblock"><pre><code>breakpoint()  # Python 3.7+
</code></pre></div><p class="paragraph" style="text-align:left;">For web frameworks (Flask, Django, FastAPI), use <code>debugpy</code></p><div class="codeblock"><pre><code>pip install debugpy
python -m debugpy --listen 5678 --wait-for-client app.py
</code></pre></div><p class="paragraph" style="text-align:left;">VS Code: <code>&#123; &quot;type&quot;: &quot;python&quot;, &quot;request&quot;: &quot;attach&quot;, &quot;connect&quot;: &#123; &quot;host&quot;: &quot;localhost&quot;, &quot;port&quot;: 5678 &#125; &#125;</code></p><p class="paragraph" style="text-align:left;"><b>PHP:</b> Xdebug in <code>php.ini</code></p><div class="codeblock"><pre><code>zend_extension=xdebug.so
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=127.0.0.1
xdebug.client_port=9003
</code></pre></div><p class="paragraph" style="text-align:left;">Install “PHP Debug” in VS Code. Any request to the app gets caught at the next breakpoint.</p><p class="paragraph" style="text-align:left;"><b>.NET / C# -</b> <b>dnSpy</b>: decompiles and debugs without original source.</p><ol start="1"><li><p class="paragraph" style="text-align:left;">Open DLL/EXE in dnSpy</p></li><li><p class="paragraph" style="text-align:left;"><b>Debug → Attach to Process</b></p></li><li><p class="paragraph" style="text-align:left;">Set breakpoint in the decompiled view</p></li><li><p class="paragraph" style="text-align:left;">Send request</p></li></ol><p class="paragraph" style="text-align:left;">For Docker: expose port 4024 (<code>vsdbg</code>) and use JetBrains Rider to remote debug.</p><p class="paragraph" style="text-align:left;"><b>Go</b> - <b>Delve</b> (<code>dlv</code>)</p><div class="codeblock"><pre><code>go install github.com/go-delve/delve/cmd/dlv@latest
dlv debug --headless --listen=:2345 --api-version=2 ./cmd/server
</code></pre></div><p class="paragraph" style="text-align:left;">VS Code: <code>&#123; &quot;type&quot;: &quot;go&quot;, &quot;request&quot;: &quot;attach&quot;, &quot;mode&quot;: &quot;remote&quot;, &quot;port&quot;: 2345 &#125;</code></p><p class="paragraph" style="text-align:left;"><b>Ruby:</b> <code>binding.pry</code> (with <code>pry</code> gem) or <code>binding.irb</code>.</p><p class="paragraph" style="text-align:left;">For VS Code:</p><div class="codeblock"><pre><code>rdbg --open --port 12345 -- bundle exec rails server
</code></pre></div><h3 class="heading" style="text-align:left;" id="35-tracking-code-flows-with-vs-code"><b>3.5 Tracking Code Flows with VS Code Bookmarks</b></h3><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> Use labeled bookmarks to mark SOURCE → PROPAGATOR → SANITIZER → SINK as you trace.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;">Tracing a vulnerability through dozens of files means losing your thread every time you navigate away, you can fix this with bookmarks.</p><p class="paragraph" style="text-align:left;"><b>Extension:</b> <a class="link" href="https://github.com/alefragnani/vscode-numbered-bookmarks?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">vscode-numbered-bookmarks</a> by Alessandro Fragnani</p><p class="paragraph" style="text-align:left;"><b>Use labeled bookmarks</b> to describe what each point is:</p><div class="codeblock"><pre><code>Ctrl+Shift+P → &quot;Bookmarks: Toggle Labeled&quot;

&quot;SOURCE: user input enters via req.body.email&quot;
&quot;PROPAGATOR: value assigned to query variable&quot;
&quot;SANITIZER?: strip_tags called, is this effective?&quot;
&quot;SINK: SQL execute() called here&quot;
</code></pre></div><p class="paragraph" style="text-align:left;"><b>Workflow:</b></p><ol start="1"><li><p class="paragraph" style="text-align:left;">Bookmark the route handler: <code>SOURCE: entry point</code></p></li><li><p class="paragraph" style="text-align:left;">Follow data with <code>F12</code> (Go to Definition) and <code>Alt+Left</code> (Go Back)</p></li><li><p class="paragraph" style="text-align:left;">Bookmark each function boundary data passes through</p></li><li><p class="paragraph" style="text-align:left;">Bookmark any sanitizer, note if it’s actually effective</p></li><li><p class="paragraph" style="text-align:left;">Bookmark the sink</p></li></ol><p class="paragraph" style="text-align:left;">Now the Bookmarks panel is your complete flow map. Jump between SOURCE and SINK instantly. Reconstruct the chain in the next session without re-tracing.</p><p class="paragraph" style="text-align:left;"><b>For multiple flows, use prefixes:</b></p><div class="codeblock"><pre><code>[FLOW-1] SOURCE: avatar hash from URL path
[FLOW-1] PROPAGATOR: hash passed to GoFetch()
[FLOW-1] SINK: GoFetch makes outbound HTTP request
[FLOW-2] SOURCE: email from reset form
[FLOW-2] SINK: SQL query executed
</code></pre></div><p class="paragraph" style="text-align:left;"><b>Export your map:</b> “Bookmarks: List from All Files” → copy to notes. You get file paths + line numbers for everything.</p><p class="paragraph" style="text-align:left;"><b>Pair with TODO Highlight</b> for inline questions while reading:</p><div class="codeblock"><pre><code>//TODO: does url_decode run before or after this sanitizer?
//TODO: is this route reachable without auth?
</code></pre></div><p class="paragraph" style="text-align:left;">Bookmarks = jump points across the codebase. TODOs = inline questions. Use both.</p><p class="paragraph" style="text-align:left;"><b>GitLens:</b> It shows inline blame, full file history, and commit diffs right in VS Code. Especially useful for diff-based review because you can see exactly when a security-relevant line was introduced and what the commit message said.</p><h3 class="heading" style="text-align:left;" id="36-jx-scout"><b>3.6 JxScout</b></h3><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> JxScout plugs into your proxy (Burp/Caido), collects every JS file your target loads, beautifies it, pulls hidden Webpack chunks you&#39;d never trigger manually, reverses source maps, and dumps it all into a folder you can open in your IDE.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;"><b>What it gives you:</b></p><ul><li><p class="paragraph" style="text-align:left;"><b>Beautified JS</b> - every captured file is auto-formatted with Prettier so you can actually read it</p></li><li><p class="paragraph" style="text-align:left;"><b>Hidden chunks</b> - Webpack/Vite apps lazy-load numbered chunks, JxScout brute-forces chunk IDs to pull modules you&#39;d never trigger by browsing, like admin panels or feature-flagged flows</p></li><li><p class="paragraph" style="text-align:left;"><b>Source map reversal</b> - automatically tries to fetch <code>.map</code> files for every JS it captures. When it hits, you get original TypeScript/JSX with real variable names</p></li><li><p class="paragraph" style="text-align:left;"><b>AST analysis</b> - the VS Code extension parses the output and flags API endpoints, auth logic, and config values so you have starting points for manual review</p></li></ul><p class="paragraph" style="text-align:left;">We recently had Francisco doing a masterclass on how to use JxScout and he gave us some really useful tips. If you&#39;re interested in that, check our subscriptions on our Discord server.</p><h3 class="heading" style="text-align:left;" id="37-ai-source-code-whats-actually-po"><b>3.7 AI + Source Code: What&#39;s Actually Possible</b></h3><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> What AI can do for you depends entirely on what code you have. Full source code and frontend-only JS are two very different starting points.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><h4 class="heading" style="text-align:left;" id="when-you-have-full-source-code-open"><b>When you have full source code (open source, decompiled, Docker pull, etc)</b></h4><p class="paragraph" style="text-align:left;">This is where AI is most useful, because you can feed it the actual backend logic and it can reason about things that would take hours to trace manually:</p><ul><li><p class="paragraph" style="text-align:left;"><b>Taint analysis:</b> &quot;Does user input from this endpoint reach a SQL query without sanitization?&quot; AI can trace multi-file data flows that static analyzers miss, especially through stuff like middleware chains, decorators and dependency injection</p></li><li><p class="paragraph" style="text-align:left;"><b>Custom sanitizer auditing:</b> paste a sanitizer and ask &quot;how do I bypass this?&quot; AI is genuinely good at spotting incomplete blocklists, case sensitivity issues, and encoding gaps</p></li><li><p class="paragraph" style="text-align:left;"><b>Auth model mapping:</b> &quot;Which routes in this codebase have no authentication middleware?&quot; Feed it the routing config and middleware setup, get back a list sorted by exposure</p></li><li><p class="paragraph" style="text-align:left;"><b>Diffing patches:</b> give it a CVE patch and the surrounding code, ask &quot;where else does this same pattern exist?&quot; Faster than grepping when the pattern isn&#39;t a simple string match</p></li><li><p class="paragraph" style="text-align:left;"><b>Understanding internals:</b> &quot;How does this framework&#39;s deserialization work? What types can I instantiate?&quot; AI knows most major frameworks well enough to explain the plumbing you&#39;d otherwise have to read source for</p></li><li><p class="paragraph" style="text-align:left;"><b>Decompiled code translation:</b> decompiler output (jadx, dnSpy, Ghidra) is often ugly and missing context. AI can clean it up, rename variables to something meaningful and explain what a method actually does</p></li></ul><h4 class="heading" style="text-align:left;" id="when-you-only-have-frontend-java-sc"><b>When you only have frontend JavaScript (web targets, no backend access)</b></h4><p class="paragraph" style="text-align:left;">You have less to work with, but AI still accelerates the workflow significantly:</p><ul><li><p class="paragraph" style="text-align:left;"><b>API endpoint extraction:</b> feed it beautified JS bundles (from JxScout or manual pulls) and ask for every API call, endpoint path, and parameter name.</p></li><li><p class="paragraph" style="text-align:left;"><b>Hidden params:</b> JS bundles contain parameter names the UI doesn&#39;t expose. AI can pull every key name from request builders, form serializers, and config objects.</p></li><li><p class="paragraph" style="text-align:left;"><b>Auth flow reconstruction:</b> &quot;How does this app handle login, token refresh, and session storage?&quot; AI can read the auth module and map the full flow, including where tokens are stored and how they&#39;re attached to requests</p></li><li><p class="paragraph" style="text-align:left;"><b>Business logic mapping:</b> &quot;What roles exist in this app and what can each one do?&quot; Frontend code often has role checks, permission constants, and feature flags that reveal the backend&#39;s authorization model</p></li><li><p class="paragraph" style="text-align:left;"><b>Identifying dead or debug code:</b> AI can spot conditional blocks gated on <code>isDev</code>, <code>isStaging</code>, <code>enableDebug</code> flags. These features sometimes remain accessible in production if the flag is client-side only</p></li></ul><h4 class="heading" style="text-align:left;" id="both-scenarios"><b>Both scenarios</b></h4><ul><li><p class="paragraph" style="text-align:left;"><b>Generating PoCs:</b> once you&#39;ve found a promising flow, AI can draft the exploit script: build the HTTP request, handle encoding, chain the steps.</p></li><li><p class="paragraph" style="text-align:left;"><b>Rubber ducking?:</b> describe what you&#39;re seeing and what feels off. AI is a very good second pair of eyes(or a nose) for the &quot;this stinks but I don&#39;t know why&quot;</p></li></ul><h3 class="heading" style="text-align:left;" id="38-other-automated-tools"><b>3.8 Other Automated Tools</b></h3><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> Use tools to narrow down what’s worth working on manually.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;"><b>Semgrep:</b> pattern matching. Finds things that <i>look</i> wrong. Can’t trace data flow across function boundaries. Good for an initial sweep. Use the <code>elttam</code> Java ruleset.</p><p class="paragraph" style="text-align:left;"><b>CodeQL:</b> AST-based taint analysis. Actually asks “does attacker data reach this sink?” Much more powerful. Great for Java and C#. No PHP support.</p><p class="paragraph" style="text-align:left;"><b>Snyk:</b> free tier, VS Code plugin, GitHub integration. Covers both vulnerable dependencies and insecure code patterns in one pass. Lower barrier to entry than CodeQL, good for a quick first pass on a new target.</p><p class="paragraph" style="text-align:left;"><b>AI-assisted tools:</b> traditional static analyzers catch under 20% of security issues. Modern AI tools (using NLP and AST analysis to understand code intent, not just pattern-match) reach detection rates of 42–48%. Worth layering in on top of semgrep/CodeQL, especially for complex codebases where context matters.</p><p class="paragraph" style="text-align:left;"><b>Recommendation:</b> Semgrep first for an initial list, then validate manually. For Java/C#, invest in CodeQL for real taint tracking. Use AI tools as an additional layer, not a replacement for any of the above.</p><p class="paragraph" style="text-align:left;"><b>Fuzzing:</b> if the target parses a file format, archive, or custom protocol, run a fuzzer at the parser. Static analysis tells you what the code <i>should</i> do while fuzzing tells you what it does with input it wasn’t designed for.</p><p class="paragraph" style="text-align:left;">Tools by language:</p><ul><li><p class="paragraph" style="text-align:left;"><b>AFL++</b> — C/C++ native binaries</p></li><li><p class="paragraph" style="text-align:left;"><b>Jazzer</b> — Java (JVM bytecode)</p></li><li><p class="paragraph" style="text-align:left;"><b>Atheris</b> — Python</p></li><li><p class="paragraph" style="text-align:left;"><b>Fuzzilli</b> — JavaScript engines</p></li></ul><p class="paragraph" style="text-align:left;">Especially worth running when you see custom deserialization, file upload handlers, or anything that accepts binary/structured data from users.</p><h3 class="heading" style="text-align:left;" id="39-dependency-auditing"><br><b>3.9 Dependency Auditing</b></h3><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> Third-party libraries are sinks too. Don’t assume they’re safe.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;">If the app passes user input to ImageMagick, ExifTool, or any external binary, that binary inherits the attack surface.</p><ul><li><p class="paragraph" style="text-align:left;">Check every dependency version against CVE databases</p></li><li><p class="paragraph" style="text-align:left;">For libraries processing user data (image processors, XML parsers, PDF generators), read their source for edge cases</p></li><li><p class="paragraph" style="text-align:left;">Java’s <code>DocumentBuilder</code> expands external entities by default. The developer must add two lines to disable it. The missing config <i>is</i> the vulnerability.</p></li></ul><h3 class="heading" style="text-align:left;" id="310-dont-give-up-too-early-take-goo"><b>3.10 Don’t Give Up Too Early + Take good notes (again)</b></h3><p class="paragraph" style="text-align:left;">Shubs focuses on a target for at least 1~2 weeks before writing off a target. Most hunters quit after a few hours but the bugs are usually there, they just haven&#39;t understood the app well enough yet. Before ending each session, dump every interesting spot you found to a file, even things you couldn&#39;t exploit. Start the next session from that list.</p><p class="paragraph" style="text-align:left;">And always remember that the better you are at taking notes for yourself, the easier it will be for your AI agents to go through those notes and help you later! =)</p><h3 class="heading" style="text-align:left;" id="311-common-patterns-across-top-writ"><b>3.11 Common Patterns Across Top Write-ups</b></h3><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> Recurring patterns we see across write-ups from top researchers. Each one represents a way of thinking about code that consistently leads to higher severity bugs.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;"><b>Patch diffing:</b> When a vuln gets patched, reading the diff tells you exactly what the developers thought the problem was. But you&#39;ll often find the same problem in other areas of their code.</p><p class="paragraph" style="text-align:left;"><b>Parser differentials:</b> When a security layer and the backend parse the same input using different logic, they can disagree on what the data actually is. The security layer sees something safe, the backend interprets it differently. This is a classic that we keep seeing over and over.</p><p class="paragraph" style="text-align:left;"><b>Pre-auth surface:</b> Anything reachable without authentication is reachable by anyone on the internet. Even if there are only a few unauthenticated endpoints, those are the ones worth spending the most time on. This leads to unauthenticated RCE, SSRF, and information disclosure at scale.</p><p class="paragraph" style="text-align:left;"><b>Chaining:</b> A bug that is a low on its own can become a crit when combined with another. For example, an auth bypass that gives you access to an internal endpoint + injection in that endpoint = pre-auth RCE.</p><p class="paragraph" style="text-align:left;"><b>Validate-then-transform:</b> When data is validated or sanitized and then modified afterward (decoded, normalized, concatenated), the modification can undo the security check. The data that was &quot;safe&quot; at check time is no longer safe when it executes.</p><p class="paragraph" style="text-align:left;"><b>Fail-open defaults:</b> When a security check encounters an error (malformed input, unreachable auth service, unexpected type), does it block the request or let it through? Many implementations default to allowing the request when something unexpected happens.</p><p class="paragraph" style="text-align:left;"><b>Alternative paths:</b> New security controls often get added to the main request path, but legacy endpoints, debug routes, internal APIs, or older protocol handlers still reach the same backend without going through those controls.</p><p class="paragraph" style="text-align:left;"><b>Incomplete fix:</b> When a vendor patches a reported vulnerability, the fix sometimes only addresses the specific PoC rather than the underlying root cause. The same vulnerable pattern may exist in other handlers, or the fix can be bypassed with a slight variation.</p><h2 class="heading" style="text-align:left;" id="part-4-vulnerable-code-patterns-tec"><b>Part 4: Vulnerable Code Patterns & Techniques</b></h2><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> Patterns to spot while reading code, plus techniques and real-world examples. None of these are guaranteed bugs but each one is a signal to slow down and look closer.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><h3 class="heading" style="text-align:left;" id="41-sanitization-followed-by-data-mo"><b>4.1 Sanitization Followed by Data Modification</b></h3><p class="paragraph" style="text-align:left;">Sanitize input, then transform it in a way that undoes the sanitization.</p><div class="codeblock"><pre><code>$input = sanitize_html($user_input);  // strips HTML
$input = urldecode($input);           // %3Cscript%3E becomes &lt;script&gt;
echo $input;                          // XSS
</code></pre></div><p class="paragraph" style="text-align:left;"><b>Fix:</b> Decode first, then sanitize. Order matters.</p><h3 class="heading" style="text-align:left;" id="42-auth-check-inside-an-if-statemen"><b>4.2 Auth Check Inside an If Statement</b></h3><p class="paragraph" style="text-align:left;">Auth check placed inside a conditional body where the condition is user-controllable.</p><div class="codeblock"><pre><code>function process_request($action) &#123;
    if ($action === &quot;admin_only_action&quot;) &#123;
        check_admin_privilege();  // skipped for all other values
        do_admin_thing();
    &#125;
    do_general_thing();  // always runs
&#125;
</code></pre></div><p class="paragraph" style="text-align:left;">The correct pattern exits immediately:</p><div class="codeblock"><pre><code>if (!is_authenticated()) &#123;
    return unauthorized();  // stops here
&#125;
do_thing();
</code></pre></div><p class="paragraph" style="text-align:left;">Look for: security checks inside an <code>if</code> where you control the condition.</p><h3 class="heading" style="text-align:left;" id="43-security-check-with-no-control-f"><b>4.3 Security Check with No Control Flow Effect</b></h3><p class="paragraph" style="text-align:left;">The check runs, detects a problem, but doesn’t stop execution.</p><div class="codeblock"><pre><code>if (!is_valid_input($data)) &#123;
    $error = true;  // set but never checked
&#125;
execute_query($data);  // runs anyway
</code></pre></div><p class="paragraph" style="text-align:left;">Look for: security checks that don’t <code>return</code>, <code>exit</code>, <code>throw</code>, or <code>die</code>. If they only set a variable, trace whether anything downstream uses it in time.</p><h3 class="heading" style="text-align:left;" id="44-bad-regex"><b>4.4 Bad Regex</b></h3><p class="paragraph" style="text-align:left;">Regex used for security validation is very often wrong. Test any security-related regex on <a class="link" href="https://regex101.com/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">regex101.com</a> with the correct language.</p><p class="paragraph" style="text-align:left;"><b>Unescaped dot:</b> <code>.</code> matches any character. <code>example.com</code> also matches <code>exampleXcom</code>. Write <code>example\.com</code>.</p><p class="paragraph" style="text-align:left;"><b>Missing anchors:</b> <code>/example\.com/</code> matches <code>evil.com?to=example.com</code>. Add <code>^</code> and <code>$</code> anchors.</p><p class="paragraph" style="text-align:left;"><b>Multiline:</b> <code>^</code> and <code>$</code> behavior changes with the multiline flag. Smuggling on a second line may work if the flag is missing or set incorrectly.</p><p class="paragraph" style="text-align:left;"><b>Case:</b> without <code>/i</code>, <code>SELECT</code> doesn’t catch <code>select</code>.</p><ul><li><p class="paragraph" style="text-align:left;"><b>vs </b><code>+</code><b>:</b> matches zero or more. If you need at least one, is wrong. Use <code>+</code>.</p></li></ul><p class="paragraph" style="text-align:left;"><b>JS </b><code>.replace()</code><b> first-match-only:</b></p><div class="codeblock"><pre><code>&quot;aaa&quot;.replace(&quot;a&quot;, &quot;b&quot;)    // &quot;baa&quot; = only first replaced!
&quot;aaa&quot;.replace(/a/g, &quot;b&quot;)   // &quot;bbb&quot; = needs /g flag
&quot;aaa&quot;.replaceAll(&quot;a&quot;, &quot;b&quot;) // &quot;bbb&quot; = or use replaceAll
</code></pre></div><p class="paragraph" style="text-align:left;"><b>JS capture groups in replacement:</b> <code>String.replace()</code> supports <code>$&amp;</code>, <code>$&#39;</code>, <code>$&lt;n&gt;</code> in the replacement string. If user controls the replacement argument, they can reinject filtered characters.</p><h3 class="heading" style="text-align:left;" id="45-non-recursive-replace-sanitizati"><b>4.5 Non-Recursive Replace Sanitization</b></h3><p class="paragraph" style="text-align:left;">Stripping a sequence like <code>../</code> only once instead of looping.</p><div class="codeblock"><pre><code>Input:  ....//
After one pass of removing &#39;../&#39;:  ../    ← path traversal
</code></pre></div><p class="paragraph" style="text-align:left;">The attacker sandwiches the blocked sequence inside itself. One pass collapses it back.</p><p class="paragraph" style="text-align:left;"><b>Fix:</b> Loop until no change, or use <code>os.path.abspath()</code> + verify the result is inside the expected directory.</p><h3 class="heading" style="text-align:left;" id="46-dynamic-function-calls"><b>4.6 Dynamic Function Calls</b></h3><p class="paragraph" style="text-align:left;">A string variable (potentially attacker-controlled) used to call a function or method.</p><div class="codeblock"><pre><code>$method = $_GET[&#39;action&#39;];
$this-&gt;$method($_REQUEST);  // calls any public method with the full request
</code></pre></div><div class="codeblock"><pre><code>String methodName = request.getParameter(&quot;action&quot;);
Method m = this.getClass().getMethod(methodName);
m.invoke(this);  // programmable eval
</code></pre></div><p class="paragraph" style="text-align:left;">If the method name is attacker-controlled, they can invoke anything on the object.</p><p class="paragraph" style="text-align:left;">Look for: <code>$obj-&gt;$var</code>, <code>call_user_func</code>, <code>call_user_func_array</code> in PHP; reflection APIs in Java/.NET.</p><h3 class="heading" style="text-align:left;" id="47-type-confusion"><b>4.7 Type Confusion</b></h3><p class="paragraph" style="text-align:left;">App expects one type, attacker sends another, unexpected behavior follows.</p><p class="paragraph" style="text-align:left;"><b>GitLab example:</b> Password reset expected <code>email</code> as a string. Attacker sent an array:</p><div class="codeblock"><pre><code>user[email][]=victim@example.com&amp;user[email][]=attacker@example.com
</code></pre></div><p class="paragraph" style="text-align:left;">App validated using the first email but sent the reset link to <i>both</i> → ATO</p><p class="paragraph" style="text-align:left;"><b>Common scenarios:</b></p><ul><li><p class="paragraph" style="text-align:left;">Array where string expected → SQLi, query changes, string checks bypassed</p></li><li><p class="paragraph" style="text-align:left;">String where integer expected → type coercion bypasses numeric comparisons</p></li><li><p class="paragraph" style="text-align:left;"><code>null</code> where non-null expected</p></li><li><p class="paragraph" style="text-align:left;">Wrong type causes a crash → low-impact CSRF becomes DoS</p></li></ul><p class="paragraph" style="text-align:left;"><b>Most affected:</b> PHP, Ruby, JavaScript (implicit coercion).</p><p class="paragraph" style="text-align:left;"><b>Test:</b> For any ID or email param, try <code>param[]=value</code> and <code>null</code>. Unexpected errors reveal how the value is used internally.</p><h3 class="heading" style="text-align:left;" id="48-insecure-randomness"><b>4.8 Insecure Randomness</b></h3><p class="paragraph" style="text-align:left;">Using a non-cryptographic RNG for security-sensitive values (session tokens, reset tokens, CSRF tokens, API keys).</p><div class="codeblock"><pre><code>// Bad: 48-bit seed, predictable
Random r = new Random();
r.nextBytes(sessionId);

// Good: cryptographically secure
SecureRandom sr = new SecureRandom();
sr.nextBytes(sessionId);
</code></pre></div><p class="paragraph" style="text-align:left;">An attacker who observes a few outputs from <code>java.util.Random</code> can derive the seed and predict all future session IDs.</p><p class="paragraph" style="text-align:left;">Look for: <code>java.util.Random</code>, <code>Math.random()</code> (JS), <code>rand()</code> (PHP), <code>random.random()</code> (Python). Any function not explicitly documented as cryptographically secure shouldn’t touch security values.</p><h3 class="heading" style="text-align:left;" id="49-xxe-xml-parsed-without-entity-re"><b>4.9 XXE - XML Parsed Without Entity Restrictions</b></h3><p class="paragraph" style="text-align:left;">XML parsed without disabling external entity expansion and DTD processing.</p><div class="codeblock"><pre><code>// Vulnerable: expands entities by default
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
Document doc = dbf.newDocumentBuilder().parse(inputStream);

// Safe: explicitly disabled
dbf.setFeature(&quot;http://apache.org/xml/features/disallow-doctype-decl&quot;, true);
dbf.setFeature(&quot;http://xml.org/sax/features/external-general-entities&quot;, false);
dbf.setFeature(&quot;http://xml.org/sax/features/external-parameter-entities&quot;, false);
</code></pre></div><p class="paragraph" style="text-align:left;">The vulnerability is the <i>absence</i> of the defensive lines, the parser is correct but not configured.</p><p class="paragraph" style="text-align:left;">Look for: any <code>DocumentBuilder</code>, <code>SAXParser</code>, <code>XMLInputFactory</code>, <code>JAXBContext</code>, or <code>Unmarshaller</code> call with no defensive config next to it.</p><h3 class="heading" style="text-align:left;" id="410-zip-slip"><b>4.10 Zip Slip</b></h3><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> Archive extraction without path normalization lets an attacker write files anywhere on the server.</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;">A crafted ZIP or TAR can contain entries with filenames like <code>../../etc/cron.d/evil</code>. If the code extracts without checking the destination path, the file lands outside the intended directory, leading to arbitrary file write, which often chains into RCE.</p><div class="codeblock"><pre><code>// Vulnerable
ZipEntry entry = zip.getNextEntry();
File output = new File(destDir, entry.getName()); // entry.getName() is attacker-controlled
output.getParentFile().mkdirs();
Files.copy(zip, output.toPath());
</code></pre></div><div class="codeblock"><pre><code>// Safe: verify the resolved path stays inside destDir
File output = new File(destDir, entry.getName()).getCanonicalFile();
if (!output.toPath().startsWith(destDir.getCanonicalFile().toPath())) &#123;
    throw new SecurityException(&quot;Zip Slip detected&quot;);
&#125;
</code></pre></div><p class="paragraph" style="text-align:left;">Look for <code>entry.getName()</code>, <code>tarEntry.getName()</code>, <code>zipEntry.getName()</code> passed directly to a <code>File</code> or path constructor without a canonical path check afterward. Common in file upload handlers, plugin installers and anything that processes user-submitted archives.</p><h3 class="heading" style="text-align:left;" id="411-cve-202013379-grafana-unauthent"><b>4.11 CVE-2020-13379 (Grafana Unauthenticated SSRF)</b></h3><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><b>TLDR:</b> Route audit → unauthenticated endpoint → server-side URL fetch → redirect chain → full SSRF → 25+ crits</p><figcaption class="blockquote__byline"></figcaption></blockquote></div><ol start="1"><li><p class="paragraph" style="text-align:left;"><b>Scope:</b> Unauthenticated endpoints only.</p></li><li><p class="paragraph" style="text-align:left;"><b>Route audit:</b> Read <code>pkg/api/api.go</code>. Every route has <code>reqSignedIn</code> except one: <code>/avatar/:hash</code>.</p></li><li><p class="paragraph" style="text-align:left;"><b>Read the handler:</b> Calls <code>GoFetch(gravatarSource + hash + &quot;?&quot; + reqParams)</code>. Hash = attacker-controlled. Server fetches a URL built from user input.</p></li><li><p class="paragraph" style="text-align:left;"><b>Research Gravatar:</b> <code>?d=</code> redirects to <code>i0.wp.com/&lt;anything&gt;</code> when hash has no image.</p></li><li><p class="paragraph" style="text-align:left;"><b>Research </b><code>i0.wp.com</code><b>:</b> <code>i0.wp.com/1.bp.blogspot.com/...</code> redirects to blogspot. Test: <code>i0.wp.com/test%3f/1.bp.blogspot.com/</code> → redirects to blogspot.</p></li><li><p class="paragraph" style="text-align:left;"><b>Chain it:</b> Host redirect server at <code>redirect.rhynorater.com</code>. Chain: Grafana → Gravatar → i0.wp.com (<code>%3f</code> smuggling) → redirect server → <code>169.254.169.254</code>.</p></li></ol><p class="paragraph" style="text-align:left;"><b>Final payload:</b></p><div class="codeblock"><pre><code>/avatar/test%3fd%3dredirect.rhynorater.com%25253f%253b%252fbp.blogspot.com%252f169.254.169.254
</code></pre></div><p class="paragraph" style="text-align:left;"><b>Result:</b> 25+ crits, 15+ highs.</p><h3 class="heading" style="text-align:left;" id="412-ssrf-pivoting"><b>4.12 SSRF Pivoting</b></h3><p class="paragraph" style="text-align:left;">Once you have SSRF, the next step is figuring out what&#39;s actually running internally. Check <code>docker-compose.yml</code>, Kubernetes manifests, and service configs to find internal service names and ports you can now reach.</p><p class="paragraph" style="text-align:left;"><b>Cloud metadata:</b></p><div class="codeblock"><pre><code>169.254.169.254/latest/meta-data/iam/security-credentials/&lt;ROLE&gt;  → AWS IAM creds
169.254.169.254/latest/user-data                                  → often contains secrets
</code></pre></div><p class="paragraph" style="text-align:left;"><b>Internal services:</b></p><p class="paragraph" style="text-align:left;">Every target is different, the goal is to read the source code, find what&#39;s running internally, and look up how to interact with it. Some common ones you&#39;ll encounter:</p><ul><li><p class="paragraph" style="text-align:left;">Grafana Image Renderer: <code>localhost:3001/render?url=&lt;your-url&gt;</code> → renders arbitrary HTML → JS execution in headless browser</p></li><li><p class="paragraph" style="text-align:left;">Prometheus Redis Exporter: <code>localhost:9121/scrape?target=redis://127.0.0.1:7001&amp;check-keys=*</code> → dumps all Redis keys</p></li></ul><h3 class="heading" style="text-align:left;" id="413-bypassing-protections-by-chaini"><b>4.13 Bypassing Protections by Chaining</b></h3><p class="paragraph" style="text-align:left;">Finding a protection doesn&#39;t mean the vulnerability class is closed, just that you need to chain something else to bypass it.</p><p class="paragraph" style="text-align:left;">Some examples worth trying:</p><ul><li><p class="paragraph" style="text-align:left;"><b>SSRF with URL allowlist</b> → find an open redirect on an allowed domain</p></li><li><p class="paragraph" style="text-align:left;"><b>XSS with CSP</b> → find a JSONP endpoint on an allowed domain, or use <code>blob:</code>/<code>data:</code> bypass</p></li><li><p class="paragraph" style="text-align:left;"><b>SQLi with WAF</b> → encoding, comments, alternative syntax</p></li><li><p class="paragraph" style="text-align:left;"><b>Path traversal with normalization</b> → double encoding, backslashes, null bytes</p></li></ul><p class="paragraph" style="text-align:left;">If you hit a whitelist or any other partial protection, look for a way around it.</p><h3 class="heading" style="text-align:left;" id="414-config-file-parsing-bugs"><b>4.14 Config File Parsing Bugs</b></h3><p class="paragraph" style="text-align:left;">Config file parsers have quirks that can be exploited:</p><ul><li><p class="paragraph" style="text-align:left;"><b>Length/truncation limits:</b> inih (C library) at <b>200 bytes</b> (<code>INI_MAX_LINE</code>) returns a non-zero error code on overlong lines, which callers often ignore; PAM <code>pam_group</code> at <b>1000 bytes</b> (<code>PAM_GROUP_BUFLEN</code>); legacy BSD syslog (RFC 3164, now obsoleted by RFC 5424) at <b>1024 bytes</b> total. The dangerous pattern is <code>fgets()</code>-based C parsers: when a line exceeds the buffer, unconsumed bytes remain in the stream and the next <code>fgets()</code> call reads them as a new line, content past the boundary can be evaluated as a fresh directive. PHP&#39;s <code>parse_ini_file</code> has no line limit but silently truncates values at an unquoted <code>;</code> in <code>INI_SCANNER_NORMAL</code> mode (e.g. <code>password=secret;comment</code> → <code>password=secret</code>).</p></li><li><p class="paragraph" style="text-align:left;"><b>Duplicate sections:</b> Some parsers let a second section definition overwrite the first. Inject a second occurrence of a critical section to control the config.</p></li><li><p class="paragraph" style="text-align:left;"><b>Whitespace/encoding:</b> Tabs vs. spaces, <code>\r\n</code> vs <code>\n</code>, Unicode whitespace. Parsers handle these differently and that can break the expected structure.</p></li></ul><h2 class="heading" style="text-align:left;" id="part-5-bug-bounty-specifics"><b>Part 5: Bug Bounty Specifics</b></h2><h3 class="heading" style="text-align:left;" id="51-zero-days-in-deployed-software"><b>5.1 Zero-Days in Deployed Software</b></h3><p class="paragraph" style="text-align:left;">When a bug affects software used by many programs, check each program’s policy before mass exploitation. Some cover third-party software they deploy, others don’t.</p><h3 class="heading" style="text-align:left;" id="52-responsible-disclosure"><b>5.2 Responsible Disclosure</b></h3><p class="paragraph" style="text-align:left;">For bugs found outside a formal bug bounty program:</p><ol start="1"><li><p class="paragraph" style="text-align:left;">Email the vendor’s security team (not a bug bounty platform)</p></li><li><p class="paragraph" style="text-align:left;">Give 90 days (Google Project Zero standard)</p></li><li><p class="paragraph" style="text-align:left;">If they patch within 90 days, give 30 more days grace</p></li><li><p class="paragraph" style="text-align:left;">Disclose publicly after, with or without a PoC</p></li></ol><h3 class="heading" style="text-align:left;" id="53-mass-exploitation-setup"><b>5.3 Mass Exploitation Setup</b></h3><p class="paragraph" style="text-align:left;">When one bug affects many programs:</p><ul><li><p class="paragraph" style="text-align:left;">Recon pipeline to find all affected targets before public disclosure</p></li><li><p class="paragraph" style="text-align:left;">Report templates ready to customize (<code>github.com/rhynorater/reports</code>)</p></li><li><p class="paragraph" style="text-align:left;">Trusted collaborators to split targets and file simultaneously</p></li><li><p class="paragraph" style="text-align:left;">Splitting agreements set up in advance</p></li></ul><h3 class="heading" style="text-align:left;" id="54-program-selection"><b>5.4 Program Selection</b></h3><p class="paragraph" style="text-align:left;">Top 10–25% of programs pay 90% of bounties. But smaller programs (under $10K/90 days) have less competition and room to become a domain expert. Both strategies are valid.</p><h1 class="heading" style="text-align:left;" id="extra-tips-from-shubs"><b>Extra tips from </b><b><a class="link" href="https://x.com/infosec_au?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">Shubs</a></b><b>:</b></h1><p class="paragraph" style="text-align:left;">I&#39;ve spent a lot of time going through more basic tips in previous videos, so I&#39;ll impart whatever wisdom I&#39;ve learnt since those previous videos. If you want to check those out, you can look at Code Review the Offensive Security Way. Since that video, here are my top 5 new tips:</p><ol start="1"><li><p class="paragraph" style="text-align:left;">Instrument the application deeply. If you look at our Magento CosmicString vulnerability on the Assetnote blog, you&#39;ll find that it was a complex nested deserialization bug. We spent a lot of time setting up debuggers, but actually it was way easier to understand the flow by just putting in some strategic prints in the code and reading outputs. A debugger was helpful, but printing was faster and easier to grok. (<a class="link" href="https://www.assetnote.io/resources/research/why-nested-deserialization-is-harmful-magento-xxe-cve-2024-34102/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://www.assetnote.io/resources/research/why-nested-deserialization-is-harmful-magento-xxe-cve-2024-34102/</a>)</p></li><li><p class="paragraph" style="text-align:left;">Debuggers are usually non negotiable. Whether it&#39;s PHP, JavaScript, a .NET product, or Java, putting the effort into getting a sandbox environment with a debugger attached is always worth it. For C#, I highly recommend you use JetBrains Rider, it makes the debugging process so easy and fun. When we were auditing Sitecore, a debugger was absolutely key in proving an order of operations bug, and when we audited DotNetNuke, we needed to see the unicode transformation happen. See the research here: <a class="link" href="https://www.assetnote.io/resources/research/leveraging-an-order-of-operations-bug-to-achieve-rce-in-sitecore-8-x---10-x/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://www.assetnote.io/resources/research/leveraging-an-order-of-operations-bug-to-achieve-rce-in-sitecore-8-x---10-x/</a> & <a class="link" href="https://slcyber.io/research-center/abusing-windows-net-quirks-and-unicode-normalization-to-exploit-dnn-dotnetnuke/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://slcyber.io/research-center/abusing-windows-net-quirks-and-unicode-normalization-to-exploit-dnn-dotnetnuke/</a></p></li><li><p class="paragraph" style="text-align:left;">Understand dynamic code evaluation paths. This is something we&#39;ve historically had a lot of luck with at Assetnote. Many complex products support the evaluation of some sort of templating language, or in some cases as simple as XSLT transformation in Java, RhinoScript, or Jython, or Java Expression Language. Sandbox bypasses are also a lot of fun and usually worth a long period of investigation. See: <a class="link" href="https://slcyber.io/research-center/finding-critical-bugs-in-adobe-experience-manager/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://slcyber.io/research-center/finding-critical-bugs-in-adobe-experience-manager/</a></p></li><li><p class="paragraph" style="text-align:left;">Don&#39;t resist AI, instead equip your agent with everything it needs to be successful. This doesn&#39;t mean you stop manually auditing. You need to feed AI hunches, and you need to double down in specific areas when it doesn&#39;t think something is possible, but you do. The source code auditing ecosystem is changing rapidly with capable models like Opus 4.6. We have to continue to learn and adapt with every tool we have available.</p></li><li><p class="paragraph" style="text-align:left;">Make sure the code you are focusing on, is actually the code you want to be focusing on. Speed is critical in auditing code, so when looking at large enterprise bundles, you want to minimise the noise of vendor dependencies as much as possible. We created Hyoketsu at Assetnote for the community, and the project is available on GitHub for free: <a class="link" href="https://github.com/assetnote/hyoketsu?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://github.com/assetnote/hyoketsu</a> - this will save an immense amount of time decompiling large enterprise products.</p></li></ol><h3 class="heading" style="text-align:left;" id="quick-reference-checklist"><b>Quick-Reference Checklist</b></h3><p class="paragraph" style="text-align:left;"><b>Before You Start</b></p><ul><li><p class="paragraph" style="text-align:left;">[ ] Get source code (Docker Hub, marketplace, GitHub dorks, decompilation, source maps, <code>.git</code>)</p></li><li><p class="paragraph" style="text-align:left;">[ ] Local instance running for dynamic debugging</p></li><li><p class="paragraph" style="text-align:left;">[ ] Scope defined: pre-auth only or post-auth too?</p></li><li><p class="paragraph" style="text-align:left;">[ ] Sink list built for this language/framework</p></li></ul><p class="paragraph" style="text-align:left;"><b>First Pass</b></p><ul><li><p class="paragraph" style="text-align:left;">[ ] Routes mapped with auth middleware annotated</p></li><li><p class="paragraph" style="text-align:left;">[ ] Unauthenticated endpoints highlighted</p></li><li><p class="paragraph" style="text-align:left;">[ ] Sources listed (params, headers, cookies, uploads, WebSocket)</p></li><li><p class="paragraph" style="text-align:left;">[ ] Custom security functions flagged for later</p></li><li><p class="paragraph" style="text-align:left;">[ ] Hardcoded credentials and secrets scanned</p></li><li><p class="paragraph" style="text-align:left;">[ ] Config files and dependencies checked</p></li></ul><p class="paragraph" style="text-align:left;"><b>Source/Sink Analysis</b></p><ul><li><p class="paragraph" style="text-align:left;">[ ] Sink inventory built</p></li><li><p class="paragraph" style="text-align:left;">[ ] Each sink: can user input reach it?</p></li><li><p class="paragraph" style="text-align:left;">[ ] Each source: where does it end up?</p></li><li><p class="paragraph" style="text-align:left;">[ ] Middleware applied consistently across all routes?</p></li></ul><p class="paragraph" style="text-align:left;"><b>Common Patterns</b></p><ul><li><p class="paragraph" style="text-align:left;">[ ] Patch diffing: is the root cause fully addressed?</p></li><li><p class="paragraph" style="text-align:left;">[ ] Parser differentials: do security layer and backend parse the same way?</p></li><li><p class="paragraph" style="text-align:left;">[ ] Pre-auth surface mapped</p></li><li><p class="paragraph" style="text-align:left;">[ ] Chaining: can two low-severity findings combine?</p></li><li><p class="paragraph" style="text-align:left;">[ ] Validate-then-transform: data modified after security check?</p></li><li><p class="paragraph" style="text-align:left;">[ ] Fail-open: what happens when a check errors out?</p></li><li><p class="paragraph" style="text-align:left;">[ ] Alternative paths: do legacy/debug routes bypass new controls?</p></li></ul><p class="paragraph" style="text-align:left;"><b>Vulnerable Patterns</b></p><ul><li><p class="paragraph" style="text-align:left;">[ ] Custom sanitizers: case-insensitive? global? recursive? complete?</p></li><li><p class="paragraph" style="text-align:left;">[ ] Sanitize then decode?</p></li><li><p class="paragraph" style="text-align:left;">[ ] Auth inside a user-controllable if-body?</p></li><li><p class="paragraph" style="text-align:left;">[ ] Security check with no bailout?</p></li><li><p class="paragraph" style="text-align:left;">[ ] Regex: unescaped dots, missing anchors, wrong flags?</p></li><li><p class="paragraph" style="text-align:left;">[ ] JS <code>.replace()</code> without global flag?</p></li><li><p class="paragraph" style="text-align:left;">[ ] Replace sanitization single-pass?</p></li><li><p class="paragraph" style="text-align:left;">[ ] Dynamic function calls from user input?</p></li><li><p class="paragraph" style="text-align:left;">[ ] Type confusion: arrays, null, wrong types?</p></li><li><p class="paragraph" style="text-align:left;">[ ] CSPRNG for all security-sensitive random values?</p></li><li><p class="paragraph" style="text-align:left;">[ ] XML parsing: external entities and DTDs disabled?</p></li></ul><p class="paragraph" style="text-align:left;"><b>Dependencies</b></p><ul><li><p class="paragraph" style="text-align:left;">[ ] Versions checked against CVEs</p></li><li><p class="paragraph" style="text-align:left;">[ ] Source read for libraries processing user data</p></li><li><p class="paragraph" style="text-align:left;">[ ] No assumptions about standard libraries</p></li></ul><p class="paragraph" style="text-align:left;"><b>SSRF</b></p><ul><li><p class="paragraph" style="text-align:left;">[ ] All server-side URL-fetching identified</p></li><li><p class="paragraph" style="text-align:left;">[ ] Allowlists bypassable via open redirect?</p></li><li><p class="paragraph" style="text-align:left;">[ ] Internal services and ports enumerated</p></li><li><p class="paragraph" style="text-align:left;">[ ] Cloud metadata reachable?</p></li><li><p class="paragraph" style="text-align:left;">[ ] Response content and Content-Type noted</p></li></ul><p class="paragraph" style="text-align:left;"><b>Notes</b></p><ul><li><p class="paragraph" style="text-align:left;">[ ] Attack vectors dumped to markdown before ending session</p></li><li><p class="paragraph" style="text-align:left;">[ ] Interesting-but-unexploitable spots noted for next session</p></li></ul><p class="paragraph" style="text-align:left;"><b>Tools</b></p><ul><li><p class="paragraph" style="text-align:left;">[ ] Semgrep, CodeQL, regex101.com</p></li><li><p class="paragraph" style="text-align:left;">[ ] GAU, trufflehog, gitleaks</p></li><li><p class="paragraph" style="text-align:left;">[ ] JD-GUI/jadx, dotPeek/dnSpy</p></li><li><p class="paragraph" style="text-align:left;">[ ] git-dumper/GitTools, dive, restore-source-tree</p></li></ul><h3 class="heading" style="text-align:left;" id="resourcesworth-reading-and-watching"><b>Resources/worth reading and watching:</b></h3><ul><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://youtu.be/48vMaDUv530?si=XmBfAH7f-Ceqvibu&utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://youtu.be/48vMaDUv530?si=XmBfAH7f-Ceqvibu</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.youtube.com/watch?v=hwlFVIJnlJU&utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://www.youtube.com/watch?v=hwlFVIJnlJU</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://youtu.be/weFl9bDnRiA?si=j8iv8uJzDHQS-6TY&utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://youtu.be/weFl9bDnRiA?si=j8iv8uJzDHQS-6TY</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://youtu.be/p91UpSPfv1Q?si=ux2mD71vEWKYZlmR&utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://youtu.be/p91UpSPfv1Q?si=ux2mD71vEWKYZlmR</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://youtu.be/gvrZbOQrAgc?si=gLaEIuQ65LRa6IeQ&utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://youtu.be/gvrZbOQrAgc?si=gLaEIuQ65LRa6IeQ</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://youtu.be/4A13Mwjf_Jg?si=l4QPfhPGOKb0K6gb&utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://youtu.be/4A13Mwjf_Jg?si=l4QPfhPGOKb0K6gb</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://github.com/benhoyt/inih/blob/master/README.md?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://github.com/benhoyt/inih/blob/master/README.md</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://github.com/linux-pam/linux-pam/blob/master/modules/pam_group/pam_group.c?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://github.com/linux-pam/linux-pam/blob/master/modules/pam_group/pam_group.c</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.rfc-editor.org/rfc/rfc3164.html?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://www.rfc-editor.org/rfc/rfc3164.html</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://wiki.sei.cmu.edu/confluence/spaces/c/pages/87152445/FIO20-C.+Avoid+unintentional+truncation+when+using+fgets+or+fgetws?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://wiki.sei.cmu.edu/confluence/spaces/c/pages/87152445/FIO20-C.+Avoid+unintentional+truncation+when+using+fgets+or+fgetws</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://php.watch/articles/parse_ini_string-file-security-considerations?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://php.watch/articles/parse_ini_string-file-security-considerations</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://security.snyk.io/vuln/SNYK-JS-TOTALJS-1077069?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://security.snyk.io/vuln/SNYK-JS-TOTALJS-1077069</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://lab.ctbb.show/research/totaljs-rce-gadgets?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://lab.ctbb.show/research/totaljs-rce-gadgets</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://docs.ruby-lang.org/en/2.0.0/security_rdoc.html?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://docs.ruby-lang.org/en/2.0.0/security_rdoc.html</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.veracode.com/blog/secure-development/spring-view-manipulation-vulnerability/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://www.veracode.com/blog/secure-development/spring-view-manipulation-vulnerability/</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.yeswehack.com/learn-bug-bounty/open-source-guide-code-analysis?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)">https://www.yeswehack.com/learn-bug-bounty/open-source-guide-code-analysis</a></p></li></ul><p class="paragraph" style="text-align:left;">The more you know about an app, the easier it becomes to find some bugs. <i>Get intimate with it.</i></p><ul><li><p class="paragraph" style="text-align:left;"><i>Special thanks to </i><a class="link" href="https://github.com/rafax00?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)"><i>@rafax00</i></a><i>, </i><a class="link" href="https://github.com/apostolovd?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)"><i>@apostolovd</i></a><i> and </i><a class="link" href="https://x.com/infosec_au?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow" style="color: rgb(140, 170, 238)"><i>@infosec_au</i></a><i> for taking some time to review it and helping me improve this script!</i></p></li><li><p class="paragraph" style="text-align:left;"><i>Special thanks to </i><a class="link" href="https://instagram.com/gingko.be?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-172-critical-guide-to-code-review" target="_blank" rel="noopener noreferrer nofollow"><i>Autumn Buggs</i></a><i> for making the graphics and allowing me to use them on this week’s HackerNotes =)</i></p></li></ul><p class="paragraph" style="text-align:left;">Keep Hacking!</p><p class="paragraph" style="text-align:left;"><i>— Yuji @ Critical Thinking Team</i></p></div></div>
  ]]></content:encoded>
</item>

      <item>
  <title>[HackerNotes Ep. 171] Protobuf XSS, Cookie Case Sensitivity, and Clickjacking Tricks</title>
  <description>Solo episode from Justin looking at some recent bugs he found about client side stuff</description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-171-protobuf-xss-cookie-case-sensitivity-and-clickjacking-tricks</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-171-protobuf-xss-cookie-case-sensitivity-and-clickjacking-tricks</guid>
  <pubDate>Thu, 23 Apr 2026 10:01:00 +0000</pubDate>
  <atom:published>2026-04-23T10:01:00Z</atom:published>
    <dc:creator>Aituglo ‎</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr">Hacker TL;DR</h2><ul><li><p class="paragraph" style="text-align:left;">Protobuf-based XSS: constrain your payload to pure ASCII (0x01–0x7F) and align CRLF sequences with length-prefixed fields to survive browser DOM string mutation</p></li><li><p class="paragraph" style="text-align:left;">CSPT is not a web-only primitive: any component that issues HTTP requests with user-controlled path input (desktop apps, IoT, sockets) is in scope</p></li><li><p class="paragraph" style="text-align:left;">Cookie <code>Path</code> attributes are <b>case-sensitive</b>, but most back-ends treat paths case-insensitively: flip one character to drop a cookie while keeping the session</p></li><li><p class="paragraph" style="text-align:left;">Leaking an &quot;age&quot; field leaks the full date of birth: watch the day it increments and you have the month + day</p></li></ul><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/c7a25360-b133-400b-b6d2-789a252e0293/ThreatLocker-1.png?t=1773171761"/></div><p class="paragraph" style="text-align:center;"><span style="color:rgb(249, 250, 251);font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"><b>Today&#39;s Sponsor:</b></span><span style="color:rgb(249, 250, 251);font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"> Check out ThreatLocker Ringfencing </span><span style="font-family:inherit;font-size:16px;"><a class="link" href="https://www.criticalthinkingpodcast.io/tl-rf?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-171-protobuf-xss-cookie-case-sensitivity-and-clickjacking-tricks" target="_blank" rel="noopener noreferrer nofollow">https://www.criticalthinkingpodcast.io/tl-rf</a></span></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="protobuf-based-xss-binary-payload-s">Protobuf-Based XSS: Binary Payload Smithing</h2><p class="paragraph" style="text-align:left;">Justin recently found an XSS on a target that heavily uses <b>Protobuf</b>. The twist: the vulnerability only triggers when sending the <b>raw binary protobuf</b> over the wire, not <code>protojson</code>, not base64-encoded protobuf. That means the payload has to survive a top-level navigation in the browser, delivered through a form submission with <code>Content-Type: text/plain</code>.</p><p class="paragraph" style="text-align:left;">Three hard problems surfaced during exploitation.</p><h3 class="heading" style="text-align:left;" id="challenge-1-dom-string-mutation-man">Challenge 1: DOM String Mutation Mangles Binary Bytes</h3><p class="paragraph" style="text-align:left;">When raw binary lands in the DOM, the browser applies a silent normalization pass. Any byte outside the pure ASCII range (<code>0x01</code>–<code>0x7F</code>) risks being rewritten, which destroys the length-prefixed structure of protobuf.</p><p class="paragraph" style="text-align:left;">So, you should constrain the entire serialized payload to <code>0x01</code>–<code>0x7F</code>. Tweak field sizes by:</p><ul><li><p class="paragraph" style="text-align:left;">Inflating or deflating string values</p></li><li><p class="paragraph" style="text-align:left;">Shifting between protobuf wire types when possible</p></li><li><p class="paragraph" style="text-align:left;">Omitting non-required fields entirely</p></li></ul><h3 class="heading" style="text-align:left;" id="challenge-2-the-lone-lf-problem">Challenge 2: The Lone LF Problem</h3><p class="paragraph" style="text-align:left;">Protobuf encodes field number 1 with wire type 2 (length-delimited string) as <code>0x0A</code>, a bare line feed. Browsers &quot;helpfully&quot; fix orphan <code>LF</code> bytes by prepending a <code>CR</code>, which shifts every downstream byte offset and breaks the entire payload.</p><p class="paragraph" style="text-align:left;"><b>The fix:</b> Pad a junk field <b>just before</b> the problematic <code>0x0A</code> so that it ends in <code>0x0D</code>. The byte sequence now reads as a legitimate <code>CRLF</code> (<code>0x0D 0x0A</code>), the browser leaves it alone, and the protobuf parser is happy.</p><div class="codeblock"><pre><code>... 0x0D | 0x0A &lt;len&gt; &lt;string bytes&gt; ...
  ^^^^^^^^^^^^
  fake CRLF — browser skips the auto-fix
</code></pre></div><h3 class="heading" style="text-align:left;" id="challenge-3-the-mandatory-in-form-s">Challenge 3: The Mandatory <code>=</code> in Form Submissions</h3><p class="paragraph" style="text-align:left;">A <code>text/plain</code> form submission still requires at least one <code>=</code> character to split the key from the value. Stuffing a stray <code>0x3D</code> into a binary protobuf stream normally corrupts it.</p><p class="paragraph" style="text-align:left;">Pad a field so that its <b>declared length byte</b> lands exactly at the position where the <code>=</code> is needed. The length prefix <i>is</i> <code>0x3D</code>. The protobuf parser reads it as a length; the form parser reads it as the key/value delimiter. Everyone wins.</p><h3 class="heading" style="text-align:left;" id="challenge-4-the-trailing-crlf-appen">Challenge 4: The Trailing CRLF Appended by the Browser</h3><p class="paragraph" style="text-align:left;">Browsers append a <code>CRLF</code> at the end of form-submitted bodies. That extra two bytes breaks protobuf framing.</p><p class="paragraph" style="text-align:left;"><b>The solution:</b> Define the final string field with a declared length that is <b>two bytes longer</b> than the payload actually supplied. When the browser appends <code>\r\n</code>, those two bytes get absorbed into the final string field instead of being parsed as extraneous data.</p><div class="codeblock"><pre><code>last field declared: string[4]
payload provides:    2 bytes
browser appends:     \r\n (2 bytes)
result: valid 4-byte string, no parser errors
</code></pre></div><p class="paragraph" style="text-align:left;">The end result is a working XSS delivered as a raw protobuf-encoded form submission, a beautiful chain of byte-level alignment tricks.</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/dc7a3669-6d67-48a9-aa2b-5dbdd0d8e41e/_bannerHackerNotesShort.png?t=1769044664"/><div class="image__source"><a class="image__source_link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-171-protobuf-xss-cookie-case-sensitivity-and-clickjacking-tricks" rel="noopener" target="_blank"><span class="image__source_text"><p>Check out the CTBB Discord!</p></span></a></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-171-protobuf-xss-cookie-case-sensitivity-and-clickjacking-tricks" target="_blank" rel="noopener noreferrer nofollow">You can also find some hacker swag here.</a></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="pii-leak-mindset-derivative-disclos">PII Leak Mindset: Derivative Disclosure</h2><p class="paragraph" style="text-align:left;">A small disclosure can unlock a much larger one. Justin recently leaked the &quot;age&quot; field of a user on a program. On its own, an age is mild PII. But <b>age changes over time</b>, by monitoring the endpoint and watching for the day the value increments, an attacker recovers the exact date of birth.</p><p class="paragraph" style="text-align:left;">When triaging a data-leak bug, ask: <i>how does this value change, and what does the delta reveal?</i> Any monotonically-increasing or threshold-crossing field (age, account tier, loyalty points, subscription days remaining) can be differentiated against time to recover finer-grained private data.</p><h2 class="heading" style="text-align:left;" id="cspt-beyond-the-browser">CSPT Beyond the Browser</h2><p class="paragraph" style="text-align:left;">Client-Side Path Traversal is usually framed as a web vulnerability, but it applies anywhere an HTTP client accepts attacker-controlled path input. Justin hit this on a <b>desktop application</b> recently.</p><p class="paragraph" style="text-align:left;">The flow:</p><ol start="1"><li><p class="paragraph" style="text-align:left;">The desktop app exposed a WebSocket interface reachable from another machine</p></li><li><p class="paragraph" style="text-align:left;">A socket message caused the app to issue an HTTP request using user-supplied path input</p></li><li><p class="paragraph" style="text-align:left;">A path traversal sequence in that input truncated the URL and routed the request to a completely different endpoint</p></li><li><p class="paragraph" style="text-align:left;">That endpoint delivered meaningful impact</p></li></ol><p class="paragraph" style="text-align:left;">Any component that constructs HTTP requests from untrusted input is a CSPT candidate like desktop apps, IoT firmware, mobile apps, internal services. The attack surface extends far past the browser.</p><p class="paragraph" style="text-align:left;">Web hackers often hesitate to pivot into desktop, binary, or embedded targets. CSPT is a rare primitive that transfers cleanly across all of them.</p><h2 class="heading" style="text-align:left;" id="capital-letters-are-overpowered">Capital Letters Are Overpowered</h2><p class="paragraph" style="text-align:left;">A recurring bypass pattern from recent hunting: the <b>case sensitivity mismatch</b> between browsers and back-ends.</p><p class="paragraph" style="text-align:left;">The scenario: a specific cookie must be <b>absent</b> for a request to trigger a vulnerability, but the rest of the session cookies must still be present (so the victim is authenticated). A credentialless iframe strips everything, which defeats the purpose.</p><p class="paragraph" style="text-align:left;">The solution hinges on a subtle asymmetry:</p><ul><li><p class="paragraph" style="text-align:left;">The cookie&#39;s <code>Path</code> attribute is <b>case-sensitive</b> per RFC 6265</p></li><li><p class="paragraph" style="text-align:left;">Most back-end HTTP routers treat paths <b>case-insensitively</b> (or normalize them)</p></li></ul><p class="paragraph" style="text-align:left;">If the cookie was set with <code>Path=/abc</code>, sending the request to <code>/Abc</code> (capital <code>A</code>) causes the browser to <b>withhold the cookie</b> while the server still routes the request to the same handler.</p><div class="codeblock"><pre><code>Set-Cookie: session=...; Path=/abc

GET /abc  → cookie sent
GET /Abc  → cookie NOT sent, but server hits same endpoint
</code></pre></div><p class="paragraph" style="text-align:left;">Whenever you need to strip a single cookie surgically while preserving the rest of the session, test case variations on the path. Encoding tricks (<code>%2F</code>, double-encoding) work too, but plain uppercase is often enough.</p><h2 class="heading" style="text-align:left;" id="iframe-clickjacking-control-click-t">Iframe Clickjacking + Control-Click Top Navigation</h2><p class="paragraph" style="text-align:left;">A chained attack from a recent engagement:</p><ol start="1"><li><p class="paragraph" style="text-align:left;">CSRF-set a cookie via an iframe (constrained to iframe delivery)</p></li><li><p class="paragraph" style="text-align:left;">Render a link inside that iframe</p></li><li><p class="paragraph" style="text-align:left;">Victim <b>control-clicks</b> the link, opening it in a new tab as a top-level navigation</p></li><li><p class="paragraph" style="text-align:left;">The top-level navigation completes a login CSRF, leading to account takeover</p></li></ol><p class="paragraph" style="text-align:left;"><code>Ctrl+Click</code> (or <code>Cmd+Click</code> on macOS) from within an iframe is a reliable way to escalate an iframe-constrained interaction into a top-level navigation. It counts as a user gesture and bypasses pop-up blockers.</p><h3 class="heading" style="text-align:left;" id="bonus-windowopen-via-keydown-event">Bonus: <code>window.open</code> via <code>keydown</code> Event</h3><p class="paragraph" style="text-align:left;">The user gesture requirement for <code>window.open</code> isn&#39;t limited to clicks. A <code>keydown</code> event <b>also satisfies the gesture requirement</b>, which is sometimes easier to obtain than a click: think fake &quot;press any key to continue&quot; prompts or keyboard-navigable cookie banners.</p><h3 class="heading" style="text-align:left;" id="bonus-hiding-ui-until-ctrl-is-held">Bonus: Hiding UI Until Ctrl Is Held</h3><p class="paragraph" style="text-align:left;">To make control-click clickjacking more socially plausible, gate the visible UI behind the control-key state:</p><ul><li><p class="paragraph" style="text-align:left;">Listen for <code>keydown</code>/<code>keyup</code> on the <code>Control</code> key</p></li><li><p class="paragraph" style="text-align:left;">Only render the target button when the modifier is held</p></li><li><p class="paragraph" style="text-align:left;">The victim sees a cleaner UI and naturally control-clicks as instructed</p></li></ul><p class="paragraph" style="text-align:left;">Modern AI-assisted UI work makes this kind of PoC polish trivial. No excuse for janky clickjacking demos anymore.</p><h3 class="heading" style="text-align:left;" id="svg-enhanced-clickjacking-lra">SVG-Enhanced Clickjacking (LRA)</h3><p class="paragraph" style="text-align:left;">Classic clickjacking suffers from a UX tell: the invisible iframe in front of the visible UI prevents any button depression animation, and attentive users notice. <b>LRA</b>&#39;s recent SVG research solves this by building responsive, animated UI overlays using SVG magic, the target UI stays interactive-looking while the invisible iframe collects the click.</p><p class="paragraph" style="text-align:left;">Justin flagged this as library material worth building on. Expect more coverage in a dedicated episode.</p><h2 class="heading" style="text-align:left;" id="resources">Resources</h2><ul><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://protobuf.dev/programming-guides/encoding/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-171-protobuf-xss-cookie-case-sensitivity-and-clickjacking-tricks" target="_blank" rel="noopener noreferrer nofollow">Protobuf Encoding Spec</a></b>: wire format reference for byte-level payload smithing</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://datatracker.ietf.org/doc/html/rfc6265?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-171-protobuf-xss-cookie-case-sensitivity-and-clickjacking-tricks" target="_blank" rel="noopener noreferrer nofollow">RFC 6265 - HTTP State Management Mechanism</a></b>: cookie <code>Path</code> attribute semantics</p></li></ul><hr class="content_break"><p class="paragraph" style="text-align:left;">That&#39;s it for the week, keep hacking!</p></div></div>
  ]]></content:encoded>
</item>

      <item>
  <title>[HackerNotes Ep. 170] Live From South Korea : AI Exfiltration, SDK Hacking, and the Art of Self-Advocacy</title>
  <description>New live episode from South Korea to cover the latest LHE by Hackerone and by Google</description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-170-live-from-south-korea-ai-exfiltration-sdk-hacking-and-the-art-of-self-advocacy</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-170-live-from-south-korea-ai-exfiltration-sdk-hacking-and-the-art-of-self-advocacy</guid>
  <pubDate>Thu, 16 Apr 2026 10:01:00 +0000</pubDate>
  <atom:published>2026-04-16T10:01:00Z</atom:published>
    <dc:creator>Aituglo ‎</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr">Hacker TL;DR</h2><ul><li><p class="paragraph" style="text-align:left;">AI exfiltration through binary oracles and HTML injection represents a new, high-impact vulnerability class in AI-integrated products</p></li><li><p class="paragraph" style="text-align:left;">SDK code review with Claude Code eliminates the friction of tracing user input through REST API wrappers to find path traversals</p></li><li><p class="paragraph" style="text-align:left;">Auth grant scope expansion is a consistently overlooked attack surface: the OAuth consent screen may not reflect actual permissions</p></li><li><p class="paragraph" style="text-align:left;">Advocating for your findings at live events is a skill: prepare a hit list, get verbal commitments, and document everything on the report</p></li></ul><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/dc7a3669-6d67-48a9-aa2b-5dbdd0d8e41e/_bannerHackerNotesShort.png?t=1769044664"/><div class="image__source"><a class="image__source_link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-170-live-from-south-korea-ai-exfiltration-sdk-hacking-and-the-art-of-self-advocacy" rel="noopener" target="_blank"><span class="image__source_text"><p>Check out the CTBB Discord!</p></span></a></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-170-live-from-south-korea-ai-exfiltration-sdk-hacking-and-the-art-of-self-advocacy" target="_blank" rel="noopener noreferrer nofollow">You can also find some hacker swag here.</a></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="backto-back-in-south-korea">Back-to-Back in South Korea</h2><p class="paragraph" style="text-align:left;">Justin and Joseph recorded this episode while walking back to their hotel rooms after the Google LHE. The Google event was smaller than usual, around 15 to 20 invited researchers, but the quality of findings was notably higher than previous iterations.</p><p class="paragraph" style="text-align:left;">What stood out was the <b>maturity of AI vulnerability research</b>. This was not researchers forcing social engineering edge cases through chatbots. These were technically rigorous exploitation chains with demonstrated, consistent impact.</p><h2 class="heading" style="text-align:left;" id="ai-exfiltration-a-new-vulnerability">AI Exfiltration: A New Vulnerability Class</h2><h3 class="heading" style="text-align:left;" id="binary-oracle-attacks">Binary Oracle Attacks</h3><p class="paragraph" style="text-align:left;">Several researchers demonstrated <b>data exfiltration through binary channels</b> in AI products. The technique leverages the ability to ask an AI system yes-or-no questions, extracting one bit of information per query. By iterating through these boolean responses, an attacker can reconstruct sensitive data bit by bit.</p><p class="paragraph" style="text-align:left;">The impact scales with what the AI has access to. In Google&#39;s ecosystem, that often means the target&#39;s entire document library.</p><p class="paragraph" style="text-align:left;">When assessing AI features, map what data the AI agent can access. Even a single-bit exfiltration channel becomes critical when the backend has access to sensitive user data.</p><h3 class="heading" style="text-align:left;" id="html-injection-with-amplified-impac">HTML Injection With Amplified Impact</h3><p class="paragraph" style="text-align:left;">Two to three show-and-tell presentations revealed <b>HTML injection yielding far more impact than traditionally expected</b>. In conventional contexts, HTML injection rarely escalates beyond low severity. But in AI chatbots that render link previews, images, and formatted content, and that have been granted access to user data, the injection surface becomes a viable exfiltration path.</p><p class="paragraph" style="text-align:left;">The pattern repeats across Google&#39;s AI chat features: a new rendering capability ships (preview cards, image rendering, link unfurling), and suddenly, what was previously inert HTML becomes an active attack vector.</p><h2 class="heading" style="text-align:left;" id="tracking-features-as-attack-surface">Tracking Features as Attack Surface</h2><p class="paragraph" style="text-align:left;">A consistent takeaway from this event: <b>the researchers found that the best bugs were the ones tracking new feature releases</b>. Google ships AI capabilities at a rapid pace, and each new feature introduces:</p><ul><li><p class="paragraph" style="text-align:left;"><b>Cross-product pivot points</b> where a feature in one product affects another</p></li><li><p class="paragraph" style="text-align:left;"><b>New rendering surfaces</b> that may not have undergone the same security review as core functionality</p></li><li><p class="paragraph" style="text-align:left;"><b>Regression opportunities</b> where previously patched vulnerabilities reappear in new contexts</p></li></ul><p class="paragraph" style="text-align:left;">So set up regression testing for previously reported bugs. With the velocity of AI feature development, old vulnerabilities resurface regularly, and these are well-compensated findings.</p><h2 class="heading" style="text-align:left;" id="the-slop-report-problem">The Slop Report Problem</h2><p class="paragraph" style="text-align:left;">Google&#39;s triage team shared an observation: <b>AI-generated vulnerability reports are degrading the signal-to-noise ratio</b>. The consequences are significant:</p><ul><li><p class="paragraph" style="text-align:left;">Triagers close reports they cannot parse due to verbosity and lack of clarity</p></li><li><p class="paragraph" style="text-align:left;">Valid bugs get buried inside AI-written slop, discovered only when a cleaner duplicate arrives later</p></li><li><p class="paragraph" style="text-align:left;">Reports that do not demonstrate understanding of the product (confusing discovery docs with secrets, treating public API keys as vulnerabilities) erode trust</p></li></ul><h3 class="heading" style="text-align:left;" id="how-to-stand-out">How to Stand Out</h3><p class="paragraph" style="text-align:left;">Justin has refined a <b>VRP reporting agent</b> that produces concise output, though even after extensive prompt engineering, manual editing remains necessary. The key differentiator, however, is <b>POC video recording</b>.</p><p class="paragraph" style="text-align:left;">The workflow:</p><ol start="1"><li><p class="paragraph" style="text-align:left;">The moment a potential vulnerability surfaces, start screen recording</p></li><li><p class="paragraph" style="text-align:left;">Walk through the exploitation live, narrating the steps</p></li><li><p class="paragraph" style="text-align:left;">Save the recording immediately: AI behavior is non-deterministic, so capturing it on first trigger matters</p></li><li><p class="paragraph" style="text-align:left;">Write the formal report based on the video</p></li></ol><p class="paragraph" style="text-align:left;">A POC video cannot be AI-forged, which makes it a trust signal for triagers operating in a sea of generated content.</p><h2 class="heading" style="text-align:left;" id="sdk-security-path-traversals-via-cl">SDK Security: Path Traversals via Claude Code</h2><p class="paragraph" style="text-align:left;">Moving away from AI-specific findings, Justin highlighted a powerful technique: <b>auditing SDKs for path traversal and injection vulnerabilities using Claude Code</b>.</p><p class="paragraph" style="text-align:left;">The underlying pattern is straightforward:</p><ol start="1"><li><p class="paragraph" style="text-align:left;">SDKs wrap REST APIs</p></li><li><p class="paragraph" style="text-align:left;">Function parameters like <code>userId</code> map directly to URL path segments</p></li><li><p class="paragraph" style="text-align:left;">Without proper sanitization, an attacker can manipulate these parameters to traverse endpoints</p></li></ol><div class="codeblock"><pre><code>getUser(userId) → GET /api/users/&#123;userId&#125;
getUser(&quot;../organizations/target&quot;) → GET /api/organizations/target
getUser(&quot;../../admin/delete&quot;) → potential destructive action
</code></pre></div><p class="paragraph" style="text-align:left;"><b>Methodology with Claude Code:</b></p><ul><li><p class="paragraph" style="text-align:left;">Feed the SDK source code into a Claude Code session</p></li><li><p class="paragraph" style="text-align:left;">Prime it to identify all user-controlled input flowing into HTTP request construction</p></li><li><p class="paragraph" style="text-align:left;">Trace each parameter through the wrapper to the underlying REST call</p></li><li><p class="paragraph" style="text-align:left;">Identify missing sanitization at each boundary</p></li></ul><p class="paragraph" style="text-align:left;">What required manual code review at past events is now a focused Claude Code session. Someone dominated a live hacking event years ago by finding traversals in SDKs through manual review. The same approach is now dramatically more accessible.</p><h2 class="heading" style="text-align:left;" id="auth-grant-scope-expansion">Auth Grant Scope Expansion</h2><p class="paragraph" style="text-align:left;">You can find a bunch of different bugs on Oauth scope. The pattern:</p><ul><li><p class="paragraph" style="text-align:left;">An API requires an OAuth-style auth grant with defined scopes</p></li><li><p class="paragraph" style="text-align:left;">The user consents to specific permissions via checkboxes</p></li><li><p class="paragraph" style="text-align:left;">The resulting grant actually authorizes <b>broader access than what was displayed</b></p></li><li><p class="paragraph" style="text-align:left;">MCP servers and third-party integrations inherit these expanded permissions</p></li></ul><p class="paragraph" style="text-align:left;">This remains an under-explored attack surface. Most researchers click through OAuth consent screens without auditing the actual scope of the resulting token.</p><p class="paragraph" style="text-align:left;">For any target that exposes APIs behind OAuth grants, compare the displayed consent scopes against the actual token permissions.</p><h2 class="heading" style="text-align:left;" id="protobuf-hacking-with-protoscope">Protobuf Hacking With Protoscope</h2><p class="paragraph" style="text-align:left;">Google&#39;s extensive use of protobuf creates friction for security researchers, particularly when dealing with binary-encoded payloads. Justin recommends <b>protoscope</b> as the best CLI solution.</p><p class="paragraph" style="text-align:left;"><b>How it works:</b></p><div class="codeblock"><pre><code># Define fields with numbers and types
2: &#123;&quot;some string value&quot;&#125;
4: &#123;3: 6&#125;
</code></pre></div><p class="paragraph" style="text-align:left;">The tool operates via stdin/stdout, making it composable with existing workflows:</p><p class="paragraph" style="text-align:left;"><b>Workflow with Caido:</b></p><ol start="1"><li><p class="paragraph" style="text-align:left;">Base64-encode the protobuf payload</p></li><li><p class="paragraph" style="text-align:left;">Import into Caido</p></li><li><p class="paragraph" style="text-align:left;">Use the encode/decode workflow to avoid stray byte issues</p></li><li><p class="paragraph" style="text-align:left;">Modify fields, re-encode, and replay</p></li></ol><h2 class="heading" style="text-align:left;" id="web-socket-attack-surface">WebSocket Attack Surface</h2><p class="paragraph" style="text-align:left;">The HackerOne event featured heavy WebSocket usage, highlighting an <b>under-tested attack surface</b>:</p><ul><li><p class="paragraph" style="text-align:left;">WebSocket security is less understood and less assessed than traditional HTTP</p></li><li><p class="paragraph" style="text-align:left;">Caido lacked robust WebSocket support at the time (a new WebSocket repeater is shipping soon, with Justin contributing to the interface design)</p></li></ul><p class="paragraph" style="text-align:left;">WebSocket-heavy applications represent a meaningful opportunity for researchers willing to invest in tooling and methodology.</p><h2 class="heading" style="text-align:left;" id="webhook-security-and-friction-elimi">Webhook Security and Friction Elimination</h2><ul><li><p class="paragraph" style="text-align:left;">Webhooks are public endpoints by design. They exist to process third-party data</p></li><li><p class="paragraph" style="text-align:left;">Key questions: Where does ingested data flow? How is authentication validated? Is cryptographic verification implemented correctly?</p></li><li><p class="paragraph" style="text-align:left;">Most researchers encounter signature validation (SHA-256 HMAC, JWT verification) and disengage due to friction</p></li></ul><p class="paragraph" style="text-align:left;">This is where <b>Claude Code fundamentally changes the calculus</b>. Cryptographic operations that previously represented a hard barrier like re-signing payloads, computing HMACs, bit-flipping attacks on hash extensions, become trivial to implement in a Claude Code session.</p><p class="paragraph" style="text-align:left;">The implication is clear: any attack surface previously gated by cryptographic complexity deserves a second look.</p><h2 class="heading" style="text-align:left;" id="human-tokens-and-self-advocacy">Human Tokens and Self-Advocacy</h2><p class="paragraph" style="text-align:left;">NetworkChuck introduced the phrase &quot;don&#39;t waste your human tokens&quot; in a group chat, applying AI terminology to human effort allocation. Joseph expanded the concept to <b>&quot;mouth tokens&quot;</b>, specifically, the skill of advocating for your findings at live hacking events.</p><h3 class="heading" style="text-align:left;" id="the-advocacy-playbook">The Advocacy Playbook</h3><p class="paragraph" style="text-align:left;"><b>1. Know what deserves a fight.</b><br>Not every triage disagreement warrants pushback. Burning credibility on minor severity disputes costs more than the bounty delta.</p><p class="paragraph" style="text-align:left;"><b>2. Prepare a hit list.</b><br>Before engaging with triagers or program representatives, have a clear elevator pitch for each report. Know the impact, the reproduction path, and the severity argument.</p><p class="paragraph" style="text-align:left;"><b>3. Walk them through it live.</b><br>Approach triagers directly: &quot;You are going to triage this, let me walk you through it quickly.&quot; A live demonstration is more compelling than a written report.</p><p class="paragraph" style="text-align:left;"><b>4. Get verbal commitment.</b><br>Summarize at the end of each discussion: &quot;So we agree this is a High?&quot; Many researchers skip this step, leaving severity decisions ambiguous.</p><p class="paragraph" style="text-align:left;"><b>5. Document immediately.</b><br>Comment on the report on what was discussed and agreed upon. This creates an official record and signals professionalism.</p><p class="paragraph" style="text-align:left;">The researchers who consistently earn top payouts at live events are not always the ones finding the most bugs; they are the ones who articulate impact effectively and follow through on every report.</p><h2 class="heading" style="text-align:left;" id="resources">Resources</h2><ul><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://github.com/protocolbuffers/protoscope?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-170-live-from-south-korea-ai-exfiltration-sdk-hacking-and-the-art-of-self-advocacy" target="_blank" rel="noopener noreferrer nofollow">Protoscope</a></b> - CLI for binary protobuf encoding/decoding</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://bughunters.google.com/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-170-live-from-south-korea-ai-exfiltration-sdk-hacking-and-the-art-of-self-advocacy" target="_blank" rel="noopener noreferrer nofollow">Google VRP</a></b> - Google&#39;s bug bounty program</p></li></ul><hr class="content_break"><p class="paragraph" style="text-align:left;">That&#39;s it for the week, keep hacking!</p></div></div>
  ]]></content:encoded>
</item>

      <item>
  <title>[HackerNotes Ep. 169] OAuth 2.1, MCP Authorization Security, and the Framework CVEs You Should Know</title>
  <description>A practical look at OAuth 2.1 and MCP security pitfalls, from PKCE downgrades and SSRF tricks to token misuse and recent framework CVEs.</description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-169-oauth-2-1-mcp-authorization-security-and-the-framework-cves-you-should-know</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-169-oauth-2-1-mcp-authorization-security-and-the-framework-cves-you-should-know</guid>
  <pubDate>Thu, 09 Apr 2026 10:01:00 +0000</pubDate>
  <atom:published>2026-04-09T10:01:00Z</atom:published>
    <dc:creator>Aituglo ‎</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr">Hacker TL;DR</h2><ul><li><p class="paragraph" style="text-align:left;">OAuth 2.1 mandates PKCE, so strip <code>code_challenge</code> from &quot;2.1-compliant&quot; servers to test for downgrade vulnerabilities</p></li><li><p class="paragraph" style="text-align:left;">MCP&#39;s new Client Identity Metadata Documents (CIMD) turn the authorization server into an SSRF oracle via <code>client_manifest_uri</code>, <code>logo_uri</code>, and <code>jwks_uri</code></p></li><li><p class="paragraph" style="text-align:left;">Token passthrough in agentic workflows creates confused deputy vulnerabilities, so check if sub-agents inherit full-scope tokens</p></li><li><p class="paragraph" style="text-align:left;">Framework CVEs are spiking as libraries adopt 2.1: fingerprint the OAuth stack and hunt implementation quirks</p></li></ul><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/c7a25360-b133-400b-b6d2-789a252e0293/ThreatLocker-1.png?t=1773171761"/></div><p class="paragraph" style="text-align:center;"><span style="color:rgb(249, 250, 251);font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"><b>Today&#39;s Sponsor:</b></span><span style="color:rgb(249, 250, 251);font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"> Check out ThreatLocker Ringfencing </span><span style="font-family:inherit;font-size:16px;"><a class="link" href="https://www.criticalthinkingpodcast.io/tl-rf?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-169-oauth-2-1-mcp-authorization-security-and-the-framework-cves-you-should-know" target="_blank" rel="noopener noreferrer nofollow">https://www.criticalthinkingpodcast.io/tl-rf</a></span></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="in-the-news"></h2><h2 class="heading" style="text-align:left;" id="o-auth-21-what-changes-and-why-it-m">OAuth 2.1: What Changes and Why It Matters</h2><h3 class="heading" style="text-align:left;" id="the-core-protocol-shifts">The Core Protocol Shifts</h3><p class="paragraph" style="text-align:left;">OAuth 2.1 introduces four non-negotiable changes:</p><ol start="1"><li><p class="paragraph" style="text-align:left;"><b>PKCE is mandatory.</b> Servers MUST reject authorization requests missing <code>code_challenge</code>. No exceptions, no optional flag.</p></li><li><p class="paragraph" style="text-align:left;"><b>Implicit Flow is dead.</b> Officially removed from the specification.</p></li><li><p class="paragraph" style="text-align:left;"><b>ROPC is dead.</b> Resource Owner Password Credentials flow removed entirely.</p></li><li><p class="paragraph" style="text-align:left;"><b>Exact redirect URI matching.</b> No more wildcard subdomains or loose path matching. Exact string comparison only.</p></li><li><p class="paragraph" style="text-align:left;"><b>Bearer tokens prohibited in URIs.</b> No more <code>?access_token=</code> in query strings. Tokens go in the <code>Authorization: Bearer</code> header or, as a fallback, in a <code>application/x-www-form-urlencoded</code> POST body.</p></li></ol><div class="image"><img alt="" class="image__image" style="" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/864bfa12-199d-4239-af3e-239c18bad95d/image.jpeg?t=1775564390"/></div><p class="paragraph" style="text-align:left;">From a security testing perspective, the attack surface shifts significantly. The days of intercepting tokens from URL fragments (Implicit Flow) or harvesting <code>access_token</code> parameters from server logs and Referer headers are numbered, at least on compliant implementations.</p><p class="paragraph" style="text-align:left;">The real vulnerabilities live in the <i>transition gap</i>. Targets claiming 2.1 compliance while maintaining backward compatibility with 2.0 clients are prime hunting ground.</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/dc7a3669-6d67-48a9-aa2b-5dbdd0d8e41e/_bannerHackerNotesShort.png?t=1769044664"/><div class="image__source"><a class="image__source_link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-169-oauth-2-1-mcp-authorization-security-and-the-framework-cves-you-should-know" rel="noopener" target="_blank"><span class="image__source_text"><p>Check out the CTBB Discord!</p></span></a></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-169-oauth-2-1-mcp-authorization-security-and-the-framework-cves-you-should-know" target="_blank" rel="noopener noreferrer nofollow">You can also find some hacker swag here.</a></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="the-pkce-downgrade-bug-1-to-test">The PKCE Downgrade: Bug #1 to Test</h2><p class="paragraph" style="text-align:left;">In a standard PKCE flow, the client sends a <code>code_challenge</code> in the authorization request, the server stores it, and later the client proves possession by submitting the corresponding <code>code_verifier</code> during the token exchange. If they match, the token is issued.</p><div class="image"><img alt="" class="image__image" style="" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/68c61e23-1271-42b3-bbab-6fe8f2eb54c4/image.png?t=1775564446"/></div><p class="paragraph" style="text-align:left;">The downgrade attack is straightforward: <b>strip the </b><code>code_challenge</code><b> from the authorization request</b>. If the server proceeds and issues an authorization code anyway, an attacker can intercept that code and exchange it without needing a verifier.</p><div class="codeblock"><pre><code>GET /authorize?response_type=code&amp;client_id=app&amp;redirect_uri=https://app.com/callback
# Notice: no code_challenge parameter
</code></pre></div><p class="paragraph" style="text-align:left;">This is the test case for any target advertising 2.1 compliance. Many implementations added PKCE <i>support</i> without adding PKCE <i>enforcement</i>. The feature exists, but the server still happily processes requests without it.</p><p class="paragraph" style="text-align:left;">Mobile apps are a prime target here. Developers often added PKCE for newer app versions while keeping the legacy non-PKCE endpoint alive for backward compatibility. That legacy endpoint is the interception vector.</p><h2 class="heading" style="text-align:left;" id="mcp-authorization-the-cimd-threat-m">MCP Authorization: The CIMD Threat Model</h2><p class="paragraph" style="text-align:left;">Brandyn dives into what happens when OAuth 2.1 intersects with the MCP (Model Context Protocol) authorization specification. This builds on top of RFC 8414 (server metadata discovery) and RFC 7591 (dynamic client registration) to create an entirely new threat model.</p><div class="image"><img alt="" class="image__image" style="" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/83d17162-fd6d-4175-8a47-88475a513472/image.png?t=1775564405"/></div><h3 class="heading" style="text-align:left;" id="from-push-to-pull-how-registration-">From Push to Pull: How Registration Changed</h3><p class="paragraph" style="text-align:left;">The traditional Dynamic Client Registration flow involves the client POSTing a manifest (client name, redirect URIs, logos, contacts) to a <code>/register</code> endpoint. The server creates a <code>client_id</code> and <code>client_secret</code> on the spot.</p><p class="paragraph" style="text-align:left;">The new <b>Client Identity Metadata Documents (CIMD)</b> model reverses this. Instead of the client pushing data to the server, the authorization server <i>pulls</i> a <code>client.json</code> from the client&#39;s domain:</p><div class="codeblock"><pre><code>https://agent.example.com/.well-known/mcp/client.json
</code></pre></div><p class="paragraph" style="text-align:left;">Trust is established via DNS/TLS: if the server can fetch the JSON document from the claimed domain, it trusts the contents.</p><h3 class="heading" style="text-align:left;" id="vector-1-ssrf-on-the-metadata-verif">Vector 1: SSRF on the Metadata Verification Service</h3><p class="paragraph" style="text-align:left;">This is the highest-signal attack class in the CIMD architecture. The authorization server (or its Metadata Verification Service) must make an outbound HTTP request to fetch the <code>client_manifest_uri</code>. This is basically a SSRF.</p><p class="paragraph" style="text-align:left;"><b>Test Case:</b></p><div class="codeblock"><pre><code>GET /authorize?client_manifest_uri=http://169.254.169.254/latest/meta-data/iam/security-credentials/
</code></pre></div><p class="paragraph" style="text-align:left;"><b>Sub-vectors to explore:</b></p><ul><li><p class="paragraph" style="text-align:left;">Protocol smuggling: <code>client_manifest_uri=gopher://localhost:11211/X</code> (Memcached manipulation)</p></li><li><p class="paragraph" style="text-align:left;">Different redirect behavior: How does the MVS handle 301/302/307? Does it follow redirects to internal hosts?</p></li><li><p class="paragraph" style="text-align:left;">User-Agent fingerprinting: Identify the HTTP library making the fetch to find library-specific quirks</p></li><li><p class="paragraph" style="text-align:left;">JavaScript execution: Is the fetcher a headless browser?</p></li></ul><h3 class="heading" style="text-align:left;" id="vector-2-open-redirect-via-validati">Vector 2: Open Redirect via Validation Bypasses</h3><p class="paragraph" style="text-align:left;">The authorization server MUST enforce that <code>redirect_uris</code> inside the <code>client.json</code> belong to the same domain as the document. The vulnerability lives in <i>how</i> that domain matching is implemented.</p><div class="codeblock"><pre><code>&#123;
  &quot;client_id&quot;: &quot;malicious_agent&quot;,
  &quot;redirect_uris&quot;: [
    &quot;https://evil-example.com/callback&quot;,
    &quot;https://agent.example.com.evil.com/callback&quot;
  ]
&#125;
</code></pre></div><p class="paragraph" style="text-align:left;">If the server performs a <code>.contains()</code> check or uses weak regex instead of strict domain parsing, it will accept <code>agent.example.com.evil.com</code> as belonging to <code>agent.example.com</code>, leaking the authorization code to the attacker&#39;s domain.</p><h3 class="heading" style="text-align:left;" id="vector-3-secondary-ssrf-via-nested-">Vector 3: Secondary SSRF via Nested URIs</h3><p class="paragraph" style="text-align:left;">The <code>client.json</code> document contains additional URIs that the Resource Server or Tool Server may fetch independently:</p><div class="codeblock"><pre><code>&#123;
  &quot;client_id&quot;: &quot;attack_agent&quot;,
  &quot;logo_uri&quot;: &quot;http://internal.admin.panel/api/v1/delete_user?id=123&quot;,
  &quot;jwks_uri&quot;: &quot;http://169.254.169.254/latest/meta-data/&quot;
&#125;
</code></pre></div><p class="paragraph" style="text-align:left;">If the Tool Server blindly fetches <code>logo_uri</code> to render a &quot;Trusted Agent&quot; icon in the consent UI, it executes a secondary SSRF. The same applies to <code>jwks_uri</code> if the Resource Server fetches key material from an attacker-controlled or internal location.</p><h3 class="heading" style="text-align:left;" id="vector-4-toctou-via-caching">Vector 4: TOCTOU via Caching</h3><p class="paragraph" style="text-align:left;">Since the CIMD is a static document, authorization servers may cache validated results aggressively. If an attacker can alter the <code>client.json</code> after initial validation (e.g., via a file upload vulnerability on the legitimate agent&#39;s domain), the server may still trust the cached copy while the live document now contains malicious redirect URIs.</p><h2 class="heading" style="text-align:left;" id="token-exchange-rfc-8693-fixing-the-">Token Exchange (RFC 8693): Fixing the Delegation Mess</h2><p class="paragraph" style="text-align:left;">Authentication in 2026 is in a strong place. Delegation and authorization, however, remain a disaster, and this is where bugs proliferate in agentic workflows.</p><div class="image"><img alt="" class="image__image" style="" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/0cf55b6a-af51-4bd2-bbea-2f0f8cb1abdf/image.png?t=1775564431"/></div><h3 class="heading" style="text-align:left;" id="the-token-passthrough-problem">The Token Passthrough Problem</h3><p class="paragraph" style="text-align:left;">Right now, most multi-agent workflows pass the user&#39;s full-scope token downstream. Agent A receives the user token, spins up Agent B and Agent C, and simply forwards the same <code>Authorization: Bearer [User_Token]</code> header to each. Every agent in the chain inherits the full scope.</p><div class="codeblock"><pre><code>User Token (scope: full) → Agent A → Agent B → Agent C
                           (full)    (full)    (full)
</code></pre></div><p class="paragraph" style="text-align:left;">RFC 8693 (Token Exchange) addresses this by enabling scope reduction at each delegation hop:</p><div class="codeblock"><pre><code>User Token (scope: full) → Agent A (scope: calendar.read)
                           → Agent B (scope: weather.read)
                           → Agent C (scope: files.list)
</code></pre></div><p class="paragraph" style="text-align:left;">Each hop generates a new, narrowed token. The critical detail: <b>the MCP specification currently lacks a mandate for token exchange</b>, representing a significant security gap.</p><p class="paragraph" style="text-align:left;">When testing an AI agent that invokes tools (Calendar, File Manager, Weather), intercept the traffic between the agent and the tool. If a Weather Tool receives a token with <code>scope: mail.read</code>, it can be a vulnerability. The tool can now pivot and read the user&#39;s email, even though the user only authorized a weather check.</p><p class="paragraph" style="text-align:left;">Auth0&#39;s Token Vault already implements this architecture for AI agent integrations. Expect more providers to follow.</p><h2 class="heading" style="text-align:left;" id="framework-cv-es-where-the-implement">Framework CVEs: Where the Implementation Quirks Live</h2><p class="paragraph" style="text-align:left;">As frameworks and libraries race to implement 2.1 compliance, implementation quirks create exploitable gaps. Brandyn highlights several recent CVEs that demonstrate this pattern.</p><h3 class="heading" style="text-align:left;" id="djangoallauth-mutable-claims-as-acc">Django-allauth: Mutable Claims as Account UID</h3><p class="paragraph" style="text-align:left;"><b>Source:</b> <a class="link" href="https://zeropath.com/blog/django-allauth-account-takeover-vulnerabilities?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-169-oauth-2-1-mcp-authorization-security-and-the-framework-cves-you-should-know" target="_blank" rel="noopener noreferrer nofollow">ZeroPath Audit</a></p><p class="paragraph" style="text-align:left;">The vulnerability is elegant in its simplicity: Django-allauth used the <code>preferred_username</code> claim from the OAuth response as the account UID. The problem? <code>preferred_username</code> is <b>mutable</b> on providers like Okta and NetIQ. An attacker simply changes their preferred username to match the target user and achieves full account takeover.</p><p class="paragraph" style="text-align:left;">Key findings from the audit:</p><ul><li><p class="paragraph" style="text-align:left;">Okta identifiers were mutable</p></li><li><p class="paragraph" style="text-align:left;">NetIQ identifiers were mutable</p></li><li><p class="paragraph" style="text-align:left;">Tokens for deactivated users could be refreshed indefinitely</p></li><li><p class="paragraph" style="text-align:left;">Notion emails marked as verified without actual verification</p></li></ul><p class="paragraph" style="text-align:left;"><b>Patched in version 65.13.0.</b> Django-allauth remains the primary OAuth library for the Python ecosystem with over 2 million monthly downloads.</p><p class="paragraph" style="text-align:left;">When reviewing any OAuth library, check which claim is used as the source of truth for user identity. If it&#39;s anything other than the <code>sub</code> (subject) claim, and especially if it&#39;s a user-writable field like <code>preferred_username</code> or <code>email</code>, test if overwriting that claim on the IdP side leads to impersonation.</p><h3 class="heading" style="text-align:left;" id="cve-20254144-cloudflare-workers-pkc">CVE-2025-4144: Cloudflare Workers PKCE Bypass</h3><p class="paragraph" style="text-align:left;"><b>Source:</b> <a class="link" href="https://github.com/cloudflare/workers-oauth-provider/security/advisories/GHSA-qgp8-v765-qxx9?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-169-oauth-2-1-mcp-authorization-security-and-the-framework-cves-you-should-know" target="_blank" rel="noopener noreferrer nofollow">GitHub Advisory GHSA-qgp8-v765-qxx9</a></p><p class="paragraph" style="text-align:left;">The <code>handleTokenRequest</code> function in Cloudflare&#39;s Workers OAuth provider accepted a <code>code_verifier</code> even when the initial authorization request omitted the <code>code_challenge</code>. By stripping the challenge, an attacker could steal the authorization code and exchange it without a valid verifier.</p><p class="paragraph" style="text-align:left;">This is a textbook PKCE downgrade affecting the entire Cloudflare Workers deployment base. The advisory explicitly notes that the MCP specification requires OAuth 2.0, making this directly relevant to agent-to-agent authentication flows.</p><h3 class="heading" style="text-align:left;" id="cve-202554576-o-auth-2-proxy-auth-b">CVE-2025-54576: OAuth2-Proxy Auth Bypass</h3><p class="paragraph" style="text-align:left;"><b>Source:</b> <a class="link" href="https://zeropath.com/blog/cve-2025-54576-oauth2-proxy-auth-bypass?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-169-oauth-2-1-mcp-authorization-security-and-the-framework-cves-you-should-know" target="_blank" rel="noopener noreferrer nofollow">ZeroPath</a></p><p class="paragraph" style="text-align:left;">OAuth2-Proxy is a widely deployed reverse proxy for Kubernetes and cloud-native environments. The <code>skip_auth_routes</code> feature allows operators to define regex patterns for paths that should bypass authentication.</p><p class="paragraph" style="text-align:left;">The implementation flaw: the regex matched against the <b>entire request URI</b>, including query parameters.</p><div class="codeblock"><pre><code>skip_auth_routes = [ &quot;^/foo/.*/bar$&quot; ]
</code></pre></div><p class="paragraph" style="text-align:left;">An attacker could access any protected endpoint by appending the bypass pattern as a query parameter:</p><div class="codeblock"><pre><code>GET /admin/secret?param=/foo/anything/bar HTTP/1.1
</code></pre></div><p class="paragraph" style="text-align:left;">The regex matches against <code>/admin/secret?param=/foo/anything/bar</code>, finds the pattern in the query string, and skips authentication entirely.</p><h3 class="heading" style="text-align:left;" id="jwt-algorithm-confusion-still-here-">JWT Algorithm Confusion: Still Here in 2026</h3><p class="paragraph" style="text-align:left;">The <code>alg: none</code> bypass is alive and well, with case variant tricks (<code>nOnE</code>, <code>NoNE</code>, <code>NONE</code>) still working against libraries that don&#39;t lowercase before checking. CVE-2026-23993 in HarbourJwt takes it further: <b>any unrecognized algorithm value</b> causes signature verification to be bypassed entirely. Set the algorithm to <code>banana</code> and the library fails open.</p><h2 class="heading" style="text-align:left;" id="actionable-takeaways">Actionable Takeaways</h2><p class="paragraph" style="text-align:left;"><b>1. Hunt the Transition Gap.</b> The biggest bugs are in compatibility shims between 2.0 and 2.1. When a target claims 2.1 compliance, you can try a bunch of bypass</p><p class="paragraph" style="text-align:left;"><b>2. CIMD is an SSRF Goldmine.</b> The new pull-based client registration means the authorization server fetches attacker-influenced URLs by design. Test <code>client_manifest_uri</code> directly, then test <code>logo_uri</code> and <code>jwks_uri</code> inside the fetched document for secondary SSRF.</p><p class="paragraph" style="text-align:left;"><b>3. Follow the Token, Not the Login.</b> Intercept agent-to-tool traffic. If the tool receives a token with broader scope than it needs, that is a confused deputy vulnerability. Document the lack of RFC 8693 token exchange.</p><p class="paragraph" style="text-align:left;"><b>4. Fingerprint the Framework.</b> Identify which OAuth library the target uses, then review known CVEs for that specific version. Implementation quirks during spec transitions are where the bugs concentrate.</p><h2 class="heading" style="text-align:left;" id="resources">Resources</h2><ul><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://datatracker.ietf.org/doc/rfc9700/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-169-oauth-2-1-mcp-authorization-security-and-the-framework-cves-you-should-know" target="_blank" rel="noopener noreferrer nofollow">RFC 9700 - OAuth 2.1</a></b> - The core specification</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://datatracker.ietf.org/doc/rfc8693/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-169-oauth-2-1-mcp-authorization-security-and-the-framework-cves-you-should-know" target="_blank" rel="noopener noreferrer nofollow">RFC 8693 - Token Exchange</a></b> - Token delegation framework</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://modelcontextprotocol.io/specification/2025-11-25?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-169-oauth-2-1-mcp-authorization-security-and-the-framework-cves-you-should-know" target="_blank" rel="noopener noreferrer nofollow">MCP Authorization Spec</a></b> - Model Context Protocol Spec</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://zeropath.com/blog/django-allauth-account-takeover-vulnerabilities?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-169-oauth-2-1-mcp-authorization-security-and-the-framework-cves-you-should-know" target="_blank" rel="noopener noreferrer nofollow">ZeroPath Django-allauth Audit</a></b> - Full vulnerability writeup</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://github.com/cloudflare/workers-oauth-provider/security/advisories/GHSA-qgp8-v765-qxx9?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-169-oauth-2-1-mcp-authorization-security-and-the-framework-cves-you-should-know" target="_blank" rel="noopener noreferrer nofollow">GHSA-qgp8-v765-qxx9</a></b> - Cloudflare Workers PKCE bypass</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://zeropath.com/blog/cve-2025-54576-oauth2-proxy-auth-bypass?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-169-oauth-2-1-mcp-authorization-security-and-the-framework-cves-you-should-know" target="_blank" rel="noopener noreferrer nofollow">CVE-2025-54576 Writeup</a></b> - OAuth2-Proxy auth bypass</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://pentesterlab.com/blog/cve-2026-23993-harbourjwt-unknown-alg-jwt-bypass?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-169-oauth-2-1-mcp-authorization-security-and-the-framework-cves-you-should-know" target="_blank" rel="noopener noreferrer nofollow">CVE-2026-23993 - HarbourJwt</a></b> - JWT algorithm bypass</p></li></ul><hr class="content_break"><p class="paragraph" style="text-align:left;">That&#39;s it for the week, keep hacking!</p></div></div>
  ]]></content:encoded>
</item>

      <item>
  <title>[HackerNotes Ep. 168] Client-Side Path Traversals Across Every Framework, with XSSDoctor</title>
  <description>We dig CSPT across different frameworks with xssdoctor, discovering a nice bug in react router</description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-168-client-side-path-traversals-across-every-framework-with-xssdoctor</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-168-client-side-path-traversals-across-every-framework-with-xssdoctor</guid>
  <pubDate>Thu, 02 Apr 2026 10:01:00 +0000</pubDate>
  <atom:published>2026-04-02T10:01:00Z</atom:published>
    <dc:creator>Aituglo ‎</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr">Hacker TL;DR</h2><ul><li><p class="paragraph" style="text-align:left;">React Router&#39;s <code>useParams</code> double URL decodes path parameters, and a case-sensitive regex on the React Source Code of <code>matchPath</code> means <code>%252F</code> (uppercase F) decodes to <code>/</code> while <code>%252f</code> (lowercase f) does not</p></li><li><p class="paragraph" style="text-align:left;">Not all frameworks are equal: Vue and React are the most vulnerable to CSPT, Next.js and SvelteKit expose secondary context path traversal on the server side, while SolidStart is largely safe</p></li><li><p class="paragraph" style="text-align:left;">Pre-production endpoints may serve uploaded HTML inline instead of as <code>Content-Disposition: attachment</code>, a reliable technique to bypass XSS mitigations on file upload</p></li><li><p class="paragraph" style="text-align:left;"><code>fetch()</code> silently strips tab characters (<code>%09</code>), enabling WAF bypasses with payloads like <code>%2F%2e%09%2e%5C</code></p></li></ul><hr class="content_break"><h2 class="heading" style="text-align:left;" id="whos-xss-doctor">Who&#39;s XSS Doctor</h2><p class="paragraph" style="text-align:left;">XSSDoctor (JD) is a practicing <b>cardiologist</b> who found his way into bug bounty during the COVID-19 pandemic after stumbling on a &quot;learn to hack in 12 hours&quot; ad on Flipboard. It started with Hack The Box and CTFs, and it evolved into a deep specialization in <b>client-side security</b>, largely inspired by listening to the CTBB podcast. He has since become one of the community&#39;s most respected client-side researchers, running hackalongs, collaborating with top hunters, and building tools like <b>DoctorScan</b> for automated framework fingerprinting and CSPT source-to-sink analysis.</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/dc7a3669-6d67-48a9-aa2b-5dbdd0d8e41e/_bannerHackerNotesShort.png?t=1769044664"/><div class="image__source"><a class="image__source_link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-168-client-side-path-traversals-across-every-framework-with-xssdoctor" rel="noopener" target="_blank"><span class="image__source_text"><p>Check out the CTBB Discord!</p></span></a></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-168-client-side-path-traversals-across-every-framework-with-xssdoctor" target="_blank" rel="noopener noreferrer nofollow">You can also find some hacker swag here.</a></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="xss-to-post-message-to-home-takeove">XSS to PostMessage to Home Takeover</h2><p class="paragraph" style="text-align:left;">XSS Doctor was hacking a <b>home automation platform</b> whose AI assistant had full control over a user&#39;s house like alarms, locks, garage doors, everything. Here is the attack chain:</p><ol start="1"><li><p class="paragraph" style="text-align:left;"><b>PostMessage listener</b> on the AI interface accepted messages from <code>*.target.com</code></p></li><li><p class="paragraph" style="text-align:left;"><b>XSS via file upload</b> on a pre-production domain that served HTML inline instead of as an attachment</p></li><li><p class="paragraph" style="text-align:left;">The uploaded payload sends a <code>postMessage</code> to the AI, injecting an arbitrary prompt</p></li><li><p class="paragraph" style="text-align:left;">The AI executes the command: &quot;turn off the alarm system&quot;</p></li></ol><p class="paragraph" style="text-align:left;">The critical insight: <b>pre-production endpoints often lack the </b><code>Content-Disposition: attachment</code><b> header</b> that production enforces. JD found this pattern twice on the same target: first on one AI product, then on another where he also discovered a path parameter that let him upload to arbitrary directories, bypassing the download-only restriction.</p><p class="paragraph" style="text-align:left;">Additionally, the AI&#39;s API had a <b>CORS misconfiguration</b> (<code>Access-Control-Allow-Origin: *.target.com</code> with credentials), meaning the XSS could directly hit the API without needing the <code>postMessage</code> gadget at all.</p><p class="paragraph" style="text-align:left;">When testing file uploads for XSS, always check pre-production/staging endpoints.</p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="client-side-path-enumeration-the-re">Client-Side Path Enumeration: The Research</h2><p class="paragraph" style="text-align:left;">Xssdoctor produced a comprehensive research paper covering <b>eight major frameworks</b>: React, Next.js, Vue, Nuxt, Angular, Ember, SvelteKit, and SolidStart.</p><h3 class="heading" style="text-align:left;" id="the-core-question">The Core Question</h3><p class="paragraph" style="text-align:left;">In single-page applications, the URL bar doesn&#39;t map to files on a server. A <b>client-side router</b> parses the path and routes to views. When a developer needs a dynamic value from the URL (like <code>/settings/xssdoctor</code>), they call a framework-specific function like <code>useParams</code> in React, <code>useRoute</code> in Vue, <code>params</code> in SvelteKit. The security question is: <b>does that function URL-decode the value?</b></p><p class="paragraph" style="text-align:left;">If it does, and the developer concatenates that decoded value into a <code>fetch()</code> call, the result is a <b>Client-Side Path Traversal (CSPT)</b>.</p><h3 class="heading" style="text-align:left;" id="the-framework-breakdown">The Framework Breakdown</h3><div style="padding:14px 15px 14px;"><table class="bh__table" width="100%" style="border-collapse:collapse;"><tr class="bh__table_row"><th class="bh__table_header" width="20%"><p class="paragraph" style="text-align:left;">Framework</p></th><th class="bh__table_header" width="20%"><p class="paragraph" style="text-align:left;">Path Decoded?</p></th><th class="bh__table_header" width="20%"><p class="paragraph" style="text-align:left;">Double URL Decode?</p></th><th class="bh__table_header" width="20%"><p class="paragraph" style="text-align:left;">CSPT Risk</p></th><th class="bh__table_header" width="20%"><p class="paragraph" style="text-align:left;">Secondary Path Traversal?</p></th></tr><tr class="bh__table_row"><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;"><b>React Router</b></p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">Yes (<code>useParams</code>)</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">Yes (uppercase F only!)</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">High</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">N/A</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;"><b>Vue Router</b></p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">Yes</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">No</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">High</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">N/A</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;"><b>Angular</b></p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">Partial</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">No</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">Medium</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">N/A</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;"><b>Next.js</b> (server)</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">Yes (<code>await params</code>)</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">No</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">Low (client)</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">Yes</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;"><b>SvelteKit</b></p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">Yes (server)</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">No</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">Low (client)</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">Yes</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;"><b>Ember</b></p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">Yes</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">No</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">Medium</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">N/A</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;"><b>SolidStart</b></p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">No</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">No</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">Low</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">No</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;"><b>Nuxt</b></p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">Yes</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">No</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">High</p></td><td class="bh__table_cell" width="20%"><p class="paragraph" style="text-align:left;">N/A</p></td></tr></table></div><h3 class="heading" style="text-align:left;" id="the-react-zero-day-discovery">The React Zero-Day Discovery</h3><p class="paragraph" style="text-align:left;">They discovered a nice 0day gadget on React during this episode. While demonstrating his React lab, JD showed that <code>%252F</code> (double-encoded slash) gets decoded back to <code>/</code>, but only when the <code>F</code> is <b>uppercase</b>.</p><p class="paragraph" style="text-align:left;">The root cause is on the <code>matchPath</code> in React Router, there is a <code>replace()</code> call that maps <code>%2F</code> to <code>/</code>. This replacement is <b>case-sensitive</b>. It does not use the <code>gi</code> flag. The result:</p><ul><li><p class="paragraph" style="text-align:left;"><code>%252F</code> (uppercase F) → decoded to <code>%2F</code> → replaced to <code>/</code> → <b>path traversal works</b></p></li><li><p class="paragraph" style="text-align:left;"><code>%252f</code> (lowercase f) → decoded to <code>%2f</code> → <b>not replaced</b> → no traversal</p></li></ul><div class="codeblock"><pre><code>// React Router matchPath - the case-sensitive replace
// Only matches uppercase %2F, misses lowercase %2f
paramValue.replace(/%2F/g, &quot;/&quot;)  // Missing the &#39;i&#39; flag!
</code></pre></div><p class="paragraph" style="text-align:left;">This behavior was actually <b>reintroduced</b> after a previous double-URL-encoding bug was reported and &quot;fixed&quot;. The fix itself created this case-sensitivity gap.</p><p class="paragraph" style="text-align:left;">So a good tips is to always test both uppercase and lowercase hex encoding in your CSPT payloads. If you&#39;ve only been using <code>%2f</code>, you may have been missing vulnerabilities in React applications.</p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="the-three-sources-of-cspt">The Three Sources of CSPT</h2><p class="paragraph" style="text-align:left;">XSSDoctor&#39;s methodology identifies three injection sources in the URL:</p><ol start="1"><li><p class="paragraph" style="text-align:left;"><b>Path parameters</b> (<code>/settings/:userId</code>): the most interesting and most commonly overlooked</p></li><li><p class="paragraph" style="text-align:left;"><b>Query parameters</b> (<code>?id=value</code>): almost always decoded by frameworks, a known vector</p></li><li><p class="paragraph" style="text-align:left;"><b>Hash parameters</b> (<code>#fragment</code>): less explored but framework-dependent</p></li></ol><p class="paragraph" style="text-align:left;">The key insight from the research: <b>path parameters yield more impactful CSPTs</b> than query parameters. Developers tend to put query values into query parameters and path values into path segments, meaning path injection flows directly into API path construction.</p><h3 class="heading" style="text-align:left;" id="testing-methodology">Testing Methodology</h3><ol start="1"><li><p class="paragraph" style="text-align:left;">Find endpoints with custom-looking paths: <code>/home/xssdoctor</code></p></li><li><p class="paragraph" style="text-align:left;">Replace the dynamic segment with a unique string: <code>/home/booyakasha</code></p></li><li><p class="paragraph" style="text-align:left;">Check Caido for API calls containing <code>booyakasha</code> (e.g., <code>/api/booyakasha</code>)</p></li><li><p class="paragraph" style="text-align:left;">If reflected, try <code>/home/booyakasha%2f</code> and check if the API request becomes <code>/api/booyakasha/</code></p></li><li><p class="paragraph" style="text-align:left;">If path traversal works, assess impact: is the API <b>state-changing</b> (CSRF)? Does the response get <b>inserted into the DOM</b> (XSS)?</p></li></ol><p class="paragraph" style="text-align:left;">For XSS confirmation via CSPT, set a <b>match-and-replace rule</b> in your proxy that swaps a string in the API response with <code>&lt;img src=x&gt;</code>. If a broken image appears on the page, HTML injection is confirmed.</p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="secondary-context-path-traversal-in">Secondary Context Path Traversal in Server-Side Frameworks</h2><p class="paragraph" style="text-align:left;">Frameworks with <b>server-side rendering</b> (Next.js, SvelteKit) introduce a different attack primitive. In Next.js:</p><ul><li><p class="paragraph" style="text-align:left;"><code>useParams</code> (client-side) does <b>not</b> auto-decode: safe from CSPT</p></li><li><p class="paragraph" style="text-align:left;"><code>await params</code> (server-side) <b>does</b> auto-decode: vulnerable to <b>secondary context path traversal</b></p></li></ul><p class="paragraph" style="text-align:left;">When a Next.js server component uses <code>await params</code> and passes the decoded value to an internal API, you get a server-side path traversal triggered from the URL bar. This is blind initially, but <b>Next.js returns 500 errors</b> on invalid paths, so it can be a useful oracle for detection.</p><p class="paragraph" style="text-align:left;">On Next.js targets, test <code>%2F..%2F</code> payloads in path parameters. If you get a 500 for invalid traversal but a 200 for valid path reconstruction (<code>value/../value</code>), you&#39;ve likely found secondary context path traversal.</p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="the-waf-bypass-and-fetch-quirks">The WAF Bypass and fetch() Quirks</h2><p class="paragraph" style="text-align:left;">JD shared his favorite WAF bypass payload: <code>%2F%2e%09%2e%5C</code>, leveraging the fact that <code>fetch()</code><b> silently strips tab characters</b> (<code>%09</code>). This means <code>..</code> with a tab inserted (<code>%2e%09%2e</code>) still resolves as a directory traversal after fetch processes it, but WAFs that pattern-match on <code>..</code> or <code>%2e%2e</code> will miss it entirely.</p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="the-fix-that-wont-come">The Fix That Won&#39;t Come</h2><p class="paragraph" style="text-align:left;">Unlike SQL injection, which was largely solved by prepared statements at the framework level, CSPT is fundamentally harder to fix:</p><ul><li><p class="paragraph" style="text-align:left;"><b>At the source level:</b> frameworks need to decode for legitimate functionality, breaking this breaks apps</p></li><li><p class="paragraph" style="text-align:left;"><b>At the sink level:</b> <code>fetch()</code> receives a concatenated string and has no way to distinguish intended path segments from injected ones</p></li></ul><hr class="content_break"><h2 class="heading" style="text-align:left;" id="resources">Resources</h2><ul><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://x.com/xssdoctor?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-168-client-side-path-traversals-across-every-framework-with-xssdoctor" target="_blank" rel="noopener noreferrer nofollow">Xssdoctor&#39;s X</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://lab.ctbb.show/research/the-dot-dot-slash-that-frameworks-hand-you?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-168-client-side-path-traversals-across-every-framework-with-xssdoctor" target="_blank" rel="noopener noreferrer nofollow">The Dot-Dot-Slash That Frameworks Hand You: CSPT Across Every Major Frontend Framework</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://portswigger.net/web-security/ssrf/url-validation-bypass-cheat-sheet?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-168-client-side-path-traversals-across-every-framework-with-xssdoctor" target="_blank" rel="noopener noreferrer nofollow">PortSwigger URL Validation Bypass Cheat Sheet</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://github.com/vitorfhc/gecko?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-168-client-side-path-traversals-across-every-framework-with-xssdoctor" target="_blank" rel="noopener noreferrer nofollow">Gecko Extension by BusFactor</a></p></li></ul><hr class="content_break"><p class="paragraph" style="text-align:left;">That&#39;s it for the week, keep hacking!</p></div></div>
  ]]></content:encoded>
</item>

      <item>
  <title>[HackerNotes Ep. 167] Acquisition Hunting, Supply Chain Bugs &amp; Research Theft with krevetk0</title>
  <description>Today, a new episode about research theft with krevetk0</description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-167-acquisition-hunting-supply-chain-bugs-research-theft-with-krevetk0</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-167-acquisition-hunting-supply-chain-bugs-research-theft-with-krevetk0</guid>
  <pubDate>Thu, 26 Mar 2026 11:01:00 +0000</pubDate>
  <atom:published>2026-03-26T11:01:00Z</atom:published>
    <dc:creator>Aituglo ‎</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr">Hacker TL;DR</h2><ul><li><p class="paragraph" style="text-align:left;">Pre-position on acquisition targets: find bugs before the deal closes, document everything with screenshots</p></li><li><p class="paragraph" style="text-align:left;">Third-party vendors reusing credentials across environments create critical supply chain attack paths</p></li><li><p class="paragraph" style="text-align:left;">Stolen research is a real threat: over-detailed reports can leak through Slack integrations or duplicate collaborators</p></li><li><p class="paragraph" style="text-align:left;">Protect your intellectual property: watermark reports, host exploits on your own infra, don&#39;t reveal full chains</p></li></ul><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/c7a25360-b133-400b-b6d2-789a252e0293/ThreatLocker-1.png?t=1773171761"/></div><p class="paragraph" style="text-align:center;"><span style="color:rgb(249, 250, 251);font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"><b>Today&#39;s Sponsor:</b></span><span style="color:rgb(249, 250, 251);font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"> Check out ThreatLocker Ringfencing </span><span style="font-family:inherit;font-size:16px;"><a class="link" href="https://www.criticalthinkingpodcast.io/tl-rf?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-167-acquisition-hunting-supply-chain-bugs-research-theft-with-krevetk0" target="_blank" rel="noopener noreferrer nofollow">https://www.criticalthinkingpodcast.io/tl-rf</a></span></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="in-the-news">In the News</h2><ul><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.hackerone.com/blog/program-maturity-framework-bug-bounty-operations?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-167-acquisition-hunting-supply-chain-bugs-research-theft-with-krevetk0" target="_blank" rel="noopener noreferrer nofollow">HackerOne’s Bug Bounty Maturity Framework</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://jobs.criticalthinkingpodcast.io/jobs/product-security-analyst-25ef4706?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-167-acquisition-hunting-supply-chain-bugs-research-theft-with-krevetk0" target="_blank" rel="noopener noreferrer nofollow">Intigriti is hiring a Product Security Analyst</a></p></li></ul><h2 class="heading" style="text-align:left;" id="whos-valeriy-krevetk-0">Who&#39;s Valeriy (krevetk0)</h2><p class="paragraph" style="text-align:left;">Valeriy, known as <b>krevetk0</b> on HackerOne, is a seasoned bug bounty hunter and security engineer based in Germany since 2017. He brings a dual perspective to the table: he hunts bugs on the side while managing the bug bounty program at <b>Semrush</b> as his full-time role. His first bounty was a $6,000 PayPal bypass, and he has been consistently active for over six years.</p><h2 class="heading" style="text-align:left;" id="10-k-for-a-vulnerability-that-doesn">$10K for a Vulnerability That Doesn&#39;t Exist</h2><p class="paragraph" style="text-align:left;">Krevetk0&#39;s first bug demonstrates the value of <b>preparation and patience</b> in acquisition-based hunting. Several programs on HackerOne accept findings on acquired assets, and krevetk0 actively monitors upcoming acquisitions to pre-position his research.</p><h3 class="heading" style="text-align:left;" id="the-attack">The Attack</h3><p class="paragraph" style="text-align:left;">He identified a company announced as an acquisition target before the legal process completed. During early recon, he discovered a <b>Node.js path traversal</b> vulnerability exposing server-side information. Rather than report immediately, the acquisition was not yet finalized, he documented the finding and waited.</p><p class="paragraph" style="text-align:left;">When the acquisition closed, he verified the bug was still functional and started capturing screenshots. While doing so, he escalated by checking environment variable files and discovered <b>hardcoded AWS credentials</b> with access to the database and broader AWS infrastructure.</p><h3 class="heading" style="text-align:left;" id="the-twist">The Twist</h3><p class="paragraph" style="text-align:left;">The next day, the server was completely shut down. The vulnerability no longer existed. But krevetk0 had his screenshots. He extracted the AWS credentials from the captured images, validated them via the CLI, and confirmed they were <b>still active and unrotated</b>.</p><p class="paragraph" style="text-align:left;">The triager initially pushed back as the endpoint was down. But he demonstrated the credentials were still valid, the exposure had occurred, and incident response was necessary. The report was accepted and paid <b>$10,000–$12,000</b>.</p><p class="paragraph" style="text-align:left;">So don&#39;t forget to always capture comprehensive evidence as you go like video recordings, screenshots, saved requests and responses. Vulnerabilities can disappear at any time, but if the exposure was real, the business impact stands.</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/dc7a3669-6d67-48a9-aa2b-5dbdd0d8e41e/_bannerHackerNotesShort.png?t=1769044664"/><div class="image__source"><a class="image__source_link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-167-acquisition-hunting-supply-chain-bugs-research-theft-with-krevetk0" rel="noopener" target="_blank"><span class="image__source_text"><p>Check out the CTBB Discord!</p></span></a></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-167-acquisition-hunting-supply-chain-bugs-research-theft-with-krevetk0" target="_blank" rel="noopener noreferrer nofollow">You can also find some hacker swag here.</a></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="supply-chain-credential-reuse-via-t">Supply Chain Credential Reuse via Third-Party Agency</h2><p class="paragraph" style="text-align:left;">The second finding illustrates how <b>third-party vendor relationships</b> create a critical attack surface that organizations often overlook.</p><h3 class="heading" style="text-align:left;" id="following-the-breadcrumbs">Following the Breadcrumbs</h3><p class="paragraph" style="text-align:left;">While testing a HackerOne program, krevetk0 noticed a <b>Terms & Conditions link pointing to a completely different domain</b>, an external agency. Is this agency&#39;s relationship to the target?</p><h3 class="heading" style="text-align:left;" id="the-chain">The Chain</h3><ol start="1"><li><p class="paragraph" style="text-align:left;">Basic recon on the agency&#39;s domain revealed <b>Symfony debug mode enabled</b></p></li><li><p class="paragraph" style="text-align:left;">The Symfony debug panel exposed <b>PHP info</b> with environment variable credentials</p></li><li><p class="paragraph" style="text-align:left;">A <b>log file</b> on another subdomain confirmed the agency managed content for the target organization</p></li><li><p class="paragraph" style="text-align:left;">The credentials provided access to a <b>WordPress admin panel</b> used by the agency for content management</p></li><li><p class="paragraph" style="text-align:left;">Testing credential reuse on the <b>target&#39;s main domain</b>, the same WordPress credentials worked</p></li></ol><p class="paragraph" style="text-align:left;">So the result was full content management access on the main domain of a well-known brand, achieved entirely through a third-party vendor that reused credentials across different environments and applications.</p><p class="paragraph" style="text-align:left;">As a previous CTBB podcast guest (<b>Mathias Karlsson</b>) noted, everything that flows through your proxy history could be an in-scope asset. Third-party domains, agency subdomains, and microservice integrations often blend into the target&#39;s main domain through reverse proxies. Investigate every unfamiliar domain that appears in your traffic.</p><h2 class="heading" style="text-align:left;" id="research-theft-on-bug-bounty-platfo">Research Theft on Bug Bounty Platforms</h2><p class="paragraph" style="text-align:left;">Here is a problem that affects the entire bug bounty ecosystem: <b>intellectual property theft of security research</b>.</p><h3 class="heading" style="text-align:left;" id="the-timeline">The Timeline</h3><p class="paragraph" style="text-align:left;">In 2021, Krevetk0 developed an entirely <b>new attack vector</b>, not a single vulnerability, but a novel <i>class</i> of attacks. He verified it across approximately 10 programs on HackerOne and discussed it privately with a top-5 ranked hacker on the platform.</p><p class="paragraph" style="text-align:left;">He deliberately <b>never published the research</b>, concerned about the magnitude of potential damage if widely known. He presented it internally at Semrush, built protective mechanisms, and educated the security team on the new attack surface.</p><p class="paragraph" style="text-align:left;">Three years later, while managing the Semrush program, he received a report that was <b>word-for-word identical</b> to his original research, including exact phrasing, punctuation, references, and even the claim &quot;I did this research.&quot;</p><h3 class="heading" style="text-align:left;" id="the-investigation">The Investigation</h3><p class="paragraph" style="text-align:left;">When confronted, the reporter claimed <b>ChatGPT</b> generated the report. Krevetk0 tested this: after 10 attempts with various LLMs, none could reproduce his exact wording. The excuse did not hold up.</p><p class="paragraph" style="text-align:left;">HackerOne conducted an investigation and identified <b>between 5 and 10 hackers</b> using krevetk0&#39;s research as a submission template. The leak likely originated from one of two vectors:</p><p class="paragraph" style="text-align:left;"><b>1. Customer-Side Slack Integrations</b><br>Some organizations pipe HackerOne report notifications directly into Slack channels accessible to <b>all employees</b>, not just the security team. Marketing managers, engineering leads, or anyone with channel access can read and copy the full report content.</p><p class="paragraph" style="text-align:left;"><b>2. Duplicate Report Collaborators</b><br>When a triager adds a duplicate reporter to the original report, that collaborator gains full access to the original researcher&#39;s work. This happens without the original hacker&#39;s consent and exposes full exploitation chains, methodology, and escalation paths.</p><h3 class="heading" style="text-align:left;" id="protecting-your-research">Protecting Your Research</h3><p class="paragraph" style="text-align:left;">So here are several practical countermeasures:</p><ul><li><p class="paragraph" style="text-align:left;"><b>Don&#39;t over-detail reports.</b> Include enough information to reproduce the vulnerability, but avoid exposing your full thought process, additional attack paths, or research methodology</p></li><li><p class="paragraph" style="text-align:left;"><b>Host exploits on your own infrastructure.</b> Serve payloads through your own server without distributing binaries. Let the program replicate via your controlled endpoint</p></li><li><p class="paragraph" style="text-align:left;"><b>Watermark your reports.</b> Embed invisible markers such as zero-width Unicode characters, deliberate unique misspellings, or characters from other alphabets that can trace the content back to you</p></li><li><p class="paragraph" style="text-align:left;"><b>Avoid revealing full chains.</b> If reporting a critical chain, consider holding back certain escalation steps that aren&#39;t necessary for the program to understand the impact</p></li></ul><p class="paragraph" style="text-align:left;"><b>For bug bounty platforms:</b> krevetk0 suggests that platforms should <b>require acceptance from the original researcher</b> before adding duplicate reporters to the initial report. The current system allows triagers to unilaterally expose one hacker&#39;s work to another.</p><h2 class="heading" style="text-align:left;" id="krevetk-0-s-hunting-philosophy">Krevetk0&#39;s Hunting Philosophy</h2><ul><li><p class="paragraph" style="text-align:left;"><b>High-signal strategy</b>: every submission targets high or critical severity, no time spent on duplicates or questionable impact</p></li><li><p class="paragraph" style="text-align:left;"><b>Leaderboard competition</b>: he selects private programs and aims to surpass the top hackers on that program&#39;s leaderboard as motivation</p></li><li><p class="paragraph" style="text-align:left;"><b>Program appreciation matters</b>: human responses from program managers (not templates or AI replies) earn significantly more of his effort</p></li><li><p class="paragraph" style="text-align:left;"><b>Brand attachment</b>: he gravitates toward programs in health, sports, and finance, brands worth the personal investment</p></li></ul><h2 class="heading" style="text-align:left;" id="resources">Resources</h2><ul><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://x.com/Krevetk0Valeriy?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-167-acquisition-hunting-supply-chain-bugs-research-theft-with-krevetk0" target="_blank" rel="noopener noreferrer nofollow">krevetk0 on X</a></b></p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://hackerone.com/semrush?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-167-acquisition-hunting-supply-chain-bugs-research-theft-with-krevetk0" target="_blank" rel="noopener noreferrer nofollow">Semrush Bug Bounty Program</a></b></p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://krevetk0.medium.com/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-167-acquisition-hunting-supply-chain-bugs-research-theft-with-krevetk0" target="_blank" rel="noopener noreferrer nofollow">krevetk0&#39;s Medium</a></b></p></li></ul><hr class="content_break"><p class="paragraph" style="text-align:left;">That&#39;s it for the week, keep hacking!</p></div></div>
  ]]></content:encoded>
</item>

      <item>
  <title>[HackerNotes Ep. 166] Claude Code Skills for Bug Bounty: When, Why, and How to Build Them</title>
  <description>Exploring how Rez0 use Claude code to hunt and how to properly set it up </description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-166-claude-code-skills-for-bug-bounty-when-why-and-how-to-build-them</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-166-claude-code-skills-for-bug-bounty-when-why-and-how-to-build-them</guid>
  <pubDate>Thu, 19 Mar 2026 10:14:37 +0000</pubDate>
  <atom:published>2026-03-19T10:14:37Z</atom:published>
    <dc:creator>Aituglo ‎</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr">Hacker TL;DR</h2><ul><li><p class="paragraph" style="text-align:left;">Claude Code skills should encode knowledge the model lacks and enforce deterministic workflows, not replace its creative reasoning</p></li><li><p class="paragraph" style="text-align:left;">Build a fallback architecture in skills: primary tool → SDK/library → raw API, so the agent adapts when one layer fails</p></li><li><p class="paragraph" style="text-align:left;">Structure your notes as a funnel: notes → leads → primitives → findings → reports to keep multi-session hacking organized</p></li><li><p class="paragraph" style="text-align:left;">Run two parallel agents (one guided, one free-roaming) and cross-compare results to continuously improve your methodology</p></li></ul><h2 class="heading" style="text-align:left;" id="when-do-skills-actually-help">When Do Skills Actually Help?</h2><p class="paragraph" style="text-align:left;">The good question to ask is <b>does giving Claude a rigid skill limit its creativity, or does it make the agent more effective?</b>. It can be hard to know which one is more effective and what to do to increase the intelligence of our agents. And Rez0 answered us with his own methodology to understand how to do it properly.</p><h3 class="heading" style="text-align:left;" id="1-knowledge-the-model-doesnt-have">1. Knowledge the Model Doesn&#39;t Have</h3><p class="paragraph" style="text-align:left;">Claude&#39;s training data is massive, but it has clear blind spots. Custom tooling like <b>Caido</b> requires a skill because the model would otherwise spend half a session figuring out the SDK from scratch. The same applies to:</p><ul><li><p class="paragraph" style="text-align:left;"><b>Secret techniques</b> — gadgets, Oracle chains, or exploitation patterns from DEFCON talks that aren&#39;t widely documented online</p></li><li><p class="paragraph" style="text-align:left;"><b>Enterprise tools</b> — any software behind a paywall or requiring API keys that the model cannot sign up for on its own</p></li><li><p class="paragraph" style="text-align:left;"><b>Custom infrastructure</b> — VPS credentials, file paths, server configurations, and personal workflows that exist only in the researcher&#39;s head</p></li></ul><p class="paragraph" style="text-align:left;">So if a tool has good public documentation, Claude can often figure it out, but at the cost of significant token usage. A skill provides a head start and eliminates wasted cycles.</p><h3 class="heading" style="text-align:left;" id="2-constraining-a-large-solution-spa">2. Constraining a Large Solution Space</h3><p class="paragraph" style="text-align:left;">When there are dozens of ways to accomplish something (curl, Python, Playwright, Caido, Chrome DevTools), a skill steers the agent toward the preferred method. This matters for consistency: using Caido ensures traffic is proxied, visible in history, and available for screenshots and POC documentation.</p><p class="paragraph" style="text-align:left;">The key insight here is that <b>steering is not limiting</b>. Telling Claude where to save files, how to make requests, and what note format to use does not degrade output quality. It provides structure that makes multi-session hacking manageable.</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/dc7a3669-6d67-48a9-aa2b-5dbdd0d8e41e/_bannerHackerNotesShort.png?t=1769044664"/><div class="image__source"><a class="image__source_link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-166-claude-code-skills-for-bug-bounty-when-why-and-how-to-build-them" rel="noopener" target="_blank"><span class="image__source_text"><p>Check out the CTBB Discord!</p></span></a></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-166-claude-code-skills-for-bug-bounty-when-why-and-how-to-build-them" target="_blank" rel="noopener noreferrer nofollow">You can also find some hacker swag here.</a></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="the-fallback-architecture">The Fallback Architecture</h2><p class="paragraph" style="text-align:left;">Justin highlights a pattern observed in the Caido skill that demonstrates effective skill design. When executing a task, the agent follows a layered approach:</p><ol start="1"><li><p class="paragraph" style="text-align:left;"><b>Primary tools</b> — Use the skill&#39;s built-in commands and binaries</p></li><li><p class="paragraph" style="text-align:left;"><b>SDK/library layer</b> — If the primary tool fails, invoke the underlying TypeScript/JavaScript library directly</p></li><li><p class="paragraph" style="text-align:left;"><b>Raw API</b> — As a last resort, use GraphQL or REST calls to control the tool at the protocol level</p></li></ol><p class="paragraph" style="text-align:left;">This fallback architecture was not explicitly coded into the Caido skill and Claude developed this behavior on its own, likely from training data where agents iterated through failures.</p><p class="paragraph" style="text-align:left;">So, if you want to build your own skills, <b>design them with multiple layers of abstraction</b>, and include a line in your skill telling Claude not to limit itself to the prescribed workflow. If the skill doesn&#39;t work, it should explore alternative approaches.</p><p class="paragraph" style="text-align:left;">Maybe you can add a line like &quot;If this workflow fails or doesn&#39;t cover the situation, use your own exploration and creativity to keep going&quot; at the end of every skill.</p><h2 class="heading" style="text-align:left;" id="claudemd-best-practices-for-bug-bou">Claude.md Best Practices for Bug Bounty</h2><p class="paragraph" style="text-align:left;">Beyond skills, the <code>CLAUDE.md</code> file is the foundation of an effective hacking setup. Here is three essential elements:</p><h3 class="heading" style="text-align:left;" id="identity-and-context">Identity and Context</h3><p class="paragraph" style="text-align:left;">Tell Claude who you are and what you do. A simple declaration like &quot;I&#39;m a bug bounty hunter doing authorized ethical testing&quot; significantly reduces refusal rates and keeps the model aligned with offensive security tasks. Include directives like:</p><ul><li><p class="paragraph" style="text-align:left;">Stay in scope based on the program policy</p></li><li><p class="paragraph" style="text-align:left;">Don&#39;t perform destructive actions unless on accounts you own</p></li><li><p class="paragraph" style="text-align:left;">Always validate findings with a full POC before reporting</p></li><li><p class="paragraph" style="text-align:left;">Phrases like &quot;POC or GTFO&quot; and &quot;try harder&quot; set the right behavioral expectations</p></li></ul><h3 class="heading" style="text-align:left;" id="note-taking-structure">Note-Taking Structure</h3><p class="paragraph" style="text-align:left;">Joseph recommends doing this kind of structure:</p><div style="padding:14px 15px 14px;"><table class="bh__table" width="100%" style="border-collapse:collapse;"><tr class="bh__table_row"><th class="bh__table_header" width="50%"><p class="paragraph" style="text-align:left;">Level</p></th><th class="bh__table_header" width="50%"><p class="paragraph" style="text-align:left;">Purpose</p></th></tr><tr class="bh__table_row"><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;"><b>Notes</b></p></td><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">Raw observations, anything interesting during reconnaissance</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;"><b>Leads</b></p></td><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">Promising attack vectors that warrant further investigation</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;"><b>Primitives/Gadgets</b></p></td><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">Confirmed building blocks — IDOR patterns, auth bypasses, useful endpoints</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;"><b>Findings</b></p></td><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">Validated vulnerabilities with full reproduction steps</p></td></tr><tr class="bh__table_row"><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;"><b>Reports</b></p></td><td class="bh__table_cell" width="50%"><p class="paragraph" style="text-align:left;">Polished write-ups ready for submission</p></td></tr></table></div><p class="paragraph" style="text-align:left;">Each level filters down from the previous one, ensuring the agent maintains context across compaction cycles and doesn&#39;t lose track of partial progress.</p><h3 class="heading" style="text-align:left;" id="where-to-store-notes">Where to Store Notes</h3><p class="paragraph" style="text-align:left;">Consistency across sessions is critical. There are different options you can choose:</p><ul><li><p class="paragraph" style="text-align:left;"><b>Local folder structure</b> per target, with the <code>CLAUDE.md</code> in each folder containing target-specific context</p></li><li><p class="paragraph" style="text-align:left;"><b>Obsidian or Notion</b> via API integration</p></li><li><p class="paragraph" style="text-align:left;"><b>Custom API endpoint</b> — You can build something like <code>api.rez0.com</code> so that all leads and gadgets are accessible regardless of which machine the agent runs on</p></li><li><p class="paragraph" style="text-align:left;"><b>Caido Findings tab</b> — when hacking locally, pipe findings directly to Caido for real-time notification</p></li></ul><h2 class="heading" style="text-align:left;" id="methodology-setting-up-a-dual-agent">Methodology: Setting Up a Dual-Agent Workflow</h2><p class="paragraph" style="text-align:left;">Joseph recommends running <b>two parallel Claude Code instances</b> against the same target for maximum coverage:</p><h3 class="heading" style="text-align:left;" id="phase-1-launch-two-agents">Phase 1: Launch Two Agents</h3><ul><li><p class="paragraph" style="text-align:left;"><b>Agent A (Guided):</b> Loaded with your full skill set, custom <code>CLAUDE.md</code>, methodology steps, and target-specific context. This agent follows your deterministic workflow, like front-end analysis, source map enumeration, endpoint fuzzing, ensuring nothing you would normally check gets missed.</p></li><li><p class="paragraph" style="text-align:left;"><b>Agent B (Free-roaming):</b> Minimal skills, minimal guidance. Just the target URL and authentication. This agent explores creatively, potentially finding attack vectors outside your usual methodology.</p></li></ul><h3 class="heading" style="text-align:left;" id="phase-2-instruct-both-to-take-notes">Phase 2: Instruct Both to Take Notes</h3><p class="paragraph" style="text-align:left;">Tell both agents at launch: &quot;Keep detailed notes on what you tried, what worked, and what didn&#39;t.&quot; This is critical because context compaction will eventually erase the working memory.</p><h3 class="heading" style="text-align:left;" id="phase-3-cross-compare-results">Phase 3: Cross-Compare Results</h3><p class="paragraph" style="text-align:left;">Once both agents complete their runs, paste Agent A&#39;s output into Agent B&#39;s session (not a third agent, you want the full context). Ask:</p><ul><li><p class="paragraph" style="text-align:left;">What did the other agent find that you didn&#39;t?</p></li><li><p class="paragraph" style="text-align:left;">What techniques did it use that we didn&#39;t try?</p></li><li><p class="paragraph" style="text-align:left;">Are there gaps in our methodology?</p></li></ul><h3 class="heading" style="text-align:left;" id="phase-4-improve-the-workflow">Phase 4: Improve the Workflow</h3><p class="paragraph" style="text-align:left;">If the free-roaming agent discovered something the guided agent missed, add that technique to the skill. The methodology evolves with every iteration.</p><h2 class="heading" style="text-align:left;" id="running-claude-code-autonomously">Running Claude Code Autonomously</h2><p class="paragraph" style="text-align:left;">For extended autonomous sessions, you can use a straightforward approach: tell the agent &quot;I&#39;m going to bed. Don&#39;t ask me any questions. Don&#39;t stop hacking.&quot; This consistently produces 4+ hours of autonomous hacking.</p><p class="paragraph" style="text-align:left;">Key considerations for overnight runs:</p><ul><li><p class="paragraph" style="text-align:left;"><b>Limit sub-agents to 2-3 maximum.</b> When four or more sub-agents run simultaneously, compaction can fail catastrophically and the context fills up with no previous message to roll back to</p></li><li><p class="paragraph" style="text-align:left;"><b>Tell it to keep notes.</b> Compaction will happen so good notes survive the reset</p></li><li><p class="paragraph" style="text-align:left;"><b>Use the </b><code>CLAUDE.md</code> to encode persistent instructions that survive compaction naturally</p></li></ul><h2 class="heading" style="text-align:left;" id="agents-vs-folders">Agents vs. Folders</h2><p class="paragraph" style="text-align:left;">Claude Code offers two organizational approaches:</p><ul><li><p class="paragraph" style="text-align:left;"><b>Agents:</b> A specific system prompt with whitelisted tools. Useful for separating &quot;pentester mode&quot; from daily coding use</p></li><li><p class="paragraph" style="text-align:left;"><b>Folders:</b> Launch Claude Code from a target-specific directory. The <code>.claude/</code> folder in that directory loads automatically, layering on top of the home directory configuration</p></li></ul><p class="paragraph" style="text-align:left;">Rez0 prefers the folder approach: create a directory per target, populate its <code>CLAUDE.md</code> with program policy and scope (pulled automatically from HackerOne via H1 Brain), and launch Claude Code from there. Every future session in that folder inherits the target context automatically.</p><h2 class="heading" style="text-align:left;" id="resources">Resources</h2><ul><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://github.com/caido/skills?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-166-claude-code-skills-for-bug-bounty-when-why-and-how-to-build-them" target="_blank" rel="noopener noreferrer nofollow">Caido Mode Claude Skill</a></b> — The official Caido integration for Claude Code, enabling proxy-aware hacking with full replay, HTTP history, and findings management</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://github.com/PatrikFehrenbach/h1-brain?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-166-claude-code-skills-for-bug-bounty-when-why-and-how-to-build-them" target="_blank" rel="noopener noreferrer nofollow">H1 Brain by Patrick</a></b> — An MCP server that pulls HackerOne program policies, scope, and disclosed reports to give Claude full program context</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://docs.anthropic.com/en/docs/claude-code/skills?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-166-claude-code-skills-for-bug-bounty-when-why-and-how-to-build-them" target="_blank" rel="noopener noreferrer nofollow">Claude Code Skills Documentation</a></b> — Anthropic&#39;s official guide on building and structuring Claude Code skills with front matter, descriptions, and tool definitions</p></li></ul><hr class="content_break"><p class="paragraph" style="text-align:left;">That&#39;s it for the week, keep hacking!</p></div></div>
  ]]></content:encoded>
</item>

      <item>
  <title>[HackerNotes Ep. 165] Protobuf Hacking, AI-Powered Bug Hunting, and Self-Improving Claude Workflows</title>
  <description>Today, we are digging some protobuf stuff, and also talking about the current debate about AI finding bugs and how to use it properly</description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-165-protobuf-hacking-ai-powered-bug-hunting-and-self-improving-claude-workflows</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-165-protobuf-hacking-ai-powered-bug-hunting-and-self-improving-claude-workflows</guid>
  <pubDate>Thu, 12 Mar 2026 11:01:00 +0000</pubDate>
  <atom:published>2026-03-12T11:01:00Z</atom:published>
    <dc:creator>Aituglo ‎</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr">Hacker TL;DR</h2><ul><li><p class="paragraph" style="text-align:left;">Protobuf parameters on Google are an underexplored attack surface. Claude excels at decoding and re-encoding them</p></li><li><p class="paragraph" style="text-align:left;">Login CSRF chained with browser permissions (mic/camera) can escalate to full surveillance of victims</p></li><li><p class="paragraph" style="text-align:left;">Add a self-improving loop to your Claude.md file so it learns from mistakes and saves tokens over time</p></li><li><p class="paragraph" style="text-align:left;">Models now score ~69% on CyberBench. AI-assisted hacking is no longer hype; it&#39;s producing real bounties</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/c7a25360-b133-400b-b6d2-789a252e0293/ThreatLocker-1.png?t=1773171761"/></div></li></ul><p class="paragraph" style="text-align:center;"><span style="color:#F9FAFB;font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"><b>Today&#39;s Sponsor:</b></span><span style="color:#F9FAFB;font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"> Check out ThreatLocker Ringfencing </span><span style="font-family:inherit;font-size:16px;"><a class="link" href="https://www.criticalthinkingpodcast.io/tl-rf?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-165-protobuf-hacking-ai-powered-bug-hunting-and-self-improving-claude-workflows" target="_blank" rel="noopener noreferrer nofollow">https://www.criticalthinkingpodcast.io/tl-rf</a></span></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="in-the-news">In the News</h2><ul><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://github.com/sw33tLie/bbscope?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-165-protobuf-hacking-ai-powered-bug-hunting-and-self-improving-claude-workflows" target="_blank" rel="noopener noreferrer nofollow">BB Scope Major Update</a> - @sw33tLie shipped a big upgrade to BB Scope, now with the ability to dump your HackerOne reports via a new pull request, useful for feeding context to AI tools</p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://github.com/caido/skills?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-165-protobuf-hacking-ai-powered-bug-hunting-and-self-improving-claude-workflows" target="_blank" rel="noopener noreferrer nofollow">Caido Skills for Claude Code</a> - Caido officially released a Claude Code skill with SDK integration for searching HTTP history, adding findings, editing requests, and managing match-and-replace rules</p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://x.com/nmatt0?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-165-protobuf-hacking-ai-powered-bug-hunting-and-self-improving-claude-workflows" target="_blank" rel="noopener noreferrer nofollow">Matt Brown Goes Full-Time</a> - @nmatt0 left his job to go full-time into hardware hacking, pentesting, and content creation. His YouTube channel is the go-to resource for IoT device hacking</p></li></ul><h2 class="heading" style="text-align:left;" id="protobuf-hacking-on-google">Protobuf Hacking on Google</h2><p class="paragraph" style="text-align:left;">Justin has been finding consistent success targeting protobuf-encoded parameters across Google&#39;s attack surface. Many parameters are base64-encoded binary protobuf structures that most hunters skip, either assuming they&#39;re signed or simply not wanting to deal with the complexity.</p><h3 class="heading" style="text-align:left;" id="how-protobuf-encoding-works">How Protobuf Encoding Works</h3><p class="paragraph" style="text-align:left;">Each field in a protobuf message is encoded with a single byte containing three pieces of information:</p><ul><li><p class="paragraph" style="text-align:left;"><b>Bits 0-2 (Wire Type):</b> Defines the data type like varint, length-delimited string, fixed-size integer</p></li><li><p class="paragraph" style="text-align:left;"><b>Bits 3-6 (Field Number):</b> The index of this field in the protobuf schema</p></li><li><p class="paragraph" style="text-align:left;"><b>Bit 7 (Continuation Flag):</b> Indicates whether the metadata extends into the next byte</p></li></ul><p class="paragraph" style="text-align:left;">Without the <code>.proto</code> schema definition, you won&#39;t get field names, only field numbers and values. But by decoding nested protobuf structures, you can identify IDs, strings visible in the UI, and internal references that unlock new attack vectors.</p><p class="paragraph" style="text-align:left;">So you can use Claude to decode protobuf blobs. It handles nested structures, identifies checksums, and can generate Python scripts to re-encode modified payloads. Justin built a Caido convert workflow to do inline protobuf decoding, which he&#39;ll share on the Discord.</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/dc7a3669-6d67-48a9-aa2b-5dbdd0d8e41e/_bannerHackerNotesShort.png?t=1769044664"/><div class="image__source"><a class="image__source_link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-165-protobuf-hacking-ai-powered-bug-hunting-and-self-improving-claude-workflows" rel="noopener" target="_blank"><span class="image__source_text"><p>Check out the CTBB Discord!</p></span></a></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-165-protobuf-hacking-ai-powered-bug-hunting-and-self-improving-claude-workflows" target="_blank" rel="noopener noreferrer nofollow">You can also find some hacker swag here.</a></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="login-csrf-browser-permissions-surv">Login CSRF + Browser Permissions = Surveillance Chain</h2><p class="paragraph" style="text-align:left;">Justin shared a high-impact chain he&#39;s been reporting successfully across multiple targets:</p><ol start="1"><li><p class="paragraph" style="text-align:left;"><b>Login CSRF</b> into the attacker&#39;s account on the target application</p></li><li><p class="paragraph" style="text-align:left;"><b>CSRF the audio recording endpoint</b> to start capturing the victim&#39;s microphone</p></li><li><p class="paragraph" style="text-align:left;">The application transcribes the audio and uploads it <b>to the attacker&#39;s account</b></p></li></ol><p class="paragraph" style="text-align:left;">If the victim has previously granted the site microphone permissions (a domain-level browser setting), this requires zero user interaction. The attacker can silently record and exfiltrate audio.</p><p class="paragraph" style="text-align:left;">Browser permissions are scoped to the <b>domain</b>, not to the <b>account</b>. So a login CSRF effectively hijacks those permissions.</p><h3 class="heading" style="text-align:left;" id="iframe-permission-delegation">Iframe Permission Delegation</h3><p class="paragraph" style="text-align:left;">Check the <code>allow</code> attribute on iframes. If a parent page delegates permissions like <code>microphone</code>, <code>camera</code>, or <code>display-capture</code> to an iframe you can control, you may be able to abuse those permissions from a third-party context within a trusted top-level page.</p><h2 class="heading" style="text-align:left;" id="ai-hacking-debate-skill-gaps-and-fa">AI Hacking Debate: Skill Gaps and False Positives</h2><p class="paragraph" style="text-align:left;">Can anyone just spin up Claude Code and start finding bugs nowadays ?</p><h3 class="heading" style="text-align:left;" id="what-ai-unlocks">What AI Unlocks</h3><ul><li><p class="paragraph" style="text-align:left;">Claude Code + xssdoctor found a high/critical on a major company by testing <b>survey endpoints</b>, the kind of boring functionality experienced hunters habitually skip</p></li><li><p class="paragraph" style="text-align:left;">BusFactor had Claude find a high on a program paying $8K for highs, one bug pays for three years of usage</p></li><li><p class="paragraph" style="text-align:left;">The gap between technical and semi-technical users has shrunk dramatically, but it hasn&#39;t disappeared</p></li></ul><h3 class="heading" style="text-align:left;" id="where-human-expertise-still-matters">Where Human Expertise Still Matters</h3><ul><li><p class="paragraph" style="text-align:left;"><b>False positive triage:</b> When Claude says &quot;CRITICAL FINDING,&quot; there&#39;s roughly a 20% chance it&#39;s actually valid. Knowing that a wildcard CORS isn&#39;t exploitable or that a finding is a medium rather than a critical requires real security knowledge.</p></li><li><p class="paragraph" style="text-align:left;"><b>Validation complexity:</b> Even experienced hunters need time to fully understand and validate the unusual bugs AI discovers</p></li><li><p class="paragraph" style="text-align:left;"><b>Context and judgment:</b> Setting up infrastructure, understanding scope, and knowing what&#39;s worth reporting still requires a hacker&#39;s intuition</p></li></ul><h3 class="heading" style="text-align:left;" id="the-self-improving-claudemd-trick">The Self-Improving Claude.md Trick</h3><p class="paragraph" style="text-align:left;">Joseph shared a practical tip for leveling up your Claude Code setup:</p><p class="paragraph" style="text-align:left;">Add this to your <code>CLAUDE.md</code>:</p><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><i>Anytime I get frustrated, anytime I have to re-explain something you didn&#39;t understand, or anytime you try a command and it fails repeatedly, add that lesson to the Applied Learning section in your CLAUDE.md.</i></p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;">This creates a self-improving loop: every session makes the next one better. Claude stops wasting tokens on repeated mistakes and learns patterns specific to your workflow, like where session files are stored or which commands work on your system.</p><h2 class="heading" style="text-align:left;" id="discord-bot-for-remote-hacking">Discord Bot for Remote Hacking</h2><p class="paragraph" style="text-align:left;">Instead of using Claude RC (which doesn&#39;t support <code>--dangerously-skip-permissions</code>), Joseph built a Discord bot that acts as a remote Claude Code interface:</p><ul><li><p class="paragraph" style="text-align:left;">Each new task spawns a <b>Discord thread</b> as a session</p></li><li><p class="paragraph" style="text-align:left;">Tool calls are displayed with <b>diff-formatted code blocks</b> (green for additions, red for removals)</p></li><li><p class="paragraph" style="text-align:left;">Supports <b>voice messages and attachments</b> for input</p></li><li><p class="paragraph" style="text-align:left;">Includes a <code>--resume</code> command at the top of each thread for jumping back into sessions from a VPS</p></li><li><p class="paragraph" style="text-align:left;">Acts as a <b>DevOps engineer on speed dial</b> — validate findings, check logs, host files, all from a phone</p></li></ul><div class="image"><img alt="" class="image__image" style="" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/b3658bf1-9327-44b5-ad49-4ff4f00d21dd/CleanShot_2026-03-09_at_19.24.28.png?t=1773080681"/><div class="image__source"><span class="image__source_text"><p>The Discord Bot</p></span></div></div><h2 class="heading" style="text-align:left;" id="resources">Resources</h2><ul><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://github.com/sw33tLie/bbscope?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-165-protobuf-hacking-ai-powered-bug-hunting-and-self-improving-claude-workflows" target="_blank" rel="noopener noreferrer nofollow">BB Scope</a></b> - Dump scope and reports from HackerOne/Bugcrowd with API key support</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://github.com/caido/skills?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-165-protobuf-hacking-ai-powered-bug-hunting-and-self-improving-claude-workflows" target="_blank" rel="noopener noreferrer nofollow">Caido Skills</a></b> - Official Claude Code skill for Caido proxy integration</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://www.youtube.com/@mattbrwn?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-165-protobuf-hacking-ai-powered-bug-hunting-and-self-improving-claude-workflows" target="_blank" rel="noopener noreferrer nofollow">Matt Brown&#39;s YouTube</a></b> - Best hardware hacking content on YouTube</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://training.brownfinesecurity.com/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-165-protobuf-hacking-ai-powered-bug-hunting-and-self-improving-claude-workflows" target="_blank" rel="noopener noreferrer nofollow">Matt Brown&#39;s Courses</a></b> - Digital Signal Analysis for Hardware Hackers ($200) and Beginner&#39;s Guide to IoT Hacking ($50)</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-165-protobuf-hacking-ai-powered-bug-hunting-and-self-improving-claude-workflows#allow" target="_blank" rel="noopener noreferrer nofollow">MDN: iframe allow attribute</a></b> - Documentation on permission delegation to iframes</p></li><li><p class="paragraph" style="text-align:left;"><b><a class="link" href="https://claude.ai/settings/data-privacy-controls?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-165-protobuf-hacking-ai-powered-bug-hunting-and-self-improving-claude-workflows" target="_blank" rel="noopener noreferrer nofollow">Anthropic Privacy Settings</a></b> - Disable training on your data before feeding reports to Claude</p></li></ul><hr class="content_break"><p class="paragraph" style="text-align:left;">That&#39;s it for the week, keep hacking!</p></div></div>
  ]]></content:encoded>
</item>

      <item>
  <title>[HackerNotes Ep. 164] Tommy DeVoss: From Black Hat to Bug Bounty LEGEND</title>
  <description>Justin sits down with Tommy DeVoss to talk about his origin story, Yahoo bugs, and how Tommy first got Justin into Bug Bounty</description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-164-tommy-devoss-from-black-hat-to-bug-bounty-legend</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-164-tommy-devoss-from-black-hat-to-bug-bounty-legend</guid>
  <pubDate>Thu, 05 Mar 2026 11:01:00 +0000</pubDate>
  <atom:published>2026-03-05T11:01:00Z</atom:published>
    <dc:creator>Aituglo ‎</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr">Hacker TL;DR</h2><ul><li><p class="paragraph" style="text-align:left;">Tommy DeVoss (dawgyg) went from IRC botnets and website defacement in the 90s to prison twice, to becoming one of the first six hackers to cross $1M on HackerOne.</p></li><li><p class="paragraph" style="text-align:left;">The $180k Yahoo SSRF: octal-encode only the first octet of <code>169.254.169.254</code> → <code>0251.254.169.254</code>. Worked on 18 separate endpoints, each paid $10k. One bypass, applied systematically across the known attack surface = $180k in one session.</p></li><li><p class="paragraph" style="text-align:left;">Revisiting old reports is an underrated strategy. When you have limited time, don&#39;t start from scratch; go back to reports you&#39;ve already reported and test new variants.</p></li><li><p class="paragraph" style="text-align:left;">Tommy&#39;s fuzzing setup for Chrome: 6 parallel builds (ASAN, MSAN, UBSan, vanilla, debug, exploit-dev). Every crash gets patched locally, so the fuzzer keeps progressing past known bugs instead of looping on the same one.</p></li><li><p class="paragraph" style="text-align:left;">AI can be very useful in this case to cover the whole Chrome project, which is huge to go into it.</p></li><li><p class="paragraph" style="text-align:left;">Scope is not optional. If the company hasn&#39;t explicitly said yes (published program, VDP, signed agreement), the answer is no.</p></li></ul><h2 class="heading" style="text-align:left;" id="hackernotes">Hackernotes</h2><h2 class="heading" style="text-align:left;" id="the-origin-story">The Origin Story</h2><p class="paragraph" style="text-align:left;">Tommy DeVoss started on IRC in the early 90s. Not hacking systems, but mostly fighting over channels and doing botnets. The target was other IRC groups. Mafia Boy, the Canadian kid credited with the first large-scale DDoS attack (eBay, Yahoo, basically everything big in 2000), was one of the guys they were fighting on Efnet. Same botnets, different sides.</p><p class="paragraph" style="text-align:left;">From there, he did some website defacement. This is the late 90s, so &quot;web vulnerability&quot; wasn&#39;t really a thing yet. Websites were static HTML or maybe some flat files. There was no database for SQLi. The attack path was network-level: Telnet exploits, RPC vulnerabilities on port 111, compromising name servers, installing packet sniffers to harvest credentials of anyone who Telnetted through. University computers in Taiwan, Korea, and Hong Kong were the preferred jump boxes, running old OS versions, always exploitable, and far enough away that tracing back was expected to be hard.</p><p class="paragraph" style="text-align:left;">It worked until it didn&#39;t. Cowhead got arrested at DEF CON 2001 for ripping a gold-plated payphone off the wall during the scavenger hunt. The FBI monitored the group&#39;s website to figure out who was involved, and he got arrested too.</p><p class="paragraph" style="text-align:left;">Two and a half years in prison. Then out, back on computers within a month, because construction and cooking were boring. Defaced Yahoo again. The probation officer showed up and found a keyboard on the bed. No computer, no reason to own a keyboard, but it was a violation, so back for another year.</p><p class="paragraph" style="text-align:left;">Out in 2008. Actually, clean this time. He bought an Xbox, which technically violated his terms, too. Then his sister&#39;s fiancé showed him a browser-based game called Ebony that had exploitable mechanics, and he wrote the first bot for it. Then a nearby business got broken into, and some computers got stolen. Local cops knew his history. FBI watched him for six months, found nothing, and used the burglary as an excuse to raid him anyway. Back for another 16 months.</p><p class="paragraph" style="text-align:left;">Four months into that sentence, the FBI came back and apologized. They&#39;d found the actual burglar and the person working with Rafa. As part of what amounted to an apology, they lifted his computer ban permanently. Without that, he wouldn&#39;t have been able to do bug bounties legally.</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/dc7a3669-6d67-48a9-aa2b-5dbdd0d8e41e/_bannerHackerNotesShort.png?t=1769044664"/><div class="image__source"><a class="image__source_link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-164-tommy-devoss-from-black-hat-to-bug-bounty-legend" rel="noopener" target="_blank"><span class="image__source_text"><p>Check out the CTBB Discord!</p></span></a></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-164-tommy-devoss-from-black-hat-to-bug-bounty-legend" target="_blank" rel="noopener noreferrer nofollow">You can also find some hacker swag here.</a></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="the-bug-bounty-entry">The Bug Bounty Entry</h2><p class="paragraph" style="text-align:left;"><b>2014:</b> creates accounts on HackerOne and Bugcrowd, doesn&#39;t do anything with them. The whole thing seemed too good to be true for someone who had life in prison on the table if he got caught hacking again without authorization.</p><p class="paragraph" style="text-align:left;"><b>Late 2015:</b> starts seeing write-ups on Twitter. Actual money, actual bugs.</p><p class="paragraph" style="text-align:left;"><b>Early 2016: </b>logs into his HackerOne account, sees Yahoo in the program directory. Goes there first because he already knows Yahoo.</p><p class="paragraph" style="text-align:left;"><b>March 2016:</b> first bounty, $300.</p><p class="paragraph" style="text-align:left;">Then Hack the Pentagon. A month of hacking DoD assets, finished first or close to it, then found out you needed to pass a background check to collect bounties. Went to Twitter, vented. Someone running the program DMed him. 24 hours later: &quot;You now pass background checks.&quot;</p><p class="paragraph" style="text-align:left;"><b>2017:</b> ~$200k. Named one of H1&#39;s top earners.</p><p class="paragraph" style="text-align:left;"><b>2018:</b> ~$700k, including the $180k SSRF session.</p><p class="paragraph" style="text-align:left;"><b>2019: </b>officially crosses $1M. One of the first six hackers to do it.</p><h2 class="heading" style="text-align:left;" id="the-180-k-ssrf">The $180k SSRF</h2><p class="paragraph" style="text-align:left;">Back in October 2018, in Las Vegas, he was waiting for a friend who took two hours to get ready. Didn&#39;t want to start something new, so he opened an old Yahoo SSRF report and started playing with it.</p><p class="paragraph" style="text-align:left;">Yahoo used a blacklist to block the AWS metadata endpoint <code>169.254.169.254</code>. But you know, a blacklist can be bypassed.</p><p class="paragraph" style="text-align:left;">He&#39;d already gotten the AWS credentials from this endpoint before, so he knew the exact path and key name. Just needed a new way to represent the IP. He was stoned and decided to try something that logically shouldn&#39;t work: octal-encode just the first octet. <code>169</code> becomes <code>0251</code>. The rest of the IP stays the same: <code>0251.254.169.254</code>.</p><p class="paragraph" style="text-align:left;">It worked. He has no idea why specifically, it looks nothing like a valid IP address, but Yahoo&#39;s server handled it fine. The blacklist was checking for <code>169.254.169.254</code> and its obvious variants. <code>0251.254.169.254</code> wasn&#39;t on the list.</p><p class="paragraph" style="text-align:left;">Then he did what any bug hunter does: opened every SSRF report he&#39;d ever filed against Yahoo, three years’ worth, and tested the same encoding on all of them. Worked on every single one. 18 endpoints, each a unique location, each paid $10k. 18 reports, $180k. Four days later, he was sitting outside a car dealership, buying his GTR in cash.</p><p class="paragraph" style="text-align:left;">So when you find a working bypass, scale it horizontally. You already know the program, you already know the endpoints. One technique applied to your entire known attack surface multiplies the payout without multiplying the recon time.</p><h2 class="heading" style="text-align:left;" id="fuzzing-chrome">Fuzzing Chrome</h2><p class="paragraph" style="text-align:left;">This is Tommy&#39;s current focus. He can&#39;t disclose the specific bugs yet, hasn&#39;t been long enough since the reports were shot.</p><p class="paragraph" style="text-align:left;">The setup: 6 separate Chrome checkouts, each compiled with different instrumentation:</p><ul><li><p class="paragraph" style="text-align:left;"><b>ASAN</b> — AddressSanitizer, catches memory corruption (OOB, UAF, double-free)</p></li><li><p class="paragraph" style="text-align:left;"><b>MSAN</b> — MemorySanitizer, catches uninitialized memory reads</p></li><li><p class="paragraph" style="text-align:left;"><b>UBSan</b> — Undefined Behavior Sanitizer, flags signed integer overflow, null dereference, type confusion</p></li><li><p class="paragraph" style="text-align:left;"><b>Vanilla</b> — clean build, confirms crashes reproduce in real-world binary</p></li><li><p class="paragraph" style="text-align:left;"><b>Debug</b> — full symbols for stack trace analysis</p></li><li><p class="paragraph" style="text-align:left;"><b>Exploit dev</b> — custom build for writing PoCs</p></li></ul><p class="paragraph" style="text-align:left;">Every time he finds a crash, he patches it locally in all 6 builds before continuing. This is the part most people skip. If you don&#39;t patch locally, your fuzzer keeps hitting the same crash and never gets past it. Local patches push the fuzzer into unexplored code paths. Over time, you build a layered map of the same code region.</p><p class="paragraph" style="text-align:left;">Most of what he&#39;s found is in the renderer process. He&#39;s got one in the GPU process. Renderer bugs are the entry point and GPU process bugs are more valuable because the GPU process runs with fewer sandbox restrictions. Chain renderer-to-GPU and you&#39;re looking at sandbox escape.</p><p class="paragraph" style="text-align:left;">There&#39;s also the Google Open Source VRP angle: vulnerabilities in third-party libraries that Chrome uses can be patched upstream, then submitted to Google after 30 days. Up to $15k for standard bugs, up to $31k for supply chain compromise scenarios. The library doesn&#39;t even need to be reachable in Chrome itself.</p><h2 class="heading" style="text-align:left;" id="ai-in-the-workflow">AI in the Workflow</h2><p class="paragraph" style="text-align:left;">There is three places where he can use AI in his workflow:</p><p class="paragraph" style="text-align:left;"><b>Harness building.</b> Chrome has a full validation pipeline that runs before any vulnerable code path is reachable. Writing a harness that mimics that flow by hand requires deep knowledge of Chrome internals. AI can analyze the relevant source files and construct the call sequence automatically. The harness has to be accurate enough that crashes reproduce in the real browser, not just in isolation.</p><p class="paragraph" style="text-align:left;"><b>Crash RCA.</b> When the fuzzer finds something, tracing why it crashed, which memory operation failed, and where the state diverged from the crash site, used to take 1-2 hours manually. AI does it in ~2 minutes.</p><p class="paragraph" style="text-align:left;"><b>Codebase navigation.</b> Chrome&#39;s codebase is massive. Tracing a single code path across hundreds of files requires holding a lot of context. Gemini is the only model Tommy uses for this because of the 2M token context window. Other models lose track of the state before they can complete the trace.</p><p class="paragraph" style="text-align:left;">There&#39;s also a rule he runs: AI agents have to do a brain dump every few minutes, what they&#39;ve tried, what failed, and why. Not just what succeeded. The failure log is the decision tree. Without it, long research sessions drift into repeating dead ends.</p><p class="paragraph" style="text-align:left;">And also, AI agents are never allowed to delete files.</p><h2 class="heading" style="text-align:left;" id="about-the-scope">About the Scope</h2><p class="paragraph" style="text-align:left;">Tommy says it with more weight than most people. Life in prison is what he&#39;s got on the table for another computer crime conviction. So when he talks about scope, it&#39;s not theoretical.</p><p class="paragraph" style="text-align:left;">Bug bounty is legal only because the company explicitly said yes. Published program, VDP, signed agreement. That&#39;s it. &quot;Security research&quot; without written permission is unauthorized access, regardless of intent or severity. A pissed-off CLO having a bad day and an out-of-scope test is all it takes.</p><p class="paragraph" style="text-align:left;">If they have a VDP but no bounties, you&#39;re still covered. If they have a program with a restrictive scope, stay in it. If they have nothing, the answer is no.</p><h2 class="heading" style="text-align:left;" id="resources">Resources</h2><ul><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://hackerone.com/dawgyg/hacktivity?type=user&utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-164-tommy-devoss-from-black-hat-to-bug-bounty-legend" target="_blank" rel="noopener noreferrer nofollow">Tommy DeVoss on HackerOne</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://x.com/thedawgyg?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-164-tommy-devoss-from-black-hat-to-bug-bounty-legend" target="_blank" rel="noopener noreferrer nofollow">Tommy DeVoss on Twitter/X</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://darknetdiaries.com/episode/60/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-164-tommy-devoss-from-black-hat-to-bug-bounty-legend" target="_blank" rel="noopener noreferrer nofollow">Darknet Diaries Episode 60 — dawgyg</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.beyondtrust.com/podcast/ep-71-from-prison-to-millions-the-hacker-who-struck-yahoo-bug-bounty-gold-tommy-devoss?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-164-tommy-devoss-from-black-hat-to-bug-bounty-legend" target="_blank" rel="noopener noreferrer nofollow">BeyondTrust Podcast — From Prison to Millions</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.hackerone.com/blog/hacker-spotlight-interview-dawgyg?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-164-tommy-devoss-from-black-hat-to-bug-bounty-legend" target="_blank" rel="noopener noreferrer nofollow">HackerOne Spotlight — dawgyg</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://bughunters.google.com/about/rules/6521337925468160/google-open-source-software-vulnerability-reward-program-rules?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-164-tommy-devoss-from-black-hat-to-bug-bounty-legend" target="_blank" rel="noopener noreferrer nofollow">Google Open Source VRP</a></p><p class="paragraph" style="text-align:left;"></p></li></ul></div></div>
  ]]></content:encoded>
</item>

      <item>
  <title>[HackerNotes Ep. 163] PortSwigger Top 10 Web Hacking Techniques of 2025</title>
  <description>We are covering all the techniques from the top 10 by PortSwigger in 2025</description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-163-portswigger-top-10-web-hacking-techniques-of-2025</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-163-portswigger-top-10-web-hacking-techniques-of-2025</guid>
  <pubDate>Thu, 26 Feb 2026 11:01:00 +0000</pubDate>
  <atom:published>2026-02-26T11:01:00Z</atom:published>
    <dc:creator>Aituglo ‎</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr">Hacker TL;DR</h2><ul><li><p class="paragraph" style="text-align:left;">Parser differentials: duplicate JSON keys or double Authorization headers parsed differently by frontend vs backend → auth bypass depending on which value each component reads.</p></li><li><p class="paragraph" style="text-align:left;">HTTP/2 CONNECT tunnels: if the server supports the CONNECT verb over HTTP/2, you get a potential SSRF and internal port scanning primitive via binary-framed stream multiplexing.</p></li><li><p class="paragraph" style="text-align:left;">XSS-Leak via Chrome connection pool: saturate Chrome&#39;s 256-connection cap, trigger a cross-origin redirect, and use lexicographic host resolution ordering to binary-search leaked subdomains.</p></li><li><p class="paragraph" style="text-align:left;">Next.js internal cache poisoning: <code>__nextDataReq</code> converts responses to JSON but isn&#39;t part of the cache key; combine with <code>x-now-route-matches</code> to poison the internal cache with a stored XSS payload.</p></li><li><p class="paragraph" style="text-align:left;">Cross-site ETag length leak: ETag reflected into <code>If-None-Match</code> can overflow Express&#39;s 16KB header limit → HTTP 431 → Chrome History API length oracle leaks secret content.</p></li><li><p class="paragraph" style="text-align:left;">SOAPwn (.NET WSDL proxy): <code>file://</code> URI supplied to <code>HttpWebClientProtocol</code> causes a silent cast failure → arbitrary file write to disk as .aspx/.cshtml → instant webshell and RCE.</p></li><li><p class="paragraph" style="text-align:left;">Unicode normalization attacks: five classes of bypass (overlong encoding, byte truncation, confusables, casing, combining diacritics) all exploit the same root cause, security check runs before normalization.</p></li><li><p class="paragraph" style="text-align:left;">Novel SSRF via redirect loops: escalating redirect chains (301→302→...→310) with a final hop to AWS metadata can flip blind SSRF to full-read through error handling mismatches.</p></li><li><p class="paragraph" style="text-align:left;">ORM injection as server-side XS-leaks: filtering endpoints exposing field + operator syntax (<code>resetToken[not]=E</code>) allow binary search on sensitive field values, the new SQLi.</p></li><li><p class="paragraph" style="text-align:left;">Error-based SSTI: borrowing SQLi methodology for template injection, verbose errors leak data, division-by-zero gives a boolean oracle, and <code>(1/0).zxy.zxy</code> is a universal detection polyglot.</p></li></ul><h2 class="heading" style="text-align:left;" id="hackernotes">Hackernotes</h2><h2 class="heading" style="text-align:left;" id="parser-differentials-when-interpret">Parser Differentials: When Interpretation Becomes a Vulnerability</h2><p class="paragraph" style="text-align:left;">This talk from joernchen at OffensiveCon 25 is a clean survey of what happens when two components in the same stack parse the same data and reach different conclusions. The first example is a JSON body with duplicate <code>roles</code> keys. Erlang&#39;s JSON parser reads the first occurrence and sees <code>admin</code>. JavaScript&#39;s parser reads the last and sees an empty array. If the auth decision happens in Erlang but the session is managed by a Node service, you get a straight auth bypass with one duplicated key.</p><p class="paragraph" style="text-align:left;">The second example targets the Authorization header. Send two of them: the first carries an <code>alg:none</code> JWT with crafted admin claims, the second is a legitimate low-privilege bearer token. The frontend validates the second header (valid signature, low-priv), the backend reads the first (unsigned, admin claims). Same request, two interpretations, full privilege escalation.</p><p class="paragraph" style="text-align:left;">YAML parsing opens even more surface. The <code>!!binary</code> tag silently base64-encodes values, and the <code>&lt;&lt;</code> merge operator merges keys from anchors, both can create differential behavior between YAML parsers that handle these tags differently. Even YAML booleans are unstable: <code>yes</code>, <code>no</code>, <code>true</code>, <code>false</code> may resolve as strings or booleans depending on the parser version. CI/CD pipelines that consume YAML (GitHub Actions, GitLab CI) are high-value targets here because the parsing stack is rarely audited end-to-end.</p><h2 class="heading" style="text-align:left;" id="playing-with-http-2-connect">Playing with HTTP/2 CONNECT</h2><p class="paragraph" style="text-align:left;">HTTP/1.1 CONNECT creates a raw bidirectional tunnel. HTTP/2 CONNECT is a completely different mechanism: binary framing, stream multiplexing, individual stream identifiers. The author built a Go-based port scanner that operates inside HTTP/2 CONNECT tunnels. A successful connection returns a status 200 frame; a failure returns 503. That distinction is enough to map open internal ports behind a proxy or load balancer that supports the CONNECT verb.</p><p class="paragraph" style="text-align:left;">Beyond port scanning, raw HTTP/1.1 requests can be sent inside the tunnel and responses read back from internal HTTP servers. The hunting approach is straightforward: scan your targets for HTTP/2 CONNECT support. If the verb is accepted, you likely have a free SSRF and port scan primitive without needing any other vulnerability.</p><p class="paragraph" style="text-align:left;">But this blogpost was quite hard to understand, especially the real impact and surface of this vulnerability.</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/dc7a3669-6d67-48a9-aa2b-5dbdd0d8e41e/_bannerHackerNotesShort.png?t=1769044664"/><div class="image__source"><a class="image__source_link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-163-portswigger-top-10-web-hacking-techniques-of-2025" rel="noopener" target="_blank"><span class="image__source_text"><p>Check out the CTBB Discord!</p></span></a></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-163-portswigger-top-10-web-hacking-techniques-of-2025" target="_blank" rel="noopener noreferrer nofollow">You can also find some hacker swag here.</a></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="xss-leak-leaking-cross-origin-redir">XSS-Leak: Leaking Cross-Origin Redirects</h2><p class="paragraph" style="text-align:left;">Despite the name, this has nothing to do with XSS. It&#39;s a cross-site leak that abuses Chrome&#39;s connection pool mechanics. Chrome enforces a hard cap of 256 concurrent requests total and 6 per origin. When the pool is full and a conflict needs resolution, Chrome prioritizes by a specific order: priority table, then port, then scheme, then host, lexicographically.</p><p class="paragraph" style="text-align:left;">The attack works by filling the connection pool, then triggering a cross-origin redirect (for example via a hash change). Simultaneously, the attacker triggers a request to a guessed hostname with the same priority, port, and scheme. One slot is released, and timing is measured. If the request took longer to resolve, the cross-origin host was lexicographically smaller than the guess. This gives a binary search oracle that can leak full subdomains character by character.</p><p class="paragraph" style="text-align:left;">The technique also works for user-type detection. If an application redirects to <code>admin.example.com</code> for admins and <code>user.example.com</code> for regular users, the lexicographic ordering difference becomes a role oracle. It&#39;s a narrow primitive but a reusable one anywhere cross-origin redirects carry state.</p><h2 class="heading" style="text-align:left;" id="nextjs-cache-and-chains-the-stale-e">Next.js, cache, and chains: the stale elixir</h2><p class="paragraph" style="text-align:left;">This is a research from zhero targeting the Next.js internal cache, not a CDN-vs-backend mismatch, but the framework&#39;s own caching layer. Two components make it work. The <code>__nextDataReq</code> query parameter converts a standard page request into a &quot;data route&quot; that returns JSON instead of HTML. The <code>x-now-route-matches</code> header makes the response cacheable by the internal cache.</p><p class="paragraph" style="text-align:left;">The critical detail: <code>__nextDataReq</code> is not part of the cache key. There&#39;s no query-parameter-based cache buster available. But <code>Accept-Encoding</code> is part of the cache key, and browsers always send it. So the attacker sends the malicious request without <code>Accept-Encoding</code>, poisoning that specific cache variant. Normal users always include the header, so they never hit the poisoned entry, unless the attacker targets the variant they&#39;ll actually receive.</p><p class="paragraph" style="text-align:left;">The impact is stored XSS. The JSON blob includes the User-Agent value and gets served with a <code>text/html</code> content type, so it executes on page load. Multiple reports using this chain netted a six-figure bounty total. The takeaway here is internal caching mechanisms in frameworks are high-value audit targets. Look for internal headers and query params that alter response format without being part of the cache key.</p><h2 class="heading" style="text-align:left;" id="cross-site-e-tag-length-leak">Cross-Site ETag Length Leak</h2><p class="paragraph" style="text-align:left;">This one started as an unintended CTF solution and generalized into a reusable primitive. The chain begins with CSRF to control the length of note content. The ETag header reflects the response size. On a second request, the browser automatically sends <code>If-None-Match</code> with the ETag value, which increases the request size. The attacker buffers additional data up to Express&#39;s 16KB header limit using a query parameter. If the ETag is 4 bytes instead of 3, the total request size crosses the 16KB boundary and triggers an HTTP 431 (Request Header Fields Too Large).</p><p class="paragraph" style="text-align:left;">Here&#39;s the Chrome gadget that makes it exploitable: a 431 response replaces the current history entry instead of adding a new one. That means <code>history.length</code> becomes a binary oracle. Did the 431 happen? Then the ETag was 4 bytes. Did the content match the flag condition? Binary search loop, secret extracted.</p><p class="paragraph" style="text-align:left;">Real-world applicability is constrained: you need CSRF and granular control over content length. But the Chrome 431 History API gadget is broadly reusable as an oracle wherever you can influence response header size. The key primitive is a response header (ETag) reflected into a request header (<code>If-None-Match</code>), creating overflow-based attack surface.</p><h2 class="heading" style="text-align:left;" id="soa-pwn-pwning-net-framework-apps-t">SOAPwn: Pwning .NET Framework Apps Through HTTP Client Proxies and WSDL</h2><p class="paragraph" style="text-align:left;">WatchTowr dropped a 93-page whitepaper on this one. The core vulnerability lives in <code>HttpWebClientProtocol</code>, the .NET class used to send SOAP requests over HTTP. The code uses the <code>as</code> operator to cast a URI to an <code>HttpWebRequest</code>. When an attacker supplies a <code>file://</code> URI, the cast produces a <code>FileWebRequest</code> instead, and the <code>as</code> operator returns null rather than throwing. The code checks for null, skips all HTTP-specific setup, and returns the original <code>FileWebRequest</code> untouched. No error is thrown.</p><p class="paragraph" style="text-align:left;">The first impact is arbitrary file write. The XML SOAP body gets written to disk. Name the file <code>.cshtml</code> or <code>.aspx</code>, inject C# code into the SOAP arguments, and you have an instant webshell. The second impact comes from UNC paths: supply a UNC path as the URI, and the server attempts to write to an attacker-controlled share, leaking its NTLM hash for offline cracking.</p><p class="paragraph" style="text-align:left;">Microsoft classified this as an application-level issue rather than a framework vulnerability, so there&#39;s no universal patch. Hunting for this means looking at .NET applications that handle HTTP client proxies and WSDL ingestion, specifically for cast/URI-type confusion where <code>file://</code> or UNC schemes aren&#39;t filtered before the cast.</p><h2 class="heading" style="text-align:left;" id="lost-in-translation-exploiting-unic">Lost in Translation: Exploiting Unicode Normalization</h2><p class="paragraph" style="text-align:left;">Ryan and Isabella Barnett presented this at Black Hat USA 2025, and Ryan&#39;s position at a major WAF vendor gives the research real-world grounding. They document five distinct attack classes, all rooted in the same fundamental problem: the security check executes before Unicode normalization.</p><p class="paragraph" style="text-align:left;">Overlong encodings are the first class: a standard ASCII character encoded as 2 or 3 bytes bypasses regex checks that run before the application normalizes back to the single-byte representation. Byte truncation is the second: a 3-byte Chinese character truncated to its last two bytes can produce CRLF or LF sequences, enabling header injection through what appears to be an endianness issue. Confusables and best-fit mappings are the third class, extending Orange Tsai&#39;s &quot;worst-fit&quot; research: visually similar characters get normalized to dangerous equivalents. Casing attacks are the fourth: the dotless ı (U+0131) normalizes to <code>I</code> after <code>.upper()</code>, and the Kelvin sign K (U+212A) normalizes to <code>k</code> after <code>.lower()</code>, both bypassing WAF checks that run before case normalization. The fifth class uses combining diacritics: <code>&gt;</code> combined with a combining slash produces <code>≯</code>, which some parsers re-normalize back to <code>&gt;</code>, bypassing HTML injection filters.</p><p class="paragraph" style="text-align:left;">The updated ActiveScan++ extension includes detection for these patterns. For code review, the signal is simple: if the security check runs before normalization, it&#39;s vulnerable.</p><h2 class="heading" style="text-align:left;" id="novel-ssrf-technique-involving-http">Novel SSRF Technique Involving HTTP Redirect Loops</h2><p class="paragraph" style="text-align:left;">Shubs research started with a blind SSRF and wanted full-read. The experiments showed distinct error behaviors: a single redirect returned &quot;Exception: Invalid JSON&quot; many redirects (up to 30) returned &quot;Network Exception,&quot; and a 500 status in the response chain returned the full response body. That error state transition was the key.</p><p class="paragraph" style="text-align:left;">The working technique uses an escalating redirect chain: 301 → 302 → ... → 310, with the final redirect pointing to AWS metadata. Full-read SSRF works. Only responses from redirect number 305 onward are visible, which suggests a mismatch between libcurl&#39;s max-redirect setting and the application&#39;s error handling. The application catches a different error class once the redirect limit is exceeded and exposes the response body instead of a generic error.</p><p class="paragraph" style="text-align:left;">This technique has worked across multiple targets and tech stacks since its discovery. The hunting approach: whenever you have blind SSRF, throw redirect chains at it with escalating status codes. Error state transitions between &quot;exception type A&quot; and &quot;exception type B&quot; can flip a blind SSRF to full-read.</p><h2 class="heading" style="text-align:left;" id="orm-leaking-more-than-you-joined-fo">ORM Leaking More Than You Joined For</h2><p class="paragraph" style="text-align:left;">Alex Brown frames ORM leaks as server-side XS-leaks, and calls it the new SQLi. The attack surface is any application that exposes filtering with field and operator syntax on search, sort, or query endpoints. Key signals in the wild include patterns like <code>email[starts_with]=</code>, <code>resetToken[not]=E</code>, or double underscores in query parameters.</p><p class="paragraph" style="text-align:left;">The URL-encoded format <code>resetToken[not]=E</code> maps to the JSON representation <code>&#123;&quot;resetToken&quot;:&#123;&quot;not&quot;:&quot;E&quot;&#125;&#125;</code>, which can bypass middleware JSON validation that doesn&#39;t account for bracket-encoded operators. Even without a <code>starts_with</code> operator, logical operators like <code>gt</code> and <code>lt</code> enable binary search on field values. Database collation matters: case sensitivity and sort order vary by database engine, so the binary search has to be calibrated accordingly.</p><p class="paragraph" style="text-align:left;">Justin demonstrated a combination attack: ORM injection paired with boolean error-based blind SSTI on Microsoft Graph OData. The payload <code>startswith(ExternalId,&#39;59d&#39;) and ((UserName eq &#39;j.rhynorater@gmail.com&#39;) and (FirstName eq &#39;FalseValueHere&#39; or Id div 0 eq 1))</code> creates a blind boolean oracle via OData, exfiltrating data character by character even after direct JSON responses were blocked. The pattern generalizes: any ORM or query language that exposes operators to user input is a candidate.</p><h2 class="heading" style="text-align:left;" id="successful-errors-new-code-injectio">Successful Errors: New Code Injection and SSTI Techniques</h2><p class="paragraph" style="text-align:left;">Vladislav Korchagin took SQL injection methodology and applied it systematically to server-side template injection, documenting payloads across every major template engine. The first technique is error-based SSTI: trigger a verbose template engine error that leaks data in the error message itself. In Python, <code>getattr(obj, payload)</code> produces an error like &quot;str has no attribute &lt;output&gt;&quot; where the output contains file contents. Unlike integer conversion tricks, this approach has no size truncation.</p><p class="paragraph" style="text-align:left;">The second technique is boolean error-based blind SSTI. The expression <code>(1/(expression_evaluating_to_0))</code> triggers a division by zero, which returns HTTP 500. That&#39;s a binary oracle: true conditions evaluate to a non-zero value and return normally, false conditions hit division by zero and return 500. The universal detection polyglot <code>(1/0).zxy.zxy</code> triggers an error in every tested language and template engine, making it a reliable fingerprinting payload.</p><p class="paragraph" style="text-align:left;">Coverage includes Python, PHP, Java, Ruby, Node.js, and Elixir with both language-specific and generic payloads. Everything is integrated into the SSTImap tool, with improvements pushed the same day this episode recorded. For hunters, this research turns SSTI from a &quot;find the magic string&quot; exercise into a systematic extraction methodology with blind, error-based, and boolean-blind variants, exactly mirroring the evolution SQLi went through years ago.</p><p class="paragraph" style="text-align:left;">And that&#39;s it for today, keep hacking !</p><h2 class="heading" style="text-align:left;" id="resources">Resources</h2><ul><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.youtube.com/watch?v=Dq_KVLXzxH8&utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-163-portswigger-top-10-web-hacking-techniques-of-2025" target="_blank" rel="noopener noreferrer nofollow">Parser Differentials: When Interpretation Becomes a Vulnerability</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://blog.flomb.net/posts/http2connect/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-163-portswigger-top-10-web-hacking-techniques-of-2025" target="_blank" rel="noopener noreferrer nofollow">Playing with HTTP/2 CONNECT</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://blog.babelo.xyz/posts/cross-site-subdomain-leak/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-163-portswigger-top-10-web-hacking-techniques-of-2025" target="_blank" rel="noopener noreferrer nofollow">XSS-Leak: Leaking Cross-Origin Redirects</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://zhero-web-sec.github.io/research-and-things/nextjs-cache-and-chains-the-stale-elixir?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-163-portswigger-top-10-web-hacking-techniques-of-2025" target="_blank" rel="noopener noreferrer nofollow">Next.js, cache, and chains: the stale elixir</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://blog.arkark.dev/2025/12/26/etag-length-leak?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-163-portswigger-top-10-web-hacking-techniques-of-2025" target="_blank" rel="noopener noreferrer nofollow">Cross-Site ETag Length Leak</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://watchtowr.com/wp-content/uploads/SOAPwnwatchtowr_soappwn-research-whitepaper_10-12-2025.pdf?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-163-portswigger-top-10-web-hacking-techniques-of-2025" target="_blank" rel="noopener noreferrer nofollow">SOAPwn: Pwning .NET Framework Apps Through HTTP Client Proxies And WSDL</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.youtube.com/watch?v=ETB2w-f3pM4&utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-163-portswigger-top-10-web-hacking-techniques-of-2025" target="_blank" rel="noopener noreferrer nofollow">Lost in Translation: Exploiting Unicode Normalization</a></p><ul><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://bi.tk/utf8.html?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-163-portswigger-top-10-web-hacking-techniques-of-2025" target="_blank" rel="noopener noreferrer nofollow">https://bi.tk/utf8.html</a></p></li></ul></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://slcyber.io/research-center/novel-ssrf-technique-involving-http-redirect-loops/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-163-portswigger-top-10-web-hacking-techniques-of-2025" target="_blank" rel="noopener noreferrer nofollow">Novel SSRF Technique Involving HTTP Redirect Loops</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.elttam.com/blog/leaking-more-than-you-joined-for/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-163-portswigger-top-10-web-hacking-techniques-of-2025" target="_blank" rel="noopener noreferrer nofollow">ORM Leaking More Than You Joined For</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://github.com/vladko312/Research_Successful_Errors?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-163-portswigger-top-10-web-hacking-techniques-of-2025" target="_blank" rel="noopener noreferrer nofollow">Successful Errors: New Code Injection and SSTI Techniques</a></p></li></ul></div></div>
  ]]></content:encoded>
</item>

      <item>
  <title>[HackerNotes Ep. 162] HackerOne Training AI on Bug Bounty Data ?</title>
  <description>We received the CTO of Hackerone to discuss the recent events about AI being fed by our Bug Bounty reports</description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-162-hackerone-training-ai-on-bug-bounty-data</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-162-hackerone-training-ai-on-bug-bounty-data</guid>
  <pubDate>Thu, 19 Feb 2026 11:01:15 +0000</pubDate>
  <atom:published>2026-02-19T11:01:15Z</atom:published>
    <dc:creator>Aituglo ‎</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr">Hacker TL;DR</h2><ul><li><p class="paragraph" style="text-align:left;">HackerOne is <b>not</b> training LLMs on bug bounty reports, existing AI/ML usage is limited to spam classification and anonymized trend analysis</p></li><li><p class="paragraph" style="text-align:left;">The PTAS agentic pentesting tool draws from public benchmarks, internal benchmarks, public CVEs, and opt-in sidecar runs, not researcher submissions</p></li><li><p class="paragraph" style="text-align:left;">HackerOne&#39;s Terms of Service need updating to distinguish legacy ML from modern LLMs</p></li><li><p class="paragraph" style="text-align:left;">HackerOne decreased bounty payouts across severity tiers</p><hr class="content_break"></li></ul><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/cacc84dc-1fee-4931-86d1-ad290e017024/ZTW2.png?t=1769045492"/><div class="image__source"><a class="image__source_link" href="https://ztw.com/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-162-hackerone-training-ai-on-bug-bounty-data" rel="noopener" target="_blank"><span class="image__source_text"><p>ThreatLocker - Zero Trust World 2026</p></span></a></div></div><p class="paragraph" style="text-align:center;">Join Justin at Zero Trust World in March!<br>Get $200 off registration with Code: <b>ZTWCTBB26</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ztw.com/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-162-hackerone-training-ai-on-bug-bounty-data" target="_blank" rel="noopener noreferrer nofollow">https://ztw.com/</a></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="in-the-news">In the News</h2><ul><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.yeswehack.com/learn-bug-bounty/xml-external-entity-guide-xxe?utm_source=Critical_Thinking&utm_medium=Youtube&utm_campaign=XXE_Critical_Thinking&utm_id=XXE_CT" target="_blank" rel="noopener noreferrer nofollow">XML external entity: The ultimate Bug Bounty guide to exploiting XXE vulnerabilities</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://bugbountymaturity.com/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-162-hackerone-training-ai-on-bug-bounty-data" target="_blank" rel="noopener noreferrer nofollow">Bug Bounty Maturity Framework</a></p></li></ul><h2 class="heading" style="text-align:left;" id="whos-alex-rice">Who&#39;s Alex Rice</h2><p class="paragraph" style="text-align:left;">Alex Rice is the <b>CTO and co-founder of HackerOne,</b> and he came here today to address some issues about the latest AI moves from HackerOne.</p><h2 class="heading" style="text-align:left;" id="the-terms-of-service-controversy">The Terms of Service Controversy</h2><p class="paragraph" style="text-align:left;">There were tensions in the bug bounty community in recent weeks after researchers looked more into HackerOne&#39;s Terms of Service. The most concerning clause reads:</p><div class="blockquote"><blockquote class="blockquote__quote"><p class="paragraph" style="text-align:left;"><i>&quot;HackerOne may use confidential information to develop or improve its services, for example, to identify trends and to train AI models, provided such use does not result in disclosure of confidential information to unauthorized third parties.&quot;</i></p><figcaption class="blockquote__byline"></figcaption></blockquote></div><p class="paragraph" style="text-align:left;">For researchers who have spent years building proprietary exploitation techniques, documented inside full, unredacted vulnerability reports, it was a no-go.</p><p class="paragraph" style="text-align:left;">But Alex told us that <b>HackerOne is not training or fine-tuning LLMs on researcher submissions.</b> The &quot;train AI models&quot; language in the ToS predates modern large language models and was originally written to cover traditional machine learning, such as the spam classification engine HackerOne. Every time a customer flags a report as spam, that signal feeds a regression analysis model that updates spam classifier scores for incoming reports. That is the extent of &quot;AI training&quot; on report data.</p><p class="paragraph" style="text-align:left;">And he was aware that he was failing to update the terms as the definition of &quot;AI&quot; shifted from classical ML to generative models. HackerOne&#39;s technical documentation has been transparent about its actual data practices, but the legal terms lag behind, creating a gap with us.</p><p class="paragraph" style="text-align:left;">So HackerOne plans to update its Terms of Service to explicitly mention legacy ML from LLM training.</p><h2 class="heading" style="text-align:left;" id="what-happens-to-anonymized-data">What Happens to Anonymized Data</h2><p class="paragraph" style="text-align:left;">HackerOne does run an anonymization and aggregate trend analysis pipeline on report data, but it is not feeding LLMs. The primary output is the <b>Hacker Powered Security Report</b>, which powers benchmarking suites, trend analysis, and CVE frequency tracking. These are all pre-AI features that rely on traditional statistical analysis, not language models.</p><h3 class="heading" style="text-align:left;" id="researcher-ip-ownership">Researcher IP Ownership</h3><p class="paragraph" style="text-align:left;">A common misconception in the community is that submitting a report means surrendering intellectual property. Under <b>Section 8 of HackerOne&#39;s community terms</b>, researchers retain all IP rights. HackerOne receives a limited license solely for providing and improving its services. Customers receive a slightly more permissive license to use submissions for securing their own attack surface. So, neither license permits resale or redistribution of report data.</p><p class="paragraph" style="text-align:left;">The real leakage vector tends to be downstream: customers share findings through threat intelligence feeds, CVE advisories, and remediation efforts, which eventually enter the common pool of knowledge.</p><h2 class="heading" style="text-align:left;" id="ptas-and-the-agentic-pentesting-pla">PTAS and the Agentic Pentesting Platform</h2><p class="paragraph" style="text-align:left;">The second point centered on HackerOne&#39;s <b>Pentest as a Service (PTAS)</b> offering and its new agentic AI component. Marketing material that described agents &quot;trained and refined using proprietary exploit intelligence informed by years of testing real enterprise systems&quot; was quite alarming to us.</p><p class="paragraph" style="text-align:left;">But he clarified the PTAS backstory: HackerOne launched its pentesting business in 2018 as a <b>defense-in-depth</b> play. Companies running checkbox compliance pentests were jumping into bug bounty programs unprepared. The PTAS was offering community-powered, led by roughly 200 pentesters, and was designed to assess maturity before programs go live.</p><h3 class="heading" style="text-align:left;" id="where-the-ai-gets-its-data">Where the AI Gets Its Data</h3><p class="paragraph" style="text-align:left;">The agentic pentesting tool comes from <b>five sources</b>, and none of which are bug bounty submissions:</p><ol start="1"><li><p class="paragraph" style="text-align:left;"><b>Public benchmarks</b> — Standard vulnerability benchmarks to validate baseline capability</p></li><li><p class="paragraph" style="text-align:left;"><b>Internal benchmarks</b> — Custom vulnerable applications built with help from pentesting community members</p></li><li><p class="paragraph" style="text-align:left;"><b>Public CVEs and disclosures</b> — The only area with potential overlap, since many CVEs originate as bug bounty submissions</p></li><li><p class="paragraph" style="text-align:left;"><b>Sidecar runs</b> — Parallel testing engagements where both the pen tester and customer have explicitly opted in to run the AI agent alongside the human tester</p></li><li><p class="paragraph" style="text-align:left;"><b>Foundation models</b> — The bulk of capability comes from frontier AI models, not proprietary fine-tuning</p></li></ol><p class="paragraph" style="text-align:left;">The distinction between PTAS and bug bounty is important here. Pentesting operates under a different engagement model with clear consent and opt-in for data usage. Bug bounty reports remain untouched by the agentic system.</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/dc7a3669-6d67-48a9-aa2b-5dbdd0d8e41e/_bannerHackerNotesShort.png?t=1769044664"/><div class="image__source"><a class="image__source_link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-162-hackerone-training-ai-on-bug-bounty-data" rel="noopener" target="_blank"><span class="image__source_text"><p>Check out the CTBB Discord!</p></span></a></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-162-hackerone-training-ai-on-bug-bounty-data" target="_blank" rel="noopener noreferrer nofollow">You can also find some hacker swag here.</a></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="do-fine-tuned-models-even-help">Do Fine-Tuned Models Even Help?</h2><p class="paragraph" style="text-align:left;">Justin and Joseph tried using fine-tuned security models and they have consistently underperformed frontier general-purpose models in their testing. The ability to hack correlates heavily with the ability to code, and massive general-purpose training datasets produce better coders than any domain-specific fine-tune.</p><p class="paragraph" style="text-align:left;">Even if HackerOne were to fine-tune on every report in the platform, the output would likely underperform against the latest frontier model. Add to that the signal-to-noise problem: the percentage of truly high-quality submissions across any platform is low. The data quality problem alone makes this approach uneconomical.</p><h2 class="heading" style="text-align:left;" id="the-bounty-decrease">The Bounty Decrease</h2><p class="paragraph" style="text-align:left;">Separate from the AI debate, HackerOne reduced bounty payouts across its own bug bounty program.</p><p class="paragraph" style="text-align:left;">While low and medium decreases are understandable, cutting high and critical payouts sends a negative signal from the platform that should be leading the industry. When a bug bounty platform decreases its own payouts, it creates a permission structure for every other program to follow suit.</p><p class="paragraph" style="text-align:left;">And Alex noted the feedback, and the team is actively considering:</p><ul><li><p class="paragraph" style="text-align:left;"><b>An &quot;exceptional&quot; tier</b> with multipliers for bugs that access other researchers&#39; vulnerability submissions</p></li><li><p class="paragraph" style="text-align:left;"><b>Bifurcated critical definitions</b> to properly incentivize the highest-impact findings</p></li><li><p class="paragraph" style="text-align:left;"><b>Ongoing monitoring</b> of engagement metrics to adjust if participation drops</p></li></ul><h2 class="heading" style="text-align:left;" id="reporting-potential-data-leaks">Reporting Potential Data Leaks</h2><p class="paragraph" style="text-align:left;">Also HackerOne is committed to establishing a <b>dedicated tip line</b> for researchers who suspect their techniques have been leaked, whether through AI models, insider activity, or customer disclosure. Currently, researchers can use the mediation button on specific reports. And a purpose-built channel is in the works.</p><hr class="content_break"><p class="paragraph" style="text-align:left;">That&#39;s it for the week, keep hacking!</p><h2 class="heading" style="text-align:left;" id="resources">Resources</h2><ul><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.hackerone.com/terms/general?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-162-hackerone-training-ai-on-bug-bounty-data#:~:text=HackerOne%20may%20use%20Confidential%20Information%20to%20develop%20and/or%20improve%20its%20Services%20(for%20example%2C%20to%20identify%20trends%2C%20and%20to%20train%20AI%20models)%20provided%20such%20use%20does%20not%20result%20in%20disclosure%20of%20Confidential%20Information%20to%20unauthorized%20third%20parties" target="_blank" rel="noopener noreferrer nofollow">Confidential Information and Confidentiality Obligations</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.hackerone.com/terms/community?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-162-hackerone-training-ai-on-bug-bounty-data#:~:text=8.%20Ownership%20and%20Licenses" target="_blank" rel="noopener noreferrer nofollow">Ownership and Licenses</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://bugbounty.forum/post/183ff0fc-eb9e-47f8-991d-c0aa5b0bba71?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-162-hackerone-training-ai-on-bug-bounty-data" target="_blank" rel="noopener noreferrer nofollow">AI regarding HackerOne using Hacker reports to train PtaaS</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.reddit.com/r/bugbounty/comments/1r5hixk/hackerone_ptaas_likely_training_their_ai_on/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-162-hackerone-training-ai-on-bug-bounty-data" target="_blank" rel="noopener noreferrer nofollow">HackerOne PTaaS (likely training their AI on private reports data)</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.hackerone.com/blog/agentic-penetration-testing-as-a-service?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-162-hackerone-training-ai-on-bug-bounty-data#:~:text=Our%20agents%20are,real%20enterprise%20constraints" target="_blank" rel="noopener noreferrer nofollow">What Makes Agentic PTaaS Different in Real Environments</a></p></li></ul></div></div>
  ]]></content:encoded>
</item>

      <item>
  <title>[HackerNotes Ep. 161] AI workflows, CSRF despite XFO, and DTMF exfil</title>
  <description>YesWeHack yearly report, AI writeups and some interesting </description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-161-ai-workflows-csrf-despite-xfo-and-dtmf-exfil</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-161-ai-workflows-csrf-despite-xfo-and-dtmf-exfil</guid>
  <pubDate>Thu, 12 Feb 2026 11:01:15 +0000</pubDate>
  <atom:published>2026-02-12T11:01:15Z</atom:published>
    <dc:creator>Aituglo ‎</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr">Hacker TL;DR</h2><ul><li><p class="paragraph" style="text-align:left;"><b>AS Watson</b>: new program on Intigriti with a massive scope.</p></li><li><p class="paragraph" style="text-align:left;"><b>YesWeHack Report 2026</b>: AI usage is mainstream for learning/docs (<b>69%</b>), report drafting (<b>51%</b>), payload generation (<b>40%</b>), and code review/vuln analysis (<b>38%</b>).</p></li><li><p class="paragraph" style="text-align:left;"><b>CSRF</b>: <code>X-Frame-Options: DENY</code> does <i>not</i> stop CSRF execution via <code>iframe</code> (it blocks rendering, not request processing). The real gating control here is <b>SameSite</b>.</p></li><li><p class="paragraph" style="text-align:left;"><b>Gemini App Exfil</b>: “delivery → data access → exfil” chain using <b>intent URI + tap-jacking</b>, then exfiltrating a <b>2FA code</b> via <b>DTMF</b> (phone number + <code>;</code> to auto-play tones).</p></li><li><p class="paragraph" style="text-align:left;"><b>Cross-consumer attacks</b>: when a victim domain/path points to a <b>multi-tenant third party</b> (docs/support/hosting), test if <b>your tenant’s content</b> can be served under the <b>victim’s domain</b> (ID/slug swaps, override params, tokens/JWT) → XSS/CSP abuse.</p><hr class="content_break"></li></ul><h2 class="heading" style="text-align:left;" id="hackernotes">Hackernotes</h2><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/dc7a3669-6d67-48a9-aa2b-5dbdd0d8e41e/_bannerHackerNotesShort.png?t=1769044664"/><div class="image__source"><a class="image__source_link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-161-ai-workflows-csrf-despite-xfo-and-dtmf-exfil" rel="noopener" target="_blank"><span class="image__source_text"><p>Check out the CTBB Discord!</p></span></a></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-161-ai-workflows-csrf-despite-xfo-and-dtmf-exfil" target="_blank" rel="noopener noreferrer nofollow">You can also find some hacker swag here.</a></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="yes-we-hack-report-2026">YesWeHack Report 2026</h2><p class="paragraph" style="text-align:left;"><b>AI usage breakdown : </b></p><div class="image"><img alt="" class="image__image" style="" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/016dd052-6882-45bf-b68e-d7b1a5ac5411/CleanShot_2026-02-11_at_08.27.54_2x.png?t=1770794902"/></div><p class="paragraph" style="text-align:left;"><b>How to use AI to perform in Client Side stuff:</b></p><ul><li><p class="paragraph" style="text-align:left;">Use <b>JxScout</b> to pull JavaScript files.</p></li><li><p class="paragraph" style="text-align:left;">Feed them into a LLM workflow like Opus 4.6 to review:<br></p><ul><li><p class="paragraph" style="text-align:left;">client-side routes</p></li><li><p class="paragraph" style="text-align:left;">query parameter parsing</p></li><li><p class="paragraph" style="text-align:left;">hash parsing</p></li><li><p class="paragraph" style="text-align:left;"><code>postMessage</code> listeners</p></li></ul></li><li><p class="paragraph" style="text-align:left;">Prioritize AI for:<br></p><ul><li><p class="paragraph" style="text-align:left;"><b>payload generation</b></p></li><li><p class="paragraph" style="text-align:left;"><b>code review / vulnerability analysis</b></p></li></ul></li></ul><p class="paragraph" style="text-align:left;"><b>Other interesting stats:</b></p><ul><li><p class="paragraph" style="text-align:left;">Experience:<br></p><ul><li><p class="paragraph" style="text-align:left;"><b>44%</b> have been hacking for <b>3-5 years</b></p></li><li><p class="paragraph" style="text-align:left;"><b>38%</b> are full-time</p></li><li><p class="paragraph" style="text-align:left;"><b>62%</b> do it alongside another role</p></li></ul></li><li><p class="paragraph" style="text-align:left;">Collaboration reports up <b>520%</b></p></li><li><p class="paragraph" style="text-align:left;">Common tools are Burp Suite, ffuf, httpx</p></li><li><p class="paragraph" style="text-align:left;">Looking for proxies :<br></p><ul><li><p class="paragraph" style="text-align:left;"><b>Burp: 91%</b> adoption</p></li><li><p class="paragraph" style="text-align:left;"><b>Caido: 18%</b> adoption</p></li></ul></li><li><p class="paragraph" style="text-align:left;">Nice Research highlight: <a class="link" href="https://www.yeswehack.com/learn-bug-bounty/syntax-confusion-ambiguous-parsing-exploits?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-161-ai-workflows-csrf-despite-xfo-and-dtmf-exfil" target="_blank" rel="noopener noreferrer nofollow">https://www.yeswehack.com/learn-bug-bounty/syntax-confusion-ambiguous-parsing-exploits</a> by Brumens</p></li></ul><p class="paragraph" style="text-align:left;">Go check the full report here : <a class="link" href="https://www.yeswehack.com/community/yeswehack-report-2026-trends-security?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-161-ai-workflows-csrf-despite-xfo-and-dtmf-exfil" target="_blank" rel="noopener noreferrer nofollow">https://www.yeswehack.com/community/yeswehack-report-2026-trends-security</a></p><h2 class="heading" style="text-align:left;" id="csrf-why-x-frame-options-is-not-a-c">CSRF, why <code>X-Frame-Options</code> is not a CSRF control</h2><p class="paragraph" style="text-align:left;">Observation noticed:</p><ul><li><p class="paragraph" style="text-align:left;">Target endpoint was CSRF-able and had <code>X-Frame-Options: DENY</code>.</p></li><li><p class="paragraph" style="text-align:left;">Multiple iframes were used to trigger the state-changing action.</p></li><li><p class="paragraph" style="text-align:left;">The iframes “failed to load” visually due to X-Frame-Options, but the requests still executed server-side.</p></li></ul><p class="paragraph" style="text-align:left;">As we only care about the server-side effect and not the rendering, it&#39;s a way to perform the CSRF. So, the focus should be on the SameSite cookie behavior first (will cookies be sent cross-site?). And then <code>iframe</code> vs <code>fetch</code> depends on constraints, but “frameable” is not the decisive question for CSRF.</p><h2 class="heading" style="text-align:left;" id="writeup-from-starstrike-about-data-">Write-up from Starstrike about Data Exfiltration in Gemini</h2><p class="paragraph" style="text-align:left;">Here is the full attack chain scenario:</p><ul><li><p class="paragraph" style="text-align:left;"><b>Delivery</b>: an <b>intent URI</b> in Gemini to preload a query/prompt.</p></li><li><p class="paragraph" style="text-align:left;"><b>User interaction</b>: <b>tap-jacking</b> (have the user tap multiple times; trigger the intent swap mid-sequence; their finger ends up on “Send”).</p></li><li><p class="paragraph" style="text-align:left;"><b>Data access</b>: prompt asks Gemini to retrieve a <b>2FA code</b> from the user’s messages.</p></li><li><p class="paragraph" style="text-align:left;"><b>Exfil</b>: encode the 2FA code as <b>DTMF tones</b>.<br></p><ul><li><p class="paragraph" style="text-align:left;">Use <code>;</code> after a phone number to auto-play DTMF.</p></li><li><p class="paragraph" style="text-align:left;">Tell Gemini to <b>call</b> the number (as this is an action not requiring extra user prompting in this scenario).</p></li></ul></li></ul><p class="paragraph" style="text-align:left;">That was insane and a very creative way to exfill data.</p><h2 class="heading" style="text-align:left;" id="bug-bounty-payout-cuts">Bug Bounty payout cuts</h2><ul><li><p class="paragraph" style="text-align:left;"><b>Spotify</b> and <b>HackerOne</b> program reduced bounties</p></li><li><p class="paragraph" style="text-align:left;">HackerOne payouts are now:<br></p><ul><li><p class="paragraph" style="text-align:left;"><b>Medium: $1,500</b></p></li><li><p class="paragraph" style="text-align:left;"><b>High: $7K</b></p></li><li><p class="paragraph" style="text-align:left;"><b>Critical: $15K</b></p></li></ul></li></ul><p class="paragraph" style="text-align:left;">Also, the community came back on a change in the general terms and conditions of Hackerone ( clause 3.1 ) :</p><ul><li><p class="paragraph" style="text-align:left;">H1 may use confidential info to improve services, including “identify trends” and “train AI models,” provided it does not disclose to unauthorized third parties.</p></li><li><p class="paragraph" style="text-align:left;">Community concern is mainly “are reports being used to train models?”</p></li><li><p class="paragraph" style="text-align:left;">But it can also be intended as a broad statement tied to internal tooling (Hai) rather than a “super-hacker AI” project.</p></li></ul><p class="paragraph" style="text-align:left;">We will cover that more in a later episode, waiting for the hackerone statement about it.</p><h2 class="heading" style="text-align:left;" id="crossconsumer-attacks">Cross-consumer attacks</h2><p class="paragraph" style="text-align:left;">How it works:</p><ul><li><p class="paragraph" style="text-align:left;">Victim uses a <b>third party</b> to serve content (JS/docs/support/etc.) via a victim-controlled <b>domain or path</b>.</p></li><li><p class="paragraph" style="text-align:left;">Attacker creates their own <b>tenant</b> on the third party, hosts content (SVG/HTML/JS), then attempts to access it under the <b>victim’s domain</b> by manipulating:<br></p><ul><li><p class="paragraph" style="text-align:left;">URL IDs (like an IDOR)</p></li><li><p class="paragraph" style="text-align:left;">slugs</p></li><li><p class="paragraph" style="text-align:left;">override/debug parameters</p></li><li><p class="paragraph" style="text-align:left;">tokens / signed URLs / JWT-like access patterns</p></li></ul></li></ul><p class="paragraph" style="text-align:left;">Some patterns to check if it&#39;s possible:</p><ul><li><p class="paragraph" style="text-align:left;">Look for paths like:<br></p><ul><li><p class="paragraph" style="text-align:left;"><code>/docs/&lt;id&gt;/file.html</code></p></li><li><p class="paragraph" style="text-align:left;"><code>/docs/&lt;slug&gt;/...</code></p></li></ul></li><li><p class="paragraph" style="text-align:left;">Upload “impactful” content on your tenant:<br></p><ul><li><p class="paragraph" style="text-align:left;"><b>SVG</b>, <b>HTML</b>, <b>JS</b> (also useful for CSP interactions)</p></li></ul></li><li><p class="paragraph" style="text-align:left;">Check whether the third-party hosts are CSP holes (XSS/exfil support)</p></li><li><p class="paragraph" style="text-align:left;">Hunt for parameters that override the tenant/host (read provider docs + Google dorking on the path pattern using <code>gau</code> for instance)</p></li><li><p class="paragraph" style="text-align:left;">Test “signed URL / token / JWT” flows:<br></p><ul><li><p class="paragraph" style="text-align:left;">token not properly bound → cross-customer access</p></li></ul></li><li><p class="paragraph" style="text-align:left;">Don’t limit to HTML pages:<br></p><ul><li><p class="paragraph" style="text-align:left;">test file download flows (e.g., <b>PDFs</b>)</p></li></ul></li></ul><p class="paragraph" style="text-align:left;">As always, <b>Impact is king</b>: prove concrete impact (e.g., XSS) vs. arguing about “public content.”</p><p class="paragraph" style="text-align:left;">That&#39;s it for today, keep hacking</p><h2 class="heading" style="text-align:left;" id="resources">Resources</h2><ul><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://app.intigriti.com/programs/aswatson/watsons/detail?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-161-ai-workflows-csrf-despite-xfo-and-dtmf-exfil" target="_blank" rel="noopener noreferrer nofollow">The Watson AG Program</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.yeswehack.com/community/yeswehack-report-2026-trends-security?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-161-ai-workflows-csrf-despite-xfo-and-dtmf-exfil" target="_blank" rel="noopener noreferrer nofollow">YesWeHack report 2026</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://blog.starstrike.ai/posts/phoneleak-data-exfiltration-in-gemini-via-phone-call/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-161-ai-workflows-csrf-despite-xfo-and-dtmf-exfil" target="_blank" rel="noopener noreferrer nofollow">PhoneLeak: Data Exfiltration in Gemini via Phone Call</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://www.youtube.com/watch?v=JqvJSF2UMyY&utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-161-ai-workflows-csrf-despite-xfo-and-dtmf-exfil" target="_blank" rel="noopener noreferrer nofollow">7urb0 Research Review</a></p></li></ul></div></div>
  ]]></content:encoded>
</item>

      <item>
  <title>[HackerNotes Ep. 160] Cloudflare Zero-days &amp; Mail Unsubscribing for XSS</title>
  <description>A bunch of very interesting research writeups from Cloudflare WAF bypass to Parser discrepancies.</description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-160-cloudflare-zero-days-mail-unsubscribing-for-xss</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-160-cloudflare-zero-days-mail-unsubscribing-for-xss</guid>
  <pubDate>Thu, 05 Feb 2026 11:01:16 +0000</pubDate>
  <atom:published>2026-02-05T11:01:16Z</atom:published>
    <dc:creator>Aituglo ‎</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr">Hacker TL;DR</h2><ul><li><p class="paragraph" style="text-align:left;">Cloudflare WAF bypass: requests to <code>/.well-known/acme-challenge/&lt;token&gt;</code> could disable WAF filtering if the token matched <i>any</i> active ACME challenge across Cloudflare’s infra; path traversal then reached protected origin paths (e.g., <code>/actuator/env</code>).</p></li><li><p class="paragraph" style="text-align:left;">SMTP “List-Unsubscribe” header as an exploit primitive: injecting non-HTTP schemes (e.g., <code>javascript:</code>) can yield stored XSS (Horde mail, CVE-2025-68673); unsubscribe flows can also trigger SSRF (Nextcloud).</p></li><li><p class="paragraph" style="text-align:left;">Managed Postgres escalation pattern: overwriting functions that are later auto-invoked by provider “super users” can hand attackers superuser execution context.</p></li><li><p class="paragraph" style="text-align:left;">Parser discrepancies in <code>Content-Type</code> handling: list-based vs singleton header fields + “last match wins” behaviors can cause backend “JSON” validation while browsers interpret as <code>text/html</code>, enabling XSS (Chrome/Firefox).</p></li><li><p class="paragraph" style="text-align:left;">LLM “magic refusal” string: a documented test string that forces Claude refusals can be used as a denial-of-service vector if injected into persistent conversation history or returned via tool/MCP outputs.</p><hr class="content_break"><h3 style="text-align:left;" class="heading">A gift from Google!</h3></li></ul><ul><li><p class="paragraph" style="text-align:left;"><b>Google Cloud VRP Swag Bonus:</b> Mention <b>Critical Thinking Bug Bounty Podcast</b> in any rewarded (cash or credit) VRP report submission <b>before</b> the end of <b>April</b> to receive <b>bonus swag!</b></p><hr class="content_break"></li></ul><h2 class="heading" style="text-align:left;" id="cloudflare-wellknownacmechallenge-t">Cloudflare: <code>/.well-known/acme-challenge/</code> token → WAF bypass + path traversal</h2><p class="paragraph" style="text-align:left;">So the condition here was surprisingly simple: a request to <code>/.well-known/acme-challenge/&lt;token&gt;</code> could flip Cloudflare WAF inspection off for that request path. Token matching wasn’t bound to the target domain; if your <code>&lt;token&gt;</code> matched <b>any active ACME challenge across Cloudflare infrastructure</b>, that was enough to trigger the bypass.</p><p class="paragraph" style="text-align:left;">The exploit shape described was basically:</p><ul><li><p class="paragraph" style="text-align:left;">Generate/obtain your own valid ACME challenge token.</p></li><li><p class="paragraph" style="text-align:left;">Send a request to the victim domain using that token in the <code>.well-known/acme-challenge</code> path.</p></li><li><p class="paragraph" style="text-align:left;">Append path traversal sequences <i>after</i> the token to reach protected origin paths (like <code>/actuator/env</code>).</p></li></ul><p class="paragraph" style="text-align:left;">Operationally, the impact lands on customers who depend on Cloudflare WAF rules as their main line of origin protection.</p><h2 class="heading" style="text-align:left;" id="smtp-list-unsubscribe-header-as-xss">SMTP <code>List-Unsubscribe</code> header as XSS/SSRF gadget</h2><p class="paragraph" style="text-align:left;">To be honest, <code>List-Unsubscribe</code> keeps showing up as a really clean exploit primitive because it’s standardized and tends to get wired into “click this link” flows in UIs and server-side automation. The attack surface is the header itself, which can include <code>mailto:</code> and HTTP URIs.</p><p class="paragraph" style="text-align:left;">For stored XSS (Horde mail), the writeup described putting a <code>javascript:</code> URI into <code>List-Unsubscribe</code>. When the unsubscribe link renders in an administrative/backend UI, it can execute script by clicking on it. The referenced CVE is <b>CVE-2025-68673</b> (stored XSS in Horde mail).</p><p class="paragraph" style="text-align:left;">For SSRF (Nextcloud), the unsubscribe action makes a <b>server-side</b> request to whatever URI is in <code>List-Unsubscribe</code>. Internal targeting is configuration-dependent: internal hosts were only reachable if <code>allow local remote servers</code> is set to <b>true</b>.</p><p class="paragraph" style="text-align:left;">Hunting takeaway: anywhere list-unsubscribe is supported, test SSRF target URIs and blind-XSS payloads embedded in that header.</p><h2 class="heading" style="text-align:left;" id="heroku-postgres-function-overwrite-">Heroku Postgres: function overwrite → provider superuser execution</h2><p class="paragraph" style="text-align:left;">This one is a managed-service escalation pattern more than a single bug. Providers run background metrics/health collection using privileged access (the “super user” role in the writeup framing). If a tenant can <b>overwrite a function</b> that is later <b>automatically called</b> by that privileged account, then the tenant-controlled function body runs under the provider’s superuser context.</p><p class="paragraph" style="text-align:left;">End result, as described: privileged execution translates into attacker-side elevated permissions.</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/dc7a3669-6d67-48a9-aa2b-5dbdd0d8e41e/_bannerHackerNotesShort.png?t=1769044664"/><div class="image__source"><a class="image__source_link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-160-cloudflare-zero-days-mail-unsubscribing-for-xss" rel="noopener" target="_blank"><span class="image__source_text"><p>Check out the CTBB Discord!</p></span></a></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-160-cloudflare-zero-days-mail-unsubscribing-for-xss" target="_blank" rel="noopener noreferrer nofollow">You can also find some hacker swag here.</a></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="parser-discrepancies-listbased-vs-s">Parser discrepancies: list-based vs singleton fields → MIME confusion → XSS (Chrome/Firefox)</h2><p class="paragraph" style="text-align:left;">This one came from our lab that you can see here : https://lab.ctbb.show/</p><p class="paragraph" style="text-align:left;">The core primitive is parser disagreement: different components parse the same header value into different “effective” MIME types. The focus was <code>Content-Type</code> (and similar headers) that might be treated either as list-based fields (multiple values) or singleton fields (a single value expected), with the edge cases constrained by RFC ABNF.</p><p class="paragraph" style="text-align:left;">The discrepancy described builds on MIME validation research: something like <code>application/json; , text/html</code> can pass backend validation as “JSON”, while browsers interpret the content as <code>text/html</code>, which opens an XSS path.</p><p class="paragraph" style="text-align:left;">Practical artifact mentioned: a summary table comparing <b>libraries vs browsers</b> in how they parse/validate singleton vs list fields, intended as a hunting map for other stacks that will split on the same boundary.</p><p class="paragraph" style="text-align:left;">That was a very interesting research that you should look into.</p><h2 class="heading" style="text-align:left;" id="claude-magic-string-refusal-trigger">Claude “magic string” refusal trigger as an application DoS primitive</h2><p class="paragraph" style="text-align:left;">Anthropic documents a specific test string that triggers a <b>refusal stop reason</b> when streaming classifiers come. So if that “magic string” is inserted into an LLM app’s context (for example via indirect prompt injection), you can force responses into refusal.</p><p class="paragraph" style="text-align:left;">The nasty part is the “sticky” failure mode described: if the poisoned turn persists in conversation history, future turns keep refusing until the application drops or rewrites the offending content. Potential delivery points called out include user-controlled fields (like username/email), tool outputs, or MCP server responses.</p><h2 class="heading" style="text-align:left;" id="android-rce-chain-deep-link-trust-b">Android RCE chain: deep link trust bypass → WebView bridge → file write → OTA bundle load</h2><p class="paragraph" style="text-align:left;">This was presented as a multi-step chain that ends in remote code execution in an Android app. The steps, in order:</p><ol start="1"><li><p class="paragraph" style="text-align:left;"><b>Deep link trust bypass:</b> the app trusted <b>all subdomains of </b><code>google.com</code>.</p></li><li><p class="paragraph" style="text-align:left;"><b>WebView exposure:</b> the internal browser exposed a JS native message handler (via <code>postMessage</code>), gated by a registration/handshake flow; the discussion ultimately constrained this to Google origins.</p></li><li><p class="paragraph" style="text-align:left;"><b>Bridge abuse:</b> once initialized, an attacker could invoke native actions from the WebView JS context (e.g., <code>window.nativeMessageHandler.&lt;function&gt;</code>).</p></li><li><p class="paragraph" style="text-align:left;"><b>Arbitrary file write via path traversal:</b> the file name was concatenated onto a cache-directory path with insufficient validation.</p></li><li><p class="paragraph" style="text-align:left;"><b>React Native OTA update hijack:</b> path traversal was used to overwrite OTA-related config and plant a malicious JS bundle; on restart, the OTA loader reads the compromised config and loads the attacker bundle.</p></li></ol><p class="paragraph" style="text-align:left;">A deployment trick mentioned was using <code>sites.google.com</code> to host attacker-controlled content inside an iframe, leveraging the <code>google.com</code> trust decision. Post-exploitation actions described included reading auth tokens from keychain/manager, accessing files from database/files directories, and exfiltrating to attacker-controlled infrastructure.</p><p class="paragraph" style="text-align:left;">As always, keep hacking!</p><h2 class="heading" style="text-align:left;" id="resources">Resources</h2><ul><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://fearsoff.org/research/cloudflare-acme?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-160-cloudflare-zero-days-mail-unsubscribing-for-xss" target="_blank" rel="noopener noreferrer nofollow">https://fearsoff.org/research/cloudflare-acme</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://security.lauritz-holtmann.de/post/xss-ssrf-list-unsubscribe/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-160-cloudflare-zero-days-mail-unsubscribing-for-xss" target="_blank" rel="noopener noreferrer nofollow">https://security.lauritz-holtmann.de/post/xss-ssrf-list-unsubscribe/</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://allistair.sh/blog/breaking-heroku-postgres/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-160-cloudflare-zero-days-mail-unsubscribing-for-xss" target="_blank" rel="noopener noreferrer nofollow">https://allistair.sh/blog/breaking-heroku-postgres/</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://lab.ctbb.show/research/parse-and-parse-mime-validation-bypass-to-xss-via-parser-differential?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-160-cloudflare-zero-days-mail-unsubscribing-for-xss" target="_blank" rel="noopener noreferrer nofollow">https://lab.ctbb.show/research/parse-and-parse-mime-validation-bypass-to-xss-via-parser-differential</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://hackingthe.cloud/ai-llm/exploitation/claude_magic_string_denial_of_service/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-160-cloudflare-zero-days-mail-unsubscribing-for-xss" target="_blank" rel="noopener noreferrer nofollow">https://hackingthe.cloud/ai-llm/exploitation/claude_magic_string_denial_of_service/</a></p></li><li><p class="paragraph" style="text-align:left;"><a class="link" href="https://djini.ai/intro-to-android-webviews-and-deep-links-and-how-to-exploit-them/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-160-cloudflare-zero-days-mail-unsubscribing-for-xss" target="_blank" rel="noopener noreferrer nofollow">https://djini.ai/intro-to-android-webviews-and-deep-links-and-how-to-exploit-them/</a></p></li></ul></div></div>
  ]]></content:encoded>
</item>

      <item>
  <title>[HackerNotes Ep. 159] Maximizing Bounties on Google Cloud VRP</title>
  <description>Digging into the Google Cloud VRP to get the best of it</description>
  <link>https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-159-maximizing-bounties-on-google-cloud-vrp</link>
  <guid isPermaLink="true">https://blog.criticalthinkingpodcast.io/p/hackernotes-ep-159-maximizing-bounties-on-google-cloud-vrp</guid>
  <pubDate>Thu, 29 Jan 2026 11:01:38 +0000</pubDate>
  <atom:published>2026-01-29T11:01:38Z</atom:published>
    <dc:creator>Aituglo ‎</dc:creator>
  <content:encoded><![CDATA[
    <div class='beehiiv'><style>
  .bh__table, .bh__table_header, .bh__table_cell { border: 1px solid #C0C0C0; }
  .bh__table_cell { padding: 5px; background-color: #c0c0c0; }
  .bh__table_cell p { color: #2D2D2D; font-family: 'Helvetica',Arial,sans-serif !important; overflow-wrap: break-word; }
  .bh__table_header { padding: 5px; background-color:#535353; }
  .bh__table_header p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important; overflow-wrap: break-word; }
</style><div class='beehiiv__body'><h2 class="heading" style="text-align:left;" id="hacker-tldr">Hacker TL;DR</h2><ul><li><p class="paragraph" style="text-align:left;">Google Cloud VRP rewards based on &quot;privilege escalation delta&quot; - the gap between starting access and ending impact determines payout</p></li><li><p class="paragraph" style="text-align:left;">Service account impersonation is a consistent high-value attack pattern across GCP products. Default service accounts with elevated privileges are prime targets</p></li><li><p class="paragraph" style="text-align:left;">Report quality directly affects bounty: a 1.2x multiplier is available for exceptional reports, while poor quality triggers downgrade</p></li><li><p class="paragraph" style="text-align:left;">Tier 1 products (Cloud Storage, GKE) pay maximum bounties, but Tier 3 products can still yield $50K+ payouts when chained properly</p></li></ul><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/cacc84dc-1fee-4931-86d1-ad290e017024/ZTW2.png?t=1769045492"/><div class="image__source"><a class="image__source_link" href="https://ztw.com/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-159-maximizing-bounties-on-google-cloud-vrp" rel="noopener" target="_blank"><span class="image__source_text"><p>ThreatLocker - Zero Trust World 2026</p></span></a></div></div><p class="paragraph" style="text-align:center;">Join Justin at Zero Trust World in March!<br>Get $200 off registration with Code: <b>ZTWCTBB26</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ztw.com/?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-159-maximizing-bounties-on-google-cloud-vrp" target="_blank" rel="noopener noreferrer nofollow">https://ztw.com/</a></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="google-cloud-vrp-structure">Google Cloud VRP Structure</h2><p class="paragraph" style="text-align:left;">Google Cloud VRP operates separately from the main Google VRP, though it inherited much of the foundational process. The program launched internally in July 2024 and went public at the Malaga live hacking event in October 2024. The scope covers several hundred cloud products with varying tier classifications.</p><p class="paragraph" style="text-align:left;">The rewards table uses a severity classification system:</p><p class="paragraph" style="text-align:left;"><b>S0</b> - Cross-tenant impact with no permissions required. &quot;No rights to the organization&quot; is the key qualifier, being logged into Google doesn&#39;t matter if there&#39;s no relationship to the target org. Minimum S0 payout is $7,500.</p><p class="paragraph" style="text-align:left;"><b>S1</b> - Authenticated attacks within a project or organization, subdivided by impact:</p><ul><li><p class="paragraph" style="text-align:left;">S1a: Complete project takeover / full administrative control</p></li><li><p class="paragraph" style="text-align:left;">S1c: Multi-service privilege escalation</p></li><li><p class="paragraph" style="text-align:left;">S1f: Single-service privilege escalation (least severe S1 category)</p></li></ul><p class="paragraph" style="text-align:left;"><b>S2</b> - Lower impact issues that still warrant fixing. This includes metadata leaks, non-customer data exposure, and issues where exploitation is possible but impact is limited.</p><p class="paragraph" style="text-align:left;"><b>C0/C1</b> - Client-side vulnerabilities. C0 covers JavaScript execution (XSS with demonstrable impact), while C1 is a catch-all for other client-side issues. These are relatively rare in Cloud VRP submissions.</p><p class="paragraph" style="text-align:left;">The tiering system (Tier 1, 2, 3A, 3B) multiplies against severity. Tier classification uses an internal algorithm factoring revenue, user count, API call volume, and cross-product integration. Cloud Storage is Tier 1 because it&#39;s both heavily used directly and integrated into other GCP products. Acquisitions start at IT3a and typically move to higher tiers roughly three years post-acquisition.</p><p class="paragraph" style="text-align:left;">You can see the full reward table here: <a class="link" href="https://bughunters.google.com/about/rules/google-friends/cloud-vulnerability-reward-program-rules?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-159-maximizing-bounties-on-google-cloud-vrp" target="_blank" rel="noopener noreferrer nofollow">Reward Table</a></p><hr class="content_break"><h1 class="heading" style="text-align:center;" id="a-gift-from-google-to-us"><span style="font-family:inherit;font-size:24px;"><b>A gift from Google to us</b></span></h1><p class="paragraph" style="text-align:left;"><span style="color:#222222;font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;">Mention </span><span style="color:#222222;font-family:inherit;font-size:16px;"><b>Critical Thinking Bug Bounty Podcast</b></span><span style="color:#222222;font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"> in any rewarded (cash or credit) VRP report submission </span><span style="color:#222222;font-family:inherit;font-size:16px;"><b>before</b></span><span style="color:#222222;font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"> the end of </span><span style="color:#222222;font-family:inherit;font-size:16px;"><b>April</b></span><span style="color:#222222;font-family:"gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;font-size:16px;"> to receive </span><span style="color:#222222;font-family:inherit;font-size:16px;"><b>bonus swag!</b></span></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="how-to-get-the-best-out-of-the-goog">How to get the best out of the Google VRP program</h2><h3 class="heading" style="text-align:left;" id="privilege-escalation-delta">Privilege Escalation Delta</h3><p class="paragraph" style="text-align:left;">Google internally uses &quot;privilege escalation delta&quot; as a core rewards metric. The delta measures the gap between starting permissions and ending access.</p><p class="paragraph" style="text-align:left;">Starting with viewer access and ending with viewer access on another product = low delta. Starting with viewer access and achieving full project takeover = high delta.</p><p class="paragraph" style="text-align:left;">Reports should explicitly document:</p><ul><li><p class="paragraph" style="text-align:left;">Starting permissions (exact role, scope)</p></li><li><p class="paragraph" style="text-align:left;">Ending permissions (what you can now access/control)</p></li><li><p class="paragraph" style="text-align:left;">Data types accessible after exploitation</p></li></ul><h3 class="heading" style="text-align:left;" id="downgrades-and-upgrades">Downgrades and Upgrades</h3><p class="paragraph" style="text-align:left;"><b>Downgrades:</b></p><p class="paragraph" style="text-align:left;"><b><i>Prior Access</i></b> - If the attack requires uncommon starting permissions, payout decreases.</p><p class="paragraph" style="text-align:left;"><b><i>User Interaction</i></b> - Applies only when interaction goes &quot;beyond normal usage of the affected product.&quot; If a ticketing system requires someone to click a ticket, and clicking triggers the exploit, that&#39;s expected workflow, not a downgrade scenario. But if the victim must navigate to an unusual location or enable a hidden setting, expect a reduction.</p><p class="paragraph" style="text-align:left;"><b><i>Exploitability</i></b> - If only 100 customers operate in the specific state required for exploitation, the payout will be lower than if 100,000 customers are vulnerable. Google uses internal metrics to estimate affected customer population.</p><p class="paragraph" style="text-align:left;"><b><i>Uncommon Configuration</i></b><b> </b>- Different from exploitability. This applies when the victim&#39;s setup requires enabling features or extensions that most customers don&#39;t use. Documentation matters here: if Google&#39;s own docs recommend the configuration, cite them to counter a potential downgrade. Stack Overflow answers or popular tutorials showing the configuration can also help.</p><p class="paragraph" style="text-align:left;">And please, stop using AI to write your reports.</p><p class="paragraph" style="text-align:left;"><b>Upgrades:</b></p><p class="paragraph" style="text-align:left;"><b><i>Novelty</i></b> - Genuinely new attack patterns that Google hasn&#39;t seen.</p><p class="paragraph" style="text-align:left;"><b><i>Report Quality</i></b> - A 1.2x multiplier is available for exceptional reports. The quality matrix evaluates:</p><ul><li><p class="paragraph" style="text-align:left;">Clear impact description</p></li><li><p class="paragraph" style="text-align:left;">Effective reproduction steps</p></li><li><p class="paragraph" style="text-align:left;">Root cause analysis</p></li><li><p class="paragraph" style="text-align:left;">Concise technical writing</p></li></ul><p class="paragraph" style="text-align:left;">The 0.8x downgrade triggers when reports lack these elements. Google explicitly prefers concise reports over 20-page research papers. If the full research exists, include it as an attachment but keep the main report focused on what, how, and why.</p><p class="paragraph" style="text-align:left;">Writing reports in your native language is acceptable, Google prefers that over AI-translated text that may obscure technical details.</p><h3 class="heading" style="text-align:left;" id="report-routing-and-appeals">Report Routing and Appeals</h3><p class="paragraph" style="text-align:left;">Reports are initially routed by a Level 0 triager who only determines whether the bug is Cloud-related. Cloud bugs go to the Cloud queue regardless of specifics. The Cloud panel then reviews and may route elsewhere if the issue fits better under Abuse VRP or another program.</p><p class="paragraph" style="text-align:left;">The panel consists of security engineers, including former bug bounty researchers who actively participate in programs. Panel meetings run 60 minutes with roughly 15 bugs reviewed. Simple cases take under a minute; complex ones may exceed four minutes and get deferred.</p><p class="paragraph" style="text-align:left;">If a report is rewarded under a different VRP and you believe Cloud should have evaluated it:</p><ol start="1"><li><p class="paragraph" style="text-align:left;">Post a comment on the bug requesting re-evaluation</p></li><li><p class="paragraph" style="text-align:left;">Explicitly state which Cloud table category you believe applies</p></li><li><p class="paragraph" style="text-align:left;">Request the bug be sent to Cloud VRP panel</p></li></ol><h3 class="heading" style="text-align:left;" id="product-configuration-best-practice">Product Configuration Best Practices</h3><p class="paragraph" style="text-align:left;">Cloud products require significant setup before meaningful testing. Use Terraform with Google&#39;s sample configurations to spin up instances matching common customer deployments. GKE is particularly difficult to configure correctly from scratch.</p><p class="paragraph" style="text-align:left;">Following official documentation for setup serves dual purposes:</p><ol start="1"><li><p class="paragraph" style="text-align:left;">Reduces &quot;uncommon configuration&quot; downgrade risk</p></li><li><p class="paragraph" style="text-align:left;">Ensures testing reflects actual customer environments</p></li></ol><p class="paragraph" style="text-align:left;">Google added a specific downgrade for &quot;wacky configurations that we&#39;ve never seen before.&quot; Even if a bug is technically valid, exploiting a configuration no real customer uses limits payout.</p><hr class="content_break"><div class="image"><img alt="" class="image__image" style="border-radius:0px 0px 0px 0px;border-style:solid;border-width:0px 0px 0px 0px;box-sizing:border-box;border-color:#E5E7EB;" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/uploads/asset/file/dc7a3669-6d67-48a9-aa2b-5dbdd0d8e41e/_bannerHackerNotesShort.png?t=1769044664"/><div class="image__source"><a class="image__source_link" href="https://ctbb.show/discord?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-159-maximizing-bounties-on-google-cloud-vrp" rel="noopener" target="_blank"><span class="image__source_text"><p>Check out the CTBB Discord!</p></span></a></div></div><p class="paragraph" style="text-align:center;">We do subs at $25, $10, and $5, premium subscribers get access to:</p><p class="paragraph" style="text-align:center;">– <b>Hackalongs</b>: live bug bounty hacking on real programs, VODs available<br>– <b>Live data streams, exploits</b>, <b>tools</b>, <b>scripts </b>&<b> un-redacted bug reports</b></p><p class="paragraph" style="text-align:center;"><a class="link" href="https://ctbb.show/merch?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-159-maximizing-bounties-on-google-cloud-vrp" target="_blank" rel="noopener noreferrer nofollow">You can also find some hacker swag here.</a></p><hr class="content_break"><h2 class="heading" style="text-align:left;" id="some-possible-bugs">Some possible bugs</h2><h3 class="heading" style="text-align:left;" id="impact-demonstration-for-xss">Impact Demonstration for XSS</h3><p class="paragraph" style="text-align:left;">XSS on GCP console can chain to RCE on customer instances via Cloud Shell. But you have to demonstrate it. Client-side findings get categorized based on proven impact, not theoretical maximum. An XSS with a working Cloud Shell RCE POC rates higher than an XSS that merely claims the same potential.</p><p class="paragraph" style="text-align:left;">Delivery mechanism quality also affects rating. A scenario requiring the victim to have GCP open in one tab, Gmail in another, and clicking an emailed link is weaker than exploiting an internal GCP ticketing flow where the victim is guaranteed to have appropriate permissions.</p><h3 class="heading" style="text-align:left;" id="interesting-patterns-found">Interesting patterns found</h3><p class="paragraph" style="text-align:left;">The June 2025 Bug Swat event paid out $1.6M across 160+ reports in a five-week submission window. The highest-value findings exploited service account impersonation chains.</p><p class="paragraph" style="text-align:left;">The pattern identifies default service accounts with elevated privileges, finds impersonation paths, and chains across services. At the Bug Swat, one researcher (Jakub Domeracki) found a missing link that elevated the impact for ~10 previously submitted reports. Multiple researchers had identified impersonation paths but couldn&#39;t prove meaningful privilege escalation. Jakup demonstrated that one specific service account could be used to impersonate a much higher-privileged account. Once proven, Google retroactively rewarded everyone who had partial chains, each with unique starting points but now demonstrably leading to real impact.</p><p class="paragraph" style="text-align:left;">When submitting service account impersonation bugs, map the full chain. If impact seems limited, dig for the next hop.</p><h3 class="heading" style="text-align:left;" id="bug-bounty-resource-guide-for-2026"><a class="link" href="https://getdisclosed.com/bug-bounty-resources-2026?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-159-maximizing-bounties-on-google-cloud-vrp" target="_blank" rel="noopener noreferrer nofollow">Bug Bounty Resource Guide for 2026</a>:</h3><p class="paragraph" style="text-align:left;">Quick shoutout to our friend <a class="link" href="https://x.com/infinitelogins?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-159-maximizing-bounties-on-google-cloud-vrp" target="_blank" rel="noopener noreferrer nofollow">Harley Kimball</a> (feature on <a class="link" href="https://youtu.be/NI-eXMlXma4?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-159-maximizing-bounties-on-google-cloud-vrp" target="_blank" rel="noopener noreferrer nofollow">ep. 133</a>): he’s dropping a Bug Bounty Resource Guide for 2026 with some solid stuff for anyone who wants a curated list of things to read, tools to use, etc. -of course we&#39;re featured in it! =)</p><p class="paragraph" style="text-align:left;">If you&#39;re interested, check it out here: <br><a class="link" href="https://getdisclosed.com/bug-bounty-resources-2026?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-159-maximizing-bounties-on-google-cloud-vrp" target="_blank" rel="noopener noreferrer nofollow">https://getdisclosed.com/bug-bounty-resources-2026</a></p><p class="paragraph" style="text-align:left;">That&#39;s it for the week, keep hacking!</p><h2 class="heading" style="text-align:left;" id="resources">Resources</h2><ul><li><p class="paragraph" style="text-align:left;">‘Legendary Guy’ - Jakub Domeracki: <a class="link" href="https://x.com/GoogleVRP/status/2013660670076555418?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-159-maximizing-bounties-on-google-cloud-vrp" target="_blank" rel="noopener noreferrer nofollow">https://x.com/GoogleVRP/status/2013660670076555418</a></p></li><li><p class="paragraph" style="text-align:left;">Google Cloud VRP rewards rules: <a class="link" href="https://bughunters.google.com/about/rules/google-friends/cloud-vulnerability-reward-program-rules?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-159-maximizing-bounties-on-google-cloud-vrp#reward-amounts" target="_blank" rel="noopener noreferrer nofollow">https://bughunters.google.com/about/rules/google-friends/cloud-vulnerability-reward-program-rules#reward-amounts</a></p></li><li><p class="paragraph" style="text-align:left;">Google Cloud VRP product tiers: <a class="link" href="https://github.com/google/bughunters/blob/main/cloud-tiers/cloud-tiers.text?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-159-maximizing-bounties-on-google-cloud-vrp" target="_blank" rel="noopener noreferrer nofollow">https://github.com/google/bughunters/blob/main/cloud-tiers/cloud-tiers.text</a></p></li><li><p class="paragraph" style="text-align:left;">Bug Hunters blog on the 2025 Google Cloud VRP bugSWAT: <a class="link" href="https://bughunters.google.com/blog/hardening-google-cloud-insights-from-the-latest-cloud-vrp-bugswat?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-159-maximizing-bounties-on-google-cloud-vrp" target="_blank" rel="noopener noreferrer nofollow">https://bughunters.google.com/blog/hardening-google-cloud-insights-from-the-latest-cloud-vrp-bugswat</a></p></li><li><p class="paragraph" style="text-align:left;">Google VRP Discord: <a class="link" href="https://discord.com/invite/bzA9gc6Z?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-159-maximizing-bounties-on-google-cloud-vrp" target="_blank" rel="noopener noreferrer nofollow">https://discord.com/invite/bzA9gc6Z</a></p></li><li><p class="paragraph" style="text-align:left;">Google VRP on X: <a class="link" href="https://x.com/GoogleVRP?utm_source=blog.criticalthinkingpodcast.io&utm_medium=newsletter&utm_campaign=hackernotes-ep-159-maximizing-bounties-on-google-cloud-vrp" target="_blank" rel="noopener noreferrer nofollow">https://x.com/GoogleVRP</a></p></li></ul></div></div>
  ]]></content:encoded>
</item>

  </channel>
</rss>
