Cross-Site Scripting and Cookies, a Delicious Disaster
If you read our previous article, "The Non-Technical Explanation of Cross-Site Scripting," you probably have a solid idea of what Cross-Site Scripting (XSS) is. While good Whitehats understand the theory behind a vulnerability, great Whitehats dig a little further. Today we'll show an example of XSS on a demo blog I created using Django- a Python web framework. Our vulnerable blog, 'XSS Blog,' is publicly available on my GitHub for your learning benefit. Feel free to make changes, fix, and break all the things! Let's dive in, shall we?
To understand the complete picture of an XSS exploit, you'll first need to understand how websites authenticate users. Websites communicate over a protocol known as Hyper Text Transfer Protocol, HTTP for short. For those of you who may be new to networking, a protocol is simply a guideline for how computers can communicate efficiently. HTTP is a stateless protocol, meaning the previous request is unknown in a session of multiple HTTP requests. For example, imagine a conversation between people that can't remember what they're talking about. While there are a variety of solutions to this problem, we'll be discussing the most delicious one.
Cookies are byte-sized storage artifacts placed onto your computer by a web server. They often hold user information for the web server, such as authorization data and account preferences. Upon successful authentication to a website, the server issues the client a cookie. This cookie is then sent back and forth upon each new request. Without cookies, a client would be forced to authenticate to a website on each new request. As far as authentication is concerned, the cookie will often be a "sessionid" variable with a unique string only understood by the server. This string of characters represents a session id which should be lengthy and arbitrary. When that arbitrary string is passed to the web server, it can be used as proof of previous authentication. Therefore, theoretically, an attacker can impersonate a user if they capture their session id. Note that cookies are not inherently the most secure thing ever created. They are stored in plain text, A.K.A unencrypted, for all prying eyes to see.
Okay, for a small recap, we now understand what happens when we log in to a website. We also know how HTTP becomes artificially stateful using cookies. Now we have to figure out a way to make cookies secure. Thankfully, cookies have a couple of different flags for developers to set. These flags modify the cookie's behavior, such as configuring how long until the cookie expires. The "HttpOnly" flag will ensure that your precious cookies are only sent on request to a server and are not accessible via JavaScript. This configuration prevents attackers from leveraging malicious client-side code to snatch cookies from a user's browser. Let's jump over to XSS Blog and look at what happens when we willingly ignore security best practices!
After authenticating with XSS Blog using the provided credentials in the README.md file, we can inspect our first cookie. I'm utilizing Google Chrome's built-in console, but you can use any modern browser. If you're following along with Chrome, navigate to the "Application" tab, then click the "Cookies" dropdown on the sidebar.
Notice that the "HttpOnly" box is unchecked for our secret "sessionid" cookie. Since this box is unchecked, JavaScript code can access the value of the cookie if requested. Now all we need to do is find a way to introduce our rogue JavaScript code onto the blog. Cough, cough, XSS. I hope your hacker senses are tingling because I sense an application-smashing vulnerability coming along. An opportunity to maliciously interact with the blog presents itself on the "A Random Post" page in the form of a comment section.
This attack may seem complex, but HTML conveniently provides a tag to inject JavaScript code onto a website! We can link a remote script or enter code manually using the "<script>" tag. This post should serve as a proof-of-concept (PoC), so let's take the manual route. We'll enter the command "<script>alert(document.cookie.split('; ')[1]);</script>" here, but feel free to use your creativity as it will only reinforce your learning! Our hard work comes together to give us our reward upon clicking the "Add Comment" button.
This attack is the gift that keeps giving- it truly does get better! Think about what happens when a comment is submitted to a website. The comment is stored in a database for future rendering. Not only did we demonstrate a successful XSS exploit, but we also created a Stored XSS. Any user that visits this page will be automatically compromised by our simple comment. Depending on the popularity of the "A Random Post" page, a significant portion of the XSS blog's user base could easily be compromised. Ladies and gentlemen, I present the dangers of a cross-site scripting attack.