Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
October 7, 2021 11:30 pm GMT

Penetration and Security in JavaScript

Premise

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 as this one was for me. Using checks is not enough.

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:

  1. Probing & Double Getter
  2. Prototype Bribing
  3. 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.

function Connector(options) {  const validAddresses = ['partner-account', 'investments', 'mutual']  if (!options.address || typeof options.address !== 'string') _err1()  if (!validAddresses.includes(options.address)) _err2(options, validAddresses)  console.info(`Connection to address [${options.address}] enstablished`)  return {    transfer,  }  function transfer(amount) {    if (!amount || amount <= 0) _err3()    if (amount > 500) _err4()    console.info(      `Transfered an amount of [${amount}] to the address [${options.address}]`    )  }}

Do not focus on _err functions. Not important here.

The happy path is the following:

const c = Connector({ address: 'investments' })// Connection to address [investments] enstablishedc.transfer(300)//Transfered an amount of [300] to the address [investments]

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.

Connector({ address: 'malicious' })// The address malicious is not valid. Valid ones are: partner-account, investments, mutual

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:

let i = 0const probe = {  get address() {    console.count('probe')    return 'investments'  },}const c = Connector(probe)// probe: 1// probe: 2// probe: 3// probe: 4// Connection to address [investments] enstablishedc.transfer(300)// probe: 5

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.

let j = 0const doubleGetter = {  get address() {    if (++i === 5) return 'malicious'    return 'investments'  },}const c = Connector(doubleGetter)// Connection to address [investments] enstablishedc.transfer(300)// Transfered an amount of [300] to the address [malicious]

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:

function Connector({ address }) { ... }

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:

const includesBackup = Array.prototype.includes// bribe it...Array.prototype.includes = () => trueconst c = Connector({ address: 'malicious' })// Connection to address [malicious] enstablished// ...and immediately everything in the normArray.prototype.includes = includesBackupc.transfer(300)// Transfered an amount of [300] to the address [malicious]

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.

{  const safeIncludes = Array.prototype.includes  function Connector({ address }) {    const validAddresses = ['partner-account', 'investments', 'mutual']    ...    const isValidAddress = safeIncludes.bind(validAddresses)    if (!isValidAddress(address)) _err2(address, validAddresses)    ...  }  global.Connector = Connector // window if browser}

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.

const includesBackup = Array.prototype.includesconst bindBackup = Function.prototype.bindArray.prototype.includes = () => trueFunction.prototype.bind = () => () => trueconst c = Connector({ address: 'malicious' })// Connection to address [malicious] enstablishedArray.prototype.includes = includesBackupFunction.prototype.bind = bindBackupc.transfer(300)// Transfered an amount of [300] to the address [malicious]

Once again, you managed to evade the guards.

Primitive Illusion

The Connector instance provides thetransfer 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.

// Connector#transferfunction transfer(amount) {  if (!amount || amount <= 0) _err3()  if (amount > 500) _err4()  console.info(    `Transfered an amount of [${amount}] to the address [${options.address}]`  )}

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:

Number.prototype.valueOf = () => {  console.count('probe')  return this}

this in the case of Number 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:

const number = new Number(42)console.log(number)// [Number: 42]console.log(+number)// probe: 1// 42console.log(number > 0)// probe: 2// true

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.

c.transfer(number)// probe: 1// probe: 2// Transfered an amount of [42] to the address [hacker-address]

The two logs of the probe correspond precisely in amount <= 0 andamount> 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.

Number.prototype.valueOf = () => 1const number = new Number(100000)c.transfer(number)// Transfered an amount of [100000] to the address [hacker-address]

Again, you managed to get what you wanted.

If you want to chat about nerdy things or just say hi, you can find me here:


Original Link: https://dev.to/didof/penetration-and-security-in-javascript-probing-double-getter-p47

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To