This is our second article, this will not directly follow on from the first - though this is laying some ground material, and we will loop back later! This article then, will focus on an interesting security issue in JavaScript.
JavaScript is a widespread programming language, used in both client-side (especially via frameworks such as REACT or Angular) and server-side (e.g. Node.js) frameworks. With the sheer amount of platforms dependent upon JavaScript security issues can lead to widespread risks.
To better understand this article, it is useful to have an understanding of Object Orientation and how it has been implemented in JavaScript. As such, we have two primer articles on these topics on our website:
Who is this article for?
Understanding application layer security threats is important for a wide-range of professions, including:
Security Architects and Solution Architects, as they need to understand the potential risks and mitigations to take appropriate design decisions, such as access control.
Software Engineers / Developers, because they need to understand how to build their applications in a secure manner and avoid critical mistakes.
Security Consultants of various types as they need to understand the risks of what they may be reviewing or consulting on.
Penetration Testers who need to understand how to attack these systems!
As discussed in the primer on Object Orientation and Prototypes in JavaScript, it is possible to modify prototypes dynamically. Prototypes are part of the mechanism by which inheritance (an Object Orientation concept) functions in JavaScript, and are defined at runtime, hence why they can be modified.
In the primer article, it was shown how it was possible to add a new method, or function, to an existing prototype within JavaScript. From a security perspective, so what?
If we can add a new function, what else can we do? What happens if we try to reassign an existing function to a new value? Perhaps with code like the following:
Date.prototype.getYear = function() { console.log("Function Overwritten!"); };
The following screenshot, from the Mozilla Firefox browser console, shows the before and after effect of such an action:
As shown in the screenshot, the behaviour of the inbuilt JavaScript Date type has been modified. From code that returns a numerical representation of the current year, to code that logs the message "Function Overwritten" to the console.
So far, this should demonstrate, that if an attacker can execute some JavaScript, they could certainly cause some disruption. Modifying inbuilt types to log messages would certainly causes some problems.
What else can be done?
Well, we can also grab a reference to an existing function, as the following code demonstrates:
d1 = new Date();
d1.getYear();
Date.prototype.getYearCopy = Date.prototype.getYear;
d1.getYearCopy(); // returns the same result as d1.getYear, as it is the same function
The following screenshot demonstrates this behaviour:
An interesting thing to note with this example is that the effect is applies to all existing instances, not only new ones!
So, if we can both take a copy of an existing function and overwrite an existing function, what can we do with this?
JavaScript stores its function data in memory, in the heap. (For reference, memory is typically organised into two main parts: the stack, usually for primitive variable types, and the heap, for more complex structures, including functions).
Earlier, we showed how you can overwrite the behaviour of existing functions, this certainly has some destructive capabilities. However, as can both take a copy of existing behaviour (functions) and then overwrite existing behaviour, we are able to add in, or extend, existing functionality with our own code.
Lets continue with the Date class to demonstrate:
Date.prototype.getYearOriginal = Date.prototype.getYear;
Date.prototype.getYear = function() {
console.log("I've added extra behaviour before the standard implementation!");
console.log(this.getYearOriginal());
console.log("I've added extra behaviour after the standard implementation!");
}
d1 = new Date();
d1.getYear();
The behaviour is shown in the following screenshot:
A Man-in-the-Middle (MitM) attack is typically focused on communications flows over networks, however, this attack allows us to MitM the functions on the call stack! A particularly neat feature of this is, this attack can be executed without end-users noticing, as the core behaviour of the function can be preserved, thus, not affecting the behaviour of the application from an end-user's perspective!
Obviously, this specific example is of potentially limited use, given the example function that we have selected. However, what other standard inbuilt functions can we apply this to? Well, any and all!
The next question is, would there be any particularly interesting functions? What about XMLHttpRequest.open and XMLHttpRequest.send?
These two methods are used, in conjunction, to enable a lot of modern websites to communicate between the browser and the back-end server/API. This starts to sound interesting, what if credentials are being sent via this method? What if credit card details are being sent? Other personal data?
There are some barriers to this attack, most notably:
These will be discussed in subsequent articles!
Prototype Pollution is a very powerful technique, whereby existing JavaScript types can be modified in destructive ways, as well as more subtle, insidious ways. The latter, perhaps being far more interesting.
At Firesand, we see this as a particularly dangerous attack given the prevalence of JavaScript, and the ability to execute arbitrary code.
Cookie Notice
We use cookies to ensure that we give you the best experience on our website. Please confirm you are happy to continue.