The createElement function accepts a string in the type argument. If the string is controlled by a user it would be possible to create an arbitrary React component:
// Dynamically create an element from a string stored in the backend// stored_value is a user-controlled stringelement_name = stored_value;React.createElement(element_name,null);
However, this would result only in a plain, attribute-less HTML element (pretty useless).
Injecting props
The createElement function accepts a list of attributes in the props argument. If the list is controlled by a user it would be possible to inject arbitrary props into the new element:
// Parse user-supplied JSON and pass the resulting object as props// stored_value is a user-controlled JSON string with attributesprops =JSON.parse(stored_value);React.createElement("span", props);
You can use the following payload to set the dangerouslySetInnerHTML attribute:
If a user-supplied data is used to set the dangerouslySetInnerHTML attribute you can insert arbitrary JavaScript code:
Explicitly setting href
If a user-supplied data is used to set the href attribute you can insert a javascript: URL:
Some other attributes such as formaction in HTML5 buttons or HTML5 imports also can be vulnerable:
Abusing server-side rendering
If a user-controlled data is passed to code that relies on user generated content and input without proper sanitization you can inject arbitrary JavaScript code.
In the above sample, result of the JSON.stringify function is assigned to a global variable in a <script> tag, this is the vulnerability. If a Redux store has the following value:
When a browser parses the page and encounters that <script> tag, it will continue reading until </script>. The browser will not read until the last curly bracket, instead, it will actually finish the script tag after bio: "as.