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");
}
}
}
}