From 46852e03b0a87d1244c22a8742442ceb652b5b39 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Tue, 17 Dec 2024 11:15:51 -0500 Subject: [PATCH] Add rule: `TeamCity API Token` (#240) * Add new rule: TeamCity API Token * Fix `rules check` bug that allowed empty capture groups to go undetected --- CHANGELOG.md | 1 + README.md | 4 +-- .../src/cmd_rules/cmd_rules_check.rs | 4 ++- ...parker__rules__rules_check_builtins-2.snap | 2 +- ...noseyparker__rules__rules_list_json-2.snap | 27 ++++++++++++++++-- ...seyparker__rules__rules_list_noargs-2.snap | 3 +- .../data/default/builtin/rules/generic.yml | 1 + .../data/default/builtin/rules/jwt.yml | 1 + .../data/default/builtin/rules/teamcity.yml | 28 +++++++++++++++++++ .../data/default/builtin/rulesets/default.yml | 1 + 10 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 crates/noseyparker/data/default/builtin/rules/teamcity.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 0741ed4e5..894a100ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ Note that the use of semantic versioning applies to the command-line interface a - `Credentials in .NET System.Net.NetworkCredential` ([#234](https://github.com/praetorian-inc/noseyparker/pull/234)) - `Kubernetes Bootstrap Token` ([#235](https://github.com/praetorian-inc/noseyparker/pull/235)) - `Sensitive Value in .NET Configuration` ([#237](https://github.com/praetorian-inc/noseyparker/pull/237)) + - `TeamCity API Token` ([#240](https://github.com/praetorian-inc/noseyparker/pull/240)) - Rules now contain an optional `description` string field. This is intended to be a message for human consumption that indicates (a) what was detected and (b) how an attacker might use it. diff --git a/README.md b/README.md index 139017351..690cb3926 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ It has found secrets in hundreds of offensive security engagements at [Praetoria **Key features:** - **Flexiblity:** It natively scans files, directories, GitHub, and Git history, and has an extensible input enumeration mechanism -- **Field-tested rules:** It uses regular expressions with [161 patterns](crates/noseyparker/data/default/builtin/rules) chosen for high precision based on feedback from security engineers +- **Field-tested rules:** It uses regular expressions with [162 patterns](crates/noseyparker/data/default/builtin/rules) chosen for high precision based on feedback from security engineers - **Signal-to-noise:** It deduplicates matches that share the same secret, reducing review burden by 10-1000x or more - **Speed & scalability:** it can scan at GB/s on a multicore system, and has scanned inputs as large as 20TB during security engagements @@ -31,7 +31,7 @@ brew install noseyparker ### Prebuilt binaries -The [latest release page](https://github.com/praetorian-inc/noseyparker/releases/latest) contains prebuilt binaries for x86_64/aarch64 Linux and macOS. +The [latest release page](https://github.com/praetorian-inc/noseyparker/releases/latest) contains prebuilt binaries for x86_64/aarch64 Linux and macOS. ### Docker: x86_64/aarch64 diff --git a/crates/noseyparker-cli/src/cmd_rules/cmd_rules_check.rs b/crates/noseyparker-cli/src/cmd_rules/cmd_rules_check.rs index 15668effd..21c1e2356 100644 --- a/crates/noseyparker-cli/src/cmd_rules/cmd_rules_check.rs +++ b/crates/noseyparker-cli/src/cmd_rules/cmd_rules_check.rs @@ -224,7 +224,9 @@ fn check_rule(rule: &Rule, args: &RulesCheckArgs) -> Result { Ok(pat) => { // Check that the rule has at least one capture group match pat.static_captures_len() { - Some(0) => { + // the default is a single capture group for the entire match + // not sure if 0 can actually happen + Some(0) | Some(1) => { error!("Rule has no capture groups"); num_errors += 1; } diff --git a/crates/noseyparker-cli/tests/rules/snapshots/test_noseyparker__rules__rules_check_builtins-2.snap b/crates/noseyparker-cli/tests/rules/snapshots/test_noseyparker__rules__rules_check_builtins-2.snap index 0ca72bee9..7215fce10 100644 --- a/crates/noseyparker-cli/tests/rules/snapshots/test_noseyparker__rules__rules_check_builtins-2.snap +++ b/crates/noseyparker-cli/tests/rules/snapshots/test_noseyparker__rules__rules_check_builtins-2.snap @@ -2,4 +2,4 @@ source: crates/noseyparker-cli/tests/rules/mod.rs expression: stdout --- -161 rules and 3 rulesets: no issues detected +162 rules and 3 rulesets: no issues detected diff --git a/crates/noseyparker-cli/tests/rules/snapshots/test_noseyparker__rules__rules_list_json-2.snap b/crates/noseyparker-cli/tests/rules/snapshots/test_noseyparker__rules__rules_list_json-2.snap index 0d08f70f6..c506b64a1 100644 --- a/crates/noseyparker-cli/tests/rules/snapshots/test_noseyparker__rules__rules_list_json-2.snap +++ b/crates/noseyparker-cli/tests/rules/snapshots/test_noseyparker__rules__rules_list_json-2.snap @@ -1368,7 +1368,8 @@ expression: stdout "\n\n" ], "negative_examples": [ - "\n" + "\n", + "" ], "references": [ "https://learn.microsoft.com/en-us/dotnet/desktop/winforms/advanced/application-settings-overview", @@ -2029,7 +2030,8 @@ expression: stdout "NUCLEAR_SERVICES_ANON_KEY=eyJhbGciOiJIUzI1NiIsEnR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InFqcnVqc2lzY2Nzdnl2am5xdG5xIiwicm9sZSI6ImEub24iLCJpYXQiOjE2NTY1OTY0NjEsImV4cCI6MTk3MjE3MjQ2MX0.WQWcwBAQFNE259f2o8ruFln_UMLTFEnEaUD7KHrs9Aw" ], "negative_examples": [ - "it \"sets the relation to nil\" do\n eye.eyeable.should be_nill\nend\n" + "it \"sets the relation to nil\" do\n eye.eyeable.should be_nill\nend\n", + "" ], "references": [ "https://en.wikipedia.org/wiki/JSON_Web_Token", @@ -3778,6 +3780,25 @@ expression: stdout ] } }, + { + "id": "np.teamcity.1", + "structural_id": "30070e2cca5869c3ed8b7cd1ed39ee2c858bd596", + "name": "TeamCity API Token", + "syntax": { + "name": "TeamCity API Token", + "id": "np.teamcity.1", + "pattern": "(?x)\n\\b (\neyJ0eXAiOiAiVENWMiJ9 (?# decodes to `{\"typ\": \"TCV2\"}` )\n\\.\n[A-Za-z0-9_-]{36}\n\\.\n[A-Za-z0-9_-]{48}\n) (?: [^A-Za-z0-9_-] | $ )\n", + "description": "A TeamCity REST API token was found. TeamCity is a CI/CD platform from JetBrains. An attacker may be able to use this token to access source code, secrets, and build resources, enabling a supply chain attack.\n", + "examples": [ + "" + ], + "negative_examples": [], + "references": [ + "https://www.jetbrains.com/help/teamcity/rest/teamcity-rest-api-documentation.html" + ], + "categories": [] + } + }, { "id": "np.telegram.1", "structural_id": "ee0a6f62cff7ae26886389e9a542f673c0cfdc00", @@ -4062,7 +4083,7 @@ expression: stdout { "id": "default", "name": "Nosey Parker default rules", - "num_rules": 140 + "num_rules": 141 }, { "id": "np.assets", diff --git a/crates/noseyparker-cli/tests/rules/snapshots/test_noseyparker__rules__rules_list_noargs-2.snap b/crates/noseyparker-cli/tests/rules/snapshots/test_noseyparker__rules__rules_list_noargs-2.snap index 1e67b9cfb..7a9fb86da 100644 --- a/crates/noseyparker-cli/tests/rules/snapshots/test_noseyparker__rules__rules_list_noargs-2.snap +++ b/crates/noseyparker-cli/tests/rules/snapshots/test_noseyparker__rules__rules_list_noargs-2.snap @@ -154,6 +154,7 @@ expression: stdout np.stackhawk.1 StackHawk API Key api, secret np.stripe.1 Stripe API Key api, secret np.stripe.2 Stripe API Test Key api, secret, test + np.teamcity.1 TeamCity API Token np.telegram.1 Telegram Bot Token api, secret np.thingsboard.1 ThingsBoard Access Token api, identifier, secret np.thingsboard.2 ThingsBoard Provision Device Key api, secret @@ -168,6 +169,6 @@ expression: stdout Ruleset ID Ruleset Name Rules ───────────────────────────────────────────────────────── - default Nosey Parker default rules 140 + default Nosey Parker default rules 141 np.assets Nosey Parker asset detection rules 15 np.hashes Nosey Parker password hash rules 6 diff --git a/crates/noseyparker/data/default/builtin/rules/generic.yml b/crates/noseyparker/data/default/builtin/rules/generic.yml index 19b195777..eb735bb23 100644 --- a/crates/noseyparker/data/default/builtin/rules/generic.yml +++ b/crates/noseyparker/data/default/builtin/rules/generic.yml @@ -364,6 +364,7 @@ rules: negative_examples: - | + - '' references: diff --git a/crates/noseyparker/data/default/builtin/rules/jwt.yml b/crates/noseyparker/data/default/builtin/rules/jwt.yml index 164a0aebe..53791e35e 100644 --- a/crates/noseyparker/data/default/builtin/rules/jwt.yml +++ b/crates/noseyparker/data/default/builtin/rules/jwt.yml @@ -36,6 +36,7 @@ rules: it "sets the relation to nil" do eye.eyeable.should be_nill end + - '' # This kind of thing is often seen in spring config files and other configs diff --git a/crates/noseyparker/data/default/builtin/rules/teamcity.yml b/crates/noseyparker/data/default/builtin/rules/teamcity.yml new file mode 100644 index 000000000..4ba61a216 --- /dev/null +++ b/crates/noseyparker/data/default/builtin/rules/teamcity.yml @@ -0,0 +1,28 @@ +rules: + +- name: TeamCity API Token + + id: np.teamcity.1 + + # This is a JWT-like format that always seems to have the same `{"typ": "TCV2"}` header. + # Note that the payload part does not decode as a JSON object, and hence makes this an invalid JWT. + pattern: | + (?x) + \b ( + eyJ0eXAiOiAiVENWMiJ9 (?# decodes to `{"typ": "TCV2"}` ) + \. + [A-Za-z0-9_-]{36} + \. + [A-Za-z0-9_-]{48} + ) (?: [^A-Za-z0-9_-] | $ ) + + examples: + - '' + + references: + - https://www.jetbrains.com/help/teamcity/rest/teamcity-rest-api-documentation.html + + description: > + A TeamCity REST API token was found. + TeamCity is a CI/CD platform from JetBrains. + An attacker may be able to use this token to access source code, secrets, and build resources, enabling a supply chain attack. diff --git a/crates/noseyparker/data/default/builtin/rulesets/default.yml b/crates/noseyparker/data/default/builtin/rulesets/default.yml index 0ec308899..08d12c148 100644 --- a/crates/noseyparker/data/default/builtin/rulesets/default.yml +++ b/crates/noseyparker/data/default/builtin/rulesets/default.yml @@ -142,6 +142,7 @@ rulesets: - np.stackhawk.1 # StackHawk API Key - np.stripe.1 # Stripe API Key - np.stripe.2 # Stripe API Test Key + - np.teamcity.1 # TeamCity API Token - np.telegram.1 # Telegram Bot Token - np.thingsboard.1 # ThingsBoard Access Token - np.thingsboard.2 # ThingsBoard Provision Device Key