mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge pull request #20939 from geoffw0/saltmodel
Rust: Add heuristic sinks for passwords, initialization vectors etc
This commit is contained in:
@@ -9,6 +9,7 @@ private import codeql.rust.dataflow.FlowSource
|
||||
private import codeql.rust.dataflow.FlowSink
|
||||
private import codeql.rust.Concepts
|
||||
private import codeql.rust.security.SensitiveData
|
||||
private import codeql.rust.dataflow.internal.Node as Node
|
||||
|
||||
/**
|
||||
* A kind of cryptographic value.
|
||||
@@ -98,6 +99,37 @@ module HardcodedCryptographicValue {
|
||||
override CryptographicValueKind getKind() { result = kind }
|
||||
}
|
||||
|
||||
/**
|
||||
* A heuristic sink for hard-coded cryptographic value vulnerabilities.
|
||||
*/
|
||||
private class HeuristicSinks extends Sink {
|
||||
CryptographicValueKind kind;
|
||||
|
||||
HeuristicSinks() {
|
||||
// any argument going to a parameter whose name matches a credential name
|
||||
exists(Call c, Function f, int argIndex, string argName |
|
||||
c.getPositionalArgument(argIndex) = this.asExpr() and
|
||||
c.getStaticTarget() = f and
|
||||
f.getParam(argIndex).getPat().(IdentPat).getName().getText() = argName and
|
||||
(
|
||||
argName = "password" and kind = "password"
|
||||
or
|
||||
argName = "iv" and kind = "iv"
|
||||
or
|
||||
argName = "nonce" and kind = "nonce"
|
||||
or
|
||||
argName = "salt" and kind = "salt"
|
||||
//
|
||||
// note: matching "key" results in too many false positives
|
||||
) and
|
||||
// don't duplicate modeled sinks
|
||||
not exists(ModelsAsDataSinks s | s.(Node::FlowSummaryNode).getSinkElement().getCall() = c)
|
||||
)
|
||||
}
|
||||
|
||||
override CryptographicValueKind getKind() { result = kind }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `getrandom` that is a barrier.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `rust/hard-coded-cryptographic-value` query has been extended with new heuristic sinks identifying passwords, initialization vectors, nonces and salts.
|
||||
@@ -39,6 +39,11 @@ module HardcodedCryptographicValueConfig implements DataFlow::ConfigSig {
|
||||
// case like `[0, 0, 0, 0]`)
|
||||
isSource(node)
|
||||
}
|
||||
|
||||
predicate isBarrierOut(DataFlow::Node node) {
|
||||
// make sinks barriers so that we only report the closest instance
|
||||
isSink(node)
|
||||
}
|
||||
}
|
||||
|
||||
module HardcodedCryptographicValueFlow = TaintTracking::Global<HardcodedCryptographicValueConfig>;
|
||||
|
||||
@@ -10,37 +10,45 @@
|
||||
| test_cookie.rs:21:28:21:34 | [0; 64] | test_cookie.rs:21:28:21:34 | [0; 64] | test_cookie.rs:22:16:22:24 | ...::from | This hard-coded value is used as $@. | test_cookie.rs:22:16:22:24 | ...::from | a key |
|
||||
| test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:42:14:42:32 | ...::from | This hard-coded value is used as $@. | test_cookie.rs:42:14:42:32 | ...::from | a key |
|
||||
| test_cookie.rs:49:23:49:25 | 0u8 | test_cookie.rs:49:23:49:25 | 0u8 | test_cookie.rs:53:14:53:32 | ...::from | This hard-coded value is used as $@. | test_cookie.rs:53:14:53:32 | ...::from | a key |
|
||||
| test_heuristic.rs:44:31:44:38 | [0u8; 16] | test_heuristic.rs:44:31:44:38 | [0u8; 16] | test_heuristic.rs:45:41:45:48 | const_iv | This hard-coded value is used as $@. | test_heuristic.rs:45:41:45:48 | const_iv | an initialization vector |
|
||||
| test_heuristic.rs:63:30:63:37 | "secret" | test_heuristic.rs:63:30:63:37 | "secret" | test_heuristic.rs:63:30:63:37 | "secret" | This hard-coded value is used as $@. | test_heuristic.rs:63:30:63:37 | "secret" | a password |
|
||||
| test_heuristic.rs:64:20:64:27 | [0u8; 16] | test_heuristic.rs:64:20:64:27 | [0u8; 16] | test_heuristic.rs:64:19:64:27 | &... | This hard-coded value is used as $@. | test_heuristic.rs:64:19:64:27 | &... | a nonce |
|
||||
| test_heuristic.rs:65:31:65:38 | [0u8; 16] | test_heuristic.rs:65:31:65:38 | [0u8; 16] | test_heuristic.rs:65:30:65:38 | &... | This hard-coded value is used as $@. | test_heuristic.rs:65:30:65:38 | &... | a salt |
|
||||
| test_heuristic.rs:67:22:67:22 | 0 | test_heuristic.rs:67:22:67:22 | 0 | test_heuristic.rs:67:22:67:22 | 0 | This hard-coded value is used as $@. | test_heuristic.rs:67:22:67:22 | 0 | a salt |
|
||||
| test_heuristic.rs:69:32:69:32 | 1 | test_heuristic.rs:69:32:69:32 | 1 | test_heuristic.rs:69:22:69:32 | ... + ... | This hard-coded value is used as $@. | test_heuristic.rs:69:22:69:32 | ... + ... | a salt |
|
||||
| test_heuristic.rs:70:34:70:35 | 32 | test_heuristic.rs:70:34:70:35 | 32 | test_heuristic.rs:70:22:70:62 | ... ^ ... | This hard-coded value is used as $@. | test_heuristic.rs:70:22:70:62 | ... ^ ... | a salt |
|
||||
| test_heuristic.rs:70:52:70:61 | 0xFFFFFFFF | test_heuristic.rs:70:52:70:61 | 0xFFFFFFFF | test_heuristic.rs:70:22:70:62 | ... ^ ... | This hard-coded value is used as $@. | test_heuristic.rs:70:22:70:62 | ... ^ ... | a salt |
|
||||
edges
|
||||
| test_cipher.rs:18:9:18:14 | const1 [&ref] | test_cipher.rs:19:73:19:78 | const1 [&ref] | provenance | |
|
||||
| test_cipher.rs:18:28:18:36 | &... [&ref] | test_cipher.rs:18:9:18:14 | const1 [&ref] | provenance | |
|
||||
| test_cipher.rs:18:29:18:36 | [0u8; 16] | test_cipher.rs:18:28:18:36 | &... [&ref] | provenance | |
|
||||
| test_cipher.rs:19:49:19:79 | ...::from_slice(...) [&ref] | test_cipher.rs:19:30:19:47 | ...::new | provenance | MaD:3 Sink:MaD:3 |
|
||||
| test_cipher.rs:19:73:19:78 | const1 [&ref] | test_cipher.rs:19:49:19:79 | ...::from_slice(...) [&ref] | provenance | MaD:9 |
|
||||
| test_cipher.rs:19:73:19:78 | const1 [&ref] | test_cipher.rs:19:49:19:79 | ...::from_slice(...) [&ref] | provenance | MaD:10 |
|
||||
| test_cipher.rs:25:9:25:14 | const4 [&ref] | test_cipher.rs:26:66:26:71 | const4 [&ref] | provenance | |
|
||||
| test_cipher.rs:25:28:25:36 | &... [&ref] | test_cipher.rs:25:9:25:14 | const4 [&ref] | provenance | |
|
||||
| test_cipher.rs:25:29:25:36 | [0u8; 16] | test_cipher.rs:25:28:25:36 | &... [&ref] | provenance | |
|
||||
| test_cipher.rs:26:42:26:72 | ...::from_slice(...) [&ref] | test_cipher.rs:26:30:26:40 | ...::new | provenance | MaD:4 Sink:MaD:4 |
|
||||
| test_cipher.rs:26:66:26:71 | const4 [&ref] | test_cipher.rs:26:42:26:72 | ...::from_slice(...) [&ref] | provenance | MaD:9 |
|
||||
| test_cipher.rs:26:66:26:71 | const4 [&ref] | test_cipher.rs:26:42:26:72 | ...::from_slice(...) [&ref] | provenance | MaD:10 |
|
||||
| test_cipher.rs:29:9:29:14 | const5 [&ref] | test_cipher.rs:30:95:30:100 | const5 [&ref] | provenance | |
|
||||
| test_cipher.rs:29:28:29:36 | &... [&ref] | test_cipher.rs:29:9:29:14 | const5 [&ref] | provenance | |
|
||||
| test_cipher.rs:29:29:29:36 | [0u8; 16] | test_cipher.rs:29:28:29:36 | &... [&ref] | provenance | |
|
||||
| test_cipher.rs:30:72:30:101 | ...::from_slice(...) [&ref] | test_cipher.rs:30:30:30:40 | ...::new | provenance | MaD:5 Sink:MaD:5 |
|
||||
| test_cipher.rs:30:95:30:100 | const5 [&ref] | test_cipher.rs:30:72:30:101 | ...::from_slice(...) [&ref] | provenance | MaD:9 |
|
||||
| test_cipher.rs:30:95:30:100 | const5 [&ref] | test_cipher.rs:30:72:30:101 | ...::from_slice(...) [&ref] | provenance | MaD:10 |
|
||||
| test_cipher.rs:37:9:37:14 | const7 | test_cipher.rs:38:74:38:79 | const7 | provenance | |
|
||||
| test_cipher.rs:37:27:37:74 | [...] | test_cipher.rs:37:9:37:14 | const7 | provenance | |
|
||||
| test_cipher.rs:38:49:38:80 | ...::from_slice(...) [&ref] | test_cipher.rs:38:30:38:47 | ...::new | provenance | MaD:3 Sink:MaD:3 |
|
||||
| test_cipher.rs:38:73:38:79 | &const7 [&ref] | test_cipher.rs:38:49:38:80 | ...::from_slice(...) [&ref] | provenance | MaD:9 |
|
||||
| test_cipher.rs:38:73:38:79 | &const7 [&ref] | test_cipher.rs:38:49:38:80 | ...::from_slice(...) [&ref] | provenance | MaD:10 |
|
||||
| test_cipher.rs:38:74:38:79 | const7 | test_cipher.rs:38:73:38:79 | &const7 [&ref] | provenance | |
|
||||
| test_cipher.rs:41:9:41:14 | const8 [&ref] | test_cipher.rs:42:73:42:78 | const8 [&ref] | provenance | |
|
||||
| test_cipher.rs:41:28:41:76 | &... [&ref] | test_cipher.rs:41:9:41:14 | const8 [&ref] | provenance | |
|
||||
| test_cipher.rs:41:29:41:76 | [...] | test_cipher.rs:41:28:41:76 | &... [&ref] | provenance | |
|
||||
| test_cipher.rs:42:49:42:79 | ...::from_slice(...) [&ref] | test_cipher.rs:42:30:42:47 | ...::new | provenance | MaD:3 Sink:MaD:3 |
|
||||
| test_cipher.rs:42:73:42:78 | const8 [&ref] | test_cipher.rs:42:49:42:79 | ...::from_slice(...) [&ref] | provenance | MaD:9 |
|
||||
| test_cipher.rs:42:73:42:78 | const8 [&ref] | test_cipher.rs:42:49:42:79 | ...::from_slice(...) [&ref] | provenance | MaD:10 |
|
||||
| test_cipher.rs:50:9:50:15 | const10 [element] | test_cipher.rs:51:75:51:81 | const10 [element] | provenance | |
|
||||
| test_cipher.rs:50:37:50:52 | ...::zeroed | test_cipher.rs:50:37:50:54 | ...::zeroed(...) [element] | provenance | Src:MaD:7 |
|
||||
| test_cipher.rs:50:37:50:54 | ...::zeroed(...) [element] | test_cipher.rs:50:9:50:15 | const10 [element] | provenance | |
|
||||
| test_cipher.rs:51:50:51:82 | ...::from_slice(...) [&ref, element] | test_cipher.rs:51:31:51:48 | ...::new | provenance | MaD:3 Sink:MaD:3 Sink:MaD:3 |
|
||||
| test_cipher.rs:51:74:51:81 | &const10 [&ref, element] | test_cipher.rs:51:50:51:82 | ...::from_slice(...) [&ref, element] | provenance | MaD:9 |
|
||||
| test_cipher.rs:51:74:51:81 | &const10 [&ref, element] | test_cipher.rs:51:50:51:82 | ...::from_slice(...) [&ref, element] | provenance | MaD:10 |
|
||||
| test_cipher.rs:51:75:51:81 | const10 [element] | test_cipher.rs:51:74:51:81 | &const10 [&ref, element] | provenance | |
|
||||
| test_cipher.rs:73:9:73:14 | const2 [&ref] | test_cipher.rs:74:46:74:51 | const2 [&ref] | provenance | |
|
||||
| test_cipher.rs:73:18:73:26 | &... [&ref] | test_cipher.rs:73:9:73:14 | const2 [&ref] | provenance | |
|
||||
@@ -59,9 +67,19 @@ edges
|
||||
| test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:38:18:38:37 | ...::from(...) | provenance | MaD:8 |
|
||||
| test_cookie.rs:42:34:42:39 | array2 | test_cookie.rs:42:14:42:32 | ...::from | provenance | MaD:2 Sink:MaD:2 |
|
||||
| test_cookie.rs:49:9:49:14 | array3 [element] | test_cookie.rs:53:34:53:39 | array3 [element] | provenance | |
|
||||
| test_cookie.rs:49:23:49:25 | 0u8 | test_cookie.rs:49:23:49:29 | ...::from_elem(...) [element] | provenance | MaD:10 |
|
||||
| test_cookie.rs:49:23:49:25 | 0u8 | test_cookie.rs:49:23:49:29 | ...::from_elem(...) [element] | provenance | MaD:11 |
|
||||
| test_cookie.rs:49:23:49:29 | ...::from_elem(...) [element] | test_cookie.rs:49:9:49:14 | array3 [element] | provenance | |
|
||||
| test_cookie.rs:53:34:53:39 | array3 [element] | test_cookie.rs:53:14:53:32 | ...::from | provenance | MaD:2 Sink:MaD:2 |
|
||||
| test_heuristic.rs:44:9:44:16 | const_iv [&ref] | test_heuristic.rs:45:41:45:48 | const_iv | provenance | |
|
||||
| test_heuristic.rs:44:30:44:38 | &... [&ref] | test_heuristic.rs:44:9:44:16 | const_iv [&ref] | provenance | |
|
||||
| test_heuristic.rs:44:31:44:38 | [0u8; 16] | test_heuristic.rs:44:30:44:38 | &... [&ref] | provenance | |
|
||||
| test_heuristic.rs:64:20:64:27 | [0u8; 16] | test_heuristic.rs:64:19:64:27 | &... | provenance | |
|
||||
| test_heuristic.rs:65:31:65:38 | [0u8; 16] | test_heuristic.rs:65:30:65:38 | &... | provenance | |
|
||||
| test_heuristic.rs:69:32:69:32 | 1 | test_heuristic.rs:69:22:69:32 | ... + ... | provenance | |
|
||||
| test_heuristic.rs:70:23:70:35 | ... << ... | test_heuristic.rs:70:22:70:62 | ... ^ ... | provenance | |
|
||||
| test_heuristic.rs:70:34:70:35 | 32 | test_heuristic.rs:70:22:70:62 | ... ^ ... | provenance | |
|
||||
| test_heuristic.rs:70:34:70:35 | 32 | test_heuristic.rs:70:23:70:35 | ... << ... | provenance | MaD:9 |
|
||||
| test_heuristic.rs:70:52:70:61 | 0xFFFFFFFF | test_heuristic.rs:70:22:70:62 | ... ^ ... | provenance | |
|
||||
models
|
||||
| 1 | Sink: <_ as crypto_common::KeyInit>::new_from_slice; Argument[0]; credentials-key |
|
||||
| 2 | Sink: <biscotti::crypto::master::Key>::from; Argument[0]; credentials-key |
|
||||
@@ -71,8 +89,9 @@ models
|
||||
| 6 | Sink: <cookie::secure::key::Key>::from; Argument[0].Reference; credentials-key |
|
||||
| 7 | Source: core::mem::zeroed; ReturnValue.Element; constant-source |
|
||||
| 8 | Summary: <_ as core::convert::From>::from; Argument[0]; ReturnValue; taint |
|
||||
| 9 | Summary: <generic_array::GenericArray>::from_slice; Argument[0].Reference; ReturnValue.Reference; value |
|
||||
| 10 | Summary: alloc::vec::from_elem; Argument[0]; ReturnValue.Element; value |
|
||||
| 9 | Summary: <core::u64 as core::ops::bit::Shl>::shl; Argument[0]; ReturnValue; taint |
|
||||
| 10 | Summary: <generic_array::GenericArray>::from_slice; Argument[0].Reference; ReturnValue.Reference; value |
|
||||
| 11 | Summary: alloc::vec::from_elem; Argument[0]; ReturnValue.Element; value |
|
||||
nodes
|
||||
| test_cipher.rs:18:9:18:14 | const1 [&ref] | semmle.label | const1 [&ref] |
|
||||
| test_cipher.rs:18:28:18:36 | &... [&ref] | semmle.label | &... [&ref] |
|
||||
@@ -136,4 +155,20 @@ nodes
|
||||
| test_cookie.rs:49:23:49:29 | ...::from_elem(...) [element] | semmle.label | ...::from_elem(...) [element] |
|
||||
| test_cookie.rs:53:14:53:32 | ...::from | semmle.label | ...::from |
|
||||
| test_cookie.rs:53:34:53:39 | array3 [element] | semmle.label | array3 [element] |
|
||||
| test_heuristic.rs:44:9:44:16 | const_iv [&ref] | semmle.label | const_iv [&ref] |
|
||||
| test_heuristic.rs:44:30:44:38 | &... [&ref] | semmle.label | &... [&ref] |
|
||||
| test_heuristic.rs:44:31:44:38 | [0u8; 16] | semmle.label | [0u8; 16] |
|
||||
| test_heuristic.rs:45:41:45:48 | const_iv | semmle.label | const_iv |
|
||||
| test_heuristic.rs:63:30:63:37 | "secret" | semmle.label | "secret" |
|
||||
| test_heuristic.rs:64:19:64:27 | &... | semmle.label | &... |
|
||||
| test_heuristic.rs:64:20:64:27 | [0u8; 16] | semmle.label | [0u8; 16] |
|
||||
| test_heuristic.rs:65:30:65:38 | &... | semmle.label | &... |
|
||||
| test_heuristic.rs:65:31:65:38 | [0u8; 16] | semmle.label | [0u8; 16] |
|
||||
| test_heuristic.rs:67:22:67:22 | 0 | semmle.label | 0 |
|
||||
| test_heuristic.rs:69:22:69:32 | ... + ... | semmle.label | ... + ... |
|
||||
| test_heuristic.rs:69:32:69:32 | 1 | semmle.label | 1 |
|
||||
| test_heuristic.rs:70:22:70:62 | ... ^ ... | semmle.label | ... ^ ... |
|
||||
| test_heuristic.rs:70:23:70:35 | ... << ... | semmle.label | ... << ... |
|
||||
| test_heuristic.rs:70:34:70:35 | 32 | semmle.label | 32 |
|
||||
| test_heuristic.rs:70:52:70:61 | 0xFFFFFFFF | semmle.label | 0xFFFFFFFF |
|
||||
subpaths
|
||||
|
||||
71
rust/ql/test/query-tests/security/CWE-798/test_heuristic.rs
Normal file
71
rust/ql/test/query-tests/security/CWE-798/test_heuristic.rs
Normal file
@@ -0,0 +1,71 @@
|
||||
|
||||
// --- tests ---
|
||||
|
||||
fn encrypt_with(plaintext: &str, key: &[u8;16], iv: &[u8;16]) {
|
||||
// ...
|
||||
}
|
||||
|
||||
fn encrypt2(plaintext: &str, crypto_key: &[u8;16], iv_bytes: &[u8;16]) {
|
||||
// ...
|
||||
}
|
||||
|
||||
fn database_op(text: &str, primary_key: &str, pivot: &str) {
|
||||
// note: this one has nothing to do with encryption, but has
|
||||
// `key` and `iv` contained within the parameter names.
|
||||
}
|
||||
|
||||
struct MyCryptor {
|
||||
}
|
||||
|
||||
impl MyCryptor {
|
||||
fn new(password: &str) -> MyCryptor {
|
||||
MyCryptor { }
|
||||
}
|
||||
|
||||
fn set_nonce(&self, nonce: &[u8;16]) {
|
||||
// ...
|
||||
}
|
||||
|
||||
fn encrypt(&self, plaintext: &str, salt: &[u8;16]) {
|
||||
// ...
|
||||
}
|
||||
|
||||
fn set_salt_u64(&self, salt: u64) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
fn test(var_string: &str, var_data: &[u8;16], var_u64: u64) {
|
||||
encrypt_with("plaintext", var_data, var_data);
|
||||
|
||||
let const_key: &[u8;16] = &[0u8;16]; // $ MISSING: Alert[rust/hard-coded-cryptographic-value]
|
||||
encrypt_with("plaintext", const_key, var_data); // $ MISSING: Sink
|
||||
|
||||
let const_iv: &[u8;16] = &[0u8;16]; // $ Alert[rust/hard-coded-cryptographic-value]
|
||||
encrypt_with("plaintext", var_data, const_iv); // $ Sink
|
||||
|
||||
encrypt2("plaintext", var_data, var_data);
|
||||
|
||||
let const_key2: &[u8;16] = &[1u8;16]; // $ MISSING: Alert[rust/hard-coded-cryptographic-value]
|
||||
encrypt2("plaintext", const_key2, var_data); // $ MISSING: Sink
|
||||
|
||||
let const_iv: &[u8;16] = &[1u8;16]; // $ MISSING: Alert[rust/hard-coded-cryptographic-value]
|
||||
encrypt2("plaintext", var_data, const_iv); // $ MISSING: Sink
|
||||
|
||||
let const_key_str = "primary_key";
|
||||
let const_pivot_str = "pivot";
|
||||
database_op("text", const_key_str, const_pivot_str);
|
||||
|
||||
let mc1 = MyCryptor::new(var_string);
|
||||
mc1.set_nonce(var_data);
|
||||
mc1.encrypt("plaintext", var_data);
|
||||
|
||||
let mc2 = MyCryptor::new("secret"); // $ Alert[rust/hard-coded-cryptographic-value]
|
||||
mc2.set_nonce(&[0u8;16]); // $ Alert[rust/hard-coded-cryptographic-value]
|
||||
mc2.encrypt("plaintext", &[0u8;16]); // $ Alert[rust/hard-coded-cryptographic-value]
|
||||
|
||||
mc2.set_salt_u64(0); // $ Alert[rust/hard-coded-cryptographic-value]
|
||||
mc2.set_salt_u64(var_u64);
|
||||
mc2.set_salt_u64(var_u64 + 1); // $ SPURIOUS: Alert[rust/hard-coded-cryptographic-value]
|
||||
mc2.set_salt_u64((var_u64 << 32) ^ (var_u64 & 0xFFFFFFFF)); // $ SPURIOUS: Alert[rust/hard-coded-cryptographic-value]
|
||||
}
|
||||
Reference in New Issue
Block a user