Skip to content

Commit

Permalink
Add rule: TeamCity API Token (#240)
Browse files Browse the repository at this point in the history
* Add new rule: TeamCity API Token
* Fix `rules check` bug that allowed empty capture groups to go undetected
  • Loading branch information
bradlarsen authored Dec 17, 2024
1 parent 882f6b4 commit 46852e0
Show file tree
Hide file tree
Showing 10 changed files with 64 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down
4 changes: 3 additions & 1 deletion crates/noseyparker-cli/src/cmd_rules/cmd_rules_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,9 @@ fn check_rule(rule: &Rule, args: &RulesCheckArgs) -> Result<CheckStats> {
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -1368,7 +1368,8 @@ expression: stdout
"<add key=\"FromEmailAddress\" value=\"killer.joe@example.com\"/>\n<add key=\"FromEmailPassword\" value=\"Prestigitariu$\"/>\n"
],
"negative_examples": [
"<add key=\"ConnectionString_B2B\" value=\"Server=awsbman-t,1433;Database=B2B;User ID=b2buser;Password=#gets0m3;Trusted_Connection=False\" />\n"
"<add key=\"ConnectionString_B2B\" value=\"Server=awsbman-t,1433;Database=B2B;User ID=b2buser;Password=#gets0m3;Trusted_Connection=False\" />\n",
"<add key=\"ClearTextPassword\" value=\"eyJ0eXAiOiAiVENWMiJ9.RkNWLXdXS3M1RVBfencxM4A0WmJzdVlCQzFj.OGY1OWRkNGMtYTUxYS04ZDYwLWFiZGYtZWE5MWFhZWJiODhh\" />"
],
"references": [
"https://learn.microsoft.com/en-us/dotnet/desktop/winforms/advanced/application-settings-overview",
Expand Down Expand Up @@ -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",
"<add key=\"ClearTextPassword\" value=\"eyJ0eXAiOiAiVENWMiJ9.RkNWLXdXS3M1RVBfencxM4A0WmJzdVlCQzFj.OGY1OWRkNGMtYTUxYS04ZDYwLWFiZGYtZWE5MWFhZWJiODhh\" />"
],
"references": [
"https://en.wikipedia.org/wiki/JSON_Web_Token",
Expand Down Expand Up @@ -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": [
"<add key=\"ClearTextPassword\" value=\"eyJ0eXAiOiAiVENWMiJ9.RkNWLXdXS3M1RVBfencxM4A0WmJzdVlCQzFj.OGY1OWRkNGMtYTUxYS04ZDYwLWFiZGYtZWE5MWFhZWJiODhh\" />"
],
"negative_examples": [],
"references": [
"https://www.jetbrains.com/help/teamcity/rest/teamcity-rest-api-documentation.html"
],
"categories": []
}
},
{
"id": "np.telegram.1",
"structural_id": "ee0a6f62cff7ae26886389e9a542f673c0cfdc00",
Expand Down Expand Up @@ -4062,7 +4083,7 @@ expression: stdout
{
"id": "default",
"name": "Nosey Parker default rules",
"num_rules": 140
"num_rules": 141
},
{
"id": "np.assets",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
1 change: 1 addition & 0 deletions crates/noseyparker/data/default/builtin/rules/generic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ rules:
negative_examples:
- |
<add key="ConnectionString_B2B" value="Server=awsbman-t,1433;Database=B2B;User ID=b2buser;Password=#gets0m3;Trusted_Connection=False" />
- '<add key="ClearTextPassword" value="eyJ0eXAiOiAiVENWMiJ9.RkNWLXdXS3M1RVBfencxM4A0WmJzdVlCQzFj.OGY1OWRkNGMtYTUxYS04ZDYwLWFiZGYtZWE5MWFhZWJiODhh" />'
references:
Expand Down
1 change: 1 addition & 0 deletions crates/noseyparker/data/default/builtin/rules/jwt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ rules:
it "sets the relation to nil" do
eye.eyeable.should be_nill
end
- '<add key="ClearTextPassword" value="eyJ0eXAiOiAiVENWMiJ9.RkNWLXdXS3M1RVBfencxM4A0WmJzdVlCQzFj.OGY1OWRkNGMtYTUxYS04ZDYwLWFiZGYtZWE5MWFhZWJiODhh" />'


# This kind of thing is often seen in spring config files and other configs
Expand Down
28 changes: 28 additions & 0 deletions crates/noseyparker/data/default/builtin/rules/teamcity.yml
Original file line number Diff line number Diff line change
@@ -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:
- '<add key="ClearTextPassword" value="eyJ0eXAiOiAiVENWMiJ9.RkNWLXdXS3M1RVBfencxM4A0WmJzdVlCQzFj.OGY1OWRkNGMtYTUxYS04ZDYwLWFiZGYtZWE5MWFhZWJiODhh" />'

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.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 46852e0

Please sign in to comment.