Skip to content

Commit 4fa4624

Browse files
authored
Merge pull request #1 from jketema/amammad-cpp-bombs
Cleanup cpp bombs
2 parents 386e45a + 9b905d5 commit 4fa4624

29 files changed

+522
-1230
lines changed

cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/Brotli.qll renamed to cpp/ql/src/experimental/Security/CWE/CWE-409/Brotli.qll

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,24 @@
33
*/
44

55
import cpp
6-
import semmle.code.cpp.ir.dataflow.TaintTracking
76
import DecompressionBomb
87

98
/**
10-
* The `BrotliDecoderDecompress` function is used in flow sink. * Ref: https://www.brotli.org/decode.html#af68
9+
* The `BrotliDecoderDecompress` function is used in flow sink.
10+
* See https://www.brotli.org/decode.html.
1111
*/
1212
class BrotliDecoderDecompressFunction extends DecompressionFunction {
13-
BrotliDecoderDecompressFunction() { this.hasGlobalName(["BrotliDecoderDecompress"]) }
13+
BrotliDecoderDecompressFunction() { this.hasGlobalName("BrotliDecoderDecompress") }
1414

1515
override int getArchiveParameterIndex() { result = 1 }
1616
}
1717

1818
/**
19-
* The `BrotliDecoderDecompressStream` function is used in flow sink. * Ref: https://www.brotli.org/decode.html#a234
19+
* The `BrotliDecoderDecompressStream` function is used in flow sink.
20+
* See https://www.brotli.org/decode.html.
2021
*/
2122
class BrotliDecoderDecompressStreamFunction extends DecompressionFunction {
22-
BrotliDecoderDecompressStreamFunction() { this.hasGlobalName(["BrotliDecoderDecompressStream"]) }
23+
BrotliDecoderDecompressStreamFunction() { this.hasGlobalName("BrotliDecoderDecompressStream") }
2324

2425
override int getArchiveParameterIndex() { result = 2 }
2526
}

cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBomb.qll renamed to cpp/ql/src/experimental/Security/CWE/CWE-409/DecompressionBomb.qll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ abstract class DecompressionFunction extends Function {
1818
/**
1919
* The Decompression Flow Steps, extend this class to define new decompression sinks.
2020
*/
21-
abstract class DecompressionFlowStep extends Function {
21+
abstract class DecompressionFlowStep extends string {
22+
bindingset[this]
23+
DecompressionFlowStep() { any() }
24+
2225
abstract predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2);
2326
}

cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBomb.qhelp renamed to cpp/ql/src/experimental/Security/CWE/CWE-409/DecompressionBombs.qhelp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
"qhelp.dtd">
44
<qhelp>
55
<overview>
6-
<p>Extracting Compressed files with any compression algorithm like gzip can cause to denial of service attacks.</p>
7-
<p>Attackers can compress a huge file which created by repeated similiar byte and convert it to a small compressed file.</p>
6+
<p>Extracting Compressed files with any compression algorithm like gzip can cause denial of service attacks.</p>
7+
<p>Attackers can compress a huge file consisting of repeated similiar bytes into a small compressed file.</p>
88
</overview>
99
<recommendation>
1010

@@ -14,12 +14,12 @@
1414
<example>
1515

1616
<p>
17-
Reading uncompressed Gzip file within a loop and check for a threshold size in each cycle.
17+
Reading an uncompressed Gzip file within a loop and check for a threshold size in each cycle.
1818
</p>
1919
<sample src="example_good.cpp"/>
2020

2121
<p>
22-
An Unsafe Approach can be this example which we don't check for uncompressed size.
22+
The following example is unsafe, as we do not check the uncompressed size.
2323
</p>
2424
<sample src="example_bad.cpp" />
2525

@@ -28,11 +28,11 @@ An Unsafe Approach can be this example which we don't check for uncompressed siz
2828
<references>
2929

3030
<li>
31-
<a href="https://zlib.net/manual.html">Zlib Documentation</a>
31+
<a href="https://zlib.net/manual.html">Zlib documentation</a>
3232
</li>
3333

3434
<li>
35-
<a href="https://www.bamsoftware.com/hacks/zipbomb/">A great research to gain more impact by this kind of attacks</a>
35+
<a href="https://www.bamsoftware.com/hacks/zipbomb/">An explanation of the attack</a>
3636
</li>
3737

3838
</references>

cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/DecompressionBombs.ql renamed to cpp/ql/src/experimental/Security/CWE/CWE-409/DecompressionBombs.ql

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,38 @@
33
* @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
44
* @kind path-problem
55
* @problem.severity error
6-
* @security-severity 7.8
7-
* @precision high
8-
* @id cpp/data-decompression
6+
* @precision low
7+
* @id cpp/data-decompression-bomb
98
* @tags security
109
* experimental
1110
* external/cwe/cwe-409
1211
*/
1312

1413
import cpp
15-
import semmle.code.cpp.ir.dataflow.TaintTracking
1614
import semmle.code.cpp.security.FlowSources
1715
import DecompressionBomb
1816

17+
predicate isSink(FunctionCall fc, DataFlow::Node sink) {
18+
exists(DecompressionFunction f | fc.getTarget() = f |
19+
fc.getArgument(f.getArchiveParameterIndex()) = [sink.asExpr(), sink.asIndirectExpr()]
20+
)
21+
}
22+
1923
module DecompressionTaintConfig implements DataFlow::ConfigSig {
2024
predicate isSource(DataFlow::Node source) { source instanceof FlowSource }
2125

22-
predicate isSink(DataFlow::Node sink) {
23-
exists(FunctionCall fc, DecompressionFunction f | fc.getTarget() = f |
24-
fc.getArgument(f.getArchiveParameterIndex()) = [sink.asExpr(), sink.asIndirectExpr()]
25-
)
26-
}
26+
predicate isSink(DataFlow::Node sink) { isSink(_, sink) }
2727

2828
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
29-
any(DecompressionFlowStep f).isAdditionalFlowStep(node1, node2) or
30-
nextInAdditionalFlowStep(node1, node2)
29+
any(DecompressionFlowStep s).isAdditionalFlowStep(node1, node2)
3130
}
3231
}
3332

3433
module DecompressionTaint = TaintTracking::Global<DecompressionTaintConfig>;
3534

3635
import DecompressionTaint::PathGraph
3736

38-
from DecompressionTaint::PathNode source, DecompressionTaint::PathNode sink
39-
where DecompressionTaint::flowPath(source, sink)
40-
select sink.getNode(), source, sink, "This Decompression output $@.", source.getNode(),
41-
"is not limited"
37+
from DecompressionTaint::PathNode source, DecompressionTaint::PathNode sink, FunctionCall fc
38+
where DecompressionTaint::flowPath(source, sink) and isSink(fc, sink.getNode())
39+
select sink.getNode(), source, sink, "The decompression output of $@ is not limited", fc,
40+
fc.getTarget().getName()

cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/LibArchive.qll renamed to cpp/ql/src/experimental/Security/CWE/CWE-409/LibArchive.qll

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
*/
44

55
import cpp
6-
import semmle.code.cpp.ir.dataflow.TaintTracking
76
import DecompressionBomb
87

98
/**
109
* The `archive_read_data*` functions are used in flow sink.
11-
* [Examples](https://github.com/libarchive/libarchive/wiki/Examples)
10+
* See https://github.com/libarchive/libarchive/wiki/Examples.
1211
*/
1312
class Archive_read_data_block extends DecompressionFunction {
1413
Archive_read_data_block() {
@@ -21,11 +20,11 @@ class Archive_read_data_block extends DecompressionFunction {
2120
/**
2221
* The `archive_read_open_filename` function as a flow step.
2322
*/
24-
class ReadOpenFunction extends DecompressionFlowStep {
25-
ReadOpenFunction() { this.hasGlobalName("archive_read_open_filename") }
23+
class ReadOpenFunctionStep extends DecompressionFlowStep {
24+
ReadOpenFunctionStep() { this = "ReadOpenFunction" }
2625

2726
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
28-
exists(FunctionCall fc | fc.getTarget() = this |
27+
exists(FunctionCall fc | fc.getTarget().hasGlobalName("archive_read_open_filename") |
2928
node1.asIndirectExpr() = fc.getArgument(1) and
3029
node2.asIndirectExpr() = fc.getArgument(0)
3130
)

cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/MiniZip.qll renamed to cpp/ql/src/experimental/Security/CWE/CWE-409/MiniZip.qll

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
*/
44

55
import cpp
6-
import semmle.code.cpp.ir.dataflow.TaintTracking
76
import DecompressionBomb
87

98
/**
109
* The `mz_zip_entry` function is used in flow sink.
11-
* [docuemnt](https://github.com/zlib-ng/minizip-ng/blob/master/doc/mz_zip.md)
10+
* See https://github.com/zlib-ng/minizip-ng/blob/master/doc/mz_zip.md.
1211
*/
1312
class Mz_zip_entry extends DecompressionFunction {
1413
Mz_zip_entry() { this.hasGlobalName("mz_zip_entry_read") }
@@ -18,7 +17,7 @@ class Mz_zip_entry extends DecompressionFunction {
1817

1918
/**
2019
* The `mz_zip_reader_entry_*` and `mz_zip_reader_save_all` functions are used in flow sink.
21-
* [docuemnt](https://github.com/zlib-ng/minizip-ng/blob/master/doc/mz_zip_rw.md)
20+
* See https://github.com/zlib-ng/minizip-ng/blob/master/doc/mz_zip_rw.md.
2221
*/
2322
class Mz_zip_reader_entry extends DecompressionFunction {
2423
Mz_zip_reader_entry() {
@@ -43,13 +42,13 @@ class UnzOpenFunction extends DecompressionFunction {
4342
/**
4443
* The `mz_zip_reader_open_file` and `mz_zip_reader_open_file_in_memory` functions as a flow step.
4544
*/
46-
class ReaderOpenFunction extends DecompressionFlowStep {
47-
ReaderOpenFunction() {
48-
this.hasGlobalName(["mz_zip_reader_open_file_in_memory", "mz_zip_reader_open_file"])
49-
}
45+
class ReaderOpenFunctionStep extends DecompressionFlowStep {
46+
ReaderOpenFunctionStep() { this = "ReaderOpenFunctionStep" }
5047

5148
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
52-
exists(FunctionCall fc | fc.getTarget() = this |
49+
exists(FunctionCall fc |
50+
fc.getTarget().hasGlobalName(["mz_zip_reader_open_file_in_memory", "mz_zip_reader_open_file"])
51+
|
5352
node1.asIndirectExpr() = fc.getArgument(1) and
5453
node2.asIndirectExpr() = fc.getArgument(0)
5554
)

cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZSTD.qll renamed to cpp/ql/src/experimental/Security/CWE/CWE-409/ZSTD.qll

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
*/
44

55
import cpp
6-
import semmle.code.cpp.ir.dataflow.TaintTracking
76
import DecompressionBomb
87

98
/**
109
* The `ZSTD_decompress` function is used in flow sink.
1110
*/
1211
class ZstdDecompressFunction extends DecompressionFunction {
13-
ZstdDecompressFunction() { this.hasGlobalName(["ZSTD_decompress"]) }
12+
ZstdDecompressFunction() { this.hasGlobalName("ZSTD_decompress") }
1413

1514
override int getArchiveParameterIndex() { result = 2 }
1615
}
@@ -19,7 +18,7 @@ class ZstdDecompressFunction extends DecompressionFunction {
1918
* The `ZSTD_decompressDCtx` function is used in flow sink.
2019
*/
2120
class ZstdDecompressDctxFunction extends DecompressionFunction {
22-
ZstdDecompressDctxFunction() { this.hasGlobalName(["ZSTD_decompressDCtx"]) }
21+
ZstdDecompressDctxFunction() { this.hasGlobalName("ZSTD_decompressDCtx") }
2322

2423
override int getArchiveParameterIndex() { result = 3 }
2524
}
@@ -28,7 +27,7 @@ class ZstdDecompressDctxFunction extends DecompressionFunction {
2827
* The `ZSTD_decompressStream` function is used in flow sink.
2928
*/
3029
class ZstdDecompressStreamFunction extends DecompressionFunction {
31-
ZstdDecompressStreamFunction() { this.hasGlobalName(["ZSTD_decompressStream"]) }
30+
ZstdDecompressStreamFunction() { this.hasGlobalName("ZSTD_decompressStream") }
3231

3332
override int getArchiveParameterIndex() { result = 2 }
3433
}
@@ -37,19 +36,19 @@ class ZstdDecompressStreamFunction extends DecompressionFunction {
3736
* The `ZSTD_decompress_usingDDict` function is used in flow sink.
3837
*/
3938
class ZstdDecompressUsingDdictFunction extends DecompressionFunction {
40-
ZstdDecompressUsingDdictFunction() { this.hasGlobalName(["ZSTD_decompress_usingDDict"]) }
39+
ZstdDecompressUsingDdictFunction() { this.hasGlobalName("ZSTD_decompress_usingDDict") }
4140

4241
override int getArchiveParameterIndex() { result = 3 }
4342
}
4443

4544
/**
4645
* The `fopen_orDie` function as a flow step.
4746
*/
48-
class FopenOrDieFunction extends DecompressionFlowStep {
49-
FopenOrDieFunction() { this.hasGlobalName("fopen_orDie") }
47+
class FopenOrDieFunctionStep extends DecompressionFlowStep {
48+
FopenOrDieFunctionStep() { this = "FopenOrDieFunctionStep" }
5049

5150
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
52-
exists(FunctionCall fc | fc.getTarget() = this |
51+
exists(FunctionCall fc | fc.getTarget().hasGlobalName("fopen_orDie") |
5352
node1.asIndirectExpr() = fc.getArgument(0) and
5453
node2.asExpr() = fc
5554
)
@@ -59,11 +58,11 @@ class FopenOrDieFunction extends DecompressionFlowStep {
5958
/**
6059
* The `fread_orDie` function as a flow step.
6160
*/
62-
class FreadOrDieFunction extends DecompressionFlowStep {
63-
FreadOrDieFunction() { this.hasGlobalName("fread_orDie") }
61+
class FreadOrDieFunctionStep extends DecompressionFlowStep {
62+
FreadOrDieFunctionStep() { this = "FreadOrDieFunctionStep" }
6463

6564
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
66-
exists(FunctionCall fc | fc.getTarget() = this |
65+
exists(FunctionCall fc | fc.getTarget().hasGlobalName("fread_orDie") |
6766
node1.asIndirectExpr() = fc.getArgument(2) and
6867
node2.asIndirectExpr() = fc.getArgument(0)
6968
)

cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibGzopen.qll renamed to cpp/ql/src/experimental/Security/CWE/CWE-409/ZlibGzopen.qll

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
*/
44

55
import cpp
6-
import semmle.code.cpp.ir.dataflow.TaintTracking
76
import DecompressionBomb
87

98
/**
@@ -44,11 +43,11 @@ class GzReadFunction extends DecompressionFunction {
4443
*
4544
* `gzdopen(int fd, const char *mode)`
4645
*/
47-
class GzdopenFunction extends DecompressionFlowStep {
48-
GzdopenFunction() { this.hasGlobalName("gzdopen") }
46+
class GzdopenFunctionStep extends DecompressionFlowStep {
47+
GzdopenFunctionStep() { this = "GzdopenFunctionStep" }
4948

5049
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
51-
exists(FunctionCall fc | fc.getTarget() = this |
50+
exists(FunctionCall fc | fc.getTarget().hasGlobalName("gzdopen") |
5251
node1.asExpr() = fc.getArgument(0) and
5352
node2.asExpr() = fc
5453
)
@@ -60,11 +59,11 @@ class GzdopenFunction extends DecompressionFlowStep {
6059
*
6160
* `gzopen(const char *path, const char *mode)`
6261
*/
63-
class GzopenFunction extends DecompressionFlowStep {
64-
GzopenFunction() { this.hasGlobalName("gzopen") }
62+
class GzopenFunctionStep extends DecompressionFlowStep {
63+
GzopenFunctionStep() { this = "GzopenFunctionStep" }
6564

6665
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
67-
exists(FunctionCall fc | fc.getTarget() = this |
66+
exists(FunctionCall fc | fc.getTarget().hasGlobalName("gzopen") |
6867
node1.asIndirectExpr() = fc.getArgument(0) and
6968
node2.asExpr() = fc
7069
)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* https://www.zlib.net/
3+
*/
4+
5+
import cpp
6+
import DecompressionBomb
7+
8+
/**
9+
* The `inflate` and `inflateSync` functions are used in flow sink.
10+
*
11+
* `inflate(z_stream strm, int flush)`
12+
*
13+
* `inflateSync(z_stream strm)`
14+
*/
15+
class InflateFunction extends DecompressionFunction {
16+
InflateFunction() { this.hasGlobalName(["inflate", "inflateSync"]) }
17+
18+
override int getArchiveParameterIndex() { result = 0 }
19+
}
20+
21+
/**
22+
* The `next_in` member of a `z_stream` variable is used in a flow steps.
23+
*/
24+
class NextInMemberStep extends DecompressionFlowStep {
25+
NextInMemberStep() { this = "NextInMemberStep" }
26+
27+
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
28+
exists(Variable nextInVar, VariableAccess zStreamAccess |
29+
nextInVar.getDeclaringType().hasName("z_stream") and
30+
nextInVar.hasName("next_in") and
31+
zStreamAccess.getType().hasName("z_stream")
32+
|
33+
nextInVar.getAnAccess().getQualifier().(VariableAccess).getTarget() =
34+
zStreamAccess.getTarget() and
35+
node1.asIndirectExpr() = nextInVar.getAnAssignedValue() and
36+
node2.asExpr() = zStreamAccess
37+
)
38+
}
39+
}

cpp/ql/src/experimental/query-tests/Security/CWE/CWE-409/ZlibUncompress.qll renamed to cpp/ql/src/experimental/Security/CWE/CWE-409/ZlibUncompress.qll

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
*/
44

55
import cpp
6-
import semmle.code.cpp.ir.dataflow.TaintTracking
76
import DecompressionBomb
87

98
/**
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include "zlib.h"
2+
3+
void UnsafeGzread(gzFile inFileZ) {
4+
const int BUFFER_SIZE = 8192;
5+
unsigned char unzipBuffer[BUFFER_SIZE];
6+
unsigned int unzippedBytes;
7+
while (true) {
8+
unzippedBytes = gzread(inFileZ, unzipBuffer, BUFFER_SIZE);
9+
if (unzippedBytes <= 0) {
10+
break;
11+
}
12+
13+
// process buffer
14+
}
15+
}

0 commit comments

Comments
 (0)