diff --git a/powershell/ql/lib/semmle/code/powershell/security/UnsafeDeserializationCustomizations.qll b/powershell/ql/lib/semmle/code/powershell/security/UnsafeDeserializationCustomizations.qll new file mode 100644 index 00000000000..25615acd77d --- /dev/null +++ b/powershell/ql/lib/semmle/code/powershell/security/UnsafeDeserializationCustomizations.qll @@ -0,0 +1,53 @@ +/** + * Provides default sources, sinks and sanitizers for reasoning about + * unsafe deserialization vulnerabilities, as well as extension points for + * adding your own. + */ + +private import semmle.code.powershell.dataflow.DataFlow +import semmle.code.powershell.ApiGraphs +private import semmle.code.powershell.dataflow.flowsources.FlowSources +private import semmle.code.powershell.Cfg + +module UnsafeDeserialization { + /** + * A data flow source for SQL-injection vulnerabilities. + */ + abstract class Source extends DataFlow::Node { + /** Gets a string that describes the type of this flow source. */ + abstract string getSourceType(); + } + + /** + * A data flow sink for SQL-injection vulnerabilities. + */ + abstract class Sink extends DataFlow::Node { + /** Gets a description of this sink. */ + abstract string getSinkType(); + + } + + /** + * A sanitizer for Unsafe Deserialization vulnerabilities. + */ + abstract class Sanitizer extends DataFlow::Node { } + + /** A source of user input, considered as a flow source for unsafe deserialization. */ + class FlowSourceAsSource extends Source instanceof SourceNode { + override string getSourceType() { result = SourceNode.super.getSourceType() } + } + + class BinaryFormatterDeserializeSink extends Sink { + BinaryFormatterDeserializeSink() { + exists(DataFlow::ObjectCreationNode ocn, DataFlow::CallNode cn | + cn.getQualifier().getALocalSource() = ocn and + ocn.getExprNode().getExpr().(CallExpr).getAnArgument().getValue().asString() = "System.Runtime.Serialization.Formatters.Binary.BinaryFormatter" and + cn.getLowerCaseName() = "deserialize" and + cn.getAnArgument() = this + ) + } + + override string getSinkType() { result = "call to BinaryFormatter.Deserialize" } + + } +} diff --git a/powershell/ql/lib/semmle/code/powershell/security/UnsafeDeserializationQuery.qll b/powershell/ql/lib/semmle/code/powershell/security/UnsafeDeserializationQuery.qll new file mode 100644 index 00000000000..fba30b507ad --- /dev/null +++ b/powershell/ql/lib/semmle/code/powershell/security/UnsafeDeserializationQuery.qll @@ -0,0 +1,28 @@ +/** + * Provides a taint tracking configuration for reasoning about + * deserialization vulnerabilities (CWE-502). + * + * Note, for performance reasons: only import this file if + * `UnsafeDeserializationFlow` is needed, otherwise + * `UnsafeDeserializationCustomizations` should be imported instead. + */ + +import powershell +import semmle.code.powershell.dataflow.flowsources.FlowSources +import semmle.code.powershell.dataflow.DataFlow +import semmle.code.powershell.dataflow.TaintTracking +import UnsafeDeserializationCustomizations::UnsafeDeserialization + +module Config implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof Source } + + predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo){ + exists(InvokeMemberExpr ime | + nodeTo.asExpr().getExpr() = ime and + nodeFrom.asExpr().getExpr() = ime.getAnArgument() + ) + } +} + +module UnsafeDeserializationFlow = TaintTracking::Global; \ No newline at end of file diff --git a/powershell/ql/src/queries/security/cwe-502/BinaryFormatterDeserialization.qhelp b/powershell/ql/src/queries/security/cwe-502/BinaryFormatterDeserialization.qhelp new file mode 100644 index 00000000000..b54aff20bf5 --- /dev/null +++ b/powershell/ql/src/queries/security/cwe-502/BinaryFormatterDeserialization.qhelp @@ -0,0 +1,37 @@ + + + + +

Using BinaryFormatter to deserialize an object from untrusted input may result in security problems, such +as denial of service or remote code execution.

+ +
+ + +

Avoid using BinaryFormatter.

+ +
+ + +

In this example, a string is deserialized using a +BinaryFormatter. BinaryFormatter is an easily exploited deserializer.

+ + + +
+ + +
  • +Muñoz, Alvaro and Mirosh, Oleksandr: +JSON Attacks. +
  • + +
  • +Microsoft: +Deserialization risks in use of BinaryFormatter and related types. +
  • + +
    +
    diff --git a/powershell/ql/src/queries/security/cwe-502/BinaryFormatterDeserialization.ql b/powershell/ql/src/queries/security/cwe-502/BinaryFormatterDeserialization.ql new file mode 100644 index 00000000000..c79f6ecda29 --- /dev/null +++ b/powershell/ql/src/queries/security/cwe-502/BinaryFormatterDeserialization.ql @@ -0,0 +1,18 @@ +/** + * @name Use of Binary Formatter deserialization + * @description Use of Binary Formatter is unsafe + * @kind problem + * @problem.severity error + * @security-severity 8.8 + * @precision high + * @id powershell/microsoft/public/binary-formatter-deserialization + * @tags correctness + * security + * external/cwe/cwe-502 + */ + +import powershell +import semmle.code.powershell.security.UnsafeDeserializationCustomizations::UnsafeDeserialization + +from BinaryFormatterDeserializeSink sink +select sink, "Call to BinaryFormatter.Deserialize" diff --git a/powershell/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp b/powershell/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp new file mode 100644 index 00000000000..26d5bda0600 --- /dev/null +++ b/powershell/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp @@ -0,0 +1,37 @@ + + + + +

    Deserializing an object from untrusted input may result in security problems, such +as denial of service or remote code execution.

    + +
    + + +

    Avoid using an unsafe deserialization framework.

    + +
    + + +

    In this example, a string is deserialized using a +BinaryFormatter. BinaryFormatter is an easily exploited deserializer.

    + + + +
    + + +
  • +Muñoz, Alvaro and Mirosh, Oleksandr: +JSON Attacks. +
  • + +
  • +Microsoft: +Deserialization risks in use of BinaryFormatter and related types. +
  • + +
    +
    diff --git a/powershell/ql/src/queries/security/cwe-502/UnsafeDeserialization.ql b/powershell/ql/src/queries/security/cwe-502/UnsafeDeserialization.ql new file mode 100644 index 00000000000..3713b0d5ed5 --- /dev/null +++ b/powershell/ql/src/queries/security/cwe-502/UnsafeDeserialization.ql @@ -0,0 +1,26 @@ +/** + * @name Unsafe deserializer + * @description Calling an unsafe deserializer with data controlled by an attacker + * can lead to denial of service and other security problems. + * @kind path-problem + * @problem.severity error + * @security-severity 8.8 + * @precision high + * @id powershell/microsoft/public/unsafe-deserialization + * @tags correctness + * security + * external/cwe/cwe-502 + */ + +import powershell +import semmle.code.powershell.security.UnsafeDeserializationQuery +import UnsafeDeserializationFlow::PathGraph + +from + UnsafeDeserializationFlow::PathNode source, UnsafeDeserializationFlow::PathNode sink, + Source sourceNode +where + UnsafeDeserializationFlow::flowPath(source, sink) and + sourceNode = source.getNode() +select sink.getNode(), source, sink, "This unsafe deserializer deserializes on a $@.", sourceNode, + sourceNode.getSourceType() diff --git a/powershell/ql/src/queries/security/cwe-502/examples/BinaryFormatterDeserialization.ps1 b/powershell/ql/src/queries/security/cwe-502/examples/BinaryFormatterDeserialization.ps1 new file mode 100644 index 00000000000..b222b1e5327 --- /dev/null +++ b/powershell/ql/src/queries/security/cwe-502/examples/BinaryFormatterDeserialization.ps1 @@ -0,0 +1,6 @@ +$untrustedBase64 = Read-Host "Enter user input" + +$formatter = New-Object System.Runtime.Serialization.Formatters.Binary.BinaryFormatter +$stream = [System.IO.MemoryStream]::new([Convert]::FromBase64String($untrustedBase64)) + +$obj = $formatter.Deserialize($stream) diff --git a/powershell/ql/test/query-tests/security/cwe-502/UnsafeDeserialization.expected b/powershell/ql/test/query-tests/security/cwe-502/UnsafeDeserialization.expected new file mode 100644 index 00000000000..7476f52e567 --- /dev/null +++ b/powershell/ql/test/query-tests/security/cwe-502/UnsafeDeserialization.expected @@ -0,0 +1,14 @@ +edges +| test.ps1:1:20:1:47 | Call to read-host | test.ps1:3:69:3:84 | untrustedBase64 | provenance | Src:MaD:0 | +| test.ps1:3:11:3:86 | Call to new | test.ps1:4:31:4:37 | stream | provenance | | +| test.ps1:3:41:3:85 | Call to frombase64string | test.ps1:3:11:3:86 | Call to new | provenance | Config | +| test.ps1:3:69:3:84 | untrustedBase64 | test.ps1:3:41:3:85 | Call to frombase64string | provenance | Config | +nodes +| test.ps1:1:20:1:47 | Call to read-host | semmle.label | Call to read-host | +| test.ps1:3:11:3:86 | Call to new | semmle.label | Call to new | +| test.ps1:3:41:3:85 | Call to frombase64string | semmle.label | Call to frombase64string | +| test.ps1:3:69:3:84 | untrustedBase64 | semmle.label | untrustedBase64 | +| test.ps1:4:31:4:37 | stream | semmle.label | stream | +subpaths +#select +| test.ps1:4:31:4:37 | stream | test.ps1:1:20:1:47 | Call to read-host | test.ps1:4:31:4:37 | stream | This unsafe deserializer deserializes on a $@. | test.ps1:1:20:1:47 | Call to read-host | read from stdin | diff --git a/powershell/ql/test/query-tests/security/cwe-502/UnsafeDeserialization.qlref b/powershell/ql/test/query-tests/security/cwe-502/UnsafeDeserialization.qlref new file mode 100644 index 00000000000..abfb453d723 --- /dev/null +++ b/powershell/ql/test/query-tests/security/cwe-502/UnsafeDeserialization.qlref @@ -0,0 +1 @@ +queries/security/cwe-502/UnsafeDeserialization.ql \ No newline at end of file diff --git a/powershell/ql/test/query-tests/security/cwe-502/test.ps1 b/powershell/ql/test/query-tests/security/cwe-502/test.ps1 new file mode 100644 index 00000000000..d35ef352cd3 --- /dev/null +++ b/powershell/ql/test/query-tests/security/cwe-502/test.ps1 @@ -0,0 +1,4 @@ +$untrustedBase64 = Read-Host "Enter user input" +$formatter = New-Object System.Runtime.Serialization.Formatters.Binary.BinaryFormatter +$stream = [System.IO.MemoryStream]::new([Convert]::FromBase64String($untrustedBase64)) +$obj = $formatter.Deserialize($stream)