diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Bombs.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Bombs.ql deleted file mode 100644 index 2e595bb11b8..00000000000 --- a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/Bombs.ql +++ /dev/null @@ -1,69 +0,0 @@ -/** - * @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 - * @security-severity 7.8 - * @precision medium - * @id cpp/user-controlled-file-decompression - * @tags security - * experimental - * external/cwe/cwe-409 - */ - -import cpp -import semmle.code.cpp.ir.dataflow.TaintTracking -import semmle.code.cpp.security.FlowSources - -/** - * The `gzopen` function, which can perform command substitution. - */ -private class GzopenFunction extends Function { - GzopenFunction() { hasGlobalName("gzopen") } -} - -/** - * The `gzread` function, which can perform command substitution. - */ -private class GzreadFunction extends Function { - GzreadFunction() { hasGlobalName("gzread") } -} - -module ZlibTaintConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { - exists(FunctionCall fc | fc.getTarget() instanceof GzopenFunction | - fc.getArgument(0) = source.asExpr() - ) - } - - predicate isSink(DataFlow::Node sink) { - exists(FunctionCall fc | fc.getTarget() instanceof GzreadFunction | - fc.getArgument(0) = sink.asExpr() and - not sanitizer(fc) - ) - } - - predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - exists(FunctionCall fc | fc.getTarget() instanceof GzopenFunction | - node1.asExpr() = fc.getArgument(0) and - node2.asExpr() = fc - ) - } -} - -predicate sanitizer(FunctionCall fc) { - exists(Expr e | fc.getTarget() instanceof GzreadFunction | - // a RelationalOperation which isn't compared with a Literal that using for end of read - TaintTracking::localExprTaint(fc, e.(RelationalOperation).getAChild*()) and - not e.getAChild*().(Literal).getValue() = ["0", "1", "-1"] - ) -} - -module ZlibTaint = TaintTracking::Global; - -import ZlibTaint::PathGraph - -from ZlibTaint::PathNode source, ZlibTaint::PathNode sink -where ZlibTaint::flowPath(source, sink) -select sink.getNode(), source, sink, "This file extraction depends on a $@.", source.getNode(), - "potentially untrusted source" diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsGzopen.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsGzopen.ql new file mode 100644 index 00000000000..644841af23e --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsGzopen.ql @@ -0,0 +1,139 @@ +/** + * @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 + * @security-severity 7.8 + * @precision high + * @id cpp/user-controlled-file-decompression1 + * @tags security + * experimental + * external/cwe/cwe-409 + */ + +import cpp +import semmle.code.cpp.ir.dataflow.TaintTracking +import semmle.code.cpp.security.FlowSources + +/** + * A gzFile Variable as a Flow source + */ +private class GzFileVar extends VariableAccess { + GzFileVar() { this.getType().hasName("gzFile") } +} + +/** + * The `gzopen` function as a Flow source + */ +private class GzopenFunction extends Function { + GzopenFunction() { hasGlobalName("gzopen") } +} + +/** + * The `gzdopen` function as a Flow source + */ +private class GzdopenFunction extends Function { + GzdopenFunction() { hasGlobalName("gzdopen") } +} + +/** + * The `gzfread` function is used in Flow sink + */ +private class GzfreadFunction extends Function { + GzfreadFunction() { hasGlobalName("gzfread") } +} + +/** + * The `gzread` function is used in Flow sink + */ +private class GzreadFunction extends Function { + GzreadFunction() { hasGlobalName("gzread") } +} + +module ZlibTaintConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + // gzopen(const char *path, const char *mode); + exists(FunctionCall fc | fc.getTarget() instanceof GzopenFunction | + fc.getArgument(0) = source.asExpr() and + // arg 0 can be a path string whichwe must do following check + not fc.getArgument(0).isConstant() + ) + or + //gzdopen(int fd, const char *mode); + // IDK whether it is good to use all file decriptors function returns as source or not + // because we can do more sanitization from fd function sources + exists(FunctionCall fc | fc.getTarget() instanceof GzdopenFunction | + fc.getArgument(0) = source.asExpr() + ) + or + source.asExpr() instanceof GzFileVar + } + + predicate isSink(DataFlow::Node sink) { + // gzread(gzFile file, voidp buf, unsigned len)); + exists(FunctionCall fc | fc.getTarget() instanceof GzreadFunction | + fc.getArgument(0) = sink.asExpr() and + not sanitizer(fc) + // TODO: and not sanitizer2(fc.getArgument(2)) + ) + or + // gzfread((voidp buf, z_size_t size, z_size_t nitems, gzFile file)); + exists(FunctionCall fc | fc.getTarget() instanceof GzfreadFunction | + sink.asExpr() = fc.getArgument(3) + ) + } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + // gzopen(const char *path, const char *mode); + // gzdopen(int fd, const char *mode); + exists(FunctionCall fc | + fc.getTarget() instanceof GzopenFunction or fc.getTarget() instanceof GzdopenFunction + | + node1.asExpr() = fc.getArgument(0) and + node2.asExpr() = fc + ) + or + // gzread(gzFile file, voidp buf, unsigned len); + exists(FunctionCall fc | fc.getTarget() instanceof GzreadFunction | + node1.asExpr() = fc.getArgument(0) and + node2.asExpr() = fc.getArgument(1) + ) + or + // gzfread(voidp buf, z_size_t size, z_size_t nitems, gzFile file); + exists(FunctionCall fc | fc.getTarget() instanceof GzfreadFunction | + node1.asExpr() = fc.getArgument(3) and + node2.asExpr() = fc.getArgument(0) + ) + } +} + +predicate sanitizer(FunctionCall fc) { + exists(Expr e | + // a RelationalOperation which isn't compared with a Literal that using for end of read + TaintTracking::localExprTaint(fc, e.(RelationalOperation).getAChild*()) and + not e.getAChild*().(Literal).getValue() = ["0", "1", "-1"] + ) +} + +// TODO: +// predicate sanitizer2(Expr arg) { +// exists(Expr e | +// // a RelationalOperation which isn't compared with a Literal that using for end of read +// TaintTracking::localExprTaint(arg, e.(RelationalOperation).getAChild*()) +// ) +// } +// predicate test(FunctionCall fc, Expr sink) { +// exists( | fc.getTarget() instanceof GzreadFunction | +// // a RelationalOperation which isn't compared with a Literal that using for end of read +// TaintTracking::localExprTaint(fc.getArgument(2).(VariableAccess), sink) +// ) and +// sink.getFile().getLocation().toString().matches("%main.cpp%") +// } +module ZlibTaint = TaintTracking::Global; + +import ZlibTaint::PathGraph + +from ZlibTaint::PathNode source, ZlibTaint::PathNode sink +where ZlibTaint::flowPath(source, sink) +select sink.getNode(), source, sink, "This Decompressiondepends on a $@.", source.getNode(), + "potentially untrusted source" diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsInflator.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsInflator.ql new file mode 100644 index 00000000000..541c23c58ec --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsInflator.ql @@ -0,0 +1,56 @@ +/** + * @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 + * @security-severity 7.8 + * @precision high + * @id cpp/user-controlled-file-decompression2 + * @tags security + * experimental + * external/cwe/cwe-409 + */ + +import cpp +import semmle.code.cpp.ir.dataflow.TaintTracking +import semmle.code.cpp.security.FlowSources + +/** + * A z_stream Variable as a Flow source + */ +private class ZStreamVar extends VariableAccess { + ZStreamVar() { this.getType().hasName("z_stream") } +} + +/** + * The `inflate`/`inflateSync` function is used in Flow sink + */ +private class DeflateFunction extends Function { + DeflateFunction() { hasGlobalName(["inflate", "inflateSync"]) } +} + +module ZlibTaintConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof ZStreamVar } + + predicate isSink(DataFlow::Node sink) { + exists(FunctionCall fc | fc.getTarget() instanceof DeflateFunction | + fc.getArgument(0) = sink.asExpr() + ) + } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(FunctionCall fc | fc.getTarget() instanceof DeflateFunction | + node1.asExpr() = fc.getArgument(0) and + node2.asExpr() = fc + ) + } +} + +module ZlibTaint = TaintTracking::Global; + +import ZlibTaint::PathGraph + +from ZlibTaint::PathNode source, ZlibTaint::PathNode sink +where ZlibTaint::flowPath(source, sink) +select sink.getNode(), source, sink, "This Decompression depends on a $@.", source.getNode(), + "potentially untrusted source" diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsUncompress.ql b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsUncompress.ql new file mode 100644 index 00000000000..09327616fd9 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-409-DecompressionBomb/DecompressionBombsUncompress.ql @@ -0,0 +1,56 @@ +/** + * @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 + * @security-severity 7.8 + * @precision high + * @id cpp/user-controlled-file-decompression3 + * @tags security + * experimental + * external/cwe/cwe-409 + */ + +import cpp +import semmle.code.cpp.ir.dataflow.TaintTracking +import semmle.code.cpp.security.FlowSources + +/** + * A Bytef Variable as a Flow source + */ +private class BytefVar extends VariableAccess { + BytefVar() { this.getType().hasName("Bytef") } +} + +/** + * The `uncompress`/`uncompress2` function is used in Flow sink + */ +private class UncompressFunction extends Function { + UncompressFunction() { hasGlobalName(["uncompress", "uncompress2"]) } +} + +module ZlibTaintConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof BytefVar } + + predicate isSink(DataFlow::Node sink) { + exists(FunctionCall fc | fc.getTarget() instanceof UncompressFunction | + fc.getArgument(0) = sink.asExpr() + ) + } + + predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(FunctionCall fc | fc.getTarget() instanceof UncompressFunction | + node1.asExpr() = fc.getArgument(0) and + node2.asExpr() = fc + ) + } +} + +module ZlibTaint = TaintTracking::Global; + +import ZlibTaint::PathGraph + +from ZlibTaint::PathNode source, ZlibTaint::PathNode sink +where ZlibTaint::flowPath(source, sink) +select sink.getNode(), source, sink, "This Decompression depends on a $@.", source.getNode(), + "potentially untrusted source"