Back to Articles

Hunting Java Deserialization Exploits with Graph Traversal: Inside Inspector-Gadget

[ View on GitHub ]

Hunting Java Deserialization Exploits with Graph Traversal: Inside Inspector-Gadget

Hook

The most devastating Java vulnerabilities of the past decade—from Apache Commons Collections to Log4Shell—all share a common ancestor: deserialization. Finding these exploit chains manually is like searching for a needle in a haystack, but what if you could query them like a database?

Context

Java deserialization vulnerabilities represent one of the most persistent and dangerous attack vectors in enterprise software. When an application deserializes untrusted data, attackers can chain together existing classes—"gadgets"—to achieve remote code execution without ever introducing malicious code. The challenge for security researchers is finding these chains: you need a Serializable class with a readObject method that calls other methods, which call other methods, eventually reaching something dangerous like Runtime.exec(). Traditional static analysis tools treat code as trees or control flow graphs, but gadget chain discovery is fundamentally a graph problem—you're traversing relationships between classes, methods, and their invocations.

Inspector-gadget, created by Chris Frohoff (also known for ysoserial, the definitive Java deserialization exploit tool), approaches this problem by converting Java bytecode into a Tinkerpop graph database and exposing it via the Gremlin query language. Instead of writing custom analysis code for each search, researchers can write declarative graph traversals that express patterns like "find all Serializable classes whose readObject methods eventually call dangerous sinks." This transforms vulnerability research from programming to querying, dramatically reducing the time to discover new gadget chains.

Technical Insight

Query Phase

Ingestion Phase

Classes, Methods,

Fields

Vertices & Edges

inheritance, calls, access

Graph Traversals

readObject → exec

Java Class Files

from Classpath

ASM Bytecode

Parser

Metadata

Extractor

Graph Builder

TinkerGraph

In-Memory Database

Gremlin Query

Engine

Gadget Chain

Results

System architecture — auto-generated

Inspector-gadget operates in two phases: ingestion and querying. During ingestion, it uses ASM (the Java bytecode manipulation framework) to parse class files from your classpath, extracting structural metadata about classes, methods, fields, and their relationships. Each class becomes a vertex in the graph, with edges representing inheritance (extends/implements), method calls, field access, and type usage. The tool then loads this structure into a Tinkerpop-compatible graph database—the reference implementation uses TinkerGraph, an in-memory graph, though you could theoretically use any Gremlin-enabled backend.

The real power emerges when you query this graph. Here's a simplified example from the repository that searches for potential deserialization gadgets:

g.V().has('class', 'serializable', true)
  .has('method', 'name', 'readObject')
  .out('calls').out('calls').out('calls')
  .has('method', 'name', 'exec')
  .path()

This Gremlin traversal reads almost like English: start at all vertices, filter to classes marked as Serializable, find their readObject methods, follow outgoing "calls" edges three levels deep, and filter to methods named "exec". The path() step returns the complete chain from source to sink. What would require hundreds of lines of custom visitor pattern code in a traditional static analyzer becomes a five-line query.

The graph schema is straightforward but expressive. Class vertices store properties like name, package, interfaces, and flags (is it Serializable? public? abstract?). Method vertices include signature information, access modifiers, and annotations. The edge types form the grammar of your queries: 'extends' and 'implements' for type hierarchies, 'declares' linking classes to their methods and fields, 'calls' for method invocations, 'reads' and 'writes' for field access, and 'instantiates' for object creation. This schema captures the relationships that matter for security analysis without drowning in implementation details.

For more sophisticated analysis, you can compose complex traversals. Suppose you want to find classes that are both Serializable and implement a specific interface, then trace all paths from their readObject methods to any method that writes to files:

g.V().has('class', 'serializable', true)
  .where(out('implements').has('name', 'java.util.Comparator'))
  .out('declares').has('method', 'name', 'readObject')
  .repeat(out('calls')).until(
    or(
      has('method', 'name', 'write'),
      has('class', 'name', 'java.io.FileOutputStream')
    )
  )
  .path().by(valueMap())

This traversal uses Gremlin's repeat/until pattern for unbounded depth searches and logical operators to match multiple sink patterns. The by(valueMap()) modulator enriches the path result with all properties, giving you complete context for each step in the chain.

The architecture's elegance lies in its separation of concerns: bytecode parsing is handled by mature libraries (ASM), graph storage by Tinkerpop, and query expressiveness by Gremlin. Inspector-gadget's actual codebase is relatively small—it's primarily glue code that maps Java's type system semantics onto graph concepts. This design means you can leverage the entire Gremlin ecosystem: write queries in Groovy, Java, Python, or JavaScript; visualize results with graph tools like Gephi; or export to Neo4j for persistence and team collaboration.

Gotcha

Inspector-gadget's biggest limitation is its maturity—or lack thereof. The project hasn't seen significant updates in years and depends on older versions of Tinkerpop (version 3.x from several years ago). Modern Java features like modules, records, sealed classes, and pattern matching aren't represented in the graph schema. If you're analyzing contemporary Java codebases, you'll miss important relationships. The tool also predates Java's stronger encapsulation in JDK 9+, so you may encounter reflection access warnings or failures when parsing certain system classes.

Performance and scalability are completely undocumented. How does it handle a classpath with thousands of classes? Is there incremental indexing, or must you rebuild the entire graph for each analysis? The in-memory TinkerGraph implementation suggests small-to-medium codebases only, but there's no guidance on memory requirements or indexing time. For production security scanning at scale, you'd likely need to extend the tool to use a persistent graph database and add caching strategies. Documentation is minimal beyond a single README example—there's no reference for the complete graph schema, no query cookbook, and no guidance on interpreting results. You'll spend significant time experimenting with queries to understand what relationships actually exist in the graph.

Verdict

Use if: You're a security researcher actively hunting for novel Java deserialization gadget chains and find yourself repeatedly writing custom static analysis code for similar relationship queries. Inspector-gadget's graph approach will accelerate your research workflow once you invest time learning Gremlin and exploring the schema. It's particularly valuable for targeted investigations of specific libraries or frameworks where you can scope the classpath appropriately. Skip if: You need a maintained, production-ready tool for automated security scanning, require support for modern Java language features (Java 9+), or want comprehensive documentation without reverse-engineering the schema. For general static analysis or vulnerability detection, mature alternatives like CodeQL offer better maintenance, documentation, and scalability. If you specifically want deserialization exploit generation rather than discovery, use ysoserial directly—it contains pre-built gadget chains for known vulnerable libraries.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/developer-tools/frohoff-inspector-gadget.svg)](https://starlog.is/api/badge-click/developer-tools/frohoff-inspector-gadget)