java.lang.security.jackson-unsafe-deserialization.jackson-unsafe-deserialization
semgrep
Author
unknown
Download Count*
License
When using Jackson to marshall/unmarshall JSON to Java objects, enabling default typing is dangerous and can lead to RCE. If an attacker can control $JSON
it might be possible to provide a malicious JSON which can be used to exploit unsecure deserialization. In order to prevent this issue, avoid to enable default typing (globally or by using "Per-class" annotations) and avoid using Object
and other dangerous types for member variable declaration which creating classes for Jackson based deserialization.
Run Locally
Run in CI
Defintion
rules:
- id: jackson-unsafe-deserialization
patterns:
- pattern-either:
- patterns:
- pattern-inside: |
ObjectMapper $OM = new ObjectMapper(...);
...
- pattern-inside: |
$OM.enableDefaultTyping();
...
- pattern: $OM.readValue($JSON, ...);
- patterns:
- pattern-inside: |
class $CLASS {
...
@JsonTypeInfo(use = Id.CLASS,...)
$TYPE $VAR;
...
}
- metavariable-regex:
metavariable: $TYPE
regex: (Object|Serializable|Comparable)
- pattern: $OM.readValue($JSON, $CLASS.class);
- patterns:
- pattern-inside: |
class $CLASS {
...
ObjectMapper $OM;
...
$INITMETHODTYPE $INITMETHOD(...) {
...
$OM = new ObjectMapper();
...
$OM.enableDefaultTyping();
...
}
...
}
- pattern-inside: |
$METHODTYPE $METHOD(...) {
...
}
- pattern: $OM.readValue($JSON, ...);
message: When using Jackson to marshall/unmarshall JSON to Java objects,
enabling default typing is dangerous and can lead to RCE. If an attacker
can control `$JSON` it might be possible to provide a malicious JSON which
can be used to exploit unsecure deserialization. In order to prevent this
issue, avoid to enable default typing (globally or by using "Per-class"
annotations) and avoid using `Object` and other dangerous types for member
variable declaration which creating classes for Jackson based
deserialization.
languages:
- java
severity: WARNING
metadata:
category: security
subcategory:
- audit
cwe:
- "CWE-502: Deserialization of Untrusted Data"
confidence: MEDIUM
likelihood: LOW
impact: HIGH
owasp:
- A8:2017 Insecure Deserialization
- A8:2021 Software and Data Integrity Failures
references:
- https://swapneildash.medium.com/understanding-insecure-implementation-of-jackson-deserialization-7b3d409d2038
- https://cowtowncoder.medium.com/on-jackson-cves-dont-panic-here-is-what-you-need-to-know-54cd0d6e8062
- https://adamcaudill.com/2017/10/04/exploiting-jackson-rce-cve-2017-7525/
technology:
- jackson
license: Commons Clause License Condition v1.0[LGPL-2.1-only]
vulnerability_class:
- "Insecure Deserialization "
Examples
jackson-unsafe-deserialization.java
private class Car {
private Fake variable;
@JsonTypeInfo(use = Id.CLASS)
private Object color;
private String type;
public Car() {
}
public Car(Object color, String type) {
this.color = color;
this.type = type;
}
public String getColor() {
return (String) this.color;
}
public void setColor(Object color) {
this.color = color;
}
public String getType() {
return this.type;
}
public void setType(String type) {
this.type = type;
}
public static void main(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enableDefaultTyping();
try {
// ruleid: jackson-unsafe-deserialization
Car car = objectMapper.readValue(Paths.get("target/payload.json").toFile(), Car.class);
System.out.println((car.getColor()));
} catch (Exception e) {
System.out.println("Exception raised:" + e.getMessage());
}
}
public static void anotherMain(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
// Disable default typing globally
// objectMapper.enableDefaultTyping();
try {
// ruleid: jackson-unsafe-deserialization
Car car = objectMapper.readValue(Paths.get("target/payload.json").toFile(), Car.class);
System.out.println((car.getColor()));
} catch (Exception e) {
System.out.println("Exception raised:" + e.getMessage());
}
}
public static void anotherMain2(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
try {
// ok: jackson-unsafe-deserialization
Car car = objectMapper.readValue(Paths.get("target/payload.json").toFile(), Another.class);
System.out.println((car.getColor()));
} catch (Exception e) {
System.out.println("Exception raised:" + e.getMessage());
}
}
}
// Additional class to test rule when ObjectMapper is created in a different
// method
@RestController
public class MyController {
private Test variable;
private ObjectMapper objectMapper;
private Test2 variable2;
@PostConstruct
public void initialize() {
this.variable = 123;
objectMapper = new ObjectMapper();
objectMapper.enableDefaultTyping();
this.variable2 = 456;
}
@RequestMapping(path = "/", method = RequestMethod.GET)
public void redirectToUserInfo(HttpServletResponse response) throws IOException {
response.sendRedirect("/somewhere");
}
@RequestMapping(path = "/vulnerable", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public GenericUser vulnerable(@CookieValue(name = "token", required = false) String token)
throws JsonParseException, JsonMappingException, IOException {
byte[] decoded = Base64.getDecoder().decode(token);
String decodedString = new String(decoded);
// ruleid: jackson-unsafe-deserialization
Car obj = objectMapper.readValue(
decodedString,
Car.class);
return obj;
}
}
Short Link: https://sg.run/GDop