Penetration and Security in JavaScript
Are you sure you are ensuring your code to be used as intended? Are you preventing it from beeing used in a malicious way? If what comes your way is putting guards in your functions, this post will open up a world for you. Using checks is not enough.
This post got inspired by this Thomas Hunter II .
Index
You will be both wolf and sheep. I created the function below so that it had everything you need to learn attack and related defenses from the techniques:
- Probing & Double Getter
- Prototype Bribing
- Primitive Illusion
The function is Connector
, which receives an options
configuration object. This must contain a property named address
which must be the same as one of those listed in validAddresses
, otherwise an exception is thrown.
Once the connection with one of the valid addresses
has been established, the instance provides the transfer
method to move a certain amount
passed as input which must not exceed the value 500
.
Do not focus on
_err
functions. Not important here.
The happy path is the following:
Probing & Double Getter
ATTACK
Suppose you are a malicious user of the script. You want to send a sum of money to an address
not included in validAddresses
.
A frontal attack is obviously blocked.
Remember, while impersonating the hacker you are not aware of the code implementation!
It is possible to send a valid address
in advance and count the number of times it is accessed. This way you can tell when it's the right time to - ZAC! - turn it into the malicious
address!
Build a probe:
It's clear. Just change the fifth reading of address
; its validity is checked in the previous four readings. It is possible using the Double Getter technique.
Thanks to this technique you have effectively bypassed the guards of the initialization phase.
DEFENSE
The problem is that address
is repeatedly accessed. Even two would be too many.
But if it were just one, Double Getterss could not fool the guards.
To access address
once, simply copy it to a variable. Since it is a string
it is primitive - the new variable is a separate copy, without the getter.
In ES6 you can use destructuring:
Run the probe and see that it actually beeps only once. The Double Getter threat is neutralized.
Prototype bribing
ATTACK
You have to find a way to infiltrate the code. But they raised the walls - we need an infiltrator, someone from inside who for a moment, just a moment, pretends not to see.
The includes
function is your man. Bribing it is simple:
Only during the initialization phase will includes
return true
indiscriminately. The discriminant guard validAddresses.include(address)
is effectively blinded and the malicious
address
can arrogantly enter through the front door.
DEFENCE
A wall is pulled around the Connector
, that is a block scope. Within this you want to have your own copy of Array.prototype.includes
that is not corruptible from the outside and use only this one.
The same trick we used earlier this time will not work and the _err2
will be thrown.
ATTACK
With a little cunning it is possible to corrupt the includes
supervisor. This is bind
.
I recommend keeping a copy of the corrupt function to get things right as soon as the offense is committed.
Once again, you managed to evade the guards.
Primitive Illusion
The Connector
instance provides the transfer
method. This requires the amount
argument which is a number and for the transfer to be successful, it must not exceed the value 500
. Suppose I had already managed to establish contact with an address
of my choice. At this point I want to transfer a higher amount than allowed.
The Primitive Illusion technique achieves an effect similar to the Double Getter but in other ways. A limitation of the DG technique is in fact that of being applicable only to variables passed by reference. Try to implement it for a primitive - Number
for example.
I find it more functional to modify Number.prototype.valueOf
. This is a method you will probably never need to call directly. JavaScript itself invokes it when it needs to retrieve the primitive value of an object (in this case, a Number
). Intuition is more likely with an example:
this
in the case ofNumber
represents the same number passed in the constructor.
You probably recognized it, it's a probe. You test different operations on an instance of Number
:
As you guess on the fly, the valueOf
method is invoked when primitive value
is expected - as in the case of a mathematical operation. At this point all that remains is to insert the probe into the transfer
method.
The two logs of the probe correspond precisely in amount <= 0
and amount> 500
. At this point you realize that you don't need to swap the value for another at some point - you just need to return a value that satisfies the above conditions when valueOf
is called.
Again, you managed to get what you wanted.
On This Page