Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 | |
Expand All @@ -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 |
Expand All @@ -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] |
Expand Down Expand Up @@ -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 changes: 71 additions & 0 deletions rust/ql/test/query-tests/security/CWE-798/test_heuristic.rs
Original file line number Diff line number Diff line change
@@ -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]
}