Merge pull request #260 from microsoft/powershell-unsafe-deserialization

Powershell Unsafe Deserialize query
This commit is contained in:
Chanel
2025-07-22 10:49:10 -07:00
committed by GitHub
10 changed files with 224 additions and 0 deletions

View File

@@ -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" }
}
}

View File

@@ -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<Config>;

View File

@@ -0,0 +1,37 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Using <code>BinaryFormatter</code> to deserialize an object from untrusted input may result in security problems, such
as denial of service or remote code execution.</p>
</overview>
<recommendation>
<p>Avoid using <code>BinaryFormatter</code>.</p>
</recommendation>
<example>
<p>In this example, a string is deserialized using a
<code>BinaryFormatter</code>. <code>BinaryFormatter</code> is an easily exploited deserializer.</p>
<sample src="examples/BinaryFormatterDeserialization.ps1" />
</example>
<references>
<li>
Mu&ntilde;oz, Alvaro and Mirosh, Oleksandr:
<a href="https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf">JSON Attacks</a>.
</li>
<li>
Microsoft:
<a href="https://learn.microsoft.com/en-us/dotnet/standard/serialization/binaryformatter-security-guide">Deserialization risks in use of BinaryFormatter and related types</a>.
</li>
</references>
</qhelp>

View File

@@ -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"

View File

@@ -0,0 +1,37 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Deserializing an object from untrusted input may result in security problems, such
as denial of service or remote code execution.</p>
</overview>
<recommendation>
<p>Avoid using an unsafe deserialization framework.</p>
</recommendation>
<example>
<p>In this example, a string is deserialized using a
<code>BinaryFormatter</code>. <code>BinaryFormatter</code> is an easily exploited deserializer.</p>
<sample src="examples/BinaryFormatterDeserialization.ps1" />
</example>
<references>
<li>
Mu&ntilde;oz, Alvaro and Mirosh, Oleksandr:
<a href="https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf">JSON Attacks</a>.
</li>
<li>
Microsoft:
<a href="https://learn.microsoft.com/en-us/dotnet/standard/serialization/binaryformatter-security-guide">Deserialization risks in use of BinaryFormatter and related types</a>.
</li>
</references>
</qhelp>

View File

@@ -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()

View File

@@ -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)

View File

@@ -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 |

View File

@@ -0,0 +1 @@
queries/security/cwe-502/UnsafeDeserialization.ql

View File

@@ -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)