See previous similar bugs for more details.
op_setglobalslot is marked as not throwing an exception in opcodes.tbl, yet looking at the verifier:
opcode.tbl:
// opCount throw stack internal name hex
ABC_OP( 0, 0, -1, 0, pushscope) // 0x30
case OP_setglobalslot:
{
// FIXME need test case
const ScopeTypeChain* scope = info->declaringScope();
if (!state->scopeDepth && !scope->size)
verifyFailed(kNoGlobalScopeError);
Traits *globalTraits = scope->size > 0 ? scope->getScopeTraitsAt(0) : state->scopeValue(0).traits;
checkStack(1,0);
checkEarlySlotBinding(globalTraits);
Traits* slotTraits = checkSlot(globalTraits, imm30-1);
emitCoerce(slotTraits, state->sp()); <-- (a)
coder->writeOp1(state, pc, opcode, imm30-1, slotTraits);
state->pop();
break;
}
At point (a) we can invoke a callback during the coercion if the particular slot of the global object has an early-bound trait of a type which can invoke a callback and we provide an object which implements such a callback (for example, the global object has a slot with an early-bound String and we provide an object atom which implements toString, as the attached PoC does.)
If that callback throws an exception then this will invoke a control-flow path not seen by the verifier which can be abused to achieve code execution.
PoC attached for avmshell.
|
setglobalslot_crash_avmshell.abc
795 bytes
Download
|