Skip to content

Commit 25df927

Browse files
authored
Merge pull request #322 from microsoft/powershell-crypto
Powershell crypto queries
2 parents 8aff770 + e64bf9b commit 25df927

24 files changed

+737
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import CryptoAlgorithmNames
2+
import CryptoArtifact
3+
import CryptographyModule
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
predicate isHashingAlgorithm(string name) {
2+
name =
3+
[
4+
"blake2", "blake2b", "blake2s", "sha2", "sha224", "sha256", "sha384", "sha512", "sha512224",
5+
"sha512256", "sha3", "sha3224", "sha3256", "sha3384", "sha3512", "shake128", "shake256",
6+
"sm3", "whirlpool", "poly1305", "havel128", "md2", "md4", "md5", "panama", "ripemd",
7+
"ripemd128", "ripemd256", "ripemd160", "ripemd320", "sha0", "sha1", "sha", "mgf1", "mgf1sha1",
8+
"mdc2", "siphash"
9+
]
10+
}
11+
12+
predicate isSymmetricAlgorithm(string name) {
13+
name =
14+
[
15+
"aes", "aes128", "aes192", "aes256", "aria", "blowfish", "bf", "ecies", "cast", "cast5",
16+
"camellia", "camellia128", "camellia192", "camellia256", "chacha", "chacha20",
17+
"chacha20poly1305", "gost", "gostr34102001", "gostr341094", "gostr341194", "gost2814789",
18+
"gostr341194", "gost2814789", "gost28147", "gostr341094", "gost89", "gost94", "gost34102012",
19+
"gost34112012", "idea", "rabbit", "seed", "sm4", "des", "desx", "3des", "tdes", "2des",
20+
"des3", "tripledes", "tdea", "tripledea", "arc2", "rc2", "arc4", "rc4", "arcfour", "arc5",
21+
"rc5", "magma", "kuznyechik"
22+
]
23+
}
24+
25+
predicate isCipherBlockModeAlgorithm(string name) {
26+
name = ["cbc", "gcm", "ccm", "cfb", "ofb", "cfb8", "ctr", "openpgp", "xts", "eax", "siv", "ecb"]
27+
}
28+
29+
string unknownAlgorithm() { result = "UNKNOWN" }
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import powershell
2+
import CryptoAlgorithmNames
3+
import semmle.code.powershell.dataflow.DataFlow
4+
5+
/*
6+
* A cryptographic artifact is a DataFlow::Node associated with some
7+
* operation, algorithm, or any other aspect of cryptography.
8+
*/
9+
10+
abstract class CryptographicArtifact extends DataFlow::Node { }
11+
12+
abstract class CryptographicAlgorithm extends CryptographicArtifact {
13+
abstract string getName();
14+
}
15+
16+
abstract class HashAlgorithm extends CryptographicAlgorithm {
17+
final string getHashName() {
18+
if exists(string n | n = this.getName() and isHashingAlgorithm(n))
19+
then result = this.getName()
20+
else result = unknownAlgorithm()
21+
}
22+
}
23+
24+
abstract class SymmetricAlgorithm extends CryptographicAlgorithm {
25+
final string getSymmetricAlgorithmName() {
26+
if exists(string n | n = this.getName() and isSymmetricAlgorithm(n))
27+
then result = this.getName()
28+
else result = unknownAlgorithm()
29+
}
30+
}
31+
32+
abstract class BlockMode extends CryptographicAlgorithm {
33+
final string getBlockModeName() {
34+
if exists(string n | n = this.getName() and isCipherBlockModeAlgorithm(n))
35+
then result = this.getName()
36+
else result = unknownAlgorithm()
37+
}
38+
}
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import powershell
2+
import semmle.code.powershell.dataflow.DataFlow
3+
import semmle.code.powershell.ApiGraphs
4+
import CryptoArtifact
5+
6+
abstract class CryptoAlgorithmObjectCreation extends DataFlow::ObjectCreationNode {
7+
string objectName;
8+
9+
CryptoAlgorithmObjectCreation() {
10+
objectName =
11+
this.getExprNode().getExpr().(CallExpr).getAnArgument().getValue().asString().toLowerCase()
12+
}
13+
14+
}
15+
16+
abstract class CryptoAlgorithmCreateCall extends DataFlow::CallNode {
17+
string objectName;
18+
19+
CryptoAlgorithmCreateCall() {
20+
this =
21+
API::getTopLevelMember("system")
22+
.getMember("security")
23+
.getMember("cryptography")
24+
.getMember(objectName)
25+
.getMember("create")
26+
.asCall()
27+
}
28+
}
29+
30+
abstract class CryptoAlgorithmCreateArgCall extends DataFlow::CallNode {
31+
string objectName;
32+
33+
CryptoAlgorithmCreateArgCall() {
34+
(
35+
this =
36+
API::getTopLevelMember("system")
37+
.getMember("security")
38+
.getMember("cryptography")
39+
.getMember(_)
40+
.getMember("create")
41+
.asCall() or
42+
this =
43+
API::getTopLevelMember("system")
44+
.getMember("security")
45+
.getMember("cryptography")
46+
.getMember("create")
47+
.asCall()
48+
) and
49+
objectName = this.getAnArgument().asExpr().getValue().asString().toLowerCase()
50+
}
51+
52+
}
53+
54+
class CryptoAlgorithmCreateFromNameCall extends DataFlow::CallNode {
55+
string objectName;
56+
57+
CryptoAlgorithmCreateFromNameCall() {
58+
this =
59+
API::getTopLevelMember("system")
60+
.getMember("security")
61+
.getMember("cryptography")
62+
.getMember("cryptoconfig")
63+
.getMember("createfromname")
64+
.asCall() and
65+
objectName = this.getAnArgument().asExpr().getValue().asString().toLowerCase()
66+
}
67+
68+
}
69+
70+
class HashAlgorithmObjectCreation extends HashAlgorithm, CryptoAlgorithmObjectCreation {
71+
string algName;
72+
73+
HashAlgorithmObjectCreation() {
74+
objectName = "system.security.cryptography." + algName + ["", "cryptoserviceprovider"] and
75+
isHashingAlgorithm(algName)
76+
}
77+
78+
override string getName() { result = algName }
79+
}
80+
81+
class HashAlgorithmCreateCall extends HashAlgorithm, CryptoAlgorithmCreateCall {
82+
string algName;
83+
84+
HashAlgorithmCreateCall() {
85+
isHashingAlgorithm(algName) and
86+
objectName = ["", "system.security.cryptography."] + algName
87+
}
88+
89+
override string getName() { result = algName }
90+
}
91+
92+
class HashAlgorithmCreateFromNameCall extends HashAlgorithm, CryptoAlgorithmCreateFromNameCall {
93+
string algName;
94+
95+
HashAlgorithmCreateFromNameCall() {
96+
objectName = ["", "system.security.cryptography."] + algName and
97+
isHashingAlgorithm(algName)
98+
}
99+
100+
override string getName() { result = algName }
101+
}
102+
103+
class SymmetricAlgorithmObjectCreation extends SymmetricAlgorithm, CryptoAlgorithmObjectCreation {
104+
string algName;
105+
106+
SymmetricAlgorithmObjectCreation() {
107+
(
108+
objectName = "system.security.cryptography." + algName or
109+
objectName = "system.security.cryptography." + algName + "cryptoserviceprovider" or
110+
objectName = "system.security.cryptography.symmetricalgorithm." + algName
111+
) and
112+
isSymmetricAlgorithm(algName)
113+
}
114+
115+
override string getName() { result = algName }
116+
}
117+
118+
class SymmetricAlgorithmCreateCall extends SymmetricAlgorithm, CryptoAlgorithmCreateCall {
119+
string algName;
120+
121+
SymmetricAlgorithmCreateCall() {
122+
isSymmetricAlgorithm(algName) and
123+
objectName = ["", "system.security.cryptography.", "system.security.cryptography.symmetricalgorithm."] + algName
124+
}
125+
126+
override string getName() { result = algName }
127+
}
128+
129+
class SymmetricAlgorithmCreateArgCall extends SymmetricAlgorithm, CryptoAlgorithmCreateArgCall {
130+
string algName;
131+
132+
SymmetricAlgorithmCreateArgCall() {
133+
objectName = ["", "system.security.cryptography."] + algName and
134+
isSymmetricAlgorithm(algName)
135+
}
136+
137+
override string getName() { result = algName }
138+
}
139+
140+
class SymmetricAlgorithmCreateFromNameCall extends SymmetricAlgorithm,
141+
CryptoAlgorithmCreateFromNameCall
142+
{
143+
string algName;
144+
145+
SymmetricAlgorithmCreateFromNameCall() {
146+
objectName = ["", "system.security.cryptography.", "system.security.cryptography.symmetricalgorithm."] + algName and
147+
isSymmetricAlgorithm(algName)
148+
}
149+
150+
override string getName() { result = algName }
151+
}
152+
153+
class CipherBlockStringConstExpr extends BlockMode {
154+
string modeName;
155+
156+
CipherBlockStringConstExpr() {
157+
exists(StringConstExpr s |
158+
s = this.asExpr().getExpr() and
159+
modeName = s.getValueString().toLowerCase() and
160+
isCipherBlockModeAlgorithm(modeName)
161+
)
162+
}
163+
164+
override string getName() { result = modeName }
165+
}
166+
167+
class CipherBlockModeEnum extends BlockMode {
168+
string modeName;
169+
170+
CipherBlockModeEnum() {
171+
exists(API::Node node |
172+
node =
173+
API::getTopLevelMember("system")
174+
.getMember("security")
175+
.getMember("cryptography")
176+
.getMember("ciphermode")
177+
.getMember(modeName) and
178+
this = node.asSource() and
179+
isCipherBlockModeAlgorithm(modeName)
180+
)
181+
}
182+
183+
override string getName() { result = modeName }
184+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
2+
<qhelp>
3+
<overview>
4+
<p>
5+
Cipher modes determine how block ciphers process data during encryption. Some cipher modes
6+
such as Electronic Codebook (ECB) and Output Feedback (OFB) are considered weak and should
7+
not be used for encryption.
8+
</p>
9+
<p>
10+
ECB mode encrypts identical plaintext blocks into identical ciphertext blocks, which can
11+
reveal patterns in the encrypted data and leak information about the plaintext. OFB mode
12+
has weaknesses that can lead to security issues when the same initialization vector (IV)
13+
is reused.
14+
</p>
15+
</overview>
16+
<recommendation>
17+
<p>
18+
Use a secure cipher mode such as Cipher Block Chaining (CBC) with a random initialization
19+
vector (IV), or an authenticated encryption mode like Galois/Counter Mode (GCM). GCM is
20+
preferred as it provides both confidentiality and integrity.
21+
</p>
22+
</recommendation>
23+
<references>
24+
<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>
25+
<li>OWASP: <a href="https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html">Cryptographic Storage Cheat Sheet</a>.</li>
26+
<li>CWE-327: <a href="https://cwe.mitre.org/data/definitions/327.html">Use of a Broken or Risky Cryptographic Algorithm</a>.</li>
27+
</references>
28+
</qhelp>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* @name Insecure Symmetric cipher mode
3+
* @description Use of insecure cipher mode
4+
* @problem.severity error
5+
* @kind path-problem
6+
* @security-severity 8.8
7+
* @precision high
8+
* @id powershell/microsoft/public/weak-cipher-mode
9+
* @tags correctness
10+
* security
11+
* external/cwe/cwe-327
12+
*/
13+
14+
import powershell
15+
import semmle.code.powershell.dataflow.DataFlow
16+
import semmle.code.powershell.dataflow.TaintTracking
17+
import semmle.code.powershell.ApiGraphs
18+
import semmle.code.powershell.security.cryptography.Concepts
19+
import WeakEncryptionFlow::PathGraph
20+
21+
class AesModeProperty extends MemberExpr {
22+
AesModeProperty() {
23+
exists(DataFlow::ObjectCreationNode aesObjectCreation, DataFlow::Node aesObjectAccess |
24+
(
25+
aesObjectCreation
26+
.asExpr()
27+
.getExpr()
28+
.(ObjectCreation)
29+
.getAnArgument()
30+
.getValue()
31+
.stringMatches("System.Security.Cryptography.AesManaged") or
32+
aesObjectCreation =
33+
API::getTopLevelMember("system")
34+
.getMember("security")
35+
.getMember("cryptography")
36+
.getMember("aes")
37+
.getMember("create")
38+
.asCall()
39+
) and
40+
aesObjectAccess.getALocalSource() = aesObjectCreation and
41+
aesObjectAccess.asExpr().getExpr() = this.getQualifier() and
42+
this.getLowerCaseMemberName() = "mode"
43+
)
44+
}
45+
}
46+
47+
module Config implements DataFlow::ConfigSig {
48+
predicate isSource(DataFlow::Node source) {
49+
exists(BlockMode blockMode |
50+
source = blockMode and
51+
not blockMode.getBlockModeName() = ["cbc", "cts", "xts"]
52+
)
53+
}
54+
55+
predicate isSink(DataFlow::Node sink) {
56+
sink.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr().getExpr() =
57+
any(AesModeProperty mode).getQualifier()
58+
}
59+
60+
predicate allowImplicitRead(DataFlow::Node n, DataFlow::ContentSet cs) {
61+
isSink(n) and
62+
exists(DataFlow::Content::FieldContent fc |
63+
cs.isSingleton(fc) and
64+
fc.getLowerCaseName() = "mode"
65+
)
66+
}
67+
}
68+
69+
module WeakEncryptionFlow = TaintTracking::Global<Config>;
70+
71+
from WeakEncryptionFlow::PathNode source, WeakEncryptionFlow::PathNode sink
72+
where WeakEncryptionFlow::flowPath(source, sink)
73+
select sink.getNode(), source, sink, ""
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
2+
<qhelp>
3+
<overview>
4+
<p>
5+
Key Derivation Functions (KDFs) are used to derive cryptographic keys from passwords or
6+
other secret values. Using obsolete or weak KDF algorithms can compromise password security.
7+
</p>
8+
<p>
9+
The <code>PasswordDeriveBytes</code> class implements the PBKDF1 algorithm, which is
10+
considered obsolete and insecure. PBKDF1 has known weaknesses and is limited to producing
11+
keys that are at most 160 bits in length.
12+
</p>
13+
</overview>
14+
<recommendation>
15+
<p>
16+
Use the <code>Rfc2898DeriveBytes</code> class which implements the PBKDF2 algorithm with
17+
a SHA-2 hash function (such as SHA-256 or SHA-512). Additionally, use a high iteration
18+
count (at least 100,000) to increase resistance against brute-force attacks.
19+
</p>
20+
21+
</recommendation>
22+
<references>
23+
<li>NIST, SP 800-132: <a href="https://csrc.nist.gov/publications/detail/sp/800-132/final">Recommendation for Password-Based Key Derivation</a>.</li>
24+
<li>OWASP: <a href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html">Password Storage Cheat Sheet</a>.</li>
25+
<li>CWE-327: <a href="https://cwe.mitre.org/data/definitions/327.html">Use of a Broken or Risky Cryptographic Algorithm</a>.</li>
26+
<li>CWE-328: <a href="https://cwe.mitre.org/data/definitions/328.html">Use of Weak Hash</a>.</li>
27+
</references>
28+
</qhelp>

0 commit comments

Comments
 (0)