This commit is contained in:
amammad
2023-09-30 05:56:32 +10:00
parent 3e9c3e6987
commit 83cffea1a1

View File

@@ -24,29 +24,53 @@ import java
* Providing Decompression sinks and additional taint steps for `org.xerial.snappy` package
*/
module XserialSnappy {
/**
* A type that is responsible for `SnappyInputStream` Class
*/
class TypeInputStream extends RefType {
TypeInputStream() {
this.getASupertype*().hasQualifiedName("org.xerial.snappy", "SnappyInputStream")
}
}
/**
* Gets `n1` and `n2` which `SnappyInputStream n2 = new SnappyInputStream(n2)` or
* `n1.read(n2)`,
* second one is added because of sanitizer, we want to compare return value of each `read` or similar method
* that whether there is a flow to a comparison between total read of decompressed stream and a constant value
*/
predicate inputStreamAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(Call call |
(
call.getCallee().getDeclaringType() instanceof TypeInputStream or
call.(MethodAccess).getReceiverType() instanceof TypeInputStream
) and
// Constructors
call.getCallee().getDeclaringType() = any(TypeInputStream t) and
call.getArgument(0) = n1.asExpr() and
call = n2.asExpr()
or
// Method calls
call.(MethodAccess).getReceiverType() = any(TypeInputStream t) and
call.getCallee().hasName(["read", "readNBytes", "readAllBytes"]) and
(
call.getArgument(0) = n2.asExpr() and
call.getQualifier() = n1.asExpr()
or
call.getArgument(0) = n1.asExpr() and
call = n2.asExpr()
)
)
}
/**
* The methods that read bytes and belong to `SnappyInputStream` Types
*/
class ReadInputStreamCall extends MethodAccess {
ReadInputStreamCall() {
this.getReceiverType() instanceof TypeInputStream and
this.getCallee().hasName(["read", "readNBytes", "readAllBytes"])
}
/**
* An Argument which responsible for the destination of decompressed bytes and is used as a sink
*/
Expr getAWriteArgument() { result = this.getArgument(0) }
// look at Zip4j comments for this method
@@ -58,6 +82,9 @@ module XserialSnappy {
* Providing Decompression sinks and additional taint steps for `org.apache.commons.compress` package
*/
module ApacheCommons {
/**
* A type that is responsible for `ArchiveInputStream` Class
*/
class TypeArchiveInputStream extends RefType {
TypeArchiveInputStream() {
this.getASupertype*()
@@ -65,6 +92,9 @@ module ApacheCommons {
}
}
/**
* A type that is responsible for `CompressorInputStream` Class
*/
class TypeCompressorInputStream extends RefType {
TypeCompressorInputStream() {
this.getASupertype*()
@@ -72,7 +102,13 @@ module ApacheCommons {
}
}
/**
* Providing Decompression sinks and additional taint steps for `org.apache.commons.compress.compressors.*` Types
*/
module Compressors {
/**
* The types that are responsible for specific compression format of `CompressorInputStream` Class
*/
class TypeCompressors extends RefType {
TypeCompressors() {
this.getASupertype*()
@@ -113,23 +149,44 @@ module ApacheCommons {
}
}
/**
* Gets `n1` and `n2` which `*CompressorInputStream n2 = new *CompressorInputStream(n2)` or
* `n2 = inputStream.read(n1)` or `n1.read(n2)`,
* second one is added because of sanitizer, we want to compare return value of each `read` or similar method
* that whether there is a flow to a comparison between total read of decompressed stream and a constant value
*/
predicate inputStreamAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(Call call |
(
call.getCallee().getDeclaringType() instanceof TypeCompressors or
call.(MethodAccess).getReceiverType() instanceof TypeCompressors
) and
// Constructors
call.getCallee().getDeclaringType() = any(TypeCompressors t) and
call.getArgument(0) = n1.asExpr() and
call = n2.asExpr()
or
// Method calls
call.(MethodAccess).getReceiverType() = any(TypeCompressors t) and
call.getCallee().hasName(["read", "readNBytes", "readAllBytes"]) and
(
call.getArgument(0) = n2.asExpr() and
call.getQualifier() = n1.asExpr()
or
call.getArgument(0) = n1.asExpr() and
call = n2.asExpr()
)
)
}
/**
* The methods that read bytes and belong to `*CompressorInputStream` Types
*/
class ReadInputStreamCall extends MethodAccess {
ReadInputStreamCall() {
this.getReceiverType() instanceof TypeCompressors and
this.getCallee().hasName(["read", "readNBytes", "readAllBytes"])
}
/**
* An Argument which responsible for the destination of decompressed bytes and is used as a sink
*/
Expr getAWriteArgument() { result = this.getArgument(0) }
// look at Zip4j comments for this method
@@ -137,7 +194,13 @@ module ApacheCommons {
}
}
/**
* Providing Decompression sinks and additional taint steps for Types from `org.apache.commons.compress.archivers.*` packages
*/
module Archivers {
/**
* The types that are responsible for specific compression format of `ArchiveInputStream` Class
*/
class TypeArchivers extends RefType {
TypeArchivers() {
this.getASupertype*()
@@ -156,32 +219,43 @@ module ApacheCommons {
}
/**
*```java
* ZipArchiveInputStream n2 = new ZipArchiveInputStream(n1);
* ZipArchiveInputStream n = new ZipArchiveInputStream(inputStream);
* n2 = n.read(n1);
*```
* Gets `n1` and `n2` which `*ArchiveInputStream n2 = new *ArchiveInputStream(n2)` or
* `n2 = inputStream.read(n2)` or `n1.read(n2)`,
* second one is added because of sanitizer, we want to compare return value of each `read` or similar method
* that whether there is a flow to a comparison between total read of decompressed stream and a constant value
*/
predicate inputStreamAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(Call call |
// Constructors
call.getCallee().getDeclaringType() = any(TypeArchivers t) and
call.getArgument(0) = n1.asExpr() and
call = n2.asExpr()
or
// Method calls
call.(MethodAccess).getReceiverType() = any(TypeArchivers t) and
call.getCallee().hasName(["read", "readNBytes", "readAllBytes"]) and
(
// constructors
call.getCallee().getDeclaringType() instanceof TypeArchivers
call.getArgument(0) = n2.asExpr() and
call.getQualifier() = n1.asExpr()
or
// Method calls
call.(MethodAccess).getReceiverType() instanceof TypeArchivers
) and
n1.asExpr() = call.getArgument(0) and
n2.asExpr() = call
call.getArgument(0) = n1.asExpr() and
call = n2.asExpr()
)
)
}
/**
* The methods that read bytes and belong to `*ArchiveInputStream` Types
*/
class ReadInputStreamCall extends MethodAccess {
ReadInputStreamCall() {
this.getReceiverType() instanceof TypeArchivers and
this.getCallee().hasName(["read", "readNBytes", "readAllBytes"])
}
/**
* An Argument which responsible for the destination of decompressed bytes and is used as a sink
*/
Expr getAWriteArgument() { result = this.getArgument(0) }
// look at Zip4j comments for this method
@@ -189,7 +263,13 @@ module ApacheCommons {
}
}
/**
* Providing Decompression sinks and additional taint steps for `CompressorStreamFactory` and `ArchiveStreamFactory` Types
*/
module Factory {
/**
* A type that is responsible for `ArchiveInputStream` Class
*/
class TypeArchivers extends RefType {
TypeArchivers() {
this.getASupertype*()
@@ -197,6 +277,9 @@ module ApacheCommons {
}
}
/**
* A type that is responsible for `CompressorStreamFactory` Class
*/
class TypeCompressors extends RefType {
TypeCompressors() {
this.getASupertype*()
@@ -205,29 +288,43 @@ module ApacheCommons {
}
/**
* ```java
*CompressorInputStream n2 = new CompressorStreamFactory().createCompressorInputStream(n1)
*ArchiveInputStream n2 = new ArchiveStreamFactory().createArchiveInputStream(n1)
* ```
* Gets `n1` and `n2` which `CompressorInputStream n2 = new CompressorStreamFactory().createCompressorInputStream(n1)`
* or `ArchiveInputStream n2 = new ArchiveStreamFactory().createArchiveInputStream(n1)` or
* `n1.read(n2)`,
* second one is added because of sanitizer, we want to compare return value of each `read` or similar method
* that whether there is a flow to a comparison between total read of decompressed stream and a constant value
*/
predicate inputStreamAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(Call call |
// Constructors
(
// Constructors
call.getCallee().getDeclaringType() = any(TypeCompressors t)
or
call.getCallee().getDeclaringType() = any(TypeArchivers t)
or
// Method calls
) and
call.getArgument(0) = n1.asExpr() and
call = n2.asExpr()
or
// Method calls
(
call.(MethodAccess).getReceiverType() = any(TypeArchiveInputStream t)
or
call.(MethodAccess).getReceiverType() = any(TypeCompressorInputStream t)
) and
n1.asExpr() = call.getArgument(0) and
n2.asExpr() = call
call.getCallee().hasName(["read", "readNBytes", "readAllBytes"]) and
(
call.getArgument(0) = n2.asExpr() and
call.getQualifier() = n1.asExpr()
or
call.getArgument(0) = n1.asExpr() and
call = n2.asExpr()
)
)
}
/**
* The methods that read bytes and belong to `CompressorInputStream` or `ArchiveInputStream` Types
*/
class ReadInputStreamCall extends MethodAccess {
ReadInputStreamCall() {
(
@@ -238,6 +335,9 @@ module ApacheCommons {
this.getCallee().hasName(["read", "readNBytes", "readAllBytes"])
}
/**
* An Argument which responsible for the destination of decompressed bytes and is used as a sink
*/
Expr getAWriteArgument() { result = this.getArgument(0) }
// look at Zip4j comments for this method
@@ -250,6 +350,9 @@ module ApacheCommons {
* Providing Decompression sinks and additional taint steps for `net.lingala.zip4j.io` package
*/
module Zip4j {
/**
* A type that is responsible for `ZipInputStream` Class
*/
class TypeZipInputStream extends RefType {
TypeZipInputStream() {
this.hasQualifiedName("net.lingala.zip4j.io.inputstream", "ZipInputStream")
@@ -257,10 +360,7 @@ module Zip4j {
}
/**
* ```java
* n = new net.lingala.zip4j.io.inputstream.ZipInputStream(inputStream);
* this = n.read(readBuffer);
* ```
* The methods that read bytes and belong to `CompressorInputStream` or `ArchiveInputStream` Types
*/
class ReadInputStreamCall extends MethodAccess {
ReadInputStreamCall() {
@@ -268,6 +368,9 @@ module Zip4j {
this.getMethod().hasName(["read", "readNBytes", "readAllBytes"])
}
/**
* An Argument which responsible for the destination of decompressed bytes and is used as a sink
*/
Expr getAWriteArgument() { result = this.getArgument(0) }
// while ((readLen = zipInputStream.read(readBuffer)) != -1) {
@@ -288,22 +391,27 @@ module Zip4j {
}
/**
* ```java
* n2 = new net.lingala.zip4j.io.inputstream.ZipInputStream(n1);
* // or
* n = new net.lingala.zip4j.io.inputstream.ZipInputStream(inputStream);
* n2 = n.Method(n1);
* ```
* Gets `n1` and `n2` which `ZipInputStream n2 = new ZipInputStream(n1)` or `n2 = zipInputStream.read(n1)` or `n1.read(n2)`,
* second one is added because of sanitizer, we want to compare return value of each `read` or similar method
* that whether there is a flow to a comparison between total read of decompressed stream and a constant value
*/
predicate inputStreamAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(Call call |
(
call.getCallee().getDeclaringType() instanceof TypeZipInputStream or
call.(MethodAccess).getReceiverType() instanceof TypeZipInputStream
) and
// Constructors
call.getCallee().getDeclaringType() = any(TypeZipInputStream t) and
call.getArgument(0) = n1.asExpr() and
call = n2.asExpr()
or
// Method calls
call.(MethodAccess).getReceiverType() = any(TypeZipInputStream t) and
call.getCallee().hasName(["read", "readNBytes", "readAllBytes"]) and
call.getArgument(0) = n2.asExpr() and
call.getQualifier() = n1.asExpr()
(
call.getArgument(0) = n2.asExpr() and
call.getQualifier() = n1.asExpr()
or
call.getArgument(0) = n1.asExpr() and
call = n2.asExpr()
)
)
}
}
@@ -312,6 +420,9 @@ module Zip4j {
* Providing sinks that can be related to reading uncontrolled buffer and bytes for `org.apache.commons.io` package
*/
module CommonsIO {
/**
* The Access to Methods which work with byes and inputStreams and buffers
*/
class IOUtils extends MethodAccess {
IOUtils() {
this.getMethod()
@@ -328,6 +439,9 @@ module CommonsIO {
* Providing Decompression sinks and additional taint steps for `java.util.zip` package
*/
module Zip {
/**
* The Types that are responsible for `ZipInputStream`, `GZIPInputStream`, `InflaterInputStream` Classes
*/
class TypeInputStream extends RefType {
TypeInputStream() {
this.getASupertype*()
@@ -336,44 +450,65 @@ module Zip {
}
}
/**
* The methods that read bytes and belong to `*InputStream` Types
*/
class ReadInputStreamCall extends MethodAccess {
ReadInputStreamCall() {
this.getReceiverType() instanceof TypeInputStream and
this.getCallee().hasName(["read", "readNBytes", "readAllBytes"])
}
/**
* An Argument which responsible for the destination of decompressed bytes and is used as a sink
*/
Expr getAWriteArgument() { result = this.getArgument(0) }
// look at Zip4j comments for this method
predicate isControlledRead() { none() }
}
// *InputStream Izis = new *InputStream(inputStream)
/**
* Gets `n1` and `n2` which `*InputStream n2 = new *InputStream(n1)` or
* `n2 = data.read(n1, 0, BUFFER)` or `n1.read(n2, 0, BUFFER)`,
* second one is added because of sanitizer, we want to compare return value of each `read` or similar method
* that whether there is a flow to a comparison between total read of decompressed stream and a constant value
*/
predicate inputStreamAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(Call call |
(
call.getCallee().getDeclaringType() instanceof TypeInputStream or
call.(MethodAccess).getReceiverType() instanceof TypeInputStream
) and
// Constructors
call.getCallee().getDeclaringType() = any(TypeInputStream t) and
call.getArgument(0) = n1.asExpr() and
call = n2.asExpr()
or
// Method calls
call.(MethodAccess).getReceiverType() = any(TypeInputStream t) and
call.getCallee().hasName(["read", "readNBytes", "readAllBytes"]) and
(
call.getArgument(0) = n2.asExpr() and
call.getQualifier() = n1.asExpr()
or
call.getArgument(0) = n1.asExpr() and
call = n2.asExpr()
)
)
}
/**
* A type that is responsible for `Inflater` Class
*/
class TypeInflator extends RefType {
TypeInflator() { this.hasQualifiedName("java.util.zip", "Inflater") }
}
class Inflatorsource extends Call {
Inflatorsource() {
exists(Call c | c.getCallee().(Constructor).getDeclaringType() instanceof TypeInflator |
this = c
)
}
}
/**
* Gets `n1` and `n2` which `Inflater inflater_As_n2 = new Inflater(); inflater_As_n2 = inflater.setInput(n1)` or `n1.inflate(n2)` or
* `n2 = inflater.inflate(n1)`,
* third one is added because of sanitizer, we want to compare return value of each `read` or similar method
* that whether there is a flow to a comparison between total read of decompressed stream and a constant value
*/
predicate inflatorAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
// inflater.inflate(n2)
// n1.inflate(n2)
exists(MethodAccess ma |
ma.getReceiverType() instanceof TypeInflator and
ma.getArgument(0) = n2.asExpr() and
@@ -381,8 +516,16 @@ module Zip {
ma.getCallee().hasName("inflate")
)
or
// n2 = inflater.inflate(n1)
exists(MethodAccess ma |
ma.getReceiverType() instanceof TypeInflator and
ma = n2.asExpr() and
ma.getArgument(0) = n1.asExpr() and
ma.getCallee().hasName("inflate")
)
or
// Inflater inflater = new Inflater();
// n2 = inflater.setInput(n1)
// inflater_As_n2 = inflater.setInput(n1)
exists(MethodAccess ma |
ma.getReceiverType() instanceof TypeInflator and
n1.asExpr() = ma.getArgument(0) and
@@ -391,32 +534,35 @@ module Zip {
)
}
/**
* The methods that read bytes and belong to `Inflater` Type
*/
class InflateCall extends MethodAccess {
InflateCall() {
this.getReceiverType() instanceof TypeInflator and
this.getCallee().hasName("inflate")
}
/**
* An Argument which responsible for the destination of decompressed bytes and is used as a sink
*/
Expr getAWriteArgument() { result = this.getArgument(0) }
// look at Zip4j comments for this method
predicate isControlledRead() { none() }
}
/**
* A type that is responsible for `ZipFile` Class
*/
class TypeZipFile extends RefType {
TypeZipFile() { this.hasQualifiedName("java.util.zip", "ZipFile") }
}
class ZipFilesource extends Call {
ZipFilesource() {
exists(Call c | c.getCallee().(Constructor).getDeclaringType() instanceof TypeZipFile |
this = c
)
}
}
// ZipFile zipFileAsn2 = new ZipFile(n1);
// InputStream n2 = zipFile.getInputStream(n1);
/**
* Gets `n1` and `n2` which `ZipFile n2 = new ZipFile(n1);` or
* `InputStream n2 = zipFile.getInputStream(n1);` or `zipFile_As_n1.getInputStream(n2);`
*/
predicate zipFileadditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(MethodAccess ma |
ma.getReceiverType() instanceof TypeZipFile and
@@ -433,6 +579,50 @@ module Zip {
}
}
/**
* Providing InputStream and it subClasses that mostly related to Sinks of ZipFile Type,
* we can do
*/
module InputStream {
/**
* The Types that are responsible for `InputStream` Class and all classes that are child of InputStream Class
*/
class TypeInputStream extends RefType {
TypeInputStream() { this.getASupertype*().hasQualifiedName("java.io", "InputStream") }
}
/**
* The methods that read bytes and belong to `InputStream` Type and all Types that are child of InputStream Type
*/
class Read extends MethodAccess {
Read() {
this.getReceiverType() instanceof TypeInputStream and
this.getCallee().hasName(["read", "readNBytes", "readAllBytes"])
}
}
/**
* general additional taint steps for all inputStream and all Types that are child of inputStream
*/
predicate additionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(Call call |
// Method calls
call.(MethodAccess).getReceiverType() = any(InputStream::TypeInputStream t) and
call.getCallee().hasName(["read", "readNBytes", "readAllBytes"]) and
(
// call.getArgument(0) = n2.asExpr() and
// call.getQualifier() = n1.asExpr()
// or
// call.getArgument(0) = n1.asExpr() and
// call = n2.asExpr()
// or
// TODO: only implement following
call.getQualifier() = n1.asExpr() and
call = n2.asExpr()
)
)
}
}
module DecompressionBombsConfig implements DataFlow::StateConfigSig {
class FlowState = DataFlow::FlowState;
@@ -483,9 +673,6 @@ module DecompressionBombsConfig implements DataFlow::StateConfigSig {
)
}
// predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// inputStreamAdditionalTaintStep(nodeFrom, nodeTo)
// }
predicate isAdditionalFlowStep(
DataFlow::Node nodeFrom, FlowState stateFrom, DataFlow::Node nodeTo, FlowState stateTo
) {