Try-Catch Control Flow Obfuscation

JavaScript exception handling can be exploited to create non-linear code flow that confuses analysis.

"a lot of vm's i worked on in the past would also exploit errors to force a catch execution to continue" - Draco

Error-Driven Control Flow Obfuscation

JavaScript exception handling can be exploited to create non-linear code flow that confuses analysis.

Given you probably don't want to make the fact that the function or operation you called is always going to throw an error, as that makes it easier for attackers to identify that you're doing this.

try {
  // code that appears to be the main execution path
  // but is deliberately designed to throw an error
  undefinedFunction(); // forces immediate jump to catch
  
  // this code is never executed, go wild
} catch (e) {
  if (e instanceof ReferenceError) {
    // actual intended functionality
    executeRealFunctionality();
  }
}

Example:

function foo(input) {
  let stage = 0;
  
  try {
    // set state before error
    stage = 42;
    
    // force error after setting state
    if (stage === 42) {
      nonExistentFunction(); // ReferenceError
    }
    
    // never executes
    return false;
  } catch (e) {
    // only executes if the try block ran first and set the stage
    if (stage === 42) {
      // this is the actual payload
      return input === "secret_value";
    }
    
    // if catch is accessed directly (bypassing try), this runs instead
    return false;
  }
}

foo("abc") // false
foo("secret_value") // true

Chain multiple try-catch blocks to create complex execution paths:

try {
  throw {stage: 1};
} catch (e1) {
  if (e1.stage === 1) {
    try {
      throw {stage: 2};
    } catch (e2) {
      if (e2.stage === 2) {
        // Execution only reaches here through specific error chain
        console.log("foo");
      }
    }
  }
}