Skip to content

Powershell Unsafe Deserialize query #260

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

Merged
merged 8 commits into from
Jul 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -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" }

}
}
Original file line number Diff line number Diff line change
@@ -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>;
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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"
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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()
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -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 |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
queries/security/cwe-502/UnsafeDeserialization.ql
4 changes: 4 additions & 0 deletions powershell/ql/test/query-tests/security/cwe-502/test.ps1
Original file line number Diff line number Diff line change
@@ -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)