The vulnerability
CVE-2016-4977 is a remote code execution vulnerability in Spring Security OAuth, the module Pivotal (now part of VMware/Broadcom) maintained for building OAuth2 authorization servers and resource servers on top of Spring. The flaw sat in the framework's built-in "whitelabel" views — the default error and approval pages that ship out of the box so teams don't have to build their own OAuth2 authorization and error screens from scratch.
Those whitelabel views rendered certain request-derived values using Spring Expression Language (SpEL) rather than treating them as inert text. Specifically, the WhitelabelErrorEndpoint (and related approval endpoint) evaluated the additionalInformation map of an OAuth2Exception — including custom exception implementations — through a SpEL-aware view rendering path. Because SpEL is a full expression language capable of invoking arbitrary methods and constructing objects, an attacker who could influence the content that landed in that map could get the server to execute a crafted expression instead of just displaying it. In practice, that meant a specially crafted request to an OAuth2 endpoint (most commonly /oauth/authorize) could trigger evaluation of attacker-supplied SpEL, resulting in arbitrary Java code execution on the server hosting the authorization or resource server.
This is the same broad vulnerability family as other Spring-ecosystem SpEL and expression-injection bugs from that era: user-controlled input flows into a template or expression evaluator that has more power than the developer intended, and "display this error message" becomes "run this code." What made CVE-2016-4977 notable is where it lived — directly in an authentication/authorization component that is, by definition, exposed to unauthenticated traffic. An OAuth2 authorization endpoint has to be reachable by clients before they've proven who they are, so this wasn't a bug buried behind a login wall. It was reachable by design.
Affected versions and components
The vulnerability affects Spring Security OAuth, distributed as the spring-security-oauth and spring-security-oauth2 artifacts, specifically:
- The 2.x line prior to 2.0.10
- The 1.x line prior to 1.0.5
The issue lives in the library's default whitelabel views (error and approval pages) that are auto-configured unless an application explicitly replaces them. Any Spring application using Spring Security OAuth to stand up an OAuth2/authorization server or resource server — and relying on the framework's default views — was exposed. Applications that had already implemented fully custom error and approval views (bypassing the whitelabel templates entirely) had a smaller, though not necessarily zero, attack surface, since the underlying handling of OAuth2Exception.additionalInformation was the root cause rather than the templates alone.
Because spring-security-oauth was widely pulled in as a transitive dependency by projects built on Spring Boot's OAuth2 starters at the time, the practical footprint extended well beyond teams that consciously chose the library — many inherited it as part of a broader Spring Boot / Spring Cloud Security stack.
It's also worth noting for anyone auditing dependencies today: the spring-security-oauth project was later declared end-of-life by its maintainers, with new development redirected to Spring Security's native OAuth2 support and Spring Authorization Server. Any codebase still pinned to the legacy spring-security-oauth artifacts is worth flagging for migration regardless of this specific CVE, since it no longer receives security patches as a first-class citizen.
CVSS, EPSS, and KEV context
NVD scored CVE-2016-4977 under CVSS v2 at 7.5 (High), reflecting a network-exploitable, low-complexity vulnerability that requires no authentication and impacts confidentiality, integrity, and availability — consistent with unauthenticated remote code execution. The CVE predates the period when NVD consistently back-filled CVSSv3 scores for every entry, so many trackers and scanners still surface it primarily via its v2 vector.
We're not going to cite a specific EPSS percentile here, since EPSS scores drift over time and a stale number is worse than none — check a live feed (FIRST.org's EPSS API or your SCA tool) if you need a current exploitation-probability estimate for prioritization. As of this writing, CVE-2016-4977 does not appear on CISA's Known Exploited Vulnerabilities (KEV) catalog, which focuses on vulnerabilities with confirmed in-the-wild exploitation rather than theoretical severity. Its absence from KEV doesn't mean it's unimportant — plenty of software supply chains still carry ancient, unpatched spring-security-oauth jars precisely because nobody re-scans dependencies that "already shipped," and proof-of-concept exploit code for this class of SpEL injection has circulated publicly for years.
Timeline
- Mid-2016 — Pivotal, then the maintainer of Spring Security OAuth, identified and fixed the SpEL evaluation issue in the project's whitelabel error and approval views.
- July 2016 — The fix was released in Spring Security OAuth 2.0.10 and 1.0.5, and the issue was assigned CVE-2016-4977, published via NVD and Pivotal's own security advisories.
- Following months — Security researchers and blog writeups dissected the root cause (unsanitized SpEL evaluation of
OAuth2Exception.additionalInformation) and demonstrated proof-of-concept RCE chains, cementing this CVE as a reference case for expression-language injection in Java web frameworks. - Ongoing — Because
spring-security-oauthwas bundled transitively into many Spring Boot projects for years afterward, vulnerable versions have continued to surface in dependency audits and SBOM reviews well past the original disclosure, particularly in long-lived internal services that were never explicitly upgraded.
Remediation
- Upgrade the library. Move to Spring Security OAuth 2.0.10 or later (2.x line) or 1.0.5 or later (1.x line). This is the authoritative fix — it changes how the whitelabel views handle exception attributes so they're no longer evaluated as expressions.
- Audit for transitive inclusion. Don't just check your
pom.xmlorbuild.gradletop-level dependencies — run a full dependency tree resolution (mvn dependency:tree,gradle dependencies) to catchspring-security-oauthpulled in indirectly through Spring Boot starters, internal shared libraries, or older Spring Cloud Security modules. - Review custom
OAuth2Exceptionimplementations. If your application extendedOAuth2Exceptionand populatedadditionalInformationwith anything derived from request input, treat that as a red flag independent of the library version — the pattern of feeding request-controlled data into a rendering path that might evaluate expressions is worth eliminating on principle. - Replace whitelabel views in production. Regardless of patch status, production authorization servers should ship custom, minimal error and approval views rather than relying on framework defaults. Default views are convenient for prototyping but are a bigger attack surface precisely because they're generic and rarely audited by the teams using them.
- Plan a longer-term migration. Since
spring-security-oauthis end-of-life, teams still running it should treat this CVE as a forcing function to migrate to Spring Security's native OAuth2 resource-server/client support or Spring Authorization Server, which are actively maintained. - Scan continuously, not once. A one-time upgrade doesn't prevent regression — a teammate reintroducing an old internal library, a Docker base image with a stale jar baked in, or a forked microservice that missed the memo can all reintroduce this exact CVE months later.
How Safeguard helps
CVE-2016-4977 is a textbook example of why point-in-time vulnerability scanning isn't enough: it's a nine-year-old CVE with a known fix, and it still turns up in modern dependency audits because it hides in transitive dependencies, forked services, and container images nobody revisited after the initial patch cycle.
Safeguard is built to catch exactly this pattern across your software supply chain:
- Full dependency graph visibility — Safeguard resolves transitive dependencies, not just direct ones, so a
spring-security-oauthjar pulled in three layers deep through an internal shared library gets flagged the same as a top-level dependency. - Continuous SBOM monitoring — instead of a one-time scan at release time, Safeguard keeps a living inventory of every component across your services and container images, so a stale artifact reintroduced later — via a base image, a fork, or a copy-pasted service — is caught automatically rather than waiting for the next manual audit.
- CVE and EPSS-aware prioritization — Safeguard surfaces known-vulnerable components with current severity and exploitability context, helping teams triage a decade-old RCE like this one against the noise of lower-risk findings, instead of treating every CVE as equally urgent.
- End-of-life and unmaintained dependency detection — because
spring-security-oauthitself is EOL, Safeguard flags dependencies that have exited active maintenance, giving teams a signal to plan migrations before the next CVE in an abandoned library catches them off guard. - Build and deployment provenance — by tracking what actually shipped in each build and container, Safeguard helps confirm that a patched version made it all the way to production, closing the gap between "we upgraded the library" and "every running instance is actually patched."
Old, well-understood CVEs like CVE-2016-4977 are often the easiest wins in a supply chain security program — the fix is known, the patched versions have existed for years, and the only real obstacle is visibility. Safeguard's job is to make sure that obstacle never lasts long enough to matter.