[Java] Add taint tracking through Jackson deserialization

This commit is contained in:
Jonathan Leitschuh
2021-04-30 19:18:35 -04:00
parent d7e560c611
commit 56b1f15dda
6 changed files with 125 additions and 58 deletions

View File

@@ -50,6 +50,15 @@ library class JacksonWriteValueMethod extends Method, TaintPreservingCallable {
}
}
library class JacksonReadValueMethod extends Method, TaintPreservingCallable {
JacksonReadValueMethod() {
getDeclaringType().hasQualifiedName("com.fasterxml.jackson.databind", "ObjectReader") and
hasName("readValue")
}
override predicate returnsTaintFrom(int arg) { arg = 0 }
}
/** A type whose values are explicitly serialized in a call to a Jackson method. */
library class ExplicitlyWrittenJacksonSerializableType extends JacksonSerializableType {
ExplicitlyWrittenJacksonSerializableType() {
@@ -135,6 +144,16 @@ class JacksonDeserializableField extends DeserializableField {
}
}
class JacksonDeserializableFieldAccess extends FieldAccess {
JacksonDeserializableFieldAccess() { getField() instanceof JacksonDeserializableField }
}
class JacksonDeseializedTaintStep extends AdditionalTaintStep {
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
node2.asExpr().(JacksonDeserializableFieldAccess).getQualifier() = node1.asExpr()
}
}
/**
* A call to the `addMixInAnnotations` or `addMixIn` Jackson method.
*

View File

@@ -8,28 +8,44 @@ import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.ObjectReader;
class Test {
public static class Potato {
private String name;
private String getName() {
return name;
}
}
public static String taint() {
return "tainted";
}
public static void sink(Object any) {}
public static void jacksonObjectMapper() throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException {
String s = taint();
ObjectMapper om = new ObjectMapper();
File file = new File("testFile");
om.writeValue(file, s);
sink(file); //$hasTaintFlow
OutputStream out = new FileOutputStream(file);
om.writeValue(out, s);
sink(file); //$hasTaintFlow
Writer writer = new StringWriter();
om.writeValue(writer, s);
sink(writer); //$hasTaintFlow
JsonGenerator generator = new JsonFactory().createGenerator(new StringWriter());
om.writeValue(generator, s);
sink(generator); //$hasTaintFlow
String t = om.writeValueAsString(s);
System.out.println(t);
sink(t); //$hasTaintFlow
byte[] bs = om.writeValueAsBytes(s);
String reconstructed = new String(bs, "utf-8");
System.out.println(reconstructed);
sink(bs); //$hasTaintFlow
sink(reconstructed); //$hasTaintFlow
}
public static void jacksonObjectWriter() throws java.io.FileNotFoundException, java.io.UnsupportedEncodingException {
@@ -37,16 +53,30 @@ class Test {
ObjectWriter ow = new ObjectWriter();
File file = new File("testFile");
ow.writeValue(file, s);
sink(file); //$hasTaintFlow
OutputStream out = new FileOutputStream(file);
ow.writeValue(out, s);
sink(out); //$hasTaintFlow
Writer writer = new StringWriter();
ow.writeValue(writer, s);
sink(writer); //$hasTaintFlow
JsonGenerator generator = new JsonFactory().createGenerator(new StringWriter());
ow.writeValue(generator, s);
sink(generator); //$hasTaintFlow
String t = ow.writeValueAsString(s);
System.out.println(t);
sink(t); //$hasTaintFlow
byte[] bs = ow.writeValueAsBytes(s);
String reconstructed = new String(bs, "utf-8");
System.out.println(reconstructed);
sink(bs); //$hasTaintFlow
sink(reconstructed); //$hasTaintFlow
}
public static void jacksonObjectReader() throws java.io.IOException {
String s = taint();
ObjectMapper om = new ObjectMapper();
ObjectReader reader = om.readerFor(Potato.class);
sink(reader.readValue(s)); //$hasTaintFlow
sink(reader.readValue(s, Potato.class).name); //$hasTaintFlow
sink(reader.readValue(s, Potato.class).getName()); //$hasTaintFlow
}
}

View File

@@ -1,48 +0,0 @@
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:10:43:10:54 | value |
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:13:73:13:84 | value |
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:16:44:16:55 | value |
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:19:36:19:47 | value |
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:22:35:22:46 | value |
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectMapper.java:26:36:26:47 | value |
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:10:43:10:54 | value |
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:13:73:13:84 | value |
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:16:44:16:55 | value |
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:19:36:19:47 | value |
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:22:35:22:46 | value |
| ../../../stubs/jackson-databind-2.10/com/fasterxml/jackson/databind/ObjectWriter.java:26:36:26:47 | value |
| Test.java:18:14:18:20 | taint(...) |
| Test.java:21:17:21:20 | file [post update] |
| Test.java:21:23:21:23 | s |
| Test.java:22:43:22:46 | file |
| Test.java:23:17:23:19 | out [post update] |
| Test.java:23:22:23:22 | s |
| Test.java:25:17:25:22 | writer [post update] |
| Test.java:25:25:25:25 | s |
| Test.java:27:17:27:25 | generator [post update] |
| Test.java:27:28:27:28 | s |
| Test.java:28:14:28:37 | writeValueAsString(...) |
| Test.java:28:36:28:36 | s |
| Test.java:29:22:29:22 | t |
| Test.java:30:15:30:37 | writeValueAsBytes(...) |
| Test.java:30:36:30:36 | s |
| Test.java:31:26:31:48 | new String(...) |
| Test.java:31:37:31:38 | bs |
| Test.java:32:22:32:34 | reconstructed |
| Test.java:36:14:36:20 | taint(...) |
| Test.java:39:17:39:20 | file [post update] |
| Test.java:39:23:39:23 | s |
| Test.java:40:43:40:46 | file |
| Test.java:41:17:41:19 | out [post update] |
| Test.java:41:22:41:22 | s |
| Test.java:43:17:43:22 | writer [post update] |
| Test.java:43:25:43:25 | s |
| Test.java:45:17:45:25 | generator [post update] |
| Test.java:45:28:45:28 | s |
| Test.java:46:14:46:37 | writeValueAsString(...) |
| Test.java:46:36:46:36 | s |
| Test.java:47:22:47:22 | t |
| Test.java:48:15:48:37 | writeValueAsBytes(...) |
| Test.java:48:36:48:36 | s |
| Test.java:49:26:49:48 | new String(...) |
| Test.java:49:37:49:38 | bs |
| Test.java:50:22:50:34 | reconstructed |

View File

@@ -1,17 +1,34 @@
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.FlowSources
import TestUtilities.InlineExpectationsTest
class Conf extends TaintTracking::Configuration {
Conf() { this = "qltest:dataflow:jackson" }
override predicate isSource(DataFlow::Node source) {
source.asExpr().(MethodAccess).getMethod().hasName("taint")
override predicate isSource(DataFlow::Node n) {
n.asExpr().(MethodAccess).getMethod().hasName("taint")
or
n instanceof RemoteFlowSource
}
override predicate isSink(DataFlow::Node sink) { any() }
override predicate isSink(DataFlow::Node n) {
exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument())
}
}
from DataFlow::Node source, DataFlow::Node sink, Conf config
where config.hasFlow(source, sink)
select sink
class HasFlowTest extends InlineExpectationsTest {
HasFlowTest() { this = "HasFlowTest" }
override string getARelevantTag() { result = "hasTaintFlow" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasTaintFlow" and
exists(DataFlow::Node src, DataFlow::Node sink, Conf conf | conf.hasFlow(src, sink) |
sink.getLocation() = location and
element = sink.toString() and
value = ""
)
}
}

View File

@@ -26,4 +26,8 @@ public class ObjectMapper {
public String writeValueAsString(Object value) {
return null;
}
public ObjectReader readerFor(Class<?> type) {
return null;
}
}

View File

@@ -0,0 +1,45 @@
package com.fasterxml.jackson.databind;
import java.io.*;
public class ObjectReader {
public ObjectReader forType(Class<?> valueType) {
return null;
}
public <T> T readValue(String src) {
return null;
}
public <T> T readValue(String src, Class<T> valueType) throws IOException {
return null;
}
public <T> T readValue(byte[] content) throws IOException {
return null;
}
public <T> T readValue(byte[] content, Class<T> valueType) throws IOException {
return null;
}
public <T> T readValue(File src) throws IOException {
return null;
}
public <T> T readValue(InputStream src) throws IOException {
return null;
}
public <T> T readValue(InputStream src, Class<T> valueType) throws IOException {
return null;
}
public <T> T readValue(Reader src) throws IOException {
return null;
}
public <T> T readValue(Reader src, Class<T> valueType) throws IOException {
return null;
}
}