Skip to content

Java: catch delayed unsafe deserialization #8766

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* @name Delayed deserialization of user-controlled data
* @description Delayed deserializing user-controlled data may allow attackers to
* execute arbitrary code.
* @kind path-problem
* @problem.severity error
* @security-severity 9.8
* @precision high
* @id java/delayed-unsafe-deserialization
* @tags security
* external/cwe/cwe-502
*/

import java
import semmle.code.java.security.UnsafeDeserializationQuery
import DataFlow::PathGraph

/**
* Holds if `fromNode` to `toNode` is a dataflow step that stores data to a byte array field.
*/
private predicate byteArrayFieldFlowStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
exists(FieldRead access, Field field | field = access.getField() |
field.getType().hasName("byte[]") and
access = fromNode.asExpr() and
field.getAnAccess().(FieldRead) = toNode.asExpr()
)
}

/**
* Tracks flows from remote user input to a deserialization sink
* taking into account that taited data can be stored in a byte field.
*/
private class DelayedUnsafeDeserializationConfig extends UnsafeDeserializationConfig {
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
UnsafeDeserializationConfig.super.isAdditionalTaintStep(pred, succ)
or
byteArrayFieldFlowStep(pred, succ)
}
}

from DataFlow::PathNode source, DataFlow::PathNode sink, DelayedUnsafeDeserializationConfig conf
where conf.hasFlowPath(source, sink)
select sink.getNode().(UnsafeDeserializationSink).getMethodAccess(), source, sink,
"Delayed unsafe deserialization of $@.", source.getNode(), "user input"
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
edges
| DelayedUnsafeDeserialization.java:10:9:10:14 | holder [post update] [buffer] : byte[] | DelayedUnsafeDeserialization.java:11:9:11:14 | holder [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:10:21:10:43 | getInputStream(...) : InputStream | DelayedUnsafeDeserialization.java:10:9:10:14 | holder [post update] [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:10:21:10:43 | getInputStream(...) : InputStream | DelayedUnsafeDeserialization.java:27:22:27:38 | input : InputStream |
| DelayedUnsafeDeserialization.java:11:9:11:14 | holder [buffer] : byte[] | DelayedUnsafeDeserialization.java:35:19:35:27 | parameter this [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:17:9:17:31 | getInputStream(...) : InputStream | DelayedUnsafeDeserialization.java:17:38:17:42 | input [post update] : byte[] |
| DelayedUnsafeDeserialization.java:17:38:17:42 | input [post update] : byte[] | DelayedUnsafeDeserialization.java:18:21:18:25 | input : byte[] |
| DelayedUnsafeDeserialization.java:18:9:18:14 | holder [post update] [buffer] : byte[] | DelayedUnsafeDeserialization.java:19:9:19:14 | holder [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:18:21:18:25 | input : byte[] | DelayedUnsafeDeserialization.java:18:9:18:14 | holder [post update] [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:18:21:18:25 | input : byte[] | DelayedUnsafeDeserialization.java:31:22:31:33 | input : byte[] |
| DelayedUnsafeDeserialization.java:19:9:19:14 | holder [buffer] : byte[] | DelayedUnsafeDeserialization.java:35:19:35:27 | parameter this [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:27:22:27:38 | input : InputStream | DelayedUnsafeDeserialization.java:28:9:28:13 | input : InputStream |
| DelayedUnsafeDeserialization.java:28:9:28:13 | input : InputStream | DelayedUnsafeDeserialization.java:28:20:28:25 | buffer [post update] : byte[] |
| DelayedUnsafeDeserialization.java:28:20:28:25 | buffer : byte[] | DelayedUnsafeDeserialization.java:28:20:28:25 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:28:20:28:25 | buffer : byte[] | DelayedUnsafeDeserialization.java:28:20:28:25 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:28:20:28:25 | buffer : byte[] | DelayedUnsafeDeserialization.java:28:31:28:36 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:28:20:28:25 | buffer : byte[] | DelayedUnsafeDeserialization.java:28:31:28:36 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:28:20:28:25 | buffer : byte[] | DelayedUnsafeDeserialization.java:36:62:36:67 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:28:20:28:25 | buffer : byte[] | DelayedUnsafeDeserialization.java:36:62:36:67 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:28:20:28:25 | buffer [post update] : byte[] | DelayedUnsafeDeserialization.java:28:20:28:25 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:28:20:28:25 | buffer [post update] : byte[] | DelayedUnsafeDeserialization.java:28:20:28:25 | this <.field> [post update] [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:28:20:28:25 | buffer [post update] : byte[] | DelayedUnsafeDeserialization.java:28:31:28:36 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:28:20:28:25 | this <.field> [post update] [buffer] : byte[] | DelayedUnsafeDeserialization.java:28:31:28:36 | this <.field> [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:28:31:28:36 | buffer : byte[] | DelayedUnsafeDeserialization.java:28:20:28:25 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:28:31:28:36 | buffer : byte[] | DelayedUnsafeDeserialization.java:28:20:28:25 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:28:31:28:36 | buffer : byte[] | DelayedUnsafeDeserialization.java:28:31:28:36 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:28:31:28:36 | buffer : byte[] | DelayedUnsafeDeserialization.java:28:31:28:36 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:28:31:28:36 | buffer : byte[] | DelayedUnsafeDeserialization.java:36:62:36:67 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:28:31:28:36 | buffer : byte[] | DelayedUnsafeDeserialization.java:36:62:36:67 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:28:31:28:36 | this <.field> [buffer] : byte[] | DelayedUnsafeDeserialization.java:28:31:28:36 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:31:22:31:33 | input : byte[] | DelayedUnsafeDeserialization.java:32:18:32:22 | input : byte[] |
| DelayedUnsafeDeserialization.java:32:18:32:22 | input : byte[] | DelayedUnsafeDeserialization.java:32:9:32:14 | this <.field> [post update] [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:35:19:35:27 | parameter this [buffer] : byte[] | DelayedUnsafeDeserialization.java:36:62:36:67 | this <.field> [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:36:37:36:68 | new ByteArrayInputStream(...) : ByteArrayInputStream | DelayedUnsafeDeserialization.java:37:55:37:58 | bais : ByteArrayInputStream |
| DelayedUnsafeDeserialization.java:36:37:36:68 | new ByteArrayInputStream(...) : ByteArrayInputStream | DelayedUnsafeDeserialization.java:37:55:37:58 | bais : ByteArrayInputStream |
| DelayedUnsafeDeserialization.java:36:37:36:68 | new ByteArrayInputStream(...) : ByteArrayInputStream | DelayedUnsafeDeserialization.java:38:16:38:18 | ois |
| DelayedUnsafeDeserialization.java:36:37:36:68 | new ByteArrayInputStream(...) : ByteArrayInputStream | DelayedUnsafeDeserialization.java:38:16:38:18 | ois |
| DelayedUnsafeDeserialization.java:36:62:36:67 | buffer : byte[] | DelayedUnsafeDeserialization.java:28:20:28:25 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:36:62:36:67 | buffer : byte[] | DelayedUnsafeDeserialization.java:28:20:28:25 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:36:62:36:67 | buffer : byte[] | DelayedUnsafeDeserialization.java:28:31:28:36 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:36:62:36:67 | buffer : byte[] | DelayedUnsafeDeserialization.java:28:31:28:36 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:36:62:36:67 | buffer : byte[] | DelayedUnsafeDeserialization.java:36:37:36:68 | new ByteArrayInputStream(...) : ByteArrayInputStream |
| DelayedUnsafeDeserialization.java:36:62:36:67 | buffer : byte[] | DelayedUnsafeDeserialization.java:36:37:36:68 | new ByteArrayInputStream(...) : ByteArrayInputStream |
| DelayedUnsafeDeserialization.java:36:62:36:67 | buffer : byte[] | DelayedUnsafeDeserialization.java:37:55:37:58 | bais : ByteArrayInputStream |
| DelayedUnsafeDeserialization.java:36:62:36:67 | buffer : byte[] | DelayedUnsafeDeserialization.java:37:55:37:58 | bais : ByteArrayInputStream |
| DelayedUnsafeDeserialization.java:36:62:36:67 | buffer : byte[] | DelayedUnsafeDeserialization.java:38:16:38:18 | ois |
| DelayedUnsafeDeserialization.java:36:62:36:67 | buffer : byte[] | DelayedUnsafeDeserialization.java:38:16:38:18 | ois |
| DelayedUnsafeDeserialization.java:36:62:36:67 | this <.field> [buffer] : byte[] | DelayedUnsafeDeserialization.java:36:62:36:67 | buffer : byte[] |
| DelayedUnsafeDeserialization.java:37:33:37:59 | new ObjectInputStream(...) : ObjectInputStream | DelayedUnsafeDeserialization.java:38:16:38:18 | ois |
| DelayedUnsafeDeserialization.java:37:33:37:59 | new ObjectInputStream(...) : ObjectInputStream | DelayedUnsafeDeserialization.java:38:16:38:18 | ois |
| DelayedUnsafeDeserialization.java:37:55:37:58 | bais : ByteArrayInputStream | DelayedUnsafeDeserialization.java:37:33:37:59 | new ObjectInputStream(...) : ObjectInputStream |
| DelayedUnsafeDeserialization.java:37:55:37:58 | bais : ByteArrayInputStream | DelayedUnsafeDeserialization.java:37:33:37:59 | new ObjectInputStream(...) : ObjectInputStream |
nodes
| DelayedUnsafeDeserialization.java:10:9:10:14 | holder [post update] [buffer] : byte[] | semmle.label | holder [post update] [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:10:21:10:43 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
| DelayedUnsafeDeserialization.java:11:9:11:14 | holder [buffer] : byte[] | semmle.label | holder [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:17:9:17:31 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
| DelayedUnsafeDeserialization.java:17:38:17:42 | input [post update] : byte[] | semmle.label | input [post update] : byte[] |
| DelayedUnsafeDeserialization.java:18:9:18:14 | holder [post update] [buffer] : byte[] | semmle.label | holder [post update] [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:18:21:18:25 | input : byte[] | semmle.label | input : byte[] |
| DelayedUnsafeDeserialization.java:19:9:19:14 | holder [buffer] : byte[] | semmle.label | holder [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:27:22:27:38 | input : InputStream | semmle.label | input : InputStream |
| DelayedUnsafeDeserialization.java:28:9:28:13 | input : InputStream | semmle.label | input : InputStream |
| DelayedUnsafeDeserialization.java:28:20:28:25 | buffer : byte[] | semmle.label | buffer : byte[] |
| DelayedUnsafeDeserialization.java:28:20:28:25 | buffer : byte[] | semmle.label | buffer : byte[] |
| DelayedUnsafeDeserialization.java:28:20:28:25 | buffer [post update] : byte[] | semmle.label | buffer [post update] : byte[] |
| DelayedUnsafeDeserialization.java:28:20:28:25 | this <.field> [post update] [buffer] : byte[] | semmle.label | this <.field> [post update] [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:28:31:28:36 | buffer : byte[] | semmle.label | buffer : byte[] |
| DelayedUnsafeDeserialization.java:28:31:28:36 | buffer : byte[] | semmle.label | buffer : byte[] |
| DelayedUnsafeDeserialization.java:28:31:28:36 | this <.field> [buffer] : byte[] | semmle.label | this <.field> [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:31:22:31:33 | input : byte[] | semmle.label | input : byte[] |
| DelayedUnsafeDeserialization.java:32:9:32:14 | this <.field> [post update] [buffer] : byte[] | semmle.label | this <.field> [post update] [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:32:18:32:22 | input : byte[] | semmle.label | input : byte[] |
| DelayedUnsafeDeserialization.java:35:19:35:27 | parameter this [buffer] : byte[] | semmle.label | parameter this [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:36:37:36:68 | new ByteArrayInputStream(...) : ByteArrayInputStream | semmle.label | new ByteArrayInputStream(...) : ByteArrayInputStream |
| DelayedUnsafeDeserialization.java:36:37:36:68 | new ByteArrayInputStream(...) : ByteArrayInputStream | semmle.label | new ByteArrayInputStream(...) : ByteArrayInputStream |
| DelayedUnsafeDeserialization.java:36:62:36:67 | buffer : byte[] | semmle.label | buffer : byte[] |
| DelayedUnsafeDeserialization.java:36:62:36:67 | buffer : byte[] | semmle.label | buffer : byte[] |
| DelayedUnsafeDeserialization.java:36:62:36:67 | this <.field> [buffer] : byte[] | semmle.label | this <.field> [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:37:33:37:59 | new ObjectInputStream(...) : ObjectInputStream | semmle.label | new ObjectInputStream(...) : ObjectInputStream |
| DelayedUnsafeDeserialization.java:37:33:37:59 | new ObjectInputStream(...) : ObjectInputStream | semmle.label | new ObjectInputStream(...) : ObjectInputStream |
| DelayedUnsafeDeserialization.java:37:55:37:58 | bais : ByteArrayInputStream | semmle.label | bais : ByteArrayInputStream |
| DelayedUnsafeDeserialization.java:37:55:37:58 | bais : ByteArrayInputStream | semmle.label | bais : ByteArrayInputStream |
| DelayedUnsafeDeserialization.java:38:16:38:18 | ois | semmle.label | ois |
subpaths
| DelayedUnsafeDeserialization.java:10:21:10:43 | getInputStream(...) : InputStream | DelayedUnsafeDeserialization.java:27:22:27:38 | input : InputStream | DelayedUnsafeDeserialization.java:28:20:28:25 | this <.field> [post update] [buffer] : byte[] | DelayedUnsafeDeserialization.java:10:9:10:14 | holder [post update] [buffer] : byte[] |
| DelayedUnsafeDeserialization.java:18:21:18:25 | input : byte[] | DelayedUnsafeDeserialization.java:31:22:31:33 | input : byte[] | DelayedUnsafeDeserialization.java:32:9:32:14 | this <.field> [post update] [buffer] : byte[] | DelayedUnsafeDeserialization.java:18:9:18:14 | holder [post update] [buffer] : byte[] |
#select
| DelayedUnsafeDeserialization.java:38:16:38:31 | readObject(...) | DelayedUnsafeDeserialization.java:10:21:10:43 | getInputStream(...) : InputStream | DelayedUnsafeDeserialization.java:38:16:38:18 | ois | Delayed unsafe deserialization of $@. | DelayedUnsafeDeserialization.java:10:21:10:43 | getInputStream(...) | user input |
| DelayedUnsafeDeserialization.java:38:16:38:31 | readObject(...) | DelayedUnsafeDeserialization.java:17:9:17:31 | getInputStream(...) : InputStream | DelayedUnsafeDeserialization.java:38:16:38:18 | ois | Delayed unsafe deserialization of $@. | DelayedUnsafeDeserialization.java:17:9:17:31 | getInputStream(...) | user input |
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.Socket;

public class DelayedUnsafeDeserialization {

public void testWithInputStream(Socket socket) throws Exception {
ObjectHolder holder = new ObjectHolder();
holder.init(socket.getInputStream());
holder.getObject();
}

public void testWithByteArray(Socket socket) throws Exception {
ObjectHolder holder = new ObjectHolder();
byte[] input = new byte[1024];
socket.getInputStream().read(input, 0, input.length);
holder.init(input);
holder.getObject();
}
}

class ObjectHolder {

private byte[] buffer = new byte[1024];

public void init(InputStream input) throws Exception {
input.read(buffer, 0, buffer.length);
}

public void init(byte[] input) throws Exception {
buffer = input;
}

public Object getObject() throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
experimental/Security/CWE/CWE-502/DelayedUnsafeDeserialization.ql