-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into criemen/bzlmod-upgrades
- Loading branch information
Showing
36 changed files
with
1,193 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/** | ||
* https://github.com/google/brotli | ||
*/ | ||
|
||
import cpp | ||
import DecompressionBomb | ||
|
||
/** | ||
* The `BrotliDecoderDecompress` function is used in flow sink. | ||
* See https://www.brotli.org/decode.html. | ||
*/ | ||
class BrotliDecoderDecompressFunction extends DecompressionFunction { | ||
BrotliDecoderDecompressFunction() { this.hasGlobalName("BrotliDecoderDecompress") } | ||
|
||
override int getArchiveParameterIndex() { result = 1 } | ||
} | ||
|
||
/** | ||
* The `BrotliDecoderDecompressStream` function is used in flow sink. | ||
* See https://www.brotli.org/decode.html. | ||
*/ | ||
class BrotliDecoderDecompressStreamFunction extends DecompressionFunction { | ||
BrotliDecoderDecompressStreamFunction() { this.hasGlobalName("BrotliDecoderDecompressStream") } | ||
|
||
override int getArchiveParameterIndex() { result = 2 } | ||
} |
26 changes: 26 additions & 0 deletions
26
cpp/ql/src/experimental/Security/CWE/CWE-409/DecompressionBomb.qll
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import cpp | ||
import semmle.code.cpp.ir.dataflow.TaintTracking | ||
import MiniZip | ||
import ZlibGzopen | ||
import ZlibInflator | ||
import ZlibUncompress | ||
import LibArchive | ||
import ZSTD | ||
import Brotli | ||
|
||
/** | ||
* The Decompression Sink instances, extend this class to define new decompression sinks. | ||
*/ | ||
abstract class DecompressionFunction extends Function { | ||
abstract int getArchiveParameterIndex(); | ||
} | ||
|
||
/** | ||
* The Decompression Flow Steps, extend this class to define new decompression sinks. | ||
*/ | ||
abstract class DecompressionFlowStep extends string { | ||
bindingset[this] | ||
DecompressionFlowStep() { any() } | ||
|
||
abstract predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2); | ||
} |
39 changes: 39 additions & 0 deletions
39
cpp/ql/src/experimental/Security/CWE/CWE-409/DecompressionBombs.qhelp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<!DOCTYPE qhelp PUBLIC | ||
"-//Semmle//qhelp//EN" | ||
"qhelp.dtd"> | ||
<qhelp> | ||
<overview> | ||
<p>Extracting Compressed files with any compression algorithm like gzip can cause denial of service attacks.</p> | ||
<p>Attackers can compress a huge file consisting of repeated similiar bytes into a small compressed file.</p> | ||
</overview> | ||
<recommendation> | ||
|
||
<p>When you want to decompress a user-provided compressed file you must be careful about the decompression ratio or read these files within a loop byte by byte to be able to manage the decompressed size in each cycle of the loop.</p> | ||
|
||
</recommendation> | ||
<example> | ||
|
||
<p> | ||
Reading an uncompressed Gzip file within a loop and check for a threshold size in each cycle. | ||
</p> | ||
<sample src="example_good.cpp"/> | ||
|
||
<p> | ||
The following example is unsafe, as we do not check the uncompressed size. | ||
</p> | ||
<sample src="example_bad.cpp" /> | ||
|
||
</example> | ||
|
||
<references> | ||
|
||
<li> | ||
<a href="https://zlib.net/manual.html">Zlib documentation</a> | ||
</li> | ||
|
||
<li> | ||
<a href="https://www.bamsoftware.com/hacks/zipbomb/">An explanation of the attack</a> | ||
</li> | ||
|
||
</references> | ||
</qhelp> |
40 changes: 40 additions & 0 deletions
40
cpp/ql/src/experimental/Security/CWE/CWE-409/DecompressionBombs.ql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/** | ||
* @name User-controlled file decompression | ||
* @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous | ||
* @kind path-problem | ||
* @problem.severity error | ||
* @precision low | ||
* @id cpp/data-decompression-bomb | ||
* @tags security | ||
* experimental | ||
* external/cwe/cwe-409 | ||
*/ | ||
|
||
import cpp | ||
import semmle.code.cpp.security.FlowSources | ||
import DecompressionBomb | ||
|
||
predicate isSink(FunctionCall fc, DataFlow::Node sink) { | ||
exists(DecompressionFunction f | fc.getTarget() = f | | ||
fc.getArgument(f.getArchiveParameterIndex()) = [sink.asExpr(), sink.asIndirectExpr()] | ||
) | ||
} | ||
|
||
module DecompressionTaintConfig implements DataFlow::ConfigSig { | ||
predicate isSource(DataFlow::Node source) { source instanceof FlowSource } | ||
|
||
predicate isSink(DataFlow::Node sink) { isSink(_, sink) } | ||
|
||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { | ||
any(DecompressionFlowStep s).isAdditionalFlowStep(node1, node2) | ||
} | ||
} | ||
|
||
module DecompressionTaint = TaintTracking::Global<DecompressionTaintConfig>; | ||
|
||
import DecompressionTaint::PathGraph | ||
|
||
from DecompressionTaint::PathNode source, DecompressionTaint::PathNode sink, FunctionCall fc | ||
where DecompressionTaint::flowPath(source, sink) and isSink(fc, sink.getNode()) | ||
select sink.getNode(), source, sink, "The decompression output of $@ is not limited", fc, | ||
fc.getTarget().getName() |
32 changes: 32 additions & 0 deletions
32
cpp/ql/src/experimental/Security/CWE/CWE-409/LibArchive.qll
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/** | ||
* https://github.com/libarchive/libarchive/wiki | ||
*/ | ||
|
||
import cpp | ||
import DecompressionBomb | ||
|
||
/** | ||
* The `archive_read_data*` functions are used in flow sink. | ||
* See https://github.com/libarchive/libarchive/wiki/Examples. | ||
*/ | ||
class Archive_read_data_block extends DecompressionFunction { | ||
Archive_read_data_block() { | ||
this.hasGlobalName(["archive_read_data_block", "archive_read_data", "archive_read_data_into_fd"]) | ||
} | ||
|
||
override int getArchiveParameterIndex() { result = 0 } | ||
} | ||
|
||
/** | ||
* The `archive_read_open_filename` function as a flow step. | ||
*/ | ||
class ReadOpenFunctionStep extends DecompressionFlowStep { | ||
ReadOpenFunctionStep() { this = "ReadOpenFunction" } | ||
|
||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { | ||
exists(FunctionCall fc | fc.getTarget().hasGlobalName("archive_read_open_filename") | | ||
node1.asIndirectExpr() = fc.getArgument(1) and | ||
node2.asIndirectExpr() = fc.getArgument(0) | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/** | ||
* https://github.com/zlib-ng/minizip-ng | ||
*/ | ||
|
||
import cpp | ||
import DecompressionBomb | ||
|
||
/** | ||
* The `mz_zip_entry` function is used in flow sink. | ||
* See https://github.com/zlib-ng/minizip-ng/blob/master/doc/mz_zip.md. | ||
*/ | ||
class Mz_zip_entry extends DecompressionFunction { | ||
Mz_zip_entry() { this.hasGlobalName("mz_zip_entry_read") } | ||
|
||
override int getArchiveParameterIndex() { result = 1 } | ||
} | ||
|
||
/** | ||
* The `mz_zip_reader_entry_*` and `mz_zip_reader_save_all` functions are used in flow sink. | ||
* See https://github.com/zlib-ng/minizip-ng/blob/master/doc/mz_zip_rw.md. | ||
*/ | ||
class Mz_zip_reader_entry extends DecompressionFunction { | ||
Mz_zip_reader_entry() { | ||
this.hasGlobalName([ | ||
"mz_zip_reader_entry_save", "mz_zip_reader_entry_read", "mz_zip_reader_entry_save_process", | ||
"mz_zip_reader_entry_save_file", "mz_zip_reader_entry_save_buffer", "mz_zip_reader_save_all" | ||
]) | ||
} | ||
|
||
override int getArchiveParameterIndex() { result = 0 } | ||
} | ||
|
||
/** | ||
* The `UnzOpen*` functions are used in flow sink. | ||
*/ | ||
class UnzOpenFunction extends DecompressionFunction { | ||
UnzOpenFunction() { this.hasGlobalName(["UnzOpen", "unzOpen64", "unzOpen2", "unzOpen2_64"]) } | ||
|
||
override int getArchiveParameterIndex() { result = 0 } | ||
} | ||
|
||
/** | ||
* The `mz_zip_reader_open_file` and `mz_zip_reader_open_file_in_memory` functions as a flow step. | ||
*/ | ||
class ReaderOpenFunctionStep extends DecompressionFlowStep { | ||
ReaderOpenFunctionStep() { this = "ReaderOpenFunctionStep" } | ||
|
||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { | ||
exists(FunctionCall fc | | ||
fc.getTarget().hasGlobalName(["mz_zip_reader_open_file_in_memory", "mz_zip_reader_open_file"]) | ||
| | ||
node1.asIndirectExpr() = fc.getArgument(1) and | ||
node2.asIndirectExpr() = fc.getArgument(0) | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/** | ||
* https://github.com/facebook/zstd/blob/dev/examples/streaming_decompression.c | ||
*/ | ||
|
||
import cpp | ||
import DecompressionBomb | ||
|
||
/** | ||
* The `ZSTD_decompress` function is used in flow sink. | ||
*/ | ||
class ZstdDecompressFunction extends DecompressionFunction { | ||
ZstdDecompressFunction() { this.hasGlobalName("ZSTD_decompress") } | ||
|
||
override int getArchiveParameterIndex() { result = 2 } | ||
} | ||
|
||
/** | ||
* The `ZSTD_decompressDCtx` function is used in flow sink. | ||
*/ | ||
class ZstdDecompressDctxFunction extends DecompressionFunction { | ||
ZstdDecompressDctxFunction() { this.hasGlobalName("ZSTD_decompressDCtx") } | ||
|
||
override int getArchiveParameterIndex() { result = 3 } | ||
} | ||
|
||
/** | ||
* The `ZSTD_decompressStream` function is used in flow sink. | ||
*/ | ||
class ZstdDecompressStreamFunction extends DecompressionFunction { | ||
ZstdDecompressStreamFunction() { this.hasGlobalName("ZSTD_decompressStream") } | ||
|
||
override int getArchiveParameterIndex() { result = 2 } | ||
} | ||
|
||
/** | ||
* The `ZSTD_decompress_usingDDict` function is used in flow sink. | ||
*/ | ||
class ZstdDecompressUsingDdictFunction extends DecompressionFunction { | ||
ZstdDecompressUsingDdictFunction() { this.hasGlobalName("ZSTD_decompress_usingDDict") } | ||
|
||
override int getArchiveParameterIndex() { result = 3 } | ||
} | ||
|
||
/** | ||
* The `fopen_orDie` function as a flow step. | ||
*/ | ||
class FopenOrDieFunctionStep extends DecompressionFlowStep { | ||
FopenOrDieFunctionStep() { this = "FopenOrDieFunctionStep" } | ||
|
||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { | ||
exists(FunctionCall fc | fc.getTarget().hasGlobalName("fopen_orDie") | | ||
node1.asIndirectExpr() = fc.getArgument(0) and | ||
node2.asExpr() = fc | ||
) | ||
} | ||
} | ||
|
||
/** | ||
* The `fread_orDie` function as a flow step. | ||
*/ | ||
class FreadOrDieFunctionStep extends DecompressionFlowStep { | ||
FreadOrDieFunctionStep() { this = "FreadOrDieFunctionStep" } | ||
|
||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { | ||
exists(FunctionCall fc | fc.getTarget().hasGlobalName("fread_orDie") | | ||
node1.asExpr() = fc.getArgument(2) and | ||
node2.asIndirectExpr() = fc.getArgument(0) | ||
) | ||
} | ||
} | ||
|
||
/** | ||
* The `src` member of a `ZSTD_inBuffer` variable is used in a flow steps. | ||
*/ | ||
class SrcMember extends DecompressionFlowStep { | ||
SrcMember() { this = "SrcMember" } | ||
|
||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { | ||
exists(VariableAccess inBufferAccess, Field srcField, ClassAggregateLiteral c | | ||
inBufferAccess.getType().hasName("ZSTD_inBuffer") and | ||
srcField.hasName("src") | ||
| | ||
node2.asExpr() = inBufferAccess and | ||
inBufferAccess.getTarget().getInitializer().getExpr() = c and | ||
node1.asIndirectExpr() = c.getFieldExpr(srcField, _) | ||
) | ||
} | ||
} |
71 changes: 71 additions & 0 deletions
71
cpp/ql/src/experimental/Security/CWE/CWE-409/ZlibGzopen.qll
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
/** | ||
* https://www.zlib.net/ | ||
*/ | ||
|
||
import cpp | ||
import DecompressionBomb | ||
|
||
/** | ||
* The `gzfread` function is used in flow sink. | ||
* | ||
* `gzfread(voidp buf, z_size_t size, z_size_t nitems, gzFile file)` | ||
*/ | ||
class GzFreadFunction extends DecompressionFunction { | ||
GzFreadFunction() { this.hasGlobalName("gzfread") } | ||
|
||
override int getArchiveParameterIndex() { result = 3 } | ||
} | ||
|
||
/** | ||
* The `gzgets` function is used in flow sink. | ||
* | ||
* `gzgets(gzFile file, char *buf, int len)` | ||
*/ | ||
class GzGetsFunction extends DecompressionFunction { | ||
GzGetsFunction() { this.hasGlobalName("gzgets") } | ||
|
||
override int getArchiveParameterIndex() { result = 0 } | ||
} | ||
|
||
/** | ||
* The `gzread` function is used in flow sink. | ||
* | ||
* `gzread(gzFile file, voidp buf, unsigned len)` | ||
*/ | ||
class GzReadFunction extends DecompressionFunction { | ||
GzReadFunction() { this.hasGlobalName("gzread") } | ||
|
||
override int getArchiveParameterIndex() { result = 0 } | ||
} | ||
|
||
/** | ||
* The `gzdopen` function is used in flow steps. | ||
* | ||
* `gzdopen(int fd, const char *mode)` | ||
*/ | ||
class GzdopenFunctionStep extends DecompressionFlowStep { | ||
GzdopenFunctionStep() { this = "GzdopenFunctionStep" } | ||
|
||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { | ||
exists(FunctionCall fc | fc.getTarget().hasGlobalName("gzdopen") | | ||
node1.asExpr() = fc.getArgument(0) and | ||
node2.asExpr() = fc | ||
) | ||
} | ||
} | ||
|
||
/** | ||
* The `gzopen` function is used in flow steps. | ||
* | ||
* `gzopen(const char *path, const char *mode)` | ||
*/ | ||
class GzopenFunctionStep extends DecompressionFlowStep { | ||
GzopenFunctionStep() { this = "GzopenFunctionStep" } | ||
|
||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { | ||
exists(FunctionCall fc | fc.getTarget().hasGlobalName("gzopen") | | ||
node1.asIndirectExpr() = fc.getArgument(0) and | ||
node2.asExpr() = fc | ||
) | ||
} | ||
} |
Oops, something went wrong.