Skip to content
Open
Show file tree
Hide file tree
Changes from 10 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,3 @@
import CryptoAlgorithmNames
import CryptoArtifact
import CryptographyModule
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
predicate isHashingAlgorithm(string name) {
name =
[
"blake2", "blake2b", "blake2s", "sha2", "sha224", "sha256", "sha384", "sha512", "sha512224",
"sha512256", "sha3", "sha3224", "sha3256", "sha3384", "sha3512", "shake128", "shake256",
"sm3", "whirlpool", "poly1305", "havel128", "md2", "md4", "md5", "panama", "ripemd",
"ripemd128", "ripemd256", "ripemd160", "ripemd320", "sha0", "sha1", "sha", "mgf1", "mgf1sha1",
"mdc2", "siphash"
]
}

predicate isSymmetricAlgorithm(string name) {
name =
[
"aes", "aes128", "aes192", "aes256", "aria", "blowfish", "bf", "ecies", "cast", "cast5",
"camellia", "camellia128", "camellia192", "camellia256", "chacha", "chacha20",
"chacha20poly1305", "gost", "gostr34102001", "gostr341094", "gostr341194", "gost2814789",
"gostr341194", "gost2814789", "gost28147", "gostr341094", "gost89", "gost94", "gost34102012",
"gost34112012", "idea", "rabbit", "seed", "sm4", "des", "desx", "3des", "tdes", "2des",
"des3", "tripledes", "tdea", "tripledea", "arc2", "rc2", "arc4", "rc4", "arcfour", "arc5",
"rc5", "magma", "kuznyechik"
]
}

predicate isCipherBlockModeAlgorithm(string name) {
name = ["cbc", "gcm", "ccm", "cfb", "ofb", "cfb8", "ctr", "openpgp", "xts", "eax", "siv", "ecb"]
}

string unknownAlgorithm() { result = "UNKNOWN" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import powershell
import CryptoAlgorithmNames
import semmle.code.powershell.dataflow.DataFlow

/*
* A cryptographic artifact is a DataFlow::Node associated with some
* operation, algorithm, or any other aspect of cryptography.
*/

abstract class CryptographicArtifact extends DataFlow::Node { }

abstract class CryptographicAlgorithm extends CryptographicArtifact {
abstract string getName();
}

abstract class HashAlgorithm extends CryptographicAlgorithm {
final string getHashName() {
if exists(string n | n = this.getName() and isHashingAlgorithm(n))
then isHashingAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
}

abstract class SymmetricAlgorithm extends CryptographicAlgorithm {
final string getSymmetricAlgorithmName() {
if exists(string n | n = this.getName() and isSymmetricAlgorithm(n))
then isSymmetricAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
}

abstract class BlockMode extends CryptographicAlgorithm {
final string getBlockModeName() {
if exists(string n | n = this.getName() and isCipherBlockModeAlgorithm(n))
then isCipherBlockModeAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import powershell
import semmle.code.powershell.dataflow.DataFlow
import semmle.code.powershell.ApiGraphs
import CryptoArtifact

class CryptoAlgorithmObjectCreation extends DataFlow::ObjectCreationNode {
string objectName;

CryptoAlgorithmObjectCreation() {
objectName =
this.getExprNode().getExpr().(CallExpr).getAnArgument().getValue().asString().toLowerCase()
}

string getObjectName() { result = objectName }
}

class CryptoAlgorithmCreateCall extends DataFlow::CallNode {
string objectName;

CryptoAlgorithmCreateCall() {
this =
API::getTopLevelMember("system")
.getMember("security")
.getMember("cryptography")
.getMember(objectName)
.getMember("create")
.asCall()
}

string getObjectName() { result = objectName }
}

class CryptoAlgorithmCreateArgCall extends DataFlow::CallNode {
string objectName;

CryptoAlgorithmCreateArgCall() {
(
this =
API::getTopLevelMember("system")
.getMember("security")
.getMember("cryptography")
.getMember(_)
.getMember("create")
.asCall() or
this =
API::getTopLevelMember("system")
.getMember("security")
.getMember("cryptography")
.getMember("create")
.asCall()
) and
objectName = this.getAnArgument().asExpr().getValue().asString().toLowerCase()
}

string getObjectName() { result = objectName }
}

class CryptoAlgorithmCreateFromNameCall extends DataFlow::CallNode {
string objectName;

CryptoAlgorithmCreateFromNameCall() {
this =
API::getTopLevelMember("system")
.getMember("security")
.getMember("cryptography")
.getMember("cryptoconfig")
.getMember("createfromname")
.asCall() and
objectName = this.getAnArgument().asExpr().getValue().asString().toLowerCase()
}

string getObjectName() { result = objectName }
}

class HashAlgorithmObjectCreation extends HashAlgorithm, CryptoAlgorithmObjectCreation {
string algName;

HashAlgorithmObjectCreation() {
(
this.getObjectName() = "system.security.cryptography." + algName or
this.getObjectName() = "system.security.cryptography." + algName + "cryptoserviceprovider"
) and
isHashingAlgorithm(algName)
}

override string getName() { result = algName }
}

class HashAlgorithmCreateCall extends HashAlgorithm, CryptoAlgorithmCreateCall {
string algName;

HashAlgorithmCreateCall() {
isHashingAlgorithm(this.getObjectName()) and
(
this.getObjectName() = algName or
this.getObjectName() = "system.security.cryptography." + algName
)
}

override string getName() { result = algName }
}

class HashAlgorithmCreateFromNameCall extends HashAlgorithm, CryptoAlgorithmCreateFromNameCall {
string algName;

HashAlgorithmCreateFromNameCall() {
(
this.getObjectName() = algName or
this.getObjectName() = "system.security.cryptography." + algName
) and
isHashingAlgorithm(algName)
}

override string getName() { result = algName }
}

class SymmetricAlgorithmObjectCreation extends SymmetricAlgorithm, CryptoAlgorithmObjectCreation {
string algName;

SymmetricAlgorithmObjectCreation() {
(
this.getObjectName() = "system.security.cryptography." + algName or
this.getObjectName() = "system.security.cryptography." + algName + "cryptoserviceprovider" or
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
this.getObjectName() = "system.security.cryptography." + algName or
this.getObjectName() = "system.security.cryptography." + algName + "cryptoserviceprovider" or
this.getObjectName() = "system.security.cryptography." + algName + ["", "cryptoserviceprovider"] or

this.getObjectName() = "system.security.cryptography.symmetricalgorithm." + algName
) and
isSymmetricAlgorithm(algName)
}

override string getName() { result = algName }
}

class SymmetricAlgorithmCreateCall extends SymmetricAlgorithm, CryptoAlgorithmCreateCall {
string algName;

SymmetricAlgorithmCreateCall() {
isSymmetricAlgorithm(this.getObjectName()) and
(
this.getObjectName() = algName or
this.getObjectName() = "system.security.cryptography." + algName or
this.getObjectName() = "system.security.cryptography.symmetricalgorithm." + algName
)
}

override string getName() { result = algName }
}

class SymmetricAlgorithmCreateArgCall extends SymmetricAlgorithm, CryptoAlgorithmCreateArgCall {
string algName;

SymmetricAlgorithmCreateArgCall() {
algName = this.getObjectName() and
isSymmetricAlgorithm(algName)
or
this.getObjectName() = "system.security.cryptography." + algName and
isSymmetricAlgorithm(algName)
}

override string getName() { result = algName }
}

class SymmetricAlgorithmCreateFromNameCall extends SymmetricAlgorithm,
CryptoAlgorithmCreateFromNameCall
{
string algName;

SymmetricAlgorithmCreateFromNameCall() {
(
this.getObjectName() = algName or
this.getObjectName() = "system.security.cryptography." + algName or
this.getObjectName() = "system.security.cryptography.symmetricalgorithm." + algName
) and
isSymmetricAlgorithm(algName)
}

override string getName() { result = algName }
}

class CipherBlockStringConstExpr extends BlockMode {
string modeName;

CipherBlockStringConstExpr() {
exists(StringConstExpr s |
s = this.asExpr().getExpr() and
modeName = s.getValueString().toLowerCase() and
isCipherBlockModeAlgorithm(modeName)
)
}

override string getName() { result = modeName }
}

class CipherBlockModeEnum extends BlockMode {
string modeName;

CipherBlockModeEnum() {
exists(API::Node node |
node =
API::getTopLevelMember("system")
.getMember("security")
.getMember("cryptography")
.getMember("ciphermode")
.getMember(modeName) and
this = node.asSource() and
isCipherBlockModeAlgorithm(modeName)
)
}

override string getName() { result = modeName }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>
Cipher modes determine how block ciphers process data during encryption. Some cipher modes
such as Electronic Codebook (ECB) and Output Feedback (OFB) are considered weak and should
not be used for encryption.
</p>
<p>
ECB mode encrypts identical plaintext blocks into identical ciphertext blocks, which can
reveal patterns in the encrypted data and leak information about the plaintext. OFB mode
has weaknesses that can lead to security issues when the same initialization vector (IV)
is reused.
</p>
</overview>
<recommendation>
<p>
Use a secure cipher mode such as Cipher Block Chaining (CBC) with a random initialization
vector (IV), or an authenticated encryption mode like Galois/Counter Mode (GCM). GCM is
preferred as it provides both confidentiality and integrity.
</p>
</recommendation>
<references>
<li>NIST, SP 800-38A: <a href="https://csrc.nist.gov/publications/detail/sp/800-38a/final">Recommendation for Block Cipher Modes of Operation</a>.</li>
<li>OWASP: <a href="https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html">Cryptographic Storage Cheat Sheet</a>.</li>
<li>CWE-327: <a href="https://cwe.mitre.org/data/definitions/327.html">Use of a Broken or Risky Cryptographic Algorithm</a>.</li>
</references>
</qhelp>
71 changes: 71 additions & 0 deletions powershell/ql/src/queries/security/cwe-327/ApprovedCipherMode.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* @name Insecure Symmetric cipher mode
* @description Use of insecure cipher mode
* @problem.severity error
* @kind path-problem
* @security-severity 8.8
* @precision high
* @id powershell/microsoft/public/weak-cipher-mode
* @tags correctness
* security
* external/cwe/cwe-327
*/

import powershell
import semmle.code.powershell.dataflow.DataFlow
import semmle.code.powershell.dataflow.TaintTracking
import semmle.code.powershell.ApiGraphs
import semmle.code.powershell.security.cryptography.Concepts
import WeakEncryptionFlow::PathGraph

class AesModeProperty extends MemberExpr {
AesModeProperty() {
exists(DataFlow::ObjectCreationNode aesObjectCreation, DataFlow::Node aesObjectAccess |
(
aesObjectCreation
.asExpr()
.getExpr()
.(ObjectCreation)
.getAnArgument()
.getValue()
.stringMatches("System.Security.Cryptography.AesManaged") or
aesObjectCreation.(DataFlow::CallNode) =
API::getTopLevelMember("system")
.getMember("security")
.getMember("cryptography")
.getMember("aes")
.getMember("create")
.asCall()
) and
aesObjectAccess.getALocalSource() = aesObjectCreation and
aesObjectAccess.asExpr().getExpr() = this.getQualifier() and
this.getLowerCaseMemberName() = "mode"
)
}
}

module Config implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source instanceof BlockMode and
not source.(BlockMode).getBlockModeName() = ["cbc", "cts", "xts"]
}

predicate isSink(DataFlow::Node sink) {
sink.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr().getExpr() =
any(AesModeProperty mode).getQualifier()
}

predicate allowImplicitRead(DataFlow::Node n, DataFlow::ContentSet cs) {
isSink(n) and
exists(DataFlow::Content::FieldContent fc |
cs.isSingleton(fc) and
fc.getLowerCaseName() = "mode"
)
}
}

module WeakEncryptionFlow = TaintTracking::Global<Config>;

from WeakEncryptionFlow::PathNode source, WeakEncryptionFlow::PathNode sink
where WeakEncryptionFlow::flowPath(source, sink)
select sink.getNode(), source, sink, ""
Loading