-
Notifications
You must be signed in to change notification settings - Fork 226
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add an example to detonate an attack technique and retrieve relevant …
…CloudTrail logs
- Loading branch information
1 parent
816f8cc
commit 3e5956c
Showing
4 changed files
with
1,153 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# Example: Detonating Stratus Red Team programmatically to understand the logs that an attack technique generates | ||
|
||
This example shows a more advanced example of detonating an attack using Stratus Red Team, | ||
then using the Datadog API to query the CloudTrail logs generated by this detonation. | ||
|
||
This is made possible by the fact that Stratus Red Team injects an unique header (`stratus-red-team_<uuid>`) | ||
in the user-agent of requests it performs. | ||
|
||
Usage: | ||
|
||
``` | ||
$ go run dump-logs.go aws.defense-evasion.cloudtrail-delete cloudtrail-logs.json | ||
``` | ||
|
||
Sample output (note that CloudTrail logs by design take several minutes to be delivered by AWS): | ||
|
||
``` | ||
Detonating 'aws.defense-evasion.cloudtrail-delete' with Stratus Red Team | ||
Execution UID: b071a414-5f74-48d8-9346-31e3b01a24e3 | ||
Searching for logs in Datadog | ||
No logs found. Sleeping for 1m0s | ||
Searching for logs in Datadog | ||
No logs found. Sleeping for 1m0s | ||
Searching for logs in Datadog | ||
Found 1 matching logs | ||
``` | ||
|
||
Here, `cloudtrail-logs.json` will contain (redacted for clarity): | ||
|
||
```json | ||
[ | ||
{ | ||
"awsRegion": "us-east-1", | ||
"eventCategory": "Management", | ||
"eventID": "df1dd5f2-4891-4c74-9918-2c10ba1bbd5a", | ||
"eventName": "DeleteTrail", | ||
"eventSource": "cloudtrail.amazonaws.com", | ||
"eventTime": "2022-07-29T21:17:04Z", | ||
"eventType": "AwsApiCall", | ||
"eventVersion": "1.08", | ||
"evt": { | ||
"name": "DeleteTrail" | ||
}, | ||
"http": { | ||
"useragent": "stratus-red-team_b071a414-5f74-48d8-9346-31e3b01a24e3", | ||
}, | ||
"managementEvent": true, | ||
"readOnly": false, | ||
"recipientAccountId": "012345678912", | ||
"requestParameters": { | ||
"name": "my-cloudtrail-trail-2" | ||
}, | ||
"service": "cloudtrail", | ||
"timestamp": 1659129552928, | ||
"userIdentity": { | ||
"accessKeyId": "ASIA...ZICY", | ||
"accountId": "012345678912", | ||
"arn": "arn:aws:sts::012345678912:assumed-role/my-role/christophetd", | ||
"assumed_role": "my-role", | ||
"principalId": "AROA...N6U4:christophetd", | ||
"type": "AssumedRole" | ||
} | ||
} | ||
] | ||
``` |
131 changes: 131 additions & 0 deletions
131
examples/detonate-and-dump-cloudtrail-logs/dump-logs.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"github.com/DataDog/datadog-api-client-go/api/v2/datadog" | ||
"github.com/datadog/stratus-red-team/v2/pkg/stratus" | ||
_ "github.com/datadog/stratus-red-team/v2/pkg/stratus/loader" // Note: This import is needed | ||
stratusrunner "github.com/datadog/stratus-red-team/v2/pkg/stratus/runner" | ||
"os" | ||
"strconv" | ||
"time" | ||
) | ||
|
||
// Detonates a TTP using Stratus Red Team | ||
func detonateTTP(stratusRedTeamTTP string) (*stratusrunner.Runner, error) { | ||
ttp := stratus.GetRegistry().GetAttackTechniqueByName(stratusRedTeamTTP) | ||
if ttp == nil { | ||
return nil, errors.New("unknown TTP: " + stratusRedTeamTTP) | ||
} | ||
stratusRunner := stratusrunner.NewRunner(ttp, stratusrunner.StratusRunnerNoForce) | ||
|
||
fmt.Println("Detonating '" + stratusRedTeamTTP + "' with Stratus Red Team") | ||
|
||
_, err := stratusRunner.WarmUp() | ||
if err != nil { | ||
return nil, err | ||
} | ||
err = stratusRunner.Detonate() | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &stratusRunner, nil | ||
} | ||
|
||
// Uses the Datadog API to retrieve the logs generated by a specific Stratus Red Team detonation | ||
func findLogs(detonationUuid string) (*[]map[string]interface{}, error) { | ||
ctx := context.WithValue(context.Background(), datadog.ContextAPIKeys, map[string]datadog.APIKey{ | ||
"apiKeyAuth": {Key: os.Getenv("DD_API_KEY")}, | ||
"appKeyAuth": {Key: os.Getenv("DD_APP_KEY")}, | ||
}) | ||
cfg := datadog.NewConfiguration() | ||
apiClient := datadog.NewAPIClient(cfg) | ||
|
||
query := fmt.Sprintf( | ||
"@http.useragent:stratus-red-team_%s OR @userAgent:stratus-red-team_%s", | ||
detonationUuid, | ||
detonationUuid, | ||
) | ||
queryRequest := datadog.LogsListRequest{ | ||
Filter: &datadog.LogsQueryFilter{ | ||
Query: datadog.PtrString(query), | ||
From: datadog.PtrString(strconv.FormatInt(time.Now().Add(-1*time.Hour).UnixMilli(), 10)), | ||
To: datadog.PtrString(strconv.FormatInt(time.Now().UnixMilli(), 10)), | ||
}, | ||
Sort: datadog.LOGSSORT_TIMESTAMP_ASCENDING.Ptr(), | ||
Page: &datadog.LogsListRequestPage{ | ||
Limit: datadog.PtrInt32(1000), | ||
}, | ||
} | ||
result, _, err := apiClient.LogsApi.ListLogs(ctx, *datadog.NewListLogsOptionalParameters().WithBody(queryRequest)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
logs := result.Data | ||
logEntries := []map[string]interface{}{} | ||
if len(logs) == 0 { | ||
return &logEntries, nil | ||
} | ||
|
||
for i := range logs { | ||
logEntries = append(logEntries, logs[i].Attributes.Attributes) | ||
} | ||
return &logEntries, nil | ||
} | ||
|
||
func dumpLogs(logs *[]map[string]interface{}, filename string) { | ||
fp, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) | ||
if err != nil { | ||
fmt.Println("unable to open file") | ||
return | ||
} | ||
defer fp.Close() | ||
|
||
btes, _ := json.MarshalIndent(logs, " ", " ") | ||
fp.Write(btes) | ||
} | ||
|
||
func main() { | ||
if len(os.Args) != 3 { | ||
fmt.Println("Usage: go run <filename>.go <stratus-red-team-ttp-name> <logs-output-file>") | ||
os.Exit(1) | ||
} | ||
runner, err := detonateTTP(os.Args[1]) | ||
defer runner.CleanUp() | ||
if err != nil { | ||
fmt.Println(err.Error()) | ||
return | ||
} | ||
|
||
fmt.Println("Execution UID: " + runner.GetUniqueExecutionId()) | ||
start := time.Now() | ||
const timeout = 15 * time.Minute | ||
const interval = 15 * time.Second | ||
found := false | ||
for !found && !time.Now().After(start.Add(timeout)) { | ||
time.Sleep(interval) | ||
|
||
fmt.Println("Searching for logs in Datadog") | ||
logs, err := findLogs(runner.GetUniqueExecutionId()) | ||
if err != nil { | ||
fmt.Println(err.Error()) | ||
os.Exit(1) | ||
} | ||
if numLogs := len(*logs); numLogs > 0 { | ||
fmt.Println("Found " + strconv.Itoa(numLogs) + " matching logs!") | ||
dumpLogs(logs, os.Args[2]) | ||
found = true | ||
} else { | ||
fmt.Println("No logs found. Sleeping for " + interval.String()) | ||
} | ||
} | ||
|
||
if !found { | ||
fmt.Println("Timed out") | ||
os.Exit(1) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
module github.com/datadog/stratus-red-team/example/detonate-and-dump-cloudtrail-logs | ||
|
||
go 1.18 | ||
|
||
replace github.com/datadog/stratus-red-team/v2 => ../../v2 | ||
|
||
require ( | ||
github.com/DataDog/datadog-api-client-go v1.16.0 | ||
github.com/datadog/stratus-red-team/v2 v2.2.3 | ||
) | ||
|
||
require ( | ||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0 // indirect | ||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0 // indirect | ||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect | ||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0 // indirect | ||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 // indirect | ||
github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0 // indirect | ||
github.com/DataDog/zstd v1.5.0 // indirect | ||
github.com/aws/aws-sdk-go-v2 v1.16.7 // indirect | ||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.1.0 // indirect | ||
github.com/aws/aws-sdk-go-v2/config v1.13.0 // indirect | ||
github.com/aws/aws-sdk-go-v2/credentials v1.8.0 // indirect | ||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0 // indirect | ||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.14 // indirect | ||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.8 // indirect | ||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.4 // indirect | ||
github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.13.0 // indirect | ||
github.com/aws/aws-sdk-go-v2/service/ec2 v1.26.0 // indirect | ||
github.com/aws/aws-sdk-go-v2/service/iam v1.14.0 // indirect | ||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.6.0 // indirect | ||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0 // indirect | ||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.10.0 // indirect | ||
github.com/aws/aws-sdk-go-v2/service/lambda v1.17.0 // indirect | ||
github.com/aws/aws-sdk-go-v2/service/organizations v1.12.0 // indirect | ||
github.com/aws/aws-sdk-go-v2/service/rds v1.16.0 // indirect | ||
github.com/aws/aws-sdk-go-v2/service/rolesanywhere v1.0.0 // indirect | ||
github.com/aws/aws-sdk-go-v2/service/s3 v1.23.0 // indirect | ||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.13.0 // indirect | ||
github.com/aws/aws-sdk-go-v2/service/ssm v1.20.0 // indirect | ||
github.com/aws/aws-sdk-go-v2/service/sso v1.9.0 // indirect | ||
github.com/aws/aws-sdk-go-v2/service/sts v1.14.0 // indirect | ||
github.com/aws/smithy-go v1.12.0 // indirect | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/go-logr/logr v1.2.0 // indirect | ||
github.com/gogo/protobuf v1.3.2 // indirect | ||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect | ||
github.com/golang/protobuf v1.5.2 // indirect | ||
github.com/golang/snappy v0.0.4 // indirect | ||
github.com/google/go-cmp v0.5.8 // indirect | ||
github.com/google/gofuzz v1.1.0 // indirect | ||
github.com/google/uuid v1.3.0 // indirect | ||
github.com/googleapis/gnostic v0.5.5 // indirect | ||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect | ||
github.com/hashicorp/go-version v1.4.0 // indirect | ||
github.com/hashicorp/hc-install v0.3.2 // indirect | ||
github.com/hashicorp/terraform-exec v0.15.0 // indirect | ||
github.com/hashicorp/terraform-json v0.13.0 // indirect | ||
github.com/imdario/mergo v0.3.12 // indirect | ||
github.com/jmespath/go-jmespath v0.4.0 // indirect | ||
github.com/json-iterator/go v1.1.12 // indirect | ||
github.com/kylelemons/godebug v1.1.0 // indirect | ||
github.com/moby/spdystream v0.2.0 // indirect | ||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||
github.com/modern-go/reflect2 v1.0.2 // indirect | ||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect | ||
github.com/spf13/pflag v1.0.5 // indirect | ||
github.com/zclconf/go-cty v1.9.1 // indirect | ||
golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 // indirect | ||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect | ||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect | ||
golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e // indirect | ||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect | ||
golang.org/x/text v0.3.7 // indirect | ||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect | ||
google.golang.org/appengine v1.6.7 // indirect | ||
google.golang.org/protobuf v1.27.1 // indirect | ||
gopkg.in/inf.v0 v0.9.1 // indirect | ||
gopkg.in/yaml.v2 v2.4.0 // indirect | ||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect | ||
k8s.io/api v0.23.3 // indirect | ||
k8s.io/apimachinery v0.23.3 // indirect | ||
k8s.io/client-go v0.23.3 // indirect | ||
k8s.io/klog/v2 v2.30.0 // indirect | ||
k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect | ||
k8s.io/utils v0.0.0-20211116205334-6203023598ed // indirect | ||
sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect | ||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect | ||
sigs.k8s.io/yaml v1.2.0 // indirect | ||
) |
Oops, something went wrong.