-1

I have the following code:

const Show = () => {
  const dangerousMarkup = { __html: "<script>alert('ERROR');</script>" };
  return (
             div dangerouslySetInnerHTML={dangerousMarkup} />
            <div>
              <div>
                The length of dangerousMarkup is: {dangerousMarkup.__html.length}
                <br />
                The instructions are: {dangerousMarkup.__html }
              </div>
            </div>
    )

The alert is not showing up but the text is. I will see the following on my screen:

The length of dangerousMarkup is: 32
The instructions are: <script>alert('ERROR');</script>

I have tested this on Firefox, Chrome, and Edge, all with the same results. I need to be able to have the XSS working.

Before anybody says, "This is dangerous" or "don't do this" this is for a lab exercise I'm putting together to teach students how to detect an XSS exposure. This code will only run in a controlled lab environment.

I have attempted to run this code with const dangerousMarkup = { __html: "<p>some raw html</p>" }; as the html and this does work as expected with the "some raw html" appearing in the browser.

I have also looked at the documentation.

I installed Disable Content Security Policy on my Chrome browser from the Chrome store.

BTW, this is the much simplified case of the problem as the actual code is reading the script which a user has input on another screen and then saved to a postgresql database.

3
  • Why on earth are you doing things this way? Just create normal JSX content with a useEffect that triggers the alert. There is no reason to inject raw HTML for what you're showing. If you want to show students how to detect XSS, at the very least do it the normal way: don't try to make React template text into the page, just use normal JS. E.g. const s = document.createElement("script"); s.textContent="alert(document.cookie)"; document.head.appendChild(s);. Commented Jun 28, 2024 at 17:35
  • Mike, thank you for your insights. Perhaps I didn't make myself clear, but in the end I'm doing it this way because this is how the boss said to do it. This is not the first time I've run into a situation where modern browsers have closed a exploitable hole yet, I need t a method to demonstrate the exploit. Sadly, this code is a much simplified version of what is going on. The lab calls for the students to enter a script in a text field and then be saved to a database. On another screen the inputted script is executed as part of a field being displayed. So this has to be able to function. Commented Jun 28, 2024 at 20:27
  • So get the userdata, and then "like an idiot" turn that into a DOM and inject it the normal way, e.g. DOMParser.prototype.parseFromString. You're always in JS, there is literally no need to try to force the XSS "through React": XSS is not React-specific. Commented Jun 28, 2024 at 20:47

1 Answer 1

0

I found an answer to my own question and I am posting it here for those who come behind me.

The short answer is modern browsers are now disabling scripts which originate from the on-line user. No amount of modifying the Content Security Policies will change this. Even disabling the CSP will not allow a user to input a script to run.Therefore, a strictly React, Vite solution is not possible.

But there is a work around. You can create a pure HTML file and place it in the public directory. Then have your React component call the HTML file with parameters. For my application, I placed the HTML file within a frame. The HTML file has to run code specifically for the tag and use eval(script) in order to get the script to run.

I remind you this is extremely dangerous!

Code from within the react component which creates the frame:

<iframe
      src={`/ShowSpecial.html?content=${encodeURIComponent(
        Instructions  
      )}`}
      width="100%"
      height="500px"
      title="Show Special"
    ></iframe>

Here is the HTML file:

<!-- public/ShowSpecial.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Show Special</title>
  </head>
  <body>
    <div id="special-content">Loading...</div>

    <script>
      // Function to get URL parameters
      function getUrlParameter(name) {
        name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
        var regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
        var results = regex.exec(location.search);
        return results === null
          ? ""
          : decodeURIComponent(results[1].replace(/\+/g, " "));
      }

      // Get the special content from URL parameter
      var specialContent = getUrlParameter("content");

      // Display the special content (dangerous)
      document.getElementById("special-content").innerHTML = specialContent;

      // Execute the script (dangerous)
      if (specialContent.includes("<script>")) {
        specialContent = specialContent.replace(/<\/?script[^>]*>/gi, '');
        // Execute the script (dangerous)
        try {
          eval(specialContent);
        } catch (e) {
          console.error("Error executing script:", e);
        }
      }
    </script>
  </body>
</html>

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.