All posts by Julius Callahan

XSS in JSON: Old-School Attacks for Modern Applications

Post Syndicated from Julius Callahan original https://blog.rapid7.com/2022/05/04/xss-in-json-old-school-attacks-for-modern-applications/

XSS in JSON: Old-School Attacks for Modern Applications

I recently wrote a blog post on injection-type vulnerabilities and how they were knocked down a few spots from 1 to 3 on the new OWASP Top 10 for 2022. The main focus of that article was to demonstrate how stack traces could be — and still are — used via injection attacks to gather information about an application to further an attacker’s goal. In that post, I skimmed over one of my all time favorite types of injections: cross-site scripting (XSS).

In this post, I’ll cover this gem of an exploit in much more depth, highlighting how it has managed to adapt to the newer environments of today’s modern web applications, specifically the API and Javascript Object Notation (JSON).

I know the term API is thrown around a lot when referencing web applications these days, but for this post, I will specifically be referencing requests made from the front end of a web application to the back end via ajax (Asynchronous JavaScript and XML) or more modern approaches like the fetch method in JavaScript.

Before we begin, I’d like to give a quick recap of what XSS is and how a legacy application might handle these types of requests that could trigger XSS, then dive into how XSS still thrives today in modern web applications via the methods mentioned so far.

What is cross-site scripting?

There are many types of XSS, but for this post, I’ll only be focusing on persistent XSS, which is sometimes referred to as stored XSS.

XSS is a type of injection attack, in which malicious scripts are injected into otherwise benign and trusted websites. XSS attacks occur when an attacker uses a web application to execute malicious code — generally in the form of a browser-side script like JavaScript, for example — against an unsuspecting end user. Flaws that allow these attacks to succeed are quite widespread and occur anywhere a web application accepts an input from a user without sanitizing, validating, escaping, or encoding it.

Because the end user’s browser has no way to know not to trust the malicious script, the browser will execute the script. Because of this broken trust, attackers typically leverage these vulnerabilities to steal victims’ cookies, session tokens, or other sensitive information retained by the browser. They could also redirect to other malicious sites, install keyloggers or crypto miners, or even change the content of the website.

Now for the “stored” part. As the name implies, stored XSS generally occurs when the malicious payload has been stored on the target server, usually in a database, from input that has been submitted in a message forum, visitor log, comment field, form, or any parameter that lacks proper input sanitization.

What makes this type of XSS so much more damaging is that, unlike reflected XSS – which only affects specific targets via cleverly crafted links – stored XSS affects any and everyone visiting the compromised site. This is because the XSS has been stored in the applications database, allowing for a much larger attack surface.

Old-school apps

Now that we’ve established a basic understanding of stored XSS, let’s go back in time a few decades to when web apps were much simpler in their communications between the front-end and back-end counterparts.

Let’s say you want to change some personal information on a website, like your email address on a contacts page. When you enter in your email address and click the update button, it triggers the POST method to send the form data to the back end to update that value in a database. The database updates the value in a table, then pushes a response back to the web applications front end, or UI, for you to see. This would usually result in the entire page having to reload to display only a very minimal amount of change in content, and while it’s very inefficient, nonetheless the information would be added and updated for the end user to consume.

In the example below, clicking the update button submits a POST form request to the back-end database where the application updates and stores all the values, then provides a response back to the webpage with the updated info.

XSS in JSON: Old-School Attacks for Modern Applications

XSS in JSON: Old-School Attacks for Modern Applications

Old-school XSS

As mentioned in my previous blog post on injection, I give an example where an attacker enters in a payload of <script>alert(“This is XSS”)</script> instead of their email address and clicks the update button. Again, this triggers the POST method to take our payload and send it to the back-end database to update the email table, then pushes a response back to the front end, which gets rendered back to the UI in HTML. However, this time the email value being stored and displayed is my XSS payload, <script>alert(“This is XSS”)</script>, not an actual email address.

XSS in JSON: Old-School Attacks for Modern Applications

As seen above, clicking the “update” button submits the POST form data to the back end where the database stores the values, then pushes back a response to update the UI as HTML.

XSS in JSON: Old-School Attacks for Modern Applications

However, because our payload is not being sanitized properly, our malicious JavaScript gets executed by the browser, which causes our alert box to pop up as seen below.

XSS in JSON: Old-School Attacks for Modern Applications

While the payload used in the above example is harmless, the point to drive home here is that we were able to get the web application to store and execute our JavaScript all through a simple contact form. Anyone visiting my contact page will see this alert pop up because my XSS payload has been stored in the database and gets executed every time the page loads. From this point on, the possible damage that could be done here is endless and only limited by the attacker’s imagination… well, and their coding skills.  

New-school apps

In the first example I gave, when you updated the email address on the contact page and the request was fulfilled by the backend, the entire page would reload in order to display the newly created or updated information. You can see how inefficient this is, especially if the only thing changing on the page is a single line or a few lines of text. Here is where ajax and/or the fetch method comes in.

Ajax, or the fetch method, can be used to get data from or post data to a remote source, then update the front-end UI of that web application without having to refresh the page. Only the content from the specific request is updated, not the entire page, and that is the key difference between our first example and this one.

And a very popular format for said data being sent and received is JavaScript Object Notation, most commonly known as JSON. (Don’t worry, I’ll get back to those curly braces in just a bit.)

New-school XSS

(Well, not really, but it sounds cool.)

Now, let’s pretend we’ve traveled back to the future and our contact page has been rewritten to use ajax or the fetch method to send and receive data to and from the database. From the user’s point of view, nothing has changed — still the same ol’ form. But this time, when the email address is being updated, only the contact form refreshes. The entire page and all of its contents do not refresh like in the previous version, which is a major win for efficiency and user experience.

Below is an example of what a POST might look like formatted in JSON.

XSS in JSON: Old-School Attacks for Modern Applications

“What is JSON?” you might ask. Short for JavaScript Object Notation, it is a lightweight text format for storing and transferring data and is most commonly used when sending data to and from servers. Remember those curly braces I mentioned earlier? Well, one quick and easy way to spot JSON is the formatting and the use of curly braces.

In the example above, you can see what our new POST looks like using ajax or the fetch method in JavaScript. While the end result is no different than before, as seen in the example below, the method that was used to update the page is quite different. The key difference here is that the data we’re wanting to update is being treated as just that: data, but in the form of JSON as opposed to HTML.

XSS in JSON: Old-School Attacks for Modern Applications

Now, let’s inject the same XSS payload into the same email field and hit update.  In the example below, you can see that our POST request has been wrapped in curly braces, using JSON, and is formatted a bit differently than previously before being sent to the back end to be processed.

XSS in JSON: Old-School Attacks for Modern Applications

XSS in JSON: Old-School Attacks for Modern Applications

In the example above, you can see that the application is allowing my email address to be the XSS payload in its entirety. However, the JavaScript here is only being displayed and not being executed as code by the browser, so the alert “pop” message never gets triggered as in the previous example. That again is the key difference from the original way we were fulfilling the requests versus our new, more modern way — or in short, using JSON instead of HTML.

Now you might be asking yourself, what’s wrong with allowing the XSS payload to be the email address if it’s only being displayed and not being executed as JavaScript by the browser. That is a valid question, but hear me out.

See, I’ve been working in this industry long enough to know that the two most common responses to a question or statement regarding cybersecurity begin with either “that depends…” or “what if…”  I’m going to go with the latter here and throw a couple what-ifs at you.

Now that my XSS is stored in your database, it’s only a matter of time before this ticking time bomb goes off. Just because my XSS is being treated as JSON and not HTML now does not mean that will always be the case, and attackers are betting on this.

Here are a few scenarios.

Scenario 1

What if team B handles this data differently from team A? What if team B still uses more traditional methods of sending and receiving data to and from the back end and does leverage the use of HTML and not JSON?

In that case, the XSS would most likely eventually get executed. It might not affect the website that the XSS was originally injected into, but the stored data can be (and usually is) also used elsewhere. The XSS stored in that database is probably going to be shared and used by multiple other teams and applications at some point. The odds of all those different teams leveraging the exact same standards and best practices are slim to none, and attackers know this.  

Scenario 2

What if, down the road, a developer using more modern techniques like ajax or the fetch method to send and receive data to and from the back end decides to use the .innerHTML property instead of .innerTEXT to load that JSON into the UI? All bets are off, and the stored XSS that was previously being protected by those lovely curly braces will now most likely get executed by the browser.

Scenario 3

Lastly, what if the current app had been developed to use server-side rendering, but a decision from higher up has been made that some costs need to be cut and that the company could actually save money by recoding some of their web apps to be client-side rather than server-side?

Previously, the back end was doing all the work, including sanitizing all user input, but now the shift will be for the browser to do all the heavy lifting. Good luck spotting all the XSS stored in the DB — in its previous state, it was “harmless,” but now it could get rendered to the UI as HTML, allowing the browser to execute said stored XSS. In this scenario, a decision that was made upstream will have an unexpected security impact downstream, both figuratively and literally — a situation that is all too well-known these days.

Final thoughts

Part of my job as a security advisor is to, well, advise. And it’s these types of situations that keep me up at night. I come across XSS in applications every day, and while I may not see as many fun and exciting “pops” as in years past, I see something a bit more troubling.

This type of XSS is what I like to call a “sleeper vuln” – laying dormant, waiting for the right opportunity to be woken up. If I didn’t know any better, I’d say XSS has evolved and is aware of its new surroundings. Of course, XSS hasn’t evolved, but the applications in which it lives have.

At the end of the day, we’re still talking about the same XSS from its conception, the same XSS that has been on the OWASP Top 10 for decades — what we’re really concerned about is the lack of sanitization or handling of user input. But now, with the massive adoption of JavaScript frameworks like Angular, libraries like React, the use of APIs, and the heavy reliance on them to handle the data properly, we’ve become complacent in our duties to harden applications the proper way.

There seems to be a division in camps around XSS in JSON. On the one hand, some feel that since the JavaScript isn’t being executed by the browser, everything is fine. Who cares if an email address (or any data for that matter) is potentially dangerous — as long as it’s not being executed by the browser. And on the other hand, you have the more fundamentalist, dare I say philosophical thought that all user input should never be trusted: It should always be sanitized, regardless of whether it’s treated as data or not — and not solely because of following best coding and security practices, but also because of the “that depends” and “what if” scenarios in the world.  

I’d like to point out in my previous statement above, that “as long as” is vastly different from cannot.” “As long as” implies situational awareness and that a certain set of criteria need to be met for it to be true or false, while “cannot” is definite and fixed, regardless of the situation or criteria. “As long as the XSS is wrapped in curly braces” means it does not pose a risk in its current state but could in other states. But if input is sanitized and escaped properly, the XSS would never exist in the first place, and thus it “cannot” or could not be executed by the browser, ever.

I guess I cannot really complain too much about these differences of opinions though. The fact that I’m even having these conversations with others is already a step in the right direction. But what does concern me is that it’s 2022, and we’re still seeing XSS rampant in applications, but because it’s wrapped in JSON somehow makes it acceptable. One of the core fundamentals of my job is to find and prioritize risk, then report. And while there is always room for discussion around the severity of these types of situations, lots of factors have to be taken into consideration, a spade isn’t always a spade in application security, or cybersecurity in general for that matter. But you can rest assured if I find XSS in JSON in your environment, I will be calling it out.  

I hope there will be a future where I can look back and say, “Remember that one time when curly braces were all that prevented your website from getting hacked?” Until then, JSON or not, never trust user data, and sanitize all user input (and output for that matter). A mere { } should never be the difference between your site getting hacked or not.

Additional reading:

NEVER MISS A BLOG

Get the latest stories, expertise, and news about security today.

OWASP Top 10 Deep Dive: Injection and Stack Traces From a Hacker’s Perspective

Post Syndicated from Julius Callahan original https://blog.rapid7.com/2021/10/19/owasp-top-10-deep-dive-injection-and-stack-traces-from-a-hackers-perspective/

OWASP Top 10 Deep Dive: Injection and Stack Traces From a Hacker's Perspective

In case you missed it, injection claimed the number 3 spot in OWASP’s updated Top 10 application security risks for 2021. Today, I’m going to highlight some of the reasons why injection is such a formidable threat, despite it falling two spaces from the number 1 slot on OWASP’s 2017 list.

But before we begin, I’d like to start off with a short but true story of how I got here.

The 3 letters that changed my life

A few decades ago, back in my web development days, I was working on a community-type website. This was post-MySpace and pre-Facebook — so yeah, that long ago. Back then, CMSs like WordPress and Joomla were just taking off but still all the rage. So with the LAMP stack complete and the CMS deployed, it was time to install the community component for the website. There weren’t many third-party solutions available back then — so when I found a component that offered individual profiles, message boards, picture uploads, AND a chat, I jumped on it.

A few days into getting the website configured and running, I noticed another admin user in the DB. My first thought was, “I don’t remember creating a second admin account,” and my second thought was, “Oh no, I’ve been hacked”.

Turns out the latter was true. Not even a week into building the website, it was hacked.

Now, any sane developer would have immediately torn down the server, scrambled for backups, searched for a different hosting provider, and started over. But not me — I needed to know how this happened. So I went straight to the horse’s mouth: I asked the person who hacked my site.

Remember when I mentioned the community component had a chat function? I used it to send the hacker a message: “How did you get in here?” Sure enough, a day later I received a reply of 3 simple letters that I will never forget: “SQL.”

From that day on, I knew what I wanted to do for the rest of my life. So to the hacker, whoever and wherever you are out there, thank you.

Which brings me to OWASP’s new top 10 for 2021 and our focus for this post: A03, aka injection.

What is injection, and how are attackers using it today?

I like to think of injection as a family of vulnerabilities. It can include everything from SQL and XSS — the rock stars of injection — to improper control of resource identifiers, which basically cover anything with the ability to significantly change the flow of a given process. In some cases, injection can even include the execution of arbitrary code. It should come as no surprise, then, that there are currently over 32 mapped CWEs for injection.

Many things have changed since my web developer days, but a lot has stayed the same. One would like to think the days of SQL injection, or any injection for that matter, are long gone. Sadly, that isn’t the case. While injection has been dethroned from first to third place on the new OWASP 2021 Top 10 list, it’s still very much alive in today’s web applications.

The good news is that the majority of injection issues I see today are a bit more benign than those from the past. They tend to be less likely to yield the fruitful database dumps, auth bypass, or occasional defacement we’ve all come to know so well. Don’t get me wrong — there are plenty of apps out there that are eager to give up their sensitive data because of unexpected and improperly handled input, though it does seem those numbers are on the decline. But for the apps that are a little more reluctant to share sensitive information via injection, they do so in another way: stack traces.

Stack traces sometimes do stack up

Developers have a love-hate relationship with stack traces. On the one hand, they’re helpful in spotting errors in the code they write. On the other hand, they point out the errors in the code they write.

To those that are new to the phrase “stack trace,” it refers to an error in the code that was last run by the application before it crashed. Basically, when something happens that the code isn’t prepared to handle, it spits out a bunch of technical information onto the screen so the developer can spot the issue and correct it. Stack traces have been around since the dawn of programming, and they remain a popular method of debugging an application.

For our conversation, let’s focus on the production environment of an application and highlight why stack traces in this scenario are bad for two main reasons.

First, they look terrible and are a bad user experience. When browsing an e-commerce product site, “NullPointerException” should not be one of the results. And I don’t recall “/var/www/” being part of my search string when looking for a new pair of sunglasses.

Second, most stack traces tend to divulge more sensitive information than they need to. This is by design — and while it might be helpful to the developer, it can also be all too helpful to an attacker.

So how does one “inject” something into a web application — and what role do stack traces play in this process? Well for the most part, and to keep things simple, GET and POST methods are a great place to start.

Say you come across a URL that ends with this path:

/search_get_by_id.php?id=3

Replace the 3 with a single tick so it looks like this:

/search_get_by_id=’

Now, instead of showing the results for an item with the ID of 3, the application gets confused by the single tick and throws a stack trace like this:

Invalid ProductError 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ”’ at line 1 of SELECT * FROM inventory WHERE id = ‘

Or maybe you’re updating some personal information on a website, but instead of entering in your email address, you enter “<script>alert(document.cookie)</script>” or 1,000 letter As into the parameter instead.

All these examples are forms of injection, and if done correctly, they can do some serious damage to an application. And the best-worst-case scenario for an attacker is that the application yields a stack trace instead of doing whatever it was they were initially trying to do. Either way, it’s a win-win scenario for the attacker.

How stack traces make hackers’ jobs easier

The average person who encounters a stack trace when visiting a website may not think anything other than, “Yikes, I don’t know what this is, so I’m going to hit the back button or try re-entering in my information.” However, an attacker will have a much different reaction — probably something like “YES!” or “FINALLY!”

You see, attackers love stack traces because of the information they yield. They spend hours or even days trying to get an application to throw a stack trace. While the untrained eye sees a mess of gobbledygook on the page, an attacker sees very valuable information that brings them another step closer to their goal.

In the following examples, I’ll give an overview of how an attacker views these stack traces.

*The images depicted below are entirely made up but do contain real snippets from actual stack traces. They’ve also been exaggerated for educational purposes.

Example 1: MySql

OWASP Top 10 Deep Dive: Injection and Stack Traces From a Hacker's Perspective

Things we know:

  • The application is running on a Windows machine, possibly a 32bit OS like XP or NT, judging from the file path.
  • The app is running an outdated and vulnerable version of MySQL.
  • There are multiple critical CVEs for this version of MySQL, everything from Denial of Service attacks to privilege escalation and remote code execution.

Example 2: Laravel PHP

OWASP Top 10 Deep Dive: Injection and Stack Traces From a Hacker's Perspective

Things we know:

  • The application is running on a Windows machine, based on the file path.
  • It’s running XAMPP (multiple known vulnerabilities).
  • It’s also running MySQL (multiple known vulnerabilities).
  • In addition, it’s running a vulnerable version of the PHP Laravel Framework 5.5 CVE-2018-15133.
  • This version of Laravel is susceptible to remote code execution, among other exploits.

Example 3: Apache

OWASP Top 10 Deep Dive: Injection and Stack Traces From a Hacker's Perspective

Things we know:

  • The application is running on a Linux box based on the file path.
  • It’s running Apache Struts (multiple known critical vulnerabilities).
  • It’s also running Apache Tomcat (multiple known critical vulnerabilities).
  • In addition, it’s running a vulnerable version of Apache web server.
  • This version of Apache is susceptible to multiple critical CVEs ranging from Denial of Service attacks to privilege escalation and remote code execution.

Example 4: Java

OWASP Top 10 Deep Dive: Injection and Stack Traces From a Hacker's Perspective

Things we know:

  • The application is running on a Linux box based on the file path.
  • It’s running Spring Framework (multiple known vulnerabilities).
  • It’s also running a vulnerable version of Java.
  • This version of Java is so vulnerable Oracle won’t even give you a specific vulnerability, only that it contains multiple critical CVEs ranging from remote access to self-destruction. I am exaggerating… only slightly, but you get the point.

So while injection went from gold to bronze on the new OWASP Top 10 list for 2021, attackers can and do still use it to produce a treasure trove of information — stack traces being one of those tried-and-true methods.

That said, I’m happy to see that injection has been knocked down a few pegs — this shows that the web industry has managed to move the needle forward by way of security. With the rise of even more popular frameworks that have some security baked in, and the ever-increasing concern for secure apps, developers are seeing security injected into their day-to-day (no pun intended). Whether it be SAST or DAST scanning hooked into their CI/CD, RASPs or WAFs deployed to detect attacks, or even the occasional pen test, security is becoming more and more part of the software development life cycle, and that is awesome.

Who knows? Maybe in another 4 years, injection won’t even make the top 10. If it does, you can bet I’ll write another blog about it.

Stay tuned for future installments in our series of deep dives into OWASP’s updated Top 10 list of application security threats for 2021.

NEVER MISS A BLOG

Get the latest stories, expertise, and news about security today.