Skip to content
This repository was archived by the owner on Jan 5, 2023. It is now read-only.
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
245 changes: 245 additions & 0 deletions ql/src/experimental/CWE-327/CryptoLibraries.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
/**
* Provides classes for modeling cryptographic libraries.
*/

import go

/**
* Names of cryptographic algorithms, separated into strong and weak variants.
*
* The names are normalized: upper-case, no spaces, dashes or underscores.
*
* The names are inspired by the names used in real world crypto libraries.
*
* The classification into strong and weak are based on OWASP and Wikipedia (2020).
*
* Sources (more links in qhelp file):
* https://en.wikipedia.org/wiki/Strong_cryptography#Cryptographically_strong_algorithms
* https://en.wikipedia.org/wiki/Strong_cryptography#Examples
* https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html
*/
private module AlgorithmNames {
predicate isStrongHashingAlgorithm(string name) {
name = "DSA" or
name = "ED25519" or
name = "ES256" or
name = "ECDSA256" or
name = "ES384" or
name = "ECDSA384" or
name = "ES512" or
name = "ECDSA512" or
name = "SHA2" or
name = "SHA224" or
name = "SHA256" or
name = "SHA384" or
name = "SHA512" or
name = "SHA3"
}

predicate isWeakHashingAlgorithm(string name) {
name = "HAVEL128" or
name = "MD2" or
name = "MD4" or
name = "MD5" or
name = "PANAMA" or
name = "RIPEMD" or
name = "RIPEMD128" or
name = "RIPEMD256" or
name = "RIPEMD320" or
name = "SHA0" or
name = "SHA1"
}

predicate isStrongEncryptionAlgorithm(string name) {
name = "AES" or
name = "AES128" or
name = "AES192" or
name = "AES256" or
name = "AES512" or
name = "RSA" or
name = "RABBIT" or
name = "BLOWFISH"
}

predicate isWeakEncryptionAlgorithm(string name) {
name = "DES" or
name = "3DES" or
name = "TRIPLEDES" or
name = "TDEA" or
name = "TRIPLEDEA" or
name = "ARC2" or
name = "RC2" or
name = "ARC4" or
name = "RC4" or
name = "ARCFOUR" or
name = "ARC5" or
name = "RC5"
}

predicate isStrongPasswordHashingAlgorithm(string name) {
name = "ARGON2" or
name = "PBKDF2" or
name = "BCRYPT" or
name = "SCRYPT"
}

predicate isWeakPasswordHashingAlgorithm(string name) { none() }
}

private import AlgorithmNames

/**
* A cryptographic algorithm.
*/
private newtype TCryptographicAlgorithm =
MkHashingAlgorithm(string name, boolean isWeak) {
isStrongHashingAlgorithm(name) and isWeak = false
or
isWeakHashingAlgorithm(name) and isWeak = true
} or
MkEncryptionAlgorithm(string name, boolean isWeak) {
isStrongEncryptionAlgorithm(name) and isWeak = false
or
isWeakEncryptionAlgorithm(name) and isWeak = true
} or
MkPasswordHashingAlgorithm(string name, boolean isWeak) {
isStrongPasswordHashingAlgorithm(name) and isWeak = false
or
isWeakPasswordHashingAlgorithm(name) and isWeak = true
}

/**
* A cryptographic algorithm.
*/
abstract class CryptographicAlgorithm extends TCryptographicAlgorithm {
/** Gets a textual representation of this element. */
string toString() { result = getName() }

/**
* Gets the name of this algorithm.
*/
abstract string getName();

/**
* Holds if the name of this algorithm matches `name` modulo case,
* white space, dashes and underscores.
*/
bindingset[name]
predicate matchesName(string name) {
exists(name.regexpReplaceAll("[-_]", "").regexpFind("(?i)\\Q" + getName() + "\\E", _, _))
}

/**
* Holds if this algorithm is weak.
*/
abstract predicate isWeak();
}

/**
* A hashing algorithm such as `MD5` or `SHA512`.
*/
class HashingAlgorithm extends MkHashingAlgorithm, CryptographicAlgorithm {
string name;
boolean isWeak;

HashingAlgorithm() { this = MkHashingAlgorithm(name, isWeak) }

override string getName() { result = name }

override predicate isWeak() { isWeak = true }
}

/**
* An encryption algorithm such as `DES` or `AES512`.
*/
class EncryptionAlgorithm extends MkEncryptionAlgorithm, CryptographicAlgorithm {
string name;
boolean isWeak;

EncryptionAlgorithm() { this = MkEncryptionAlgorithm(name, isWeak) }

override string getName() { result = name }

override predicate isWeak() { isWeak = true }
}

/**
* A password hashing algorithm such as `PBKDF2` or `SCRYPT`.
*/
class PasswordHashingAlgorithm extends MkPasswordHashingAlgorithm, CryptographicAlgorithm {
string name;
boolean isWeak;

PasswordHashingAlgorithm() { this = MkPasswordHashingAlgorithm(name, isWeak) }

override string getName() { result = name }

override predicate isWeak() { isWeak = true }
}

/**
* An application of a cryptographic algorithm.
*/
abstract class CryptographicOperation extends DataFlow::Node {
/**
* Gets the input the algorithm is used on, e.g. the plain text input to be encrypted.
*/
abstract Expr getInput();

/**
* Gets the applied algorithm.
*/
abstract CryptographicAlgorithm getAlgorithm();
}

/**
* Models cryptographic operations of the `crypto/md5` package.
*/
class Md5 extends CryptographicOperation, DataFlow::CallNode {
Md5() { getTarget().hasQualifiedName("crypto/md5", ["New", "Sum"]) }

override Expr getInput() { result = this.getArgument(0).asExpr() }

override CryptographicAlgorithm getAlgorithm() {
result.matchesName(this.getTarget().getPackage().getName())
}
}

/**
* Models cryptographic operations of the `crypto/sha1` package.
*/
class Sha1 extends CryptographicOperation, DataFlow::CallNode {
Sha1() { getTarget().hasQualifiedName("crypto/sha1", ["New", "Sum"]) }

override Expr getInput() { result = this.getArgument(0).asExpr() }

override CryptographicAlgorithm getAlgorithm() {
result.matchesName(this.getTarget().getPackage().getName())
}
}

/**
* Models cryptographic operations of the `crypto/des` package.
*/
class Des extends CryptographicOperation, DataFlow::CallNode {
Des() { getTarget().hasQualifiedName("crypto/des", ["NewCipher", "NewTripleDESCipher"]) }

override Expr getInput() { result = this.getArgument(0).asExpr() }

override CryptographicAlgorithm getAlgorithm() {
result.matchesName(this.getTarget().getPackage().getName())
}
}

/**
* Models cryptographic operations of the `crypto/rc4` package.
*/
class Rc4 extends CryptographicOperation, DataFlow::CallNode {
Rc4() { getTarget().hasQualifiedName("crypto/rc4", ["NewCipher"]) }

override Expr getInput() { result = this.getArgument(0).asExpr() }

override CryptographicAlgorithm getAlgorithm() {
result.matchesName(this.getTarget().getPackage().getName())
}
}
55 changes: 55 additions & 0 deletions ql/src/experimental/CWE-327/WeakCryptoAlgorithm.qhelp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Using weak cryptographic algorithms can leave data
vulnerable to being decrypted or forged by an attacker.
</p>

<p>
Many cryptographic algorithms provided by cryptography
libraries are known to be weak. Using such an
algorithm means that encrypted or hashed data is less
secure than it appears to be.
</p>

</overview>
<recommendation>

<p>
Ensure that you use a strong, modern cryptographic
algorithm. Use at least AES-128 or RSA-2048 for
encryption, and SHA-2 or SHA-3 for secure hashing.
</p>

</recommendation>
<example>

<p>
The following code uses the different packages to encrypt/hash
some secret data. The first few examples uses DES, MD5, RC4, and SHA1,
which are older algorithms that are now considered weak. The following
examples use AES and SHA256, which are stronger, more modern algorithms.
</p>

<sample src="examples/Crypto.go" />

</example>

<references>
<li>OWASP: <a
href="https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html">Cryptographic Storage Cheat Sheet</a>.
</li>
<li>Wikipedia: <a
href="https://en.wikipedia.org/wiki/Strong_cryptography#Cryptographically_strong_algorithms">Cryptographically Strong Algorithms</a>.
</li>
<li>Wikipedia: <a
href="https://en.wikipedia.org/wiki/Strong_cryptography#Examples">Strong Cryptography Examples</a>.
</li>
<li>NIST, FIPS 140 Annex a: <a href="http://csrc.nist.gov/publications/fips/fips140-2/fips1402annexa.pdf"> Approved Security Functions</a>.</li>
<li>NIST, SP 800-131A: <a href="http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar1.pdf"> Transitions: Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths</a>.</li>
</references>

</qhelp>
18 changes: 18 additions & 0 deletions ql/src/experimental/CWE-327/WeakCryptoAlgorithm.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* @name Use of a weak cryptographic algorithm
* @description Using weak cryptographic algorithms can allow an attacker to compromise security.
* @kind path-problem
* @problem.severity error
* @id go/weak-crypto-algorithm
* @tags security
* external/cwe/cwe-327
*/

import go
import WeakCryptoAlgorithmCustomizations::WeakCryptoAlgorithm
import DataFlow::PathGraph

from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "$@ is used in a weak cryptographic algorithm.",
source.getNode(), "Sensitive data"
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* Provides default sources, sinks and sanitizers for reasoning about
* sensitive information in weak cryptographic algorithms,
* as well as extension points for adding your own.
*/

import go
private import semmle.go.security.SensitiveActions
private import CryptoLibraries

module WeakCryptoAlgorithm {
/**
* A data flow source for sensitive information in weak cryptographic algorithms.
*/
abstract class Source extends DataFlow::Node { }

/**
* A data flow sink for sensitive information in weak cryptographic algorithms.
*/
abstract class Sink extends DataFlow::Node { }

/**
* A sanitizer for sensitive information in weak cryptographic algorithms.
*/
abstract class Sanitizer extends DataFlow::Node { }

/**
* A sensitive source.
*/
class SensitiveSource extends Source {
SensitiveSource() { this.asExpr() instanceof SensitiveExpr }
}

/**
* An expression used by a weak cryptographic algorithm.
*/
class WeakCryptographicOperationSink extends Sink {
WeakCryptographicOperationSink() {
exists(CryptographicOperation application |
application.getAlgorithm().isWeak() and
this.asExpr() = application.getInput()
)
}
}

/**
* A configuration depicting taint flow from sensitive information to weak cryptographic algorithms.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "WeakCryptoAlgorithm" }

override predicate isSource(DataFlow::Node source) { source instanceof Source }

override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }

override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node) or
node instanceof Sanitizer
}
}
}
Loading