-
Notifications
You must be signed in to change notification settings - Fork 16
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
feat: c2-event evidence type #1086
base: main
Are you sure you want to change the base?
Conversation
Saw this come in. Should have some time tomorrow to look through the PR. We're happy to adopt the ingestor repo over to the org. I think that makes sense as well. Do you have some samples of logs from brute ratel or cobalt strike so that we can test things end to end? Also, are the ingestors public currently? |
frontend/src/global_types.ts
Outdated
userContext: string,// The user context that the implant/beacon/agent is running under | ||
integrity: string, // "Low", "Medium", "High", "System" | ||
processName: string,// process image file shortname | ||
processID: number, // is 'number' acceptable here? Its a float :/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, number
could be fine here. That corresponds to a double sized float.
But, are you sure this is a number at all? I don't know the format, but usually IDs are strings, even if they're only comprised of numbers. A number implies you can, say, add processIDs. Does it make sense to add these IDs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That being the case, string may be more appropriate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
processID here is the PID of the beacon process - for context.
Just made the ingestors public here. There are some (very succinct) sample logs there for BR and CS (no mythic atm), as well as a python script to simulate log writing to test the ingestors. |
if (props.evidence.contentType === 'codeblock') { | ||
React.useEffect(() => { | ||
getEvidenceAsCodeblock({ | ||
operationSlug: props.operationSlug, | ||
evidenceUuid: props.evidence.uuid, | ||
}).then(codeblockField.onChange) | ||
}, [props.evidence.contentType, codeblockField.onChange, props.operationSlug, props.evidence.uuid]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's been a bit since I last did regular work in react, but I believe that we still want all of the hooks to always execute -- this shouldn't be in a conditional.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had this quite messed up in the original commit. I've fixed the useEffect issue and cleaned up the return logic.
// Copyright 2020, Verizon Media | ||
// Licensed under the terms of the MIT. See LICENSE file in project root for terms. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the majority of copyright headers have been removed, and we could probably drop the header. @jrozner or @jkennedyvz can verify.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's correct @JoelAtDeluxe we no longer use copyright headers in each file.
const cx = classnames.bind(require('./stylesheet')) | ||
|
||
|
||
export const C2EventTextArea = React.forwardRef((props: SharedProps & { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you decide to create a text area here, rather than reuse the one from components/input
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thats a very good question. I missed that component entirely when I was writing this initially. I've gone ahead and removed the custom TextArea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall things seem to work. I wasn't able to actually test with real data using the ingestors because the ashirt_worker
import is failing. Is that a separate file that you have created that isn't included within the repo?
frontend/public/index.html
Outdated
<script src="/assets/main.js"></script> | ||
</body> | ||
</html> | ||
<head> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the modified version of the file that is generated by webpack after it generates the css and js. We don't want to include this version in the PR.
I've added the missing |
I've made the changes we've discussed here. Sorry for the delay on this one, thanks for your patience. If there's anything else I can do to clean this up do lmk, this was my first time writing React 😁 |
Thanks for getting the changes in. I should have some time this afternoon to take a look |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall things look pretty good. @JoelAtDeluxe any chance you have time to look at the react components? I don't have much experience with it and can't provide meaningful feedback for idiomatic aspects.
@@ -4,7 +4,7 @@ import WithLabel from 'src/components/with_label' | |||
import classnames from 'classnames/bind' | |||
const cx = classnames.bind(require('./stylesheet')) | |||
|
|||
type SharedProps = { | |||
export type SharedProps = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to be exported?
React.useEffect(() => { | ||
if (props.evidence.contentType !== 'codeblock') { | ||
if (props.evidence.contentType !== 'codeblock' && props.evidence.contentType !== 'c2-event') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you mind running this file through a formatter? It looks like there is an extra indent here and some formatting changes below
I'll make some time this weekend to go through it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still somewhat going through this, but there are enough things here that I'd like to see changed. I can try to find time to help if you need it.
Some big thoughts:
- There are a few styling issues to resolve: the grid is too big (when viewing the data, the right side goes off screen), the box shadows aren't rendering correctly (only the top renders), when editing, the grid is off center.
- The editor and viewer are nearly identical. Maybe it makes sense to combine them into one (unexported), and create a simple component for reader and writer?
- As Joe noted, there are some files with formatting issues. I agree that this should probably be run through a formatter. I usually use VSCode to do this for me at the file level. There are also some other issues around extraneous imports. You could also run eslint to help identify a few of these issues (in frontend, run
npm run lint
), but it looks like there errors in other files outside of this PR too. 😞
It looks like basically all of the logic is correct at this point though, and I didn't see any issues specifically around React, so it seems like this PR is really just in a cleanup phase.
import ComboBox from 'src/components/combobox' | ||
import { useAsyncComponent } from 'src/helpers' | ||
import LoadingSpinner from 'src/components/loading_spinner' | ||
import WithLabel from 'src/components/with_label' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably remove unused imports to keep things cleaner.
import classnames from 'classnames/bind' | ||
const cx = classnames.bind(require('./stylesheet')) | ||
|
||
export const C2EventViewer = (props: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could probably combine the EventViewer and eventEditor
<div className={cx('user-context')}> | ||
<Input | ||
label="User Context" | ||
className={cx('c2-event-input')} | ||
value={props.value.userContext || ''} | ||
disabled={props.disabled} | ||
readOnly | ||
/> | ||
</div> | ||
<div className={cx('beacon')}> | ||
<Input | ||
label="Beacon" | ||
className={cx('c2-event-input')} | ||
value={props.value.beacon || ''} | ||
disabled={props.disabled} | ||
readOnly | ||
/> | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have a tabstop issue in your code (tabbing from operator goes to User Context, which then goes to Beacon, then Hostname. The rest are fine after this)
You just need to re-arrange the items to solve it:
<div className={cx('user-context')}> | |
<Input | |
label="User Context" | |
className={cx('c2-event-input')} | |
value={props.value.userContext || ''} | |
disabled={props.disabled} | |
readOnly | |
/> | |
</div> | |
<div className={cx('beacon')}> | |
<Input | |
label="Beacon" | |
className={cx('c2-event-input')} | |
value={props.value.beacon || ''} | |
disabled={props.disabled} | |
readOnly | |
/> | |
</div> | |
<div className={cx('beacon')}> | |
<Input | |
label="Beacon" | |
className={cx('c2-event-input')} | |
value={props.value.beacon || ''} | |
disabled={props.disabled} | |
readOnly | |
/> | |
</div> | |
<div className={cx('user-context')}> | |
<Input | |
label="User Context" | |
className={cx('c2-event-input')} | |
value={props.value.userContext || ''} | |
disabled={props.disabled} | |
readOnly | |
/> | |
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'd need to do the same thing to the editor below
<Input | ||
label="C2" | ||
className={cx('c2-event-input')} | ||
value={props.value.c2 || ''} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you need to account for an undefined/null props.value.c2
. The type specifies string
, which means not undefined or null. This is just a type hint though -- the compiler will enforce it, but it's still javascript, so if this value gets populated in another way, then we might have a problem.
Except, there should only be one way to get assets into the database (this UI, unless you make a change to the ashirt application), and and it'll force strings there. At worst, if a null did get in there, then we'd see a warning in the console. Not great, but this would be an odd case given the above.
@@ -0,0 +1,122 @@ | |||
@import '~src/vars' | |||
|
|||
.code-viewer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're not using code-viewer, so you can just remove this whole section.
<div className={cx('operator')}> | ||
<Input | ||
label="Operator" | ||
className={cx('c2-event-input')} | ||
value={props.value.c2Operator || ''} | ||
disabled={props.disabled} | ||
readOnly | ||
/> | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need the outer div. className
on Input
refers to the label, not the input, which would make this the direct child, and I think negate the need for an extra div.
<div className={cx('operator')}> | |
<Input | |
label="Operator" | |
className={cx('c2-event-input')} | |
value={props.value.c2Operator || ''} | |
disabled={props.disabled} | |
readOnly | |
/> | |
</div> | |
<Input | |
label="Operator" | |
className={cx('operator', 'c2-event-input')} | |
value={props.value.c2Operator || ''} | |
disabled={props.disabled} | |
readOnly | |
/> |
<div className={cx('procID')}> | ||
<Input | ||
label="Process ID" | ||
type="number" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
number type is probably wrong here. a couple of observations:
- you can set negative numbers (you could fix this with min=0 or min=1)
- the stepper is probably not useful (if it's a decimal number)
- the stepper is unstyled. I didn't look into how to style it. But it's a weird experience unstyled. (looks like you can hide them, but it's browser specific)
I don't know if it makes sense to restrict this input so heavily, but I'll leave that up to you. What you might want to do instead is add some logic to prevent undesired characters. Unfortunately, this can be complex since the user would normally put in the value one character at a time, so you'd need to allow bad values, then validate it later. I can't remember if we do any validation as part of the create step, so that might also need to be implemented.
Co-authored-by: Joel Smith <[email protected]>
Co-authored-by: Joel Smith <[email protected]>
Thanks for the review Joel. I'm going to get to work on the code cleanup work you noted. When it comes to styling I'd appreciate some help (especially grid sizing issue, i can't get that thing centered for the life of me), but let me give this one more pass and I'll ping for another review and any styling help I may need. |
I'll try to find some time. fit seems like the primary issue why it doesn't fit right is because of the padding on the c2-event-grid. That somewhat breaks the lightbox view, but I think that there should be a way to fix specifically that (it's been awhile though, so I don't know what it is off the top of my head). |
Adding an evidence type to capture C2 events in ASHIRT.
We have ingestors (python script + systemd service file) for both Cobalt Strike and Brute Ratel, though both have some bugs we are working through squashing. If we like the direction of adding this feature into ASHIRT I'd be partial to the ingestor repo being owned by ashirt-ops so that they can live with the rest of the project.
I confirm that this contribution is made under the terms of the license found in the root directory of this repository's source tree and that I have the authority necessary to make this contribution on behalf of its copyright owner.