Penetration and Security in JavaScript
Tags: javascript, webdev, security, discuss
Published: 2021-10-07 23:30:52

## 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](https://medium.com/intrinsic-blog/protecting-your-javascript-apis-9ce5b8a0e3b5) 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**
1. **Prototype Bribing**
1. **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`.

```js
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:

```js
const c = Connector({ address: 'investments' })
// Connection to address [investments] enstablished

c.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.

```js
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**:

```js
let i = 0
const probe = {
  get address() {
    console.count('probe')
    return 'investments'
  },
}

const c = Connector(probe)
// probe: 1
// probe: 2
// probe: 3
// probe: 4
// Connection to address [investments] enstablished

c.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.

```js
let i = 0
const doubleGetter = {
  get address() {
    if (++i === 5) return 'malicious'
    return 'investments'
  },
}

const c = Connector(doubleGetter)
// Connection to address [investments] enstablished

c.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 Getters**s 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*:

```js
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:

```js
const includesBackup = Array.prototype.includes

// bribe it...
Array.prototype.includes = () => true

const c = Connector({ address: 'malicious' })
// Connection to address [malicious] enstablished

// ...and immediately everything in the norm
Array.prototype.includes = includesBackup

c.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.

```js
{
  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.

```js
const includesBackup = Array.prototype.includes
const bindBackup = Function.prototype.bind

Array.prototype.includes = () => true
Function.prototype.bind = () => () => true

const c = Connector({ address: 'malicious' })
// Connection to address [malicious] enstablished

Array.prototype.includes = includesBackup
Function.prototype.bind = bindBackup

c.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 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.

```js
// Connector#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}]`
  )
}
```

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`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/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:

```js
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`:

```js
const number = new Number(42)

console.log(number)
// [Number: 42]

console.log(+number)
// probe: 1
// 42

console.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.

```js
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` 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.

```js
Number.prototype.valueOf = () => 1
const 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:
* [GitHub](https://github.com/didof)
* [Twitter](https://twitter.com/did0f)
* [LinkedIn](https://www.linkedin.com/in/didof/)