Turb0
Bits, bytes, and bad ideas

From Component to Compromised: XSS via React createElement

Sat Oct 11 14:49:09 UTC 2025

XSS in modern React apps isn't gone, it's just hiding in new places. In this blog, we'll expose how React createElement can be your way in.

We'll introduce several React DOM XSS lab scenarios based on real bug bounty findings from vulnerable applications in the wild. You'll see how untrusted input can make its way from a variety of realistic sources to a React createElement sink, leading to exploitable XSS, even in apps built with frameworks like Next.js.

These labs are realistic, grounded in actual bugs, and designed to sharpen your ability to spot and exploit DOM XSS in the kinds of apps bounty hunters hit every day.

Background

Back in April I put together a workshop for the Defcon Bug Bounty Village focused on scenarios where a user accessible source reaches the React createElement sink in some way, and how these scenarios can lead to cross site scripting or similar impact. After presenting it a few times since then and getting feedback on it, I've decided to put together a limited blog post about the content. I will not be covering full lab challenge walkthroughs here, or going through the full introduction to the topic available in the slides, but instead hitting some valuable highlights, and leaving the exercise of solving the challenges to the reader.

What is createElement in React?

ce def

React implements a createElement function different than document.createElement that is used internally and even offered externally for the generation of DOM elements.

How does JSX get compiled into React createElement calls?

ce vs jsx

Implementations vary massively between the latest versions of React and older versions that are still largely in use in the wild, but the usage of the React createElement function as a powerful sink still holds true.

JSX difference

In this example, we can clearly see how the JSX gets translated to createElement calls in the minified bundle that gets built.

Breaking down createElement's function signature

React CE params

Type

Props

Children

Exploitation Cheat Sheet

Assuming attacker controlled deserialized JSON being passed into this function: cheat sheet

I'm not thrilled with this cheat sheet. It serves a great utility for this lab, but I think it is still lacking some nuances.

Lab challenges

The lab challenges are accessible at https://defcon.turb0.one. The goal of each one of these is to achieve JavaScript execution. Some of the challenges include source maps, some deliberately don't. These challenges will remain up until the end of October. After that, I intend to take the box down and leave a static web page up instead that offers the tarball download to still allow people to run these locally.

Most interesting lab challenge

Based on feedback I've received, one of the most eye opening and interesting pieces of these labs is that the following webpacked and transpiled React component can lead to XSS.

xss vulnerable component

If this seems impossible, I recommend you go play around with the labs.

Further research ideas

There's a lot more to explore here. Here are some cool directions this could be taken in: