Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add kolide_jwt parsing table #1440

Merged
merged 4 commits into from
Nov 7, 2023
Merged

add kolide_jwt parsing table #1440

merged 4 commits into from
Nov 7, 2023

Conversation

zackattack01
Copy link
Contributor

@zackattack01 zackattack01 commented Nov 6, 2023

This adds a kolide_jwt table to parse claims from a JWT when queried against a file path containing one. The general functionality is similar to the kolide_json table, requiring a valid path to an existing file, and parsing the contents where available:

osquery> select * from kolide_jwt where 1=1;
Error: error generating table: The kolide_jwt table requires that you specify a single constraint for path
osquery> select * from kolide_jwt where path='/tmp/data.zta';
+---------------------------------+-------------------+---------------+----------------------------------+-------+---------------+
| fullkey                         | parent            | key           | value                            | query | path          |
+---------------------------------+-------------------+---------------+----------------------------------+-------+---------------+
| verified                        |                   | verified      | false                            | *     | /tmp/data.zta |
| claims/iat                      | claims            | iat           | 1614791923                       | *     | /tmp/data.zta |
| claims/platform                 | claims            | platform      | Windows 10                       | *     | /tmp/data.zta |
| claims/serial_number            | claims            | serial_number |                                  | *     | /tmp/data.zta |
| claims/sub                      | claims            | sub           | 99999c489c1a499999ef5b213ee99999 | *     | /tmp/data.zta |
| claims/typ                      | claims            | typ           | crowdstrike-zta+jwt              | *     | /tmp/data.zta |
| claims/assessment/os            | claims/assessment | os            | 77                               | *     | /tmp/data.zta |
| claims/assessment/sensor_config | claims/assessment | sensor_config | 30                               | *     | /tmp/data.zta |
| claims/assessment/version       | claims/assessment | version       | 2.0.1                            | *     | /tmp/data.zta |
| claims/assessment/overall       | claims/assessment | overall       | 48                               | *     | /tmp/data.zta |
| claims/exp                      | claims            | exp           | 1616001523                       | *     | /tmp/data.zta |
| header/typ                      | header            | typ           | JWT                              | *     | /tmp/data.zta |
| header/alg                      | header            | alg           | RS256                            | *     | /tmp/data.zta |
| header/kid                      | header            | kid           | US-1/v1                          | *     | /tmp/data.zta |
+---------------------------------+-------------------+---------------+----------------------------------+-------+---------------+
osquery> select value from kolide_jwt where path='/tmp/data.zta' and fullkey='claims/assessment/overall';
+-------+
| value |
+-------+
| 48    |
+-------+

This closes #1327


// attempt decode into the generic (default) MapClaims struct to ensure we capture
// any claims data that might be useful
token, _, err := new(jwt.Parser).ParseUnverified(string(tokenRaw), jwt.MapClaims{})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the second return value?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is parts = strings.Split(tokenString, ".") - we are essentially just base64 decoding parts[1] in the remaining bits

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there anything to extract from the other parts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we will get the header now too per the conversation below. I can include the raw signature as well if you think that would be helpful?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should return the raw signature. Or the contents of the file

Copy link
Contributor

@directionless directionless left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing it in pkg/dataflatten is a little weird, but I think okay. Mostly, I think of things down in here as low level, and we can build atop them. And I'm not sure what's going to want to build on this. But 🤷 it's no different than plist. So it's probably fine?

OTOH, if we wanted to bring in validation, we'd need to refactor. OTOH, we could refactor if/when we care.

pkg/dataflatten/jwt.go Show resolved Hide resolved
Comment on lines 44 to 46
for k, v := range claims {
results[k] = v
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need results or can we pass claims straight to Flatten?

And is there any more data we should pass back? Like, this is claims, but is there more? I kinda want to return

struct {
  Claims: map[string]any
}

Because then if we add validate, or file metadata, or ???, we won't need to change the structure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can try that out! we could additionally include the header (likely containing the signing algo) but in general its just:

Header Payload Signature
algo claims signature
type (=JWT)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Algo seems interesting. An issued timestamp if one exists...

And someday I think it would be super interesting to get a validation in, and that would give us a place to hang it. I guess we could preemptively add validated: false or similar.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can't pass claims to flatten directly but that will work for the header and signature. the registered claim for timestamp will be at claims->iat if included. and I will add a validated false for clarity here, thank you!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description is updated with the new format discussed here

RebeccaMahany
RebeccaMahany previously approved these changes Nov 6, 2023
Copy link
Contributor

@RebeccaMahany RebeccaMahany left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm good with this as-is, but also like seph's suggestion https://github.com/kolide/launcher/pull/1440/files#r1383951951 -- happy to reapprove if you update

pkg/dataflatten/jwt.go Show resolved Hide resolved
Copy link
Contributor

@directionless directionless left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sweet!

As a side note, GitHub has special syntax for how to link PRs to Issues and have them mark them as closed and whatnot. https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue

@zackattack01 zackattack01 added this pull request to the merge queue Nov 7, 2023
Merged via the queue into main with commit 837a7c8 Nov 7, 2023
24 checks passed
@zackattack01 zackattack01 deleted the zack/jwt_parser branch November 7, 2023 14:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Launcher should have a table to parse jwts
3 participants