loading...

Escaping the call stack

lvidakovic profile image Luka Vidaković ・2 min read

Let’s look at one way to cover the tracks within injected javascript code. It exploits asynchronous javascript nature to obfuscate the function call stack. This is a continuation of the initial post on code obfuscation.

What are we even talking about?

Call stack is best observed within exceptions, but it’s purpose is to resolve values of the called functions. It “rewinds” function calls with resulted values upon completion, but let’s not go into details.

const first = () => {
  throw new Error()
}
const second = () => first()
const third = () => second()

third()

/* pasting this to browser's console will print something like
VM80:1 Uncaught Error
    at first (<anonymous>:1:29)
    at second (<anonymous>:2:22)
    at third (<anonymous>:3:21)
*/

This is an example of a call stack. Execution started at the third() function call and then continued to second() and first() function calls until the exception was thrown.

Breaking free from the call stack

There are reasons you might want to break from the call stack. Most are malicious and try to cover up the tracks. Nonetheless, here is one way of stack escape:

function maliciousStuff() {
  throw new Error()
}

function legitChunkOfCode() {
  // ...
  // code that you don't want people to easily connect with it's caller function
  setTimeout(() => maliciousStuff())
}

legitChunkOfCode()

/* pasting this to browser's console will print something like
Uncaught Error
    at maliciousStuff (<anonymous>:2:9)
    at setTimeout (<anonymous>:7:20)
*/

The easiest way to break off the execution chain is to set up a timeout call. That breaks the synchronicity of a function call and schedules code for some time in the future. You can’t find information about the caller function in the call stack(legitChunkOfCode). We’ve broken the stack and made execution much harder to follow.

Discussion

markdown guide