❌

Reading view

There are new articles available, click to refresh the page.

FORGE Lab uses agentic tools to identify memory safety issues in Rust

Microsoft’s FORGE Lab found several memory safety issues in Rust’s standard library (std and core), including cases where code written entirely in safe Rust can trigger undefined behavior. The research used Hyenas, FORGE’s frontier research version of codename MDASH, an AI-powered vulnerability-scanning system. The findings reveal three recurring patterns of misplaced trust in unsafe code: trusting code not to panic, trusting user-provided implementations to be correct, and trusting the underlying system.

Rust’s safety guarantees are precisely what make these findings interesting. Its rich type system and ownership model rule out whole classes of memory-safety issues, but much of the standard library relies on unsafe code underneath its safe APIs. When that code breaks its safety contract, even safe Rust can encounter behavior the language is designed to prevent.

Rust’s memory-safety guarantees are increasingly important as the language becomes part of software that billions of people rely on every day. Billions of people are estimated to interact with Rust-based software or operating systems daily, often without knowing it. Microsoft made Rust a Tier-1 internal language alongside C++, C#, and TypeScript, GitHub rewrote the Copilot agent runtime in Rust, and Bun, Android, the Linux kernel, and Cloudflare are moving code to it as well. Much of this software relies on Rust’s foundational libraries, core and std, and the safety guarantees of the APIs they provide. Bugs in those libraries can have consequences well beyond the code where they originate.

Unsafe Rust has been a long-running interest for us. Our team includes the people behind Rudra, a static analyzer that looked for memory-safety bugs across the Rust ecosystem in 2021. We also write many of our own tools in Rust, so its safety is more than a research topic for us. This year, we took a closer look at the standard library to see what kinds of mistakes still show up in its unsafe code today.

In this post, we share some of the bugs we reported to the Rust issue tracker. We use them to discuss what Rust does and doesn’t guarantee, why writing correct unsafe Rust is so difficult, and why Rust’s safety model still matters even though its memory safety isn’t perfect.

How we found and validated the bugs

At Microsoft FORGE Lab, we study vulnerabilities in complex software, including the Rust standard library. Our team combines years of experience in vulnerability research with AI-assisted tools that help us investigate code and find bugs that are difficult to uncover through traditional analysis alone.

Some of the bugs in this post were found with help from Hyenas, our research version of codename MDASH. Our researchers investigated the underlying code, reproduced the bugs, assessed their potential security impact, and worked with Rust maintainers to report and fix them.

This post focuses on soundness bugs in the standard library’s unsafe code. Bugs in the compiler itself, such as miscompilations and holes in the type system, are a different topic that we don’t cover here.

A note on open issues

At publication, two of the issues reported by our team remain open. The article also references related issues reported by others and fixes that are still under review. The Rust maintainers triaged and responded to our reports quickly, and we believe maintainers and the contributors working on these issues are doing their best and making the right tradeoffs among the many bugs they handle.

If you’re interested in an open issue, please comment only when you have something genuinely new to contribute, such as new information or an affected real-world use case.

Patterns of Rust’s Safety Bugs

At the lowest level, Rust uses unsafe blocks to work with raw pointers and interact directly with the operating system. Much of the standard library, including Vec and String, is built from small unsafe pieces wrapped in safe APIs. Inside an unsafe block, the programmer takes responsibility for the guarantees the compiler can’t check, and that responsibility extends to the safe API built around it. That API must be sound: no matter how safe code uses it, it must be impossible to trigger undefined behavior.

Upholding that promise is harder than it looks. Consider a function that returns the byte at idx from any type that can be viewed as a byte slice, skipping the redundant bounds check after doing its own:

pub fn get>(s: &S, idx: usize) -> Option {
    if idx 

The hard part of unsafe Rust is knowing what you can trust. The function above looks correct, but it assumes that two calls to as_ref return the same slice. Because S is chosen by the caller, a safe AsRef implementation can return a long slice the first time and an empty one the second, and get reads out of bounds. Unsafe code must be careful with every call it makes, and the bugs we found fall into three patterns of misplaced trust: trusting code not to panic, trusting user-provided implementations to be correct, and trusting the underlying system.

Pattern 1: Trusting code not to panic

Unsafe code often breaks an invariant for a moment and restores it afterward; for example, it may allocate an uninitialized buffer and fill it directly, avoiding redundant writes to zero the memory first. That’s fine as long as the function runs to the end. But many operations in Rust can panic, from out-of-bounds indexing to calls into user-provided code, and by default a panic unwinds: it leaves the function early while still running destructors. Unwinding inside that window becomes an invisible exit that exposes the broken invariant to code that trusts it. Bugs of this kind are called panic safety bugs.

Figure 1 — Panic Safety.

MapWindows::clone (issue #156501)

We identified a panic safety issue in MapWindows::clone. The nightly API Iterator::map_windows yields overlapping windows of N items. It keeps the current window in a Buffer whose destructor drops the N items starting at start. In Rust 1.97.0, library/core/src/iter/adapters/map_windows.rs defines it as:

struct Buffer {
    // Invariant: `self.buffer[self.start..self.start + N]` is initialized,
    // with all other elements being uninitialized. This also
    // implies that `self.start ; N]; 2],
    start: usize,
}

Buffer::clone, in the same file, built the new buffer first and filled it afterward:

impl Clone for Buffer {
    fn clone(&self) -> Self {
        let mut buffer = Buffer {
            buffer: [[const { MaybeUninit::uninit() }; N], [const { MaybeUninit::uninit() }; N]],
            start: self.start,
        };
        buffer.as_uninit_array_mut().write(self.as_array_ref().clone());
        buffer
    }
}

After buffer is constructed, it breaks its own invariant: its slots are uninitialized, but its destructor is already armed. The self.as_array_ref().clone() call runs the user's T::clone before the slots are written. If that call panics, unwinding drops buffer and runs drop_in_place on N uninitialized values. Our proof of concept uses only safe code: an element type whose clone always panics, which causes a crash when the destructor accesses uninitialized memory. The fix holds the new buffer in ManuallyDrop until it is fully written, so unwinding leaks the half-built buffer instead of dropping it.

The general lesson is to treat every call to code you don’t control, such as clone, drop, comparisons, closures, and iterator methods, as a possible early exit. Either finish the work that may panic before breaking an invariant, or make the broken state safe to unwind through: hold it in ManuallyDrop, or use a guard whose destructor restores the invariant, like Vec’s internal SetLenOnDrop. The Rustonomicon covers this under exception safety.

Allocators must not unwind

The recently stabilized Allocator API shows how hard panic safety is to get right. With std, an allocation failure aborts the process by default instead of unwinding, so much of the standard library's unsafe code assumes allocation never unwinds. Custom allocators can break that assumption. While the API was being stabilized, we reported one place in BTreeMap::insert that becomes unsound when an allocator unwinds, and others reported more (#159334, #159368, #159370). Fixing them one by one would mean auditing everything that allocates, so the stabilization, shipping in Rust 1.100 on November 12, instead requires implementors never to unwind from the trait's methods or from drop. The proposal notes that "unwinding out of many allocation-related methods was found to be a pervasive source of unsoundness."

Pattern 2: Trusting user-provided implementations to be correct

Unsafe code often calls into code its caller provides: a comparator, a trait implementation, a closure. That code is safe, but safe doesn’t mean correct, and it can return inconsistent or plainly wrong answers. The question is who is responsible when it does. In C++, it’s the caller. std::sort requires its comparator to be a strict weak ordering, and breaking that rule is undefined behavior. With libstdc++, sorting equal elements with <= reads past the end of the vector:

std::vector v(100, 42);
// Bug: `

The same comparator passed to Rust's sort_by is only a logic error. The sort may panic or leave the slice in an unspecified order, but it never causes undefined behavior. The responsibility sits with the unsafe code. The comparator is safe code supplied by the caller, so the unsafe code inside the sort must stay sound no matter what it returns. The same goes for every safe trait a user can implement: comparisons can be inconsistent, an ExactSizeIterator can report a length that doesn't match the items it yields, and Borrow can return a different value on each call.

str::replace with a custom Pattern (#156491)

Another of our bug reports shows how easy this trust is to miss: str::replace with a custom Pattern can create a non-UTF-8 String. String search APIs accept any type that implements the nightly Pattern trait, and a pattern can describe itself through as_utf8_pattern. str::replace uses that description to take a fast path when replacing one ASCII byte with another. In Rust 1.96.0, the fast path in library/alloc/src/str.rs looked like this:

pub fn replace(&self, from: P, to: &str) -> String {
    // Fast path for replacing a single ASCII character with another.
    if let Some(from_byte) = match from.as_utf8_pattern() {
        Some(Utf8Pattern::StringPattern([from_byte])) => Some(*from_byte),
        Some(Utf8Pattern::CharPattern(c)) => c.as_ascii().map(|ascii_char| ascii_char.to_u8()),
        _ => None,
    } {
        if let [to_byte] = to.as_bytes() {
            return unsafe { replace_ascii(self.as_bytes(), from_byte, *to_byte) };
        }
    }

replace_ascii, in the same file, then rewrites the bytes:

unsafe fn replace_ascii(utf8_bytes: &[u8], from: u8, to: u8) -> String {
    let result: Vec = utf8_bytes.iter().map(|b| if *b == from { to } else { *b }).collect();
    // SAFETY: We replaced ascii with ascii on valid utf8 strings.
    unsafe { String::from_utf8_unchecked(result) }
}

The CharPattern branch checks that the character is ASCII, but the StringPattern branch assumes a one-byte string pattern must be ASCII. That holds for &str patterns, but StringPattern stored &[u8], and as_utf8_pattern is a safe method any Pattern can implement. In our proof of concept, a custom pattern reports the single byte 0x80, which only appears inside multibyte UTF-8 sequences:

impl Pattern for NonAsciiBytePattern {
    // ...
    fn as_utf8_pattern(&self) -> Option> {
        static NEEDLE: [u8; 1] = [0x80];
        Some(Utf8Pattern::StringPattern(&NEEDLE))
    }
}

// "\u{d000}" is [ed, 80, 80]; the result is [ed, 3f, 3f], which is not UTF-8.
let corrupted = "\u{d000}".replace(NonAsciiBytePattern, "?");

str::replace rewrites the continuation bytes and returns a String that isn't valid UTF-8, and later string operations that rely on UTF-8 reach undefined behavior. The fix changed StringPattern to hold &str instead of &[u8], so the type itself now guarantees UTF-8.

When unsafe code depends on a property of a user-provided implementation, don’t take it on faith. Call the implementation once and keep the result, validate what it returns, or encode the property in a type that safe code can’t forge, as this fix did. If none of those work, make the trait unsafe so implementors explicitly promise to uphold it.

More examples

join shows how easy it is to fix this kind of bug only partway. In 2020, we reported that it trusted Borrow::borrow to return the same string on each call (CVE-2020-36323), and the fix handled strings that shrink between calls. In April 2026, the same function needed another fix: join borrows the first element twice, and if it grows on the second call, the remaining-length calculation underflows into a heap overflow. The earlier fix had never covered that path.

Others reported similar problems in the Pattern API this year. A safe Pattern can hand back a valid searcher built for a different string, and consumers such as strip_prefix_of apply its ranges to the original string unchecked, reading out of bounds (#158265). The same problem turned up in split_once (#165076) after a report that the Searcher contract doesn't spell out what str::split relies on (#164904). Both bugs are still open.

Pattern 3: Trusting the underlying system

The first two patterns stay inside Rust code. The third sits at the boundary with the operating system, where unsafe code trusts process-wide state such as signal stacks, memory mappings, and the environment, which other threads, C libraries, or the kernel can touch at any time. These bugs have been hard to find automatically, because a static analyzer would need to model all of that surrounding behavior. AI agents change the picture: they understand enough about the environment the code runs in to make this area practical to search. We started exploring it and found some interesting bugs.

process::exit and the main thread’s signal stack (#161018)

When a thread overflows its stack, Rust prints a clear message and aborts instead of crashing with a bare segmentation fault:

thread 'main' (3307186) has overflowed its stack
fatal runtime error: stack overflow, aborting

This relies on a few pieces of process-wide state. Each thread’s stack ends in a guard page, an inaccessible region just past the stack’s limit: pthread reserves one for every spawned thread, and on Linux the kernel’s own stack guard plays that role for the main thread. Touching it raises SIGSEGV, and Rust registers a handler for that signal. The handler can’t run on the stack that just overflowed, so Rust also gives each thread an alternate signal stack, a small separate mapping with a guard page of its own, which the kernel switches to when it delivers the signal. The handler checks that the faulting address is inside the guard page, prints the message, and aborts.

Figure 2 — Stack Overflow.

We found that calling process::exit from a child thread tears down the main thread’s alternate stack while the main thread is still running. process::exit runs the runtime’s cleanup before calling libc’s exit, and in Rust 1.98.0 that cleanup ends in library/std/src/sys/pal/unix/stack_overflow.rs:

pub unsafe fn cleanup() {
    if cfg!(panic = "immediate-abort") {
        return;
    }
    // FIXME: I probably cause more bugs than I'm worth!
    // see https://github.com/rust-lang/rust/issues/111272
    unsafe { drop_handler(MAIN_ALTSTACK.load(Ordering::Relaxed)) };
}

The cleanup function passes MAIN_ALTSTACK, the main thread’s alternate stack, to drop_handler:

// SAFETY: we warned the caller this disables the alternate signal stack!
unsafe { sigaltstack(&disabling_stack, ptr::null_mut()) };
// SAFETY: We know from `get_stackp` that the alternate stack we installed is part of
// a mapping that started one page earlier, so walk back a page and unmap from there.
unsafe { munmap(data.sub(page_size), sigstack_size + page_size) };

The call to sigaltstack disables the alternate stack of the calling thread, but the subsequent call to munmap unmaps the main thread’s. The code assumes it runs on the main thread, which isn’t true when a child thread calls process::exit. The main thread keeps running until libc’s exit finishes, with its signal stack registration still pointing at unmapped memory. If it overflows its stack in that window, the kernel can’t deliver the signal, and our proof of concept dies with a plain segmentation fault instead of Rust’s message. The bug dates back to 2015, and the fix stops cleaning up the main thread’s alternate stack altogether.

We also checked whether this could become a security issue. In theory, it could lead to code execution: if a writable mapping landed at the old alternate stack’s address, the kernel would deliver the main thread’s next signal onto it, and another thread could overwrite the handler’s frame, such as its return address, while it runs. In practice, the conditions are very tight. Everything has to happen in the brief window before libc’s exit finishes, the new mapping must have exactly the right size to reuse that address, and overwriting the frame takes a second race inside a short handler. For issue #161018, our testing found no reliable exploitation path under the conditions examined, and we assessed practical exploitation as unlikely. This assessment does not automatically apply to the other issues discussed. The issue has more details in its security impact analysis.

CommandExt::exec and the global environment (#156951)

Our other report involves the process environment. On Unix, CommandExt::exec replaces the current process with a new program. When the command has its own environment variables, exec points the process-global environ at a new array, calls execvp, and restores environ if execvp fails. In Rust 1.99.0, Command::exec takes the environment lock in library/std/src/sys/process/unix/unix.rs:

let _lock = sys::env::env_read_lock();

let Err(e) = self.do_exec(theirs, envp.as_ref());

Command::do_exec, in the same file, then swaps the environment:

    _reset = Some(Reset(*sys::env::environ()));
    *sys::env::environ() = envp.as_ptr();
}

libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr());
Err(io::Error::last_os_error())

Command::exec takes only the read side of the environment lock, yet do_exec writes the process-global environ. Before the write, do_exec saves the old value so it can be restored if execvp fails. Writing environ under a read lock lets other threads in. A thread calling env::var takes the same read lock and can see variables meant only for the new program, and two failed exec calls racing each other can leave environ pointing at a freed array that a later env::vars_os() reads. Both proofs of concept use only safe Rust. A 2018 fix held an exclusive lock here, but when the environment lock became a read-write lock in 2021, exec kept the read side. A fix that avoids touching environ by calling execve directly is in review.

The environment has caused trouble before. After a discussion that began in 2015, the 2024 edition marked env::set_var as unsafe, because C functions on most Unix platforms read the environment without going through Rust's lock.

process::exit and thread-local destructors (#164243)

Getting this boundary right is hard, and more problems are still open. While exploring this area, we independently ran into a double free in safe code that process::exit can cause through thread-local destructors. It was reported by someone else (#164243), but it’s worth mentioning here. libc’s exit first runs the calling thread’s thread-local destructors, as C++ requires, and Rust registers its destructors the same way. If a destructor calls process::exit while a thread local is being replaced, libc drops that thread local a second time. A proposed fix switches process::exit to POSIX _exit, which ends the process without running these handlers.

Why Rust still matters and advancing vulnerability discovery with AI

Every unsafe block rests on assumptions about the code around it. The bugs in this post came from three kinds of misplaced trust:

  • that code won't panic at the wrong moment,
  • that a caller-provided implementation behaves correctly, and
  • that the process-wide state Rust shares with the operating system and the C library stays the way the code expects.

Familiar patterns are still difficult (Patterns 1 and 2)

The first two patterns are well studied. Rudra looked for both in 2021, and the standard library was already fixing panic safety bugs in 2015 (CVE-2015-20001). They remain challenging to avoid because any call inside unsafe code might unwind or run code the caller supplied.

What we saw this year was mixed. On the positive side, the nightly process is working. Many reports were caught while the affected APIs were still unstable, and several were fixed by changing the design before stabilization. StringPattern now holds &str instead of &[u8], and Allocator was stabilized with a rule that implementors must not unwind. At the same time, bugs in these patterns still reach stable releases, and their fixes aren't always complete. join was fixed in 2020 and needed another fix in 2026 for a path the first fix had missed.

System boundary bugs are now within reach (Pattern 3)

The third pattern is different. Its bugs live where Rust meets the layers below it: the kernel, through system calls such as sigaltstack and munmap, and the C library, through process-wide state such as environ and the cleanup that libc's exit runs. Getting them right takes knowledge that lives outside Rust's type system, which has made these bugs difficult to find with traditional static analysis. That may also be why they last so long. The process::exit signal stack bug was in the standard library for nearly 11 years, and exec has written environ under a read lock since 2021. Unlike our reports in the first two patterns, both affect stable Rust.

Language models already know a great deal about how these interfaces behave, so agentic scanners can check unsafe code against the system it runs on, not just the language. We think this boundary is one of the most promising places for agentic security research, in Rust and beyond.

Rust's high bar is hard to meet but worth keeping

Rust holds its safe APIs to a stricter standard than C and C++. Safe code must never be able to cause undefined behavior, no matter how it uses an API. In C++, a comparator that breaks std::sort's ordering requirement causes undefined behavior, while Rust's sort must stay memory-safe with any Ord implementation. POSIX doesn't guarantee that setenv is safe to call while another thread reads the environment. That makes memory safety bugs in Rust hard to compare one-to-one with bugs in other languages.

For the same reason, a soundness bug in the standard library isn't automatically a vulnerability in the programs that use it. Each bug in this post needs an unusual setup to trigger, such as a panic at exactly the wrong moment, a deliberately misbehaving implementation, or calling process::exit from a child thread. It becomes a serious vulnerability only if a real program reaches the API in that way.

Rust's safety standard is strict and difficult to meet, but it's also useful. It gives an unambiguous answer to what counts as a soundness bug, so problems can be fixed and prevented once in the library, instead of in every program that uses it. Rust's safety model strikes the right balance between control, safety, and performance for systems programming, which is why so many projects choose it.

Beyond the lab

At Microsoft FORGE Lab, we're exploring how technology such as codename MDASH can help researchers find bugs like the ones in this post at greater scale. Rust's standard library is one of several foundational open source projects where we're applying this approach.

This research also extends beyond the lab. It helps inform the development of codename MDASH, which brings agentic vulnerability discovery into Microsoft Defender. These findings can also serve as inputs to Microsoft Perception, our new multi-agent security system, where specialized agents work together across broader security workflows. For example, the Web App Pen Test Agent can use codename MDASH findings to help prioritize testing, validate exploitability against live applications, and produce actionable vulnerabilities that downstream agents can use to prioritize and support remediation. This connects AI-powered code analysis with real-world security validation and response, helping security teams move from identifying potential vulnerabilities to understanding and addressing actual risk.

Our goal isn’t to simply find more bugs, it's to identify meaningful issues, turn those findings into actionable research, and improve the safety of foundational software that developers and organizations depend on.

The post FORGE Lab uses agentic tools to identify memory safety issues in Rust appeared first on Microsoft Security Blog.

What exploited vulnerabilities have in common: A data-driven analysis of real-world attacks

The vulnerability landscape continues to expand. From 1999 through September 2026, approximately 383,000 software vulnerabilities had been disclosed across platforms and technologies, yet only about 2,000 CVEs (roughly 0.5%) have been linked to real-world exploitation.

Those vulnerabilities follow clear patterns. Threat actors disproportionately target flaws that enable system takeover, particularly remote code execution, privilege escalation, and authentication bypass. AI-assisted vulnerability discovery and exploit development could further shorten the window defenders have to respond.

Patching remains essential, but remediation can take weeks or months on critical systems, and some legacy or unsupported systems cannot be patched. Defenders therefore need to concentrate protection where it can have the greatest impact, considering both the likelihood of exploitation and the potential blast radius of a successful attack.

Classifying the vulnerability landscape

To make this large vulnerability population actionable, we analyzed each CVE across two dimensions: impact class and affected platform.

Impact class captures the outcome a threat actor could achieve by exploiting a vulnerability. Each CVE is assigned to the most consequential impact it directly enables. For example, when a vulnerability can bypass authentication and directly enable remote code execution (RCE), we classify it as RCE. This approach helps distinguish the underlying type of vulnerability from the security impact an attacker could achieve.

  • Server-side RCE: Remote code execution on a network-exposed service without user interaction.
  • Client-side RCE: Remote code execution that requires a user to open or run malicious content.
  • Local code execution: Code or command execution requiring local access or an existing foothold.
  • Local privilege escalation: Elevation from lower-level access to SYSTEM or root privileges.
  • Authentication or authorization bypass: Circumvention of authentication or access controls.
  • Security-control bypass: Circumvention of protections such as sandboxing, secure boot, or SmartScreen.
  • Information disclosure: Exposure of sensitive data or secrets.
  • Tampering or integrity: Unauthorized modification of data or system state.
  • Denial of service: Disruption of system or service availability.
  • Cross-site scripting (XSS): Script execution within a victim’s browser context.
  • Arbitrary file access: Unauthorized access to or placement of files.
  • Spoofing or redirect: Spoofing, redirection, or other forms of UI deception.

We also classified vulnerabilities by the platforms they affect, including Windows, Linux/Unix, mobile, network appliances, IoT/OT, virtualization hosts, and developer packages. A CVE can span multiple platforms. Developer packages are treated as a separate category because they are often cross-platform and represent a significant share of the overall vulnerability population.

Most CVEs in the analysis affect application code, including servers, web applications, browsers, productivity software, libraries, and firmware tooling, while a smaller share affects OS and system code such as kernels, drivers, and firmware.

This analysis covers CVEs published through September 2026 using Microsoft’s catalog of tracked vulnerabilities. Each CVE is counted once in the impact analysis and across each relevant platform in the platform analysis.

Infographic of 382,789 published CVEs: 77% affect applications and 23% operating systems; leading impacts include server-side remote code execution, denial of service, and authentication bypass. Linux/Unix and Windows are the most affected platforms.
Figure 1 – The full published-CVE population as of September 2026, broken down by impact class and affected platform, and an OS vs. application split. Breadth dominates – many impact classes spread across many platforms, with about 77% of CVEs in application code and 23% in OS/system code.

Vulnerabilities exploited in the wild

The subset of vulnerabilities exploited in the wild is much smaller than the overall CVE population, and looks very different from it. Fewer than 1 in 200 published CVEs are ever used in a real attack, and those that do get used are overwhelmingly concentrated in the takeover classes that maximize threat actor impact and exposure.

The difference becomes clear when we look at impact. Exploitation is heavily concentrated in vulnerabilities that give attackers greater control over a system. Server-side RCE rises from 20% of published CVEs to 37% of exploited CVEs, local privilege escalation from 11% to 18%, and client-side RCE from 4% to 13%. By comparison, XSS falls from 15% to 4% and denial of service from 16% to 7%. OS vulnerabilities show a similar shift, increasing from 23% of the overall CVE population to 41% of exploited vulnerabilities.

These patterns are consistent with threat actors favoring vulnerabilities that can provide meaningful access and control. Remote code execution and privilege escalation can create footholds that support subsequent stages of an attack, while vulnerabilities that primarily cause a crash or limited script execution may offer less utility for broader compromise.

Importantly, a vulnerability’s stated classification does not always reflect what an attacker can reliably achieve in practice. For example, some vulnerabilities described as enabling RCE may ultimately result only in denial of service under real-world conditions. Understanding the demonstrated impact and exploitability of a vulnerability is therefore critical to effective prioritization.

Infographic of 2,056 exploited-in-the-wild CVEs: server-side remote code execution is the leading impact, Windows the most affected platform, and vulnerabilities split 59% application to 41% operating system.
Figure 2 – The 2,056 exploited-in-the-wild CVEs distribution, compared against the full published population: ghost lines mark each class and platform’s share of all ~380K published CVEs, and badges show the shift in percentage points. Takeover classes and OS code rise, while high-volume XSS and DoS fall.

Where defenders should focus

Patching is slow, while exploitation is fast

Patching is slow, and far slower on critical systems than others. Across platforms, servers are the critical assets and the slowest to patch. Microsoft Security Exposure Management data from 2026 shows that the median vulnerability is patched in about two weeks on Windows workstations but in more than a month on Windows Servers. It stretches to 10+ weeks on Linux Servers and for applications running on MacOS, and beyond a year on network appliances. That gap is the threat actor’s window, widest on the critical systems that stay exposed the longest.

Exploitation runs on the opposite clock: the most impactful vulnerabilities are weaponized in days, increasingly on or before disclosure. According to VulnCheck, 29% of the exploits in 2025 were launched within 1 day of CVE publication. According to CISA, the mean time from CVE publication to CISA KEV catalog fell from 89 days for CVEs that were published in 2024 to 57 days for CVEs that were published in 2025. 2026 CVE publications up to September already have that number dropping to 20 days, with a median of only 3 days since publication. Industry research suggests that advances in AI are a significant factor in the time reduction required to develop exploits, further increasing pressure on defenders to respond quickly.

Impact, exposure and where to focus

Two things determine a vulnerability’s value to a threat actor: impact and exposure. Impact is what successful exploitation provides, such as remote code execution or privilege escalation. Exposure is an asset’s position in the environment and the opportunities it creates for further compromise. An asset’s exposure is shaped by the identities, privileges, and connections available from it.

Servers often use service accounts, application identities, or cloud-managed identities with permissions beyond their operational needs. Compromising such a system can provide access to additional resources, sensitive data, administrative privileges, or trusted network paths. Together, impact and exposure determine the blast radius: a vulnerability that enables full control of a highly exposed system can enable widespread on-premises or cloud compromise.

Threat actors do not exploit at random: they go after the vulnerabilities that give the highest impact and exposure: full control of a system and the privileged identities and critical assets it has access to. As defenders, focusing on these vulnerabilities first can deliver the highest return on remediation investment.

Takeover vulnerabilities like remote code execution, authentication bypass and local privilege escalation lead here. They drive the broadest exposure and the largest blast radius, account for 78% of real-world exploited vulnerabilities, and stopping their exploitation interrupts attacks at the earliest stage, before compromise can propagate across the environment.

Next in line

A second slice deserves attention where context makes it severe: denial of service against availability-critical assets is recoverable in general, but can be mission-ending in parts of your environment where uptime is everything; disclosure of credentials or secrets can be minor as an insulated incident, but it feeds the same privileged-identity path mentioned above.

Stacked bar chart of 1,239 exploited remote-takeover CVEs by platform and code layer. Server-side RCE is the largest class, led by Windows and Linux/Unix; bars distinguish application from OS/system vulnerabilities.
Figure 3 – The three remote-takeover classes analyzed across 1,239 exploited CVEs), each drilled down by affected platform and split into OS vs Application code. Bar length is the CVE count per platform; the dark segment is OS/system, the light segment is application.

How Microsoft Security helps defenders act

Threat actors do not begin with a vulnerability list. They begin by looking for a reachable way in. Microsoft Defender External Attack Surface Management approaches the environment from that same outside-in perspective, continuously discovering internet-facing infrastructure, including previously unknown or unmonitored assets, shadow IT, and third-party dependencies. This gives defenders an opportunity to remove unnecessary exposure before a reachable vulnerability becomes an entry point.

Once the reachable assets are known, the next question is which weaknesses deserve immediate attention. Defenders need a way to prioritize exposure beyond CVSS alone. Microsoft Defender Vulnerability Management addresses this challenge through Exposure Score and prioritized security recommendations.

Prioritization incorporates threat and exploitability signals, breach likelihood, internet exposure, and asset criticality, helping teams focus remediation on vulnerabilities that create the greatest actual exposure.

An initial foothold is only part of the risk. Attack Path Analysis connects assets and threat actor techniques into end-to-end routes from an entry point to a critical asset. Microsoft Security Exposure Management Attack Path Analysis provides a cross-workload view spanning endpoints, cloud, and hybrid environments, using vulnerability context from MDVM, endpoint data from Defender for Endpoint, and cloud asset data from Defender for Cloud. Within cloud environments, Defender CSPM Attack Path Analysis adds detailed context about internet exposure, vulnerabilities, network connections, identities, and permissions across Azure, AWS, and Google Cloud.

Together, these experiences show how an exposed workload could become a path through a privileged identity to sensitive data or another critical resource, helping defenders identify where to break the path before the initial compromise expands.

Patching remains essential, but it is not the only defensive action available. Microsoft Security helps defenders discover the doors threat actors can see, prioritize the weaknesses they are most likely to exploit, understand where a compromise could lead, and identify opportunities to break attack paths before compromise spreads to critical assets.

References

Sources cited or linked in the submitted draft:

Learn More

For the latest security research from the Microsoft Threat Intelligence community, check out the Microsoft Threat Intelligence Blog.

To get notified about new publications and to join discussions on social media, follow us on LinkedIn, X (formerly Twitter), and Bluesky.

To hear stories and insights from the Microsoft Threat Intelligence community about the ever-evolving threat landscape, listen to the Microsoft Threat Intelligence podcast.

Review our documentation to learn more about our real-time protection capabilities and see how to enable them within your organization.  

The post What exploited vulnerabilities have in common: A data-driven analysis of real-world attacks appeared first on Microsoft Security Blog.

3 lessons from frontier AI vulnerability research

The mission of Microsoft Security’s Frontier Offensive Research & Generative Exploitation (FORGE) Lab is to advance the frontier of autonomous security engineering. We’re building a team that enables AI-native vulnerability research at Microsoft, pushing the boundaries of finding and fixing zero-day vulnerabilities. Four principles guide that work: autonomy over labor, defense through offense, building ecosystems over individual examples, and understanding over findings. This post reflects those principles in practice across Windows, the Linux kernel, and widely used open-source projects.

From May 2026 through September 2026, FORGE helped discover Windows vulnerabilities assigned 140 common vulnerabilities and exposures (CVEs), including 52 addressed in September 2026’s security release alone. The work reaches well beyond Windows. FORGE members submitted 155 internally validated reports across 23 open-source projects, including the Linux kernel. And through Akrites, a Linux Foundation initiative that coordinates confidential remediation and disclosure for vulnerabilities in critical open-source software, one of our Linux reports became the first Akrites submission to result in a patch merged into the Linux kernel.

These results demonstrate that agentic discovery can operate at meaningful volume, but they also expose the next constraint. Finding a difficult vulnerability proves an agent’s frontier capability. Repeatedly converting such findings into security updates requires a different kind of system—one that can move credible work through validation, remediation, and release. Discovery creates security value only when validation and remediation can keep pace. For security leaders, the question is no longer only whether AI can find vulnerabilities, but whether an organization can validate and fix them as fast as they are found.

Three lessons follow, each marking a shift in how we approach vulnerability research:

  1. From frontier capability to scale.
  2. From token consumption to reasoning economics.
  3. From isolated discovery to coordinated validation and remediation.

The scarce resource is not always model intelligence. It can be a working build and deployment, a reproducible trigger, or even an engineer’s time.

At FORGE Lab, we use the multi-model agentic scanning harness, codename MDASH, to organize this work. Our May 2026 introduction detailed its design, while the June 2026 update covered pipeline improvements and benchmark analysis. Here, we focus on what those experiences teach us about operating vulnerability research at scale—across Windows and open-source projects.

Stacked bar chart titled “CVEs per cohort” shows Critical and Important  vulnerabilities by month from May launch through September. Totals are 16 CVEs in May (4 critical and 23 important), 10 in June (7 critical, 3 important), 27 in July (7 critical, 20 important), 35 in August (6 critical, 29 important), and  52 in September (2 Critical and 50 Important).
140 CVEs addressed through Microsoft Patch Tuesday since May 2026. Monthly totals reflect announcement and servicing cohorts, not discovery dates or scan throughput. Source: Microsoft FORGE Lab study, May 2026 to September 2026.

Shift 1. From frontier capability to scale

The frontier question is whether a system can find a bug that demands deep reasoning, as we explored in our early work on the CyberGym benchmark. The scale question is whether it can repeat that result across targets without rebuilding every environment, investigation, and review process from scratch. Better models still matter, especially for difficult or unfamiliar bug classes. But once discovery becomes repeatable, model capability is only one constraint on useful output.

Adding auditors can increase candidate volume without increasing the rate of validated findings or shipped fixes. If reports arrive faster than reviewers—in our case, Microsoft Security Response Center (MSRC)—can resolve them, the immediate result is a growing queue. A larger search budget can even reduce useful throughput when duplicate or poorly supported reports consume attention that stronger findings need.

The unit to optimize, therefore, is neither the scan nor the report. It is a reproducible finding that advances with enough evidence for the next stage to act. Reusable preparation, deduplication, and review capacity are therefore core components of the research system.

As agentic systems produce more candidates, the bottleneck shifts from discovery to determining which reports are real, reachable, and security-relevant. Project-specific automated provers—such as proof of vulnerability (PoV)/proof of concept (PoC) generators, harness builders, and trigger-input finders—turn plausible reports into reproducible evidence that engineers and maintainers can act on. By finding crashing inputs, confirming reachable execution paths, and producing regression-ready triggers within each project’s build and test environment, these tools can help reduce human triage, accelerate remediation, and make vulnerability research sustainable at scale. For example, by leveraging deterministic algorithms such as abstract syntax tree (AST), one of our internal projects reduced about 45% duplicate findings across multiple scans of the same code which reduced the load on the PoC generator and subsequently human triage effort.

More candidates do not create more throughput when the review queue grows faster than fixes ship.

Shift 2. From token consumption to reasoning economics

At scale, every repeated orientation, speculative branch, and redundant debate carries a cost. But minimizing tokens alone is the wrong objective. A short, ambiguous report may be cheap to generate yet expensive to investigate; a longer analysis that establishes the missing execution path may reduce total system cost.

The key question is where the next unit of reasoning will change a decision. If an index can identify callers, asking a frontier model to rediscover them wastes capacity. If the uncertainty is whether two lifetime conditions can coexist across callbacks, deeper reasoning may be warranted. If the question is whether an input triggers the failure, an executable check can provide evidence that more prose cannot.

MDASH combines frontier and distilled models, specialized auditors, and code-analysis tools, enabling work to be allocated by task. That flexibility does not guarantee optimal allocation. Routing routine work to cheaper models, reusing verified context, and escalating unresolved questions to stronger reasoning are hypotheses to test—not efficiency gains to assume.

A useful allocation policy starts by identifying what remains unknown: a caller, a build configuration, a reproducer, or a causal explanation. The next action should close that specific evidence gap. Repeating a review without adding evidence spends more tokens while preserving the same uncertainty. Early filtering can also discard real bugs, so any savings must be measured against coverage and missed findings.

Scan outcomes can also become training data. At scale, vulnerability scanning should become a training loop, not only a discovery pipeline. Each MDASH run produces signals that can improve future models: true-positive and false-positive verdicts, duplicate findings, failed reachability claims, reviewer feedback, verifier results, severity assessments, patch outcomes, and regression-test evidence. Capturing those signals with the code context and causal argument behind each candidate creates the dataset needed for reinforcement learning and fine-tuning specialized cyber models. The goal is for future models to learn not just what a vulnerability looks like, but which findings survive validation, which explanations help humans and provers act, and which patterns lead to useful remediation.

Spend reasoning to remove uncertainty, not simply to produce more analysis.

Shift 3. Validation and remediation as a continuous learning loop

Validation and remediation are not downstream cleanup; they are part of the discovery loop. Each candidate should move through automated verification, human review, patch development, and regression testing, with every stage returning evidence to the system. A reproducible trigger strengthens the report, guides the fix, and can seed a regression test. A failed validation is also useful when it records why: an unreachable path, missing precondition, incorrect build configuration, insufficient attacker control, duplicate report, or incomplete causal explanation.

That evidence is how the system improves. Project-specific provers, harnesses, and trigger generators accumulate operational knowledge about how each target builds, runs, fails, and accepts fixes. Remediation outcomes show the discovery pipeline which evidence changed a decision, which assumptions failed, and which bug patterns warrant more or less attention. The objective is to make every investigation leave reusable capacity behind: a validated trigger, clearer invariant, better harness, stronger regression test, or routing rule that prevents the same mistake.

Human attention remains essential, but it should concentrate where judgment has the highest leverage: assessing security impact, reviewing patches, and determining whether a change restores the component’s intended invariant. Automation can handle the repeatable work of establishing reachability, reproducing behavior, and preserving evidence. Over time, this loop converts individual findings into system knowledge—raising the quality of future discovery while shortening the path from credible report to shipped fix.

A reproducible defect is not yet a serviced fix. To shorten the path from discovery to remediation, MDASH needs to operate as part of the engineering loop: continuously scanning code, connecting findings to the builds and artifacts produced by continuous integration and continuous delivery (CI/CD), and feeding validation results back into development, as described in the Windows team’s blog post. Broad analysis can identify suspicious code paths, but component-specific proving needs the right binaries, symbols, configurations, harnesses, and runtime conditions to reproduce a candidate defect against the version that matters for customers and releases. Each handoff should be both owned and machine-usable: research supplies the candidate claim, causal path, and uncertainty; proving supplies execution evidence from the relevant build artifact; component teams assess the violated invariant, repair strategy, compatibility risk, and related variants; and servicing connects the approved change to release validation. A failed reproduction, rejected finding, incomplete patch, or regression result should feed back into MDASH as structured evidence, not simply move a ticket into another queue. The goal is not to remove human code review or release validation, but to automate the repeatable work around them—finding candidates, selecting artifacts, reproducing behavior, preserving evidence, suggesting related paths, and returning what was learned to the next scan.

The goal is to build an organization-wide system that automates repeatable work while preserving clear engineering accountability.

Transferring security research to help open source projects

In open source, all three constraints apply at once. Each project has its own setup costs, mix of analysis and execution work, and maintainer workflows and review capacity. Within Windows, vulnerability research connects to established component owners and servicing infrastructure. Across open-source projects, reusable methods can reduce repeated setup, but they cannot replace the technical and human context required to move a finding toward a fix.

Open-source vulnerability reporting snapshot

Over three months, FORGE members audited open-source projects spanning kernels, runtimes, networking libraries, container technologies, and media parsers. The team submitted 155 internally validated reports across 23 projects. At the time of writing, 93 reports across 14 projects or project families had documented maintainer acknowledgement or acceptance: apple/container, apple/containerization, curl, Escargot, FFmpeg, Hyperlight, Linux, llama.cpp, Node.js, PyRIT, Rust, SQLite, vLLM, etc.

Since the reports are at different stages of that process, only subsets are publicly disclosed today, such as curl’s CVE-2026-9545 and CVE-2026-13608, Node.js’s CVE-2026-56848, and Linux kernel CVE-2026-64563. Our public CVE index lists the CVEs currently cleared for disclosure, and you can find all our public channel Linux kernel reports through lore.kernel.org query.

Measured costs of automated validation

MDASH surfaced thousands of suspicious locations in the Linux kernel, and our validation agents produced supporting evidences for 627 findings. The figures below summarize automated validation costs for a subset of these efforts. Across 182 confirmed crash findings, generating a PoC averaged $3.61 in model cost and 21.5 minutes. For six selected cases, using automatic exploit generation (AEG) to test local privilege-escalation potential averaged $8.56 and 25.4 minutes.

Paired horizontal bar charts compare average model cost in USD and average completion time in minutes for two security-testing tasks. Blue bars show crash PoC generation (182 confirmed findings) at $3.61 and 21.5 minutes, versus local privilege-escalation testing (6 selected cases) at $8.56 and 25.4 minutes.
Average model and time cost for Linux kernel automated validation. Per-case averages reflect all attempts for successful cases using GPT-5.5. Source: Microsoft FORGE Lab Linux kernel automated validation study, 2026.

These evaluations used GPT-5.5, without extensive task-specific tuning of either the model or the harness. These averages include all attempts associated with the successful cases shown, but exclude initial screening, candidates that failed or were filtered out, and human investigation and patch preparation. Within those limits, the results still strongly indicate that automated validation can produce useful results at practical cost and latency, even at Linux-kernel scale. Tighter co-design of models and agent harnesses could improve both validation yield and efficiency. If costs fell by another order of magnitude, for example, 10x or even 50x, the range of findings that could be economically validated would expand substantially, further reshaping vulnerability research.

FORGE OSS Bug Hunt Party: Human coordination at scale (Hackathon)

Looking back to those efforts, technical evidence was only part of the job. Getting it fixed also required understanding and respecting each project’s context and workflow: some required a patch while others did not; some preferred private disclosure while others used public channels; and maintainers sometimes differed on whether an issue crossed a security boundary. In practice, this meant adapting to each project’s submission requirements, working with maintainers to reproduce the issue, and, where applicable, refining and retesting a proposed fix. As AI-assisted discovery increases report volume, open-source security research can scale only if research teams absorb the resulting complexity rather than pass it on to maintainers.

Scaling this work also requires more developers who understand the full security workflow. At Microsoft’s annual Hackathon, we hosted the FORGE Open Source Software (OSS) Bug Hunt Party (Project Sunshine) as a forum where developers across Microsoft could exchange security research practices and gain hands-on experience with vulnerability investigation, validation, and responsible reporting. With MDASH and supporting guidance, 50 participants produced 39 reports across six projects, including Microsoft’s open-source Hyperlight project, Linux, vLLM, Gemini CLI, and llama.cpp. Those 39 reports are included in the 155-report snapshot above. Project Sunshine has continued beyond the Hackathon as a forum for open-source security collaboration within Microsoft.

Akrites: Coordinated effort to remediate vulnerabilities

Akrites is a Linux Foundation initiative that coordinates confidential remediation and disclosure for vulnerabilities in critical open-source software. FORGE has submitted nine Linux findings with internally assessed common vulnerability scoring system (CVSS) scores above 7.0. Together with submissions from other participants, including Google, these cases helped test and refine Akrites’ early Coordinated Vulnerability Disclosure (CVD) workflow. One of those reports was the first Akrites submission to result in a patch merged into the Linux kernel. FORGE will keep contributing to those responsible disclosure efforts.

The work also showed that a systematic approach is not just a larger scan. It is a repeatable path from a finding to an upstream fix and coordinated disclosure. Each case needs reproducible evidence, a clear causal explanation, a severity assessment, and an understanding of which downstream projects may be affected. The right owners and collaborators can then be brought in at the right time and on a need-to-know basis.

What’s next for FORGE Lab?

In a matter of months, FORGE Lab has helped discover Windows vulnerabilities and assigned 140 CVEs, including 52 in September 2026’s security release. Beyond Windows, FORGE submitted 155 internally validated reports across 23 open-source projects—93 of which had documented maintainer acknowledgement or acceptance at the time of writing—and one of our Linux reports became the first Akrites submission to result in a patch merged into the Linux kernel. Our Linux kernel results also indicate that automated validation can work at practical cost, with proofs of concept averaging $3.61 in model cost and 21.5 minutes per successful case. The lesson behind those numbers: once agents can find difficult bugs, progress depends on the system around them, how it scales, where it spends reasoning, and how quickly it turns discoveries into validated fixes.

These three lessons are becoming foundational guidelines for FORGE’s next phase of research:

  1. Moving from frontier capability to scale means measuring the full pipeline, not just the number of findings: candidate arrivals, duplicates, rejected reports, validated defects, queue age, unresolved cases, and time through each stage.
  2. Moving from token consumption to reasoning economics means evaluating model and execution cost alongside human review time, and asking whether each additional pass removes uncertainty, improves coverage, or produces independently validated findings.
  3. Moving from isolated discovery to continuous validation and remediation means tracking reproduction success, patch rework, regression evidence, and elapsed time from validated defect to approved fix and release.

For security leaders evaluating AI-powered vulnerability discovery, these are useful measures too: count validated fixes, not just findings, and weigh model cost alongside the human review time it takes to act on them.

The next advance won’t come from a better model alone. It will come from research systems that combine frontier capability, systems engineering, and reasoning economics, focusing machine reasoning and human attention on turning credible findings into validated fixes.

Our team is growing. Check out our open positions at FORGE Lab.

Further reading:

To learn more about Microsoft Security solutions, visit our website. Bookmark the Security blog to keep up with our expert coverage on security matters. Also, follow us on LinkedIn (Microsoft Security) and X (@MSFTSecurity) for the latest news and updates on cybersecurity.

The post 3 lessons from frontier AI vulnerability research appeared first on Microsoft Security Blog.

3 lessons from frontier AI vulnerability research

The mission of Microsoft Security’s Frontier Offensive Research & Generative Exploitation (FORGE) Lab is to advance the frontier of autonomous security engineering. We’re building a team that enables AI-native vulnerability research at Microsoft, pushing the boundaries of finding and fixing zero-day vulnerabilities. Four principles guide that work: autonomy over labor, defense through offense, building ecosystems over individual examples, and understanding over findings. This post reflects those principles in practice across Windows, the Linux kernel, and widely used open-source projects.

From May 2026 through September 2026, FORGE helped discover Windows vulnerabilities assigned 140 common vulnerabilities and exposures (CVEs), including 52 addressed in September 2026’s security release alone. The work reaches well beyond Windows. FORGE members submitted 155 internally validated reports across 23 open-source projects, including the Linux kernel. And through Akrites, a Linux Foundation initiative that coordinates confidential remediation and disclosure for vulnerabilities in critical open-source software, one of our Linux reports became the first Akrites submission to result in a patch merged into the Linux kernel.

These results demonstrate that agentic discovery can operate at meaningful volume, but they also expose the next constraint. Finding a difficult vulnerability proves an agent’s frontier capability. Repeatedly converting such findings into security updates requires a different kind of system—one that can move credible work through validation, remediation, and release. Discovery creates security value only when validation and remediation can keep pace. For security leaders, the question is no longer only whether AI can find vulnerabilities, but whether an organization can validate and fix them as fast as they are found.

Three lessons follow, each marking a shift in how we approach vulnerability research:

  1. From frontier capability to scale.
  2. From token consumption to reasoning economics.
  3. From isolated discovery to coordinated validation and remediation.

The scarce resource is not always model intelligence. It can be a working build and deployment, a reproducible trigger, or even an engineer’s time.

At FORGE Lab, we use the multi-model agentic scanning harness, codename MDASH, to organize this work. Our May 2026 introduction detailed its design, while the June 2026 update covered pipeline improvements and benchmark analysis. Here, we focus on what those experiences teach us about operating vulnerability research at scale—across Windows and open-source projects.

Stacked bar chart titled “CVEs per cohort” shows Critical and Important  vulnerabilities by month from May launch through September. Totals are 16 CVEs in May (4 critical and 23 important), 10 in June (7 critical, 3 important), 27 in July (7 critical, 20 important), 35 in August (6 critical, 29 important), and  52 in September (2 Critical and 50 Important).
140 CVEs addressed through Microsoft Patch Tuesday since May 2026. Monthly totals reflect announcement and servicing cohorts, not discovery dates or scan throughput. Source: Microsoft FORGE Lab study, May 2026 to September 2026.

Shift 1. From frontier capability to scale

The frontier question is whether a system can find a bug that demands deep reasoning, as we explored in our early work on the CyberGym benchmark. The scale question is whether it can repeat that result across targets without rebuilding every environment, investigation, and review process from scratch. Better models still matter, especially for difficult or unfamiliar bug classes. But once discovery becomes repeatable, model capability is only one constraint on useful output.

Adding auditors can increase candidate volume without increasing the rate of validated findings or shipped fixes. If reports arrive faster than reviewers—in our case, Microsoft Security Response Center (MSRC)—can resolve them, the immediate result is a growing queue. A larger search budget can even reduce useful throughput when duplicate or poorly supported reports consume attention that stronger findings need.

The unit to optimize, therefore, is neither the scan nor the report. It is a reproducible finding that advances with enough evidence for the next stage to act. Reusable preparation, deduplication, and review capacity are therefore core components of the research system.

As agentic systems produce more candidates, the bottleneck shifts from discovery to determining which reports are real, reachable, and security-relevant. Project-specific automated provers—such as proof of vulnerability (PoV)/proof of concept (PoC) generators, harness builders, and trigger-input finders—turn plausible reports into reproducible evidence that engineers and maintainers can act on. By finding crashing inputs, confirming reachable execution paths, and producing regression-ready triggers within each project’s build and test environment, these tools can help reduce human triage, accelerate remediation, and make vulnerability research sustainable at scale. For example, by leveraging deterministic algorithms such as abstract syntax tree (AST), one of our internal projects reduced about 45% duplicate findings across multiple scans of the same code which reduced the load on the PoC generator and subsequently human triage effort.

More candidates do not create more throughput when the review queue grows faster than fixes ship.

Shift 2. From token consumption to reasoning economics

At scale, every repeated orientation, speculative branch, and redundant debate carries a cost. But minimizing tokens alone is the wrong objective. A short, ambiguous report may be cheap to generate yet expensive to investigate; a longer analysis that establishes the missing execution path may reduce total system cost.

The key question is where the next unit of reasoning will change a decision. If an index can identify callers, asking a frontier model to rediscover them wastes capacity. If the uncertainty is whether two lifetime conditions can coexist across callbacks, deeper reasoning may be warranted. If the question is whether an input triggers the failure, an executable check can provide evidence that more prose cannot.

MDASH combines frontier and distilled models, specialized auditors, and code-analysis tools, enabling work to be allocated by task. That flexibility does not guarantee optimal allocation. Routing routine work to cheaper models, reusing verified context, and escalating unresolved questions to stronger reasoning are hypotheses to test—not efficiency gains to assume.

A useful allocation policy starts by identifying what remains unknown: a caller, a build configuration, a reproducer, or a causal explanation. The next action should close that specific evidence gap. Repeating a review without adding evidence spends more tokens while preserving the same uncertainty. Early filtering can also discard real bugs, so any savings must be measured against coverage and missed findings.

Scan outcomes can also become training data. At scale, vulnerability scanning should become a training loop, not only a discovery pipeline. Each MDASH run produces signals that can improve future models: true-positive and false-positive verdicts, duplicate findings, failed reachability claims, reviewer feedback, verifier results, severity assessments, patch outcomes, and regression-test evidence. Capturing those signals with the code context and causal argument behind each candidate creates the dataset needed for reinforcement learning and fine-tuning specialized cyber models. The goal is for future models to learn not just what a vulnerability looks like, but which findings survive validation, which explanations help humans and provers act, and which patterns lead to useful remediation.

Spend reasoning to remove uncertainty, not simply to produce more analysis.

Shift 3. Validation and remediation as a continuous learning loop

Validation and remediation are not downstream cleanup; they are part of the discovery loop. Each candidate should move through automated verification, human review, patch development, and regression testing, with every stage returning evidence to the system. A reproducible trigger strengthens the report, guides the fix, and can seed a regression test. A failed validation is also useful when it records why: an unreachable path, missing precondition, incorrect build configuration, insufficient attacker control, duplicate report, or incomplete causal explanation.

That evidence is how the system improves. Project-specific provers, harnesses, and trigger generators accumulate operational knowledge about how each target builds, runs, fails, and accepts fixes. Remediation outcomes show the discovery pipeline which evidence changed a decision, which assumptions failed, and which bug patterns warrant more or less attention. The objective is to make every investigation leave reusable capacity behind: a validated trigger, clearer invariant, better harness, stronger regression test, or routing rule that prevents the same mistake.

Human attention remains essential, but it should concentrate where judgment has the highest leverage: assessing security impact, reviewing patches, and determining whether a change restores the component’s intended invariant. Automation can handle the repeatable work of establishing reachability, reproducing behavior, and preserving evidence. Over time, this loop converts individual findings into system knowledge—raising the quality of future discovery while shortening the path from credible report to shipped fix.

A reproducible defect is not yet a serviced fix. To shorten the path from discovery to remediation, MDASH needs to operate as part of the engineering loop: continuously scanning code, connecting findings to the builds and artifacts produced by continuous integration and continuous delivery (CI/CD), and feeding validation results back into development, as described in the Windows team’s blog post. Broad analysis can identify suspicious code paths, but component-specific proving needs the right binaries, symbols, configurations, harnesses, and runtime conditions to reproduce a candidate defect against the version that matters for customers and releases. Each handoff should be both owned and machine-usable: research supplies the candidate claim, causal path, and uncertainty; proving supplies execution evidence from the relevant build artifact; component teams assess the violated invariant, repair strategy, compatibility risk, and related variants; and servicing connects the approved change to release validation. A failed reproduction, rejected finding, incomplete patch, or regression result should feed back into MDASH as structured evidence, not simply move a ticket into another queue. The goal is not to remove human code review or release validation, but to automate the repeatable work around them—finding candidates, selecting artifacts, reproducing behavior, preserving evidence, suggesting related paths, and returning what was learned to the next scan.

The goal is to build an organization-wide system that automates repeatable work while preserving clear engineering accountability.

Transferring security research to help open source projects

In open source, all three constraints apply at once. Each project has its own setup costs, mix of analysis and execution work, and maintainer workflows and review capacity. Within Windows, vulnerability research connects to established component owners and servicing infrastructure. Across open-source projects, reusable methods can reduce repeated setup, but they cannot replace the technical and human context required to move a finding toward a fix.

Open-source vulnerability reporting snapshot

Over three months, FORGE members audited open-source projects spanning kernels, runtimes, networking libraries, container technologies, and media parsers. The team submitted 155 internally validated reports across 23 projects. At the time of writing, 93 reports across 14 projects or project families had documented maintainer acknowledgement or acceptance: apple/container, apple/containerization, curl, Escargot, FFmpeg, Hyperlight, Linux, llama.cpp, Node.js, PyRIT, Rust, SQLite, vLLM, etc.

Since the reports are at different stages of that process, only subsets are publicly disclosed today, such as curl’s CVE-2026-9545 and CVE-2026-13608, Node.js’s CVE-2026-56848, and Linux kernel CVE-2026-64563. Our public CVE index lists the CVEs currently cleared for disclosure, and you can find all our public channel Linux kernel reports through lore.kernel.org query.

Measured costs of automated validation

MDASH surfaced thousands of suspicious locations in the Linux kernel, and our validation agents produced supporting evidences for 627 findings. The figures below summarize automated validation costs for a subset of these efforts. Across 182 confirmed crash findings, generating a PoC averaged $3.61 in model cost and 21.5 minutes. For six selected cases, using automatic exploit generation (AEG) to test local privilege-escalation potential averaged $8.56 and 25.4 minutes.

Paired horizontal bar charts compare average model cost in USD and average completion time in minutes for two security-testing tasks. Blue bars show crash PoC generation (182 confirmed findings) at $3.61 and 21.5 minutes, versus local privilege-escalation testing (6 selected cases) at $8.56 and 25.4 minutes.
Average model and time cost for Linux kernel automated validation. Per-case averages reflect all attempts for successful cases using GPT-5.5. Source: Microsoft FORGE Lab Linux kernel automated validation study, 2026.

These evaluations used GPT-5.5, without extensive task-specific tuning of either the model or the harness. These averages include all attempts associated with the successful cases shown, but exclude initial screening, candidates that failed or were filtered out, and human investigation and patch preparation. Within those limits, the results still strongly indicate that automated validation can produce useful results at practical cost and latency, even at Linux-kernel scale. Tighter co-design of models and agent harnesses could improve both validation yield and efficiency. If costs fell by another order of magnitude, for example, 10x or even 50x, the range of findings that could be economically validated would expand substantially, further reshaping vulnerability research.

FORGE OSS Bug Hunt Party: Human coordination at scale (Hackathon)

Looking back to those efforts, technical evidence was only part of the job. Getting it fixed also required understanding and respecting each project’s context and workflow: some required a patch while others did not; some preferred private disclosure while others used public channels; and maintainers sometimes differed on whether an issue crossed a security boundary. In practice, this meant adapting to each project’s submission requirements, working with maintainers to reproduce the issue, and, where applicable, refining and retesting a proposed fix. As AI-assisted discovery increases report volume, open-source security research can scale only if research teams absorb the resulting complexity rather than pass it on to maintainers.

Scaling this work also requires more developers who understand the full security workflow. At Microsoft’s annual Hackathon, we hosted the FORGE Open Source Software (OSS) Bug Hunt Party (Project Sunshine) as a forum where developers across Microsoft could exchange security research practices and gain hands-on experience with vulnerability investigation, validation, and responsible reporting. With MDASH and supporting guidance, 50 participants produced 39 reports across six projects, including Microsoft’s open-source Hyperlight project, Linux, vLLM, Gemini CLI, and llama.cpp. Those 39 reports are included in the 155-report snapshot above. Project Sunshine has continued beyond the Hackathon as a forum for open-source security collaboration within Microsoft.

Akrites: Coordinated effort to remediate vulnerabilities

Akrites is a Linux Foundation initiative that coordinates confidential remediation and disclosure for vulnerabilities in critical open-source software. FORGE has submitted nine Linux findings with internally assessed common vulnerability scoring system (CVSS) scores above 7.0. Together with submissions from other participants, including Google, these cases helped test and refine Akrites’ early Coordinated Vulnerability Disclosure (CVD) workflow. One of those reports was the first Akrites submission to result in a patch merged into the Linux kernel. FORGE will keep contributing to those responsible disclosure efforts.

The work also showed that a systematic approach is not just a larger scan. It is a repeatable path from a finding to an upstream fix and coordinated disclosure. Each case needs reproducible evidence, a clear causal explanation, a severity assessment, and an understanding of which downstream projects may be affected. The right owners and collaborators can then be brought in at the right time and on a need-to-know basis.

What’s next for FORGE Lab?

In a matter of months, FORGE Lab has helped discover Windows vulnerabilities and assigned 140 CVEs, including 52 in September 2026’s security release. Beyond Windows, FORGE submitted 155 internally validated reports across 23 open-source projects—93 of which had documented maintainer acknowledgement or acceptance at the time of writing—and one of our Linux reports became the first Akrites submission to result in a patch merged into the Linux kernel. Our Linux kernel results also indicate that automated validation can work at practical cost, with proofs of concept averaging $3.61 in model cost and 21.5 minutes per successful case. The lesson behind those numbers: once agents can find difficult bugs, progress depends on the system around them, how it scales, where it spends reasoning, and how quickly it turns discoveries into validated fixes.

These three lessons are becoming foundational guidelines for FORGE’s next phase of research:

  1. Moving from frontier capability to scale means measuring the full pipeline, not just the number of findings: candidate arrivals, duplicates, rejected reports, validated defects, queue age, unresolved cases, and time through each stage.
  2. Moving from token consumption to reasoning economics means evaluating model and execution cost alongside human review time, and asking whether each additional pass removes uncertainty, improves coverage, or produces independently validated findings.
  3. Moving from isolated discovery to continuous validation and remediation means tracking reproduction success, patch rework, regression evidence, and elapsed time from validated defect to approved fix and release.

For security leaders evaluating AI-powered vulnerability discovery, these are useful measures too: count validated fixes, not just findings, and weigh model cost alongside the human review time it takes to act on them.

The next advance won’t come from a better model alone. It will come from research systems that combine frontier capability, systems engineering, and reasoning economics, focusing machine reasoning and human attention on turning credible findings into validated fixes.

Our team is growing. Check out our open positions at FORGE Lab.

Further reading:

To learn more about Microsoft Security solutions, visit our website. Bookmark the Security blog to keep up with our expert coverage on security matters. Also, follow us on LinkedIn (Microsoft Security) and X (@MSFTSecurity) for the latest news and updates on cybersecurity.

The post 3 lessons from frontier AI vulnerability research appeared first on Microsoft Security Blog.

❌