Introduction
Implicit type coercion conditions in dynamically typed programming languages has always lead to tricky outcomes. If you ever programmed in a language like Javascript, you have probably come across some funny cases like one of these;
[sourcecode language=”javascript”]
//example 1
[] == ![]; // true
//example 2
true == []; // false
true == ![]; // false
false == []; // true
false == ![]; //true
[/sourcecode]
Before explaining what a type coercion is and how it can be exploited, I would like to mention a few concepts.
A dynamically typed programming language means basically that you don’t have to classify a variable’s data type. You can see the difference easily by looking at variable declarations in Javascript vs. C.
Javascript: var foo = “bar”;
C: char foo[3] = “bar”;
As you can see you would have to classify the variable “foo” as a character array in C, whereas you could declare the variable in Javascript without a type specification. In Javascript, the interpreter would conclude by itself that the variable is a string. Another feature of dynamically typed languages is that you could re-assign another value of a different type to a variable and the interpreter wouldn’t bat an eye.
var foo = “bar”; foo = 42; foo = {[1,2], “hello”};
In dynamically typed languages there are usually two different operators for comparisons. In Javascript one of them is “==” (loose comparison) and the other is “===” (strict comparison).
Loose comparison is used when you want to compare two values. It does not perform a type check on the values provided. Meanwhile, the strict comparison operator would expect both values and types to be the same.
When you supply values of different types to a loose operator, the interpreter first tries to coerce both values into the same type. Let’s do this very simple example.
In the case of loose comparison, the interpreter will first try to coerce the type of one of the values into another’s. So you may say that;
“” == 0 becomes Number(“”) == 0
and therefore returns True. But when you apply strict comparison, it enforces a type check and returns False because data types are different although values are equal.
Comparison isn’t the only operator doing implicit coercions. Actually, almost every operator in Javascript does some kind of implicit coercion, although each one has a different logic.
Types of Conversion
In Javascript there are three ways an operator may convert values;
- To string
- To boolean
- To number
So when an implicit coercion has arisen, one of the values will be converted into one of these types. However, the answer to the question “which type has priority over the other” differs based on the operator and values provided to it. Each operator has its own logic. For example string has the priority over numbers in addition (“+”), meanwhile, it’s the opposite when it comes to multiplication (“*”).
Exploiting Implicit Coercions
So let’s take a hypothetical scenario where a Node.JS web application takes a user-supplied input, passes it to a blacklist-style validate()function and eventually will suffix it to a SQL query if no bad-chars are found.
Let’s see how it reacts when it sees an SQL injection attempt.
hxxp://site.com/vuln?input=foo’ OR ‘x’=’x
It works as expected. However, we know that the Node web server can parse arrays and even dictionaries out of parameters. Now let’s try to send the payload as an array and see what happens.
hxxp://site.com/vuln?input[]=foo’ OR ‘x’=’x
Good, but nothing staggering. But what happens next is more pleasant.
So after the input has passed through the validation step, the addition operator (“+”) implicitly coerced the input (which is actually an array) into a string and SQL query has been constructed without any problems.
Would this be applicable to PHP applications as well? Well, at least not this specific scenario (PHP Concat operator behaves differently) but PHP apps may certainly encounter security issues caused by implicit coercions.
Conclusion
Despite every web programming language we know of is dynamically typed and implements implicit coercion, we’ve not yet seen a paper approaching these type of bugs security-wise. So out aim was to provide you a perspective on the issues that might arise from this feature. we’ve also tried to demonstrate it via a hypothetical but very plausible scenario. Please contact us if you think/know of any other scenarios and we’ll be more than happy to add it