Skip to content
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

cpp bombs test #17376

Closed
wants to merge 50 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
4a37da3
V1
am0o0 Jun 25, 2023
430375e
fix a commit mistake
am0o0 Jun 25, 2023
ae98510
add more source and sinks and sanitizers
am0o0 Jun 25, 2023
3ddc9a8
fix warnings, more sinks,sources,comments
am0o0 Jun 25, 2023
f715a34
better examples
am0o0 Jun 25, 2023
042133a
add queries for more popular libs
am0o0 Jul 2, 2023
d4d505d
complete the minizip query
am0o0 Jul 3, 2023
56bc32f
add libarchive
am0o0 Jul 3, 2023
16be908
add Miniz
am0o0 Jul 3, 2023
065c527
update Miniz
am0o0 Jul 3, 2023
e0798b2
stash: change sinks to zip handles and sources to the zip handle init…
am0o0 Jul 4, 2023
e37ceac
merge all query files into one query file
am0o0 Jun 7, 2024
a5c9dc7
Merge branch 'github:main' into amammad-cpp-bombs
am0o0 Jun 7, 2024
184aa04
Merge branch 'amammad-cpp-bombs' of https://github.com/amammad/codeql…
am0o0 Jun 7, 2024
a536328
add implicit this
am0o0 Jun 7, 2024
273848c
remove old comments
am0o0 Jun 7, 2024
11a416e
add FlowSources as a common source for all sinks, so we don't need St…
am0o0 Jun 13, 2024
13f697c
relocate the query
am0o0 Jun 25, 2024
656dc4e
use abstract class for decompression sinks
am0o0 Jun 25, 2024
361ad6b
use abstract class for decompression flow steps
am0o0 Jun 26, 2024
87b6495
add zlib tests with stubs :)
am0o0 Jul 14, 2024
a10b502
fix tests, it is not fixed 100%
am0o0 Jul 15, 2024
6f8eec2
Merge branch 'github:main' into amammad-cpp-bombs
am0o0 Jul 28, 2024
f97b103
update test files, add one more additional flow step for inflate func…
am0o0 Jul 30, 2024
89e842b
finilize tests for zlib
am0o0 Sep 3, 2024
8c1c537
finilize tests for zlib
am0o0 Sep 3, 2024
49eaaf5
Merge branch 'amammad-cpp-bombs' of https://github.com/am0o0/codeql i…
am0o0 Sep 3, 2024
e85ca79
add tests for brotli
am0o0 Sep 3, 2024
9531701
delete miniz support because there is no good documents and i don't h…
am0o0 Sep 3, 2024
6c97096
remove unused imports, add tests for libarchive
am0o0 Sep 3, 2024
4fc971d
remove xz(lzma)
am0o0 Sep 3, 2024
81283d5
remove more unused imports, add tests for zstd, add flow steps for zstd
am0o0 Sep 3, 2024
386e45a
delete bzip2 as it is not updated for more than three years so it is …
am0o0 Sep 3, 2024
50d9e77
C++: Move experimental files into the correct locations
jketema Sep 4, 2024
d526f1d
C++: Disentangle confusing test results by declaring only a single `m…
jketema Sep 4, 2024
751e7e6
C++: Remove useless function bodies from tests
jketema Sep 4, 2024
d8a70d8
C++: Add test annotations
jketema Sep 4, 2024
ad3605c
C++: Minor test clean up
jketema Sep 4, 2024
078e635
C++: Remove code that is irrelevant for the zlib test
jketema Sep 4, 2024
09f6576
C++: Simplify libarchive test
jketema Sep 4, 2024
0f98e29
C++: Cleanup minizip test
jketema Sep 4, 2024
c048401
C++: Clean up Brotli test
jketema Sep 4, 2024
084dbc4
C++: Rename qhelp file to match ql file
jketema Sep 4, 2024
65fafbf
C++: Fix QL-for-QL warnings
jketema Sep 4, 2024
8d22d14
C++: Clean up QLDoc
jketema Sep 4, 2024
8fe0d0a
C++: Improve query output
jketema Sep 4, 2024
2369b18
C++: Make additional flow steps more uniform
jketema Sep 4, 2024
92c6170
C++: Simplify QLhelp
jketema Sep 4, 2024
238895e
C++: Fix formatting
jketema Sep 4, 2024
9b905d5
C++: Set precision to low
jketema Sep 4, 2024
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
26 changes: 26 additions & 0 deletions cpp/ql/src/experimental/Security/CWE/CWE-409/Brotli.qll
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 }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
Fixed Show fixed Hide fixed
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);
}
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>
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 cpp/ql/src/experimental/Security/CWE/CWE-409/LibArchive.qll
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)
)
}
}
56 changes: 56 additions & 0 deletions cpp/ql/src/experimental/Security/CWE/CWE-409/MiniZip.qll
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)
)
}
}
70 changes: 70 additions & 0 deletions cpp/ql/src/experimental/Security/CWE/CWE-409/ZSTD.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* 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.asIndirectExpr() = fc.getArgument(2) and
node2.asIndirectExpr() = fc.getArgument(0)
)
}
}
71 changes: 71 additions & 0 deletions cpp/ql/src/experimental/Security/CWE/CWE-409/ZlibGzopen.qll
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
)
}
}
Loading
Loading