A Silly Trick I Learned During Stripe CTF

I recently participated in Stripe Capture the Flag and after completing six of the eight levels I learned a fair bit about browser security and a few server-side attacks. It was an extremely valuable experience not simply because I learned a few tricks, but because It renwed my awareness of security holes. Of the things I've learned, the following Javascript 'trick' was probably the most amusing / my favorite.

Level 6 of the challenge takes place on a server that hosts a sort of social network. Users can log in and post things to a public 'Wall'. There is another user on the system that logs in to check on recent posts approximately once every five minutes. There is also a page on the application which will show the current user's password in plain text (!).

My first approach was to use XSS to ebmed a harvesting script in a post. The script would make an XHR request to the page that shows the current user's password and post it back to the wall automatically, leaving a landmine for the other user. Getting anything executable on the page involved some breaking parsing on the JSON string embedded in the page that showed the recent posts, and a single tag in any message body will do that. At that point, the various client-side encoding / filtering that happens when a new message is posted cannot run.

I thought I was home-free, but it turns out the server rejects any posts with a single or double quote in the body or title. Writing Javascript that will request a URL and parse a bit of the DOM without ant quotes is...hard. Loading a script from a 3rd party server to do this work wouldn't work because of the Same-Origin Policy. These scripts wouldn't have access to the current user's cookies when requesting the password page.

I had to figure out a way to embed the executable script in a post without any quotes...after a bit of brain wracking over encoding quotes, I realized the Javascript String prototype has a useful method called 'fromCharCode' that will take an arbitrary number of unicode character codes and produce a string that is the concatenation of their ASCII representations. This was my way in. So I converted all my code to a string of the form "eval(String.fromCharCode(....))" that sucessfully posted and harvested the victim's password. The fact that this is possible and that the challenge called for it was kind of awesome, so here's the ridiculously simple helper function that got me to Level 7.

Stripe really created something great with this competition. I feel much more confident in my understanding of the subtle ways in which browsers protect users from malicious content and that my own code is (hopefully!) better protected than some of these challenge's sites! ;)

Thanks, Stripe. It was fun.

On Web Application State

​I've been thinking a bit lately on the tractability of the single-page/client-side/javascript application (or whatever you want to call it) testing problem. When trying to figure out nice ways to set up test suites and catch regressions in Squarespace, I've been kind of at a loss for complete solution. Our first attempts were, in my opinion, illustrative but misguided. Granted, it was just an experiment and not difficult to cook up, but we took the approach of attempting to verify things like animations completing successfully and pixel-perfection after simulating a series of clicks, drags and inputs. This did work for very simple cases, but fell apart when we tried to generalize. 

​However, it was not misguided because it did not produce the correct results. It was misguided because it was testing the wrong thing. At Squarespace, we place a focus on achieving absolute perfection in the beauty of our presentation. We seek to leave a lasting impression and consistently sleek widgets is a big part of that. In the pursuit of such goals it's very easy to lose track of the key elements of the experience that make the application really usable.

If you view your application as an object with multiple layers, where the base layer is just the basic interactions modeled with the final HTML structure you intend to keep, you realize most applications remain surprisingly usable with just basic styling for layout and little 'pizzaz'. Clearly, these are the elements you cannot allow to regress.

​In a complex applicaton, making sure that these elements are on-screen when they need to be and the user's state in your application is always consistent is _key_. Those transitions are often the highest-level state in your application. In an ideal world, your state-management code would be as ignorant of the interface as possible. So much so that you could simply instantiate and 'use' your application from code like the following:

Having your application be just a plain old object that may delegate to sub-components (that can be replaced with mocks/dummy instances for testing) may not always be practical, but it makes things a heck of a lot simpler. Because web apps are just big state-machines, ​In theory, if you had some object that encapsulated the current state of your application (Much like Ember's router), it would be incredibly useful to verify that whenever you call app.navigateTo('library') whatever internal state representation was updated correctly. That seems obvious, but it's never occurred to me so clearly when actually testing this behavior would involve calling the methods, then string matching on 'window.location'.

​I'm aware that decoupling view from state and testing from the bottom up are nothing new, but it's very easy to forget that we can apply the same principles applied in 'regular' development to the occasionally wild world of the front-end.

Decouple your state, test your state, then move on to the pretty stuff.