forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 21
Flow from insecure random number to sensitive sink #346
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
Open
chanel-y
wants to merge
5
commits into
main
Choose a base branch
from
users/chanely/insecure-random
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
90860cb
PowerShell: Add insecure random number generator query (CWE-338)
chanel-y 3cd00a9
initial query, insecure random to sensitive sinks
chanel-y 2b6726b
Move Inline Expectation comments to correct lines
chanel-y 5abce2e
moving the pred.NextBytes(succ) when pred is a System.Random object i…
chanel-y e742d95
trying to get New-Object to work with MaD
chanel-y File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
5 changes: 5 additions & 0 deletions
5
powershell/ql/lib/semmle/code/powershell/frameworks/System.model.yml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
107 changes: 107 additions & 0 deletions
107
powershell/ql/lib/semmle/code/powershell/security/InsecureRandomnessCustomizations.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| /** | ||
| * Provides default sources, sinks and sanitizers for reasoning about | ||
| * insecure-randomness vulnerabilities, as well as extension points for | ||
| * adding your own. | ||
| */ | ||
|
|
||
| private import semmle.code.powershell.dataflow.DataFlow | ||
| import semmle.code.powershell.ApiGraphs | ||
|
|
||
| module InsecureRandomness { | ||
| /** | ||
| * A data flow source for insecure-randomness vulnerabilities. | ||
| */ | ||
| abstract class Source extends DataFlow::Node { } | ||
|
|
||
| /** | ||
| * A data flow sink for insecure-randomness vulnerabilities. | ||
| */ | ||
| abstract class Sink extends DataFlow::Node { | ||
| /** Gets a human-readable description of this sink. */ | ||
| abstract string getSinkDescription(); | ||
| } | ||
|
|
||
| /** | ||
| * A sanitizer for insecure-randomness vulnerabilities. | ||
| */ | ||
| abstract class Sanitizer extends DataFlow::Node { } | ||
|
|
||
| /** A call to the `Get-Random` cmdlet. */ | ||
| class GetRandomSource extends Source instanceof DataFlow::CallNode { | ||
| GetRandomSource() { this.matchesName("Get-Random") } | ||
| } | ||
|
|
||
| /** An instantiation of `System.Random` via `New-Object`. */ | ||
| class SystemRandomObjectCreation extends Source instanceof DataFlow::ObjectCreationNode { | ||
| SystemRandomObjectCreation() { | ||
| this.asExpr() | ||
| .getExpr() | ||
| .(ObjectCreation) | ||
| .getAnArgument() | ||
| .getValue() | ||
| .stringMatches("System.Random") | ||
| } | ||
| } | ||
|
|
||
| /** A call to `[System.Random]::new()` via the API graph. */ | ||
| class SystemRandomNewCall extends Source instanceof DataFlow::CallNode { | ||
| SystemRandomNewCall() { | ||
| this = API::getTopLevelMember("system").getMember("random").getMember("new").asCall() | ||
| } | ||
| } | ||
|
|
||
| /** Assignment to .Key or .IV on an object. */ | ||
| class CryptoKeyIvAssignmentSink extends Sink { | ||
| CryptoKeyIvAssignmentSink() { | ||
| exists(AssignStmt a, MemberExprWriteAccess m | | ||
| m = a.getLeftHandSide() and | ||
| m.getLowerCaseMemberName() in ["key", "iv"] and | ||
| this.asExpr().getExpr() = a.getRightHandSide() | ||
| ) | ||
| } | ||
|
|
||
| override string getSinkDescription() { result = "a cryptographic key or IV" } | ||
| } | ||
|
|
||
| /** Argument to a cryptographic constructor (HMAC key or KDF salt). */ | ||
| class CryptoConstructorSink extends Sink { | ||
| CryptoConstructorSink() { | ||
| exists(DataFlow::ObjectCreationNode oc | | ||
| ( | ||
| oc.getLowerCaseConstructedTypeName().matches("%hmac%") | ||
| or | ||
| oc.getLowerCaseConstructedTypeName().matches("%rfc2898derivebytes%") | ||
| ) and | ||
| this = oc.getAnArgument() and | ||
| not this = oc.getConstructedTypeNode() | ||
| ) | ||
| } | ||
|
|
||
| override string getSinkDescription() { result = "a cryptographic operation" } | ||
| } | ||
|
|
||
| /** First positional argument to ConvertTo-SecureString. */ | ||
| class SecureStringSink extends Sink { | ||
| SecureStringSink() { | ||
| exists(DataFlow::CallNode call | | ||
| call.matchesName("ConvertTo-SecureString") and | ||
| this = call.getPositionalArgument(0) | ||
| ) | ||
| } | ||
|
|
||
| override string getSinkDescription() { result = "a secure string conversion" } | ||
| } | ||
|
|
||
| /** Value used in HTTP headers passed to Invoke-RestMethod or Invoke-WebRequest. */ | ||
| class HttpHeaderValueSink extends Sink { | ||
| HttpHeaderValueSink() { | ||
| exists(DataFlow::CallNode call, HashTableExpr ht | | ||
| (call.matchesName("Invoke-RestMethod") or call.matchesName("Invoke-WebRequest")) and | ||
| call.getNamedArgument("headers").asExpr().getExpr() = ht and | ||
| this.asExpr().getExpr() = ht.getAValue() | ||
| ) | ||
| } | ||
|
|
||
| override string getSinkDescription() { result = "an HTTP header" } | ||
| } | ||
| } |
26 changes: 26 additions & 0 deletions
26
powershell/ql/lib/semmle/code/powershell/security/InsecureRandomnessQuery.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| /** | ||
| * Provides a taint tracking configuration for reasoning about | ||
| * insecure-randomness vulnerabilities (CWE-338). | ||
| * | ||
| * Note, for performance reasons: only import this file if | ||
| * `InsecureRandomnessFlow` is needed, otherwise | ||
| * `InsecureRandomnessCustomizations` should be imported instead. | ||
| */ | ||
|
|
||
| import powershell | ||
| import semmle.code.powershell.dataflow.TaintTracking | ||
| import InsecureRandomnessCustomizations::InsecureRandomness | ||
| import semmle.code.powershell.dataflow.DataFlow | ||
|
|
||
| private module Config implements DataFlow::ConfigSig { | ||
| predicate isSource(DataFlow::Node source) { source instanceof Source } | ||
|
|
||
| predicate isSink(DataFlow::Node sink) { sink instanceof Sink } | ||
|
|
||
| predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer } | ||
| } | ||
|
|
||
| /** | ||
| * Taint-tracking for reasoning about insecure-randomness vulnerabilities. | ||
| */ | ||
| module InsecureRandomnessFlow = TaintTracking::Global<Config>; |
25 changes: 25 additions & 0 deletions
25
powershell/ql/src/queries/security/cwe-338/InsecureRandomness.qhelp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| <!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd"> | ||
| <qhelp> | ||
|
|
||
| <overview> | ||
| <p>Using non-cryptographic random number generators for security-sensitive operations can compromise security. `Get-Random` and `System.Random` are not suitable for generating cryptographic keys, security tokens, or other security-critical random values.</p> | ||
| </overview> | ||
|
|
||
| <recommendation> | ||
| <p>Use a cryptographically secure random number generator such as `[System.Security.Cryptography.RandomNumberGenerator]` instead.</p> | ||
| </recommendation> | ||
|
|
||
| <example> | ||
| <p>Insecure random:</p> | ||
| <sample src="examples/InsecureRandomness/InsecureRandomnessBad.ps1" /> | ||
|
|
||
| <p>Secure alternative:</p> | ||
| <sample src="examples/InsecureRandomness/InsecureRandomnessGood.ps1" /> | ||
| </example> | ||
|
|
||
| <references> | ||
| <li><a href="https://cwe.mitre.org/data/definitions/338.html">CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)</a>.</li> | ||
| <li><a href="https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.randomnumbergenerator">System.Security.Cryptography.RandomNumberGenerator</a>.</li> | ||
| </references> | ||
|
|
||
| </qhelp> |
23 changes: 23 additions & 0 deletions
23
powershell/ql/src/queries/security/cwe-338/InsecureRandomness.ql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| /** | ||
| * @name Use of insecure random number generator in security-sensitive context | ||
| * @description Using non-cryptographic random number generators such as Get-Random or System.Random | ||
| * for security-sensitive operations can compromise security. | ||
| * @kind path-problem | ||
| * @problem.severity error | ||
| * @security-severity 7.5 | ||
| * @precision high | ||
| * @id powershell/insecure-randomness | ||
| * @tags security | ||
| * external/cwe/cwe-330 | ||
| * external/cwe/cwe-338 | ||
| */ | ||
|
|
||
| import powershell | ||
| import semmle.code.powershell.security.InsecureRandomnessQuery | ||
| import InsecureRandomnessFlow::PathGraph | ||
|
|
||
| from InsecureRandomnessFlow::PathNode source, InsecureRandomnessFlow::PathNode sink | ||
| where InsecureRandomnessFlow::flowPath(source, sink) | ||
| select sink.getNode(), source, sink, | ||
| "Insecure random value flows to " + sink.getNode().(Sink).getSinkDescription() + " from $@.", | ||
| source.getNode(), "this insecure random source" |
42 changes: 42 additions & 0 deletions
42
...ell/ql/src/queries/security/cwe-338/examples/InsecureRandomness/InsecureRandomnessBad.ps1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| # BAD: weak RNG output flows into security-sensitive .NET crypto sinks. | ||
|
|
||
| # 1) Weak bytes flow into AES .Key and .IV properties | ||
| $weak = New-Object System.Random | ||
| $keyBytes = New-Object byte[] 32 | ||
| $ivBytes = New-Object byte[] 16 | ||
| $weak.NextBytes($keyBytes) | ||
| $weak.NextBytes($ivBytes) | ||
| $aes = [System.Security.Cryptography.Aes]::Create() | ||
| $aes.Key = $keyBytes # BAD: predictable key | ||
| $aes.IV = $ivBytes # BAD: predictable IV | ||
|
|
||
| # 2) Weak bytes flow into HMAC constructor (signing key) | ||
| $hmacKeyBytes = New-Object byte[] 64 | ||
| $weak.NextBytes($hmacKeyBytes) | ||
| $hmac = New-Object System.Security.Cryptography.HMACSHA256(,$hmacKeyBytes) # BAD: predictable HMAC key | ||
|
|
||
| # 3) Weak bytes used as salt for password-based key derivation | ||
| $saltBytes = New-Object byte[] 8 | ||
| $weak.NextBytes($saltBytes) | ||
| $kdf = New-Object System.Security.Cryptography.Rfc2898DeriveBytes("password", $saltBytes) # BAD: predictable salt | ||
|
|
||
| # 4) Get-Random result flows into ConvertTo-SecureString → PSCredential | ||
| $tempPwd = (Get-Random -Maximum 999999999).ToString() | ||
| $securePwd = ConvertTo-SecureString $tempPwd -AsPlainText -Force | ||
| $cred = New-Object System.Management.Automation.PSCredential("admin", $securePwd) # BAD: predictable credential | ||
|
|
||
| # 5) Weak random flows into HTTP Authorization header | ||
| $tokenValue = Get-Random -Maximum 999999999 | ||
| Invoke-RestMethod -Uri "https://api.example.com/data" ` | ||
| -Headers @{ Authorization = "Bearer $tokenValue" } # BAD: predictable bearer token | ||
|
|
||
| # 6) Weak random used as RNGCryptoServiceProvider substitute for key bytes | ||
| $rng2 = [System.Random]::new() | ||
| $aesKey2 = New-Object byte[] 32 | ||
| $rng2.NextBytes($aesKey2) | ||
| [Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($aesKey2) | Out-Null | ||
| # The above line re-fills the buffer, but the initial weak fill might persist | ||
| # if code logic is wrong. True BAD pattern: relying solely on System.Random: | ||
| $aes2 = [System.Security.Cryptography.Aes]::Create() | ||
| $rng2.NextBytes($aesKey2) # BAD: final value comes from weak RNG | ||
| $aes2.Key = $aesKey2 # BAD: predictable key assigned to cipher |
41 changes: 41 additions & 0 deletions
41
...ll/ql/src/queries/security/cwe-338/examples/InsecureRandomness/InsecureRandomnessGood.ps1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| # GOOD: cryptographic RNG output flows into security-sensitive .NET crypto sinks. | ||
|
|
||
| # 1) CSPRNG bytes flow into AES .Key and .IV properties | ||
| $keyBytes = New-Object byte[] 32 | ||
| $ivBytes = New-Object byte[] 16 | ||
| [System.Security.Cryptography.RandomNumberGenerator]::Fill($keyBytes) | ||
| [System.Security.Cryptography.RandomNumberGenerator]::Fill($ivBytes) | ||
| $aes = [System.Security.Cryptography.Aes]::Create() | ||
| $aes.Key = $keyBytes # GOOD: cryptographically random key | ||
| $aes.IV = $ivBytes # GOOD: cryptographically random IV | ||
|
|
||
| # 2) CSPRNG bytes flow into HMAC constructor (signing key) | ||
| $hmacKeyBytes = New-Object byte[] 64 | ||
| [System.Security.Cryptography.RandomNumberGenerator]::Fill($hmacKeyBytes) | ||
| $hmac = New-Object System.Security.Cryptography.HMACSHA256(,$hmacKeyBytes) # GOOD | ||
|
|
||
| # 3) CSPRNG bytes used as salt for password-based key derivation | ||
| $saltBytes = New-Object byte[] 16 | ||
| [System.Security.Cryptography.RandomNumberGenerator]::Fill($saltBytes) | ||
| $kdf = New-Object System.Security.Cryptography.Rfc2898DeriveBytes("password", $saltBytes) # GOOD | ||
|
|
||
| # 4) CSPRNG bytes flow into ConvertTo-SecureString → PSCredential | ||
| $pwdBytes = New-Object byte[] 32 | ||
| [System.Security.Cryptography.RandomNumberGenerator]::Fill($pwdBytes) | ||
| $tempPwd = [Convert]::ToBase64String($pwdBytes) | ||
| $securePwd = ConvertTo-SecureString $tempPwd -AsPlainText -Force | ||
| $cred = New-Object System.Management.Automation.PSCredential("admin", $securePwd) # GOOD | ||
|
|
||
| # 5) CSPRNG bytes flow into HTTP Authorization header | ||
| $tokenBytes = New-Object byte[] 32 | ||
| [System.Security.Cryptography.RandomNumberGenerator]::Fill($tokenBytes) | ||
| $bearerToken = [Convert]::ToBase64String($tokenBytes) | ||
| Invoke-RestMethod -Uri "https://api.example.com/data" ` | ||
| -Headers @{ Authorization = "Bearer $bearerToken" } # GOOD | ||
|
|
||
| # 6) CSPRNG via RNGCryptoServiceProvider fills key bytes for cipher | ||
| $rng = [Security.Cryptography.RNGCryptoServiceProvider]::Create() | ||
| $aesKey2 = New-Object byte[] 32 | ||
| $rng.GetBytes($aesKey2) # GOOD: filled by CSPRNG | ||
| $aes2 = [System.Security.Cryptography.Aes]::Create() | ||
| $aes2.Key = $aesKey2 # GOOD: unpredictable key |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... I'm not quite convinced this is the right fix. I'll do some digging on this today!