Microsoft Edge Heap Memory Exposure (MemEdge)

Web browsers quietly handle credentials, tokens, and sensitive data for every tab we open, turning the browser into a critical security boundary. When that trust line cracks, even slightly, the impact can reach far beyond a single session or machine. In this post, we examine a heap memory exposure issue in Microsoft Edge, explain how it can be abused in realistic attack chains, and outline practical detection and mitigation strategies for defenders who refuse to treat the browser as a harmless user interface shell.

This blog post focuses on the memory exposure inside the Microsoft Edge Browser.

Intro

Microsoft Edge can retain sensitive authentication material in plaintext inside the browser process heap. The exposed data can include passwords, one-time codes, session identifiers, bearer tokens, OAuth artifacts, API keys, cookies, and WebAuthn ceremony data.

The risk escalates because any process running as the same standard Windows user can read Edge’s process memory using normal operating system APIs. This does not require administrator rights, SeDebugPrivilege, a UAC prompt, or a kernel exploit.

The result is a local credential exposure primitive. Any user process, such as malware delivered by phishing, a malicious package, a rogue helper process, or a compromised browser extension using native messaging, can capture a full memory dump of the Edge browser process and recover sensitive authentication data from it.

This is not limited to a browser password manager. The exposed material can include passwords the user never saved, MFA values, post-authentication session tokens, WebAuthn metadata, bearer tokens, OAuth fields, API keys, and authentication cookies.

Microsoft Edge (Chromium, Windows) does not protect the main browser process heap against full-memory extraction by a standard, non-elevated user.

The OS grants PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_DUP_HANDLE (mask 0x0450) to the process owner by default. Edge applies no Protected Process Light (PPL), no DACL restriction, and no heap-segment encryption to msedge.exe, so any co-resident standard-user process can call kernel32!OpenProcess(0x0450) and dbghelp!MiniDumpWriteDump(MiniDumpWithFullMemory) to capture the entire browser heap with no SeDebugPrivilege, no UAC elevation, and no kernel exploit.

Microsoft Edge exposes heap memory in a way that quietly erodes one of the browser’s core guarantees: isolation between untrusted web content and sensitive process state. In the right conditions, a remote website can turn this into a reliable heap disclosure primitive, leaking pointers and internal data that were never meant to cross the trust boundary.

For an attacker, this is not “just another crash bug”. Heap exposure is the missing puzzle piece that turns otherwise fragile memory corruptions into stable exploits, helps defeat modern mitigations, and makes sandbox escapes dramatically easier to iterate on. In enterprise environments where Edge is the default browser, this shifts the cost of developing and maintaining real‑world exploits in the attacker’s favor.

For defenders, that means two things: first, environments that assume “patched and sandboxed Edge” is a hard boundary need to revisit that assumption. Second, you now have a concrete case study of how a subtle implementation detail in a mainstream browser becomes operationally useful for offensive teams and how to detect and contain it before it becomes part of someone else’s toolchain.


The two design gaps

The two design gaps sit at the heart of this weakness. First, Edge keeps highly sensitive authentication data in plaintext in the browser heap. Second, any process under the same standard Windows user can read that memory through normal APIs, turning routine local compromise into powerful credential theft.

The vulnerability arises from the combination of two distinct design gaps.

Gap A: Sensitive heap data is not zeroed before deallocation

Chromium uses PartitionAlloc as its allocator. PartitionAlloc does not zero memory when an allocation is freed. That behavior is expected for performance. The responsibility for wiping sensitive buffers lies with the subsystem that owns the data. In the affected paths, sensitive fields are exposed without being explicitly zeroed first. When the object is destroyed, the allocator updates the freelist metadata, but the previous contents remain in the slot until it is reused.

Example simplified allocator behavior:

void PartitionFreeWithSlotSize(void* ptr, size_t slot_size) {
auto* slot = SlotSpan::FromSlotStart(ptr);

slot->freelist_head = ptr;

// Sensitive bytes after allocator metadata remain untouched.
// ptr + 8 through ptr + slot_size can still contain prior data.

slot->state = kFreed;
}

For example, if a password is stored in a heap-backed string, destroying the object does not guarantee the password bytes are cleared. The pointer may be released, but the memory contents can remain readable until another allocation of the same size class reuses the slot.

basic_string::~basic_string() {
if (!_M_is_local()) {
_M_destroy(_M_allocated_capacity);
}

// The backing memory is released.
// Unless the owner explicitly wiped it first, prior plaintext can remain.
}

The core issue is not that PartitionAlloc avoids zeroing freed memory. The issue is that sensitive owners do not consistently zero their backing buffers before release.

Gap B: The Edge browser process is readable by the same user processes

On Windows, a process running under a user account can often open other processes owned by the same user with query and read permissions on memory. In this case, the Edge browser process does not apply any mitigation to block same‑user callers from obtaining read access to its memory.

HANDLE hProcess = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_DUP_HANDLE,
FALSE,
edgeBrowserPid
);

A standard user process in the same logon session can obtain a valid handle to its own Edge browser process. The test case confirmed this is possible without elevation and without SeDebugPrivilege. This stands in contrast to higher-value targets, such as LSASS when Protected Process Light is enabled, and to hardened browser child processes, where renderers are constrained by sandboxing, job objects, and integrity policies.

Why does the combination matter? 

  • Gap A alone is less severe if untrusted callers cannot read the process memory, because in that case the sensitive bytes stay confined to the browser’s own heap.

  • Gap B alone is also less severe if sensitive buffers are wiped before they are freed. Even though a same‑user process can still read the browser process memory, it will mostly encounter cleared or non‑secret data rather than live passwords, tokens, or other authentication material.

Together, they create a practical and reliable path for credential disclosure from the Edge browser.

Why the browser process is the critical target

The Edge browser process is the primary target because it aggregates authentication state across multiple browser features. It hosts and coordinates several high‑value components, making it a single, dense point of concentration for credentials, tokens, and session data that attackers are highly motivated to mine.

Relevant browser process responsibilities include:

  • Network stack
  • Cookie store
  • Password manager client
  • Autofill manager
  • Request body handling
  • MSAL related token material
  • WebAuthn orchestration
  • Session state across authenticated tabs

Renderer processes also see form values through Blink and V8, but those values are typically short‑lived. The browser process is more valuable because it receives, stores, and coordinates long‑lived authentication state. The practical target is the main msedge.exe browser process, the instance without renderer, GPU, utility, or other child‑process‑type flags.


Credential data flow

A password entered on a login page can travel through several in‑memory buffers before and after submission.

Renderer process:

1. User types into an input field
HTMLInputElement::setValue()
FormFieldData::value
Renderer heap copy

2. User submits the form
blink::FrameLoader::submitForm()
net::UploadDataStream
Raw POST body contains the submitted fields

3. Mojo IPC transfers the request to the browser process
network::ResourceRequest::request_body

Browser process:

4. UploadBytesElementReader::bytes_
Raw URL encoded POST body
Plaintext credential material can exist here

5. PasswordManagerClient observes the submission
PasswordForm::password_value
Plaintext password copy

6. AutofillManager handles submitted form data
FormStructure cache
FormFieldData::value copy

As a result, the same sensitive value can reside simultaneously in multiple independent heap locations.

A key observation is that clicking “No thanks” on the save‑password prompt does not necessarily clear all plaintext password material from memory. The password value can still persist in freed heap slots or in separate request and autofill structures that retain their own copies of the credential.

Affected classes and fields

Class

Relevant field

Data type

Exposure

PasswordForm

password_value

std::u16string

Plaintext password value can remain after object destruction if not zeroed first

FormFieldData

value

std::u16string

Password and OTP field values can be copied into form structures

UploadBytesElementReader

bytes_

std::vector<uint8_t>

Raw request body can contain submitted credentials

CollectedClientData

json, challenge

std::string

WebAuthn client data and challenge can remain after ceremony handling

AuthenticatorAssertionResponse

signature_, authenticator_data_, client_data_json_, user_handle_

vector and string backed fields

WebAuthn assertion artefacts and user handle material can remain in heap memory

PublicKeyCredentialDescriptor

id

std::vector<uint8_t>

Credential identifiers can remain in memory after allowCredentials handling

net::CookieStore

Cookie entries

Internal cookie structures

Session cookies and authentication cookies can be present in plaintext

AutofillManager

FormStructure cache

FormFieldData copies

Submitted password and OTP fields can persist in cached form data

 


Exposed data class 1: Plaintext passwords

Plaintext passwords can surface in three separate browser‑process locations during a normal login flow.

  • Request body data in UploadBytesElementReader::bytes_
  • Password manager data in PasswordForm::password_value
  • Autofill in FormFieldData::value

The exposure also covers unsaved passwords. Declining the browser’s save‑password prompt does not guarantee that the typed credential is immediately purged from heap memory, so the plaintext password may still linger in browser process allocations after the user clicks “No thanks.”

T + 0 seconds

  • User types password into a login form.

T + 1 second

  • Form is submitted.
  • Browser process receives request body.

T + 2 seconds

  • Password manager prompt appears.
  • User clicks No thanks.
  • PasswordForm object can be destroyed.

After destruction

  • Allocator metadata is updated.
  • Sensitive string backing memory may remain until the slot is reused.

Later

The same user process memory dumps can still contain the plaintext password value even after the login flow completes. Credential carving can then locate it by scanning for URL‑encoded login parameters, UTF‑16LE password strings, and additional copies of form fields. A safe way to express the matching logic is simply: look for common username fields followed by common password fields in the captured memory.

Username field examples:

loginfmt
username
email
j_username
user
login

Password field examples:

passwd
password
pwd
j_password
pass

Then URL‑decode the candidate values and deduplicate all matches before reporting them. This applies to any password entered in the current session, regardless of whether password saving is enabled or the user has declined the save prompt; the credential can still remain in heap memory after login.


Exposed data class 2: Codes and strong values

OTP and TOTP values can also be copied into autofill‑related structures during form submission, leaving time‑limited codes resident in the browser process heap beyond their intended lifetime.

Fields with autocomplete=“one-time-code” or names that resemble otp, totp, verificationCode, or mfaCode can be retained in FormFieldData::value.

void AutofillManager::OnFormSubmitted(const FormData& form, ...) {
form_structures_[form.global_id()] =
std::make_unique<FormStructure>(form);

// FormStructure owns a copy of FormData.
// FormData owns fields.
// FormFieldData::value can retain OTP digits.
}

The primary risk is not replaying a 30‑second TOTP code after it expires. The greater danger is correlation and co‑location: the same heap dump that holds an OTP value may also contain the long‑lived session token issued after successful MFA.

  • The OTP can confirm the authentication event.
  • The OTP can be correlated with the username or email in nearby form data.
  • The OTP can be co-located with session cookies and bearer tokens.

The transferable secret is usually the post‑MFA session token, not the short‑lived OTP that generated it.


Exposed data class 3: WebAuthn and passkey ceremony artifacts

The research did not find private passkey keys in browser memory. That is expected because private passkey material is stored on hardware or on a protected platform.

However, WebAuthn ceremony metadata can still appear in browser process memory. These artifacts are not equivalent to private keys, but they can disclose details about the account, the relying party, and the authentication flow.

Artefact

Class and field

Security relevance

clientDataJSON

CollectedClientData::json

Reveals challenge, origin, crossOrigin, and ceremony context

challenge

CollectedClientData::challenge

Spent nonce that proves ceremony context

authenticatorData

AuthenticatorAssertionResponse::authenticator_data_

Can include RP ID hash, sign count, and authenticator metadata

signature

AuthenticatorAssertionResponse::signature_

Spent assertion signature useful for forensic reconstruction

credentialId

PublicKeyCredentialDescriptor::id

Passkey handle that can assist targeted phishing flows

userHandle

AuthenticatorAssertionResponse::user_handle_

RP assigned user identifier that may correlate account identity

caBLEv2 markers

Cross device authentication session data

Can indicate cross-device QR-based authentication activity

Recovered WebAuthn values should be treated as sensitive metadata. They do not prevent theft of a hardware-bound private key, but they may enable targeted phishing, account correlation, and reconstruction of authentication events.

Security impact of WebAuthn artifacts:

  • clientDataJSON reveals the origin, challenge, and cross origin state.
  • challenge proves that a ceremony occurred for a relying party.
  • authenticatorData can reveal relying party hash, signature counter, and authenticator metadata.
  • signature allows forensic reconstruction of a spent assertion.
  • credentialId can identify a known passkey handle.
  • userHandle may expose or correlate an RP assigned user identifier.
  • caBLEv2 markers can indicate cross device passkey use.

Exposed data class 4: keys, tokens, and cookies

The browser heap also accumulates values from authenticated requests and client-side JavaScript. Depending on the active session, memory can contain API keys, session identifiers, OAuth fields, bearer tokens, JWTs, authentication cookies, and other application secrets.

Pattern family

Example target

Notes

Google API key

AIza style public client key format

Can appear from JavaScript bundles and request handling

Generic API keys

api_key, client_secret, access_key

Application dependent

Session identifiers

JSESSIONID, SID, sessionid, session_id

Can be directly useful while valid

JWT

Three segment base64url token

Can expose claims and bearer material

Bearer header

Authorization Bearer token

Often directly transferable

OAuth fields

access_token, refresh_token, id_token

High value if valid

Authentication cookies

ESTSAUTH, auth_token variants, Set Cookie values

Can represent authenticated browser state

Cloud and developer tokens

AWS, GitHub, Slack, Stripe, OpenAI, Anthropic, Azure

Session dependent and application dependent

Some API keys embedded in client-side JavaScript are intentionally public or restricted by origin and API scope. Others are sensitive. The browser heap does not distinguish between them. From a defender’s perspective, any long-lived secret delivered to client-side JavaScript should be treated as recoverable by local memory inspection.


Proof of concept overview

The proof of concept used two stages.

The linkt to the scripts: https://github.com/eshlomo1/CloudSec/tree/main/02-Vulnerability-Research/EdgeMEM 

Stage 1: Standard user browser memory dump

The dump stage verified the caller context before proceeding. The observed run used a standard user context, medium integrity, and no SeDebugPrivilege. The target was the main Edge browser process, not a renderer child process.

Observed privilege properties:

  • User: Standard Windows user
  • Elevation: False
  • Integrity level: Medium
  • SeDebugPrivilege: Not present
  • Target: Main msedge.exe browser process
  • Result: Full memory dump succeeded without elevation

The dump used normal Windows process and dump APIs. The important security point is not the tooling. The important point is that the same user process memory access was sufficient. The memory dump captured private heap regions, including PartitionAlloc slabs, stacks, IPC buffers, request body buffers, form data, and authentication browser memory.

Stage 2: Streaming extraction

The extraction stage streamed the dump in chunks, preserved overlap between chunks to avoid clipping long secrets, applied compiled patterns, and deduplicated results.

Extractor design:

  • Input: Full Edge browser process dump
  • Read strategy: Fixed-size chunks
  • Boundary handling: Tail overlap between chunks
  • Matching: Compiled secret patterns
  • Deduplication: Hash set
  • Output: Timestamped evidence report with dump hash

The pattern groups included credential pairs, password-only strings, authentication headers, JWTs, OAuth fields, API keys, session IDs, CSRF tokens, authentication cookies, WebAuthn artifacts, FIDO-related strings, and email addresses.

Pattern categories used in analysis

  • Credential categories:
  • User and password pairs
  • Password only values
  • Authentication header categories:
  • Bearer authorization values
  • Basic authorization values
  • Token categories:
  • JWT
  • OAuth fields
  • Google OAuth related material
  • API key categories:
  • AWS access keys
  • GitHub tokens
  • Slack tokens
  • Google API keys
  • Stripe keys
  • OpenAI keys
  • Anthropic keys
  • Azure keys
  • Generic API keys
  • Session categories:
  • Session identifiers
  • CSRF values
  • Authentication cookies
  • WebAuthn and FIDO categories:
  • clientDataJSON
  • Base64url client data
  • Relying party identifiers
  • Relying party names
  • User blocks
  • User handles
  • allowCredentials entries
  • Credential identifiers
  • Authenticator attachment
  • User verification
  • Resident key
  • Attestation preference
  • Transports
  • PRF
  • largeBlob
  • credProps
  • credBlob
  • Minimum PIN length
  • Challenges
  • Attestation objects
  • Authenticator data
  • Assertion signatures
  • caBLE markers
  • FIDO URI markers
  • Miscellaneous categories:
  • Email addresses
  • Identity markers
  • Nearby account context

Combined attack chain

An attacker does not need administrator privileges, so user execution alone is sufficient.

1. Attacker code executes as the same Windows user.

2. The attacker identifies the main Edge browser process.

3. The attacker opens the process with query and memory read rights.

4. The attacker creates a full memory dump.

5. Attacker scans the dump for credential and token patterns.

6. The attacker recovers sensitive authentication data from the browser heap.

The chain does not require:

  • Administrator rights
  • UAC approval
  • SeDebugPrivilege
  • Kernel exploitation
  • Cross-user access
  • Browser sandbox escape
  • Credential manager database decryption
  • Saved browser password access

The attack is local and involves the same user, but the exposed data can include cloud and web session material that remains useful outside the local endpoint while it is valid.

Security impact

The primary impact is confidentiality.

The exposed data may include:

  • Plaintext passwords typed during the session
  • Unsaved passwords
  • MFA codes and authentication context
  • Session cookies
  • Bearer tokens
  • OAuth access tokens
  • OAuth refresh tokens
  • ID tokens
  • JWTs
  • API keys
  • WebAuthn client data
  • WebAuthn challenges
  • WebAuthn assertion signatures
  • Credential identifiers
  • User handles
  • Authentication cookies
  • The issue does not demonstrate:
  • Remote code execution
  • Local privilege escalation
  • Kernel compromise

The severity stems from local, no-elevation access to sensitive authentication data that users and organizations reasonably expect to be isolated from arbitrary processes on the same user.

Why do unsaved passwords matter? Browsers often distinguish between saved passwords and passwords that the user declines to save. Users may assume that clicking “No thanks” means the browser will not retain that password. This finding shows that the distinction is not sufficient at the memory level. Even when a password is not stored in the password manager database, transient plaintext copies can remain in heap memory after form submission and object destruction. That makes the issue relevant to enterprise users who disable password saving. Disabling password saving does not eliminate transient exposure of plaintext in browser memory.


Threat model

This bug assumes a remote, untrusted website running in a standard Microsoft Edge renderer process. No special permissions, extensions, or local foothold are required: the attacker only needs to convince a user to load attacker‑controlled content in Edge (phishing link, malvertising, watering hole, or embedded browser control in an enterprise app).

From that position, the attacker can:

  • Interact with Edge’s heap from within the renderer sandbox and groom allocations in a predictable way.
  • Abuse the vulnerable code path to expose heap memory that should remain internal to the process.
  • Turn the resulting leaks into reliable knowledge about object layouts, pointers, and allocator state.

Critically, this is not a full exploit on its own; instead, it acts as an enabler for other memory‑safety bugs in the same process. By giving an attacker a repeatable way to read heap state, it lowers the bar for building stable RCE and sandbox‑escape chains against Edge in real‑world browsing scenarios.


Exploitability

From an attacker’s point of view, this bug is a high‑value enabler rather than a one‑shot kill chain. It does not, by itself, provide direct code execution or a sandbox escape, but it quietly removes some of the hardest constraints modern mitigations try to enforce.

Pre‑conditions

For exploitation to be realistic, the following conditions must hold:

  • The victim is browsing with Microsoft Edge on a vulnerable build.
  • The attacker can run arbitrary JavaScript/HTML in a standard renderer process (for example, by luring the user to an attacker‑controlled site, injecting into a legitimate site, or abusing embedded Edge controls inside an application).
  • The vulnerable code path can be triggered in a controlled, repeatable way from that context, including the ability to groom heap allocations around the target region.

No local privileges, special flags, or extensions are required beyond that.

What the attacker actually gains

When those conditions are met, this bug exposes internal heap memory that should remain opaque to untrusted content. In practice, that means an attacker can:

  • Recover heap content that belongs to other objects in the same process.
  • Derive allocator state and object layouts that are normally hidden behind ASLR and heap hardening.
  • Turn “blind” memory corruptions into feedback‑driven primitives, dramatically improving the success rate of existing or future bugs.

The net effect is a reliable heap‑disclosure building block that can be used to:

  • Stabilize use‑after‑free or out‑of‑bounds write vulnerabilities in the same renderer.
  • Help bypass exploit mitigations that rely on memory layout uncertainty.
  • Shorten the development cycle for fully weaponized chains that target Edge in the wild.

What this bug does not do on its own

It is important to be explicit about the limits:

  • It does not directly give remote code execution from a single trigger.
  • It does not, by itself, cross the sandbox boundary into the OS or other processes.
  • It does not bypass user interaction requirements such as initial navigation or opening a crafted link.

However, once combined with any reasonably exploitable memory‑safety flaw in the same process, the heap exposure described here meaningfully lowers the bar for turning that flaw into a stable exploit.


Conclusion

Browsers are now identity infrastructure. They handle passwords, session cookies, OAuth flows, enterprise SSO, MFA, passkeys, API calls, and authentication state across modern applications. This research shows that sensitive authentication data can remain in the Microsoft Edge browser process heap and that a standard same user process can obtain a full memory dump without elevation.

The issue is the combination of memory hygiene and process access boundaries. Sensitive buffers should be zeroed before release, and the browser process should receive stronger protection against arbitrary same user memory reads. As authentication continues to move into the browser, browser memory protection must be treated as part of identity security.

Discover more from CYBERDOM

Subscribe now to keep reading and get access to the full archive.

Continue reading