Compare commits

...
44 Commits
Author SHA1 Message Date
Seth Art 4d4992da33 update version for the 1.7.2 bug fix release 2022-10-16 12:34:35 -04:00
Seth Art a5d4124bb1 Merge pull request #14 from motikan2010/bugfix/env-vars-command
small bugfix from env-vars command
2022-10-16 12:32:30 -04:00
motikan2010 6123172784 small bugfix from env-vars command 2022-10-16 22:29:33 +09:00
Seth Art a28d4e8556 Merge pull request #8 from PrestigeTicko/multiple-aws-profiles
Multiple aws profiles
2022-10-05 14:27:38 -04:00
sethsec-bf ee7c3c0387 tried to use mock filesystem for tests, but still not working. TODO 2022-10-05 14:26:33 -04:00
sethsec-bf 467f6b459b fixed missing part of all-checks/inventory 2022-10-05 14:24:24 -04:00
sethsec-bf cc38c1f147 updated error message to include profile 2022-10-05 10:32:36 -04:00
sethsec-bf bb879c34c2 removed prerun-check from aws cobra command 2022-10-04 22:55:46 -04:00
sethsec-bf 00317a5336 Added profiles that are only in .aws/config to GetAllAWSProfiles(). Added check to avoid duplicates 2022-10-04 20:57:28 -04:00
sethsec-bf 8bfee76c28 Added error handling to multi profile mode for when a profile errors out. It would crash prorgram, but now it just skips the profile 2022-10-04 18:48:41 -04:00
sethsec-bf 712ec9c382 Added a check to confirm all profiles, but also added a way to override the confirmation so that it can be run non-interactively even in this mode 2022-10-03 23:01:01 -04:00
David 1a5f64e8c5 update readme 2022-09-23 22:44:22 -04:00
David 7c323f4f75 fix removed packages 2022-09-23 22:34:41 -04:00
David 3db423b740 minor changes 2022-09-23 17:26:43 -04:00
David abe6f3da61 merge main 2022-09-23 17:10:39 -04:00
Seth Art 16fcbc2fdd Merge pull request #7 from rpunnett-bf/bug-fix-09220222
Small bug fixes and cleanup
2022-09-23 11:03:12 -04:00
sethsec-bf 7cafe1f066 ported over the loot filepath change from buckets to all other commands 2022-09-23 10:57:28 -04:00
sethsec-bf 59b082a40b renamed variables to be golang idiomatic, added comment to instances to document the reason for the improvement 2022-09-23 10:05:00 -04:00
David b33653822c update go.mod 2022-09-23 00:03:08 -04:00
David e15c71734e attempt 2 merge master 2022-09-22 23:56:55 -04:00
David 065e04affe everything working 2022-09-22 23:40:03 -04:00
unknown 26dbb56d60 Fixed some extra spaces 2022-09-22 14:22:44 -04:00
unknown 58b9c7306e Minor bug fixes and cleanups 2022-09-22 14:17:40 -04:00
David 18f6cbe35f saving work 2022-09-22 14:06:57 -04:00
Seth Art 8cdb854740 Merge pull request #6 from BishopFox/seth-dev
Added user-configurable concurrency value that applies to all commands that use concurrency
2022-09-21 21:38:38 -04:00
sethsec-bf dfe2fc1037 bumped version to 1.7.1 2022-09-21 20:56:05 -04:00
sethsec-bf e35aa612ba tidy'd up and formated 2022-09-21 20:51:07 -04:00
sethsec-bf 89c6c0ddb3 disabled ram, fixed but added to ecr that messed up the concurrency counters 2022-09-21 20:44:56 -04:00
sethsec-bf 6aee4780b3 disabled ram, fixed but added to ecr that messed up the concurrency counters 2022-09-21 20:38:04 -04:00
sethsec-bf 351a9cc724 Added concurrency limit to rest of modules that use conncurency. fixed bug with env-vars not getting the new goroutines number from cli 2022-09-21 16:54:42 -04:00
sethsec-bf b3531faead Merge remote-tracking branch 'origin' into seth-dev 2022-09-21 09:43:23 -04:00
Seth Art 7ad15cc599 Merge pull request #5 from xntrik/aws-policy-sid
Update to cloudfox-policy.json Sid
2022-09-21 09:40:44 -04:00
sethsec-bf 95d1fbc621 Added concurrency to secrets. Added concurrency flag to all aws commands, only some of them use it so far though 2022-09-20 23:12:53 -04:00
Christian Frichot 91ad816b75 Update to cloudfox-policy.json Sid
This was throwing errors due to the use of `-` and `.` characters, as per https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-reference-policy-checks.html#access-analyzer-reference-policy-checks-error-unsupported-sid
2022-09-21 11:09:14 +08:00
sethsec-bf cd3a4be1ff Merge remote-tracking branch 'origin' into seth-dev 2022-09-20 21:49:05 -04:00
Seth Art b05254d434 Merge pull request #4 from wdahlenburg/misc-services
Adding DynamoDB, Glue, SNS, and SQS to Inventory
2022-09-20 16:28:56 -04:00
sethsec-bf 79dbe13468 using aws.ToString to avoid a potential nil pointer refernce bug if aws ever changes this API 2022-09-20 16:20:15 -04:00
sethsec-bf a63c62d5b1 added new permissions to cloudfox-policy.json 2022-09-20 16:00:43 -04:00
sethsec-bf a342c4da04 Fixed NextToken bug in getGlueDevEndpointsPerRegion 2022-09-20 15:53:40 -04:00
sethsec-bf 9a7ecc5a0f enabled ram 2022-09-20 09:46:34 -04:00
sethsec-bf 8dd7fba172 updated output test 2022-09-18 18:15:12 -04:00
Wyatt Dahlenburg bc72d939f3 Adding DynamoDB to inventory 2022-09-16 11:35:19 -06:00
Wyatt Dahlenburg fe96654ae7 Adding Glue, SNS, and SQS 2022-09-16 08:45:13 -06:00
sethsec-bf a8d086bf2a Merge branch 'carlos-dev', remote-tracking branch 'origin' into seth-dev 2022-09-12 11:48:43 -04:00
24 changed files with 1330 additions and 512 deletions

No files matched your search

+1
View File
@@ -52,6 +52,7 @@ For the full documentation please refer to our [wiki](https://github.com/BishopF
### AWS
* AWS CLI installed
* Supports AWS profiles, AWS environment variables, or metadata retrieval (on an ec2 instance)
* To run commands on multiple profiles at once, you can specify the path to a file with a list of profile names seperated by a new line using the `-l` flag or pass all stored profiles with the `-a` flag.
* A principal with one recommended policies attached (described below)
* Recommended attached policies: **`SecurityAudit` + [CloudFox custom policy](./misc/aws/cloudfox-policy.json)**
+5 -3
View File
@@ -21,6 +21,7 @@ type AccessKeysModule struct {
Caller sts.GetCallerIdentityOutput
AWSProfile string
OutputFormat string
Goroutines int
CommandCounter console.CommandCounter
// Main module data
@@ -45,8 +46,9 @@ func (m *AccessKeysModule) PrintAccessKeys(filter string, outputFormat string, o
"module": m.output.CallingModule,
},
)
if m.AWSProfile == "" {
m.AWSProfile = fmt.Sprintf("%s-%s", aws.ToString(m.Caller.Account), aws.ToString(m.Caller.UserId))
m.AWSProfile = utils.BuildAWSPath(m.Caller)
}
fmt.Printf("[%s] Mapping user access keys for account: %s.\n", cyan(m.output.CallingModule), aws.ToString(m.Caller.Account))
@@ -81,7 +83,7 @@ func (m *AccessKeysModule) PrintAccessKeys(filter string, outputFormat string, o
//m.output.OutputSelector(outputFormat)
utils.OutputSelector(verbosity, outputFormat, m.output.Headers, m.output.Body, m.output.FilePath, m.output.CallingModule, m.output.CallingModule)
m.writeLoot(outputDirectory, verbosity)
m.writeLoot(m.output.FilePath, verbosity)
fmt.Printf("[%s] %s access keys found.\n", cyan(m.output.CallingModule), strconv.Itoa(len(m.output.Body)))
} else {
fmt.Printf("[%s] No access keys found, skipping the creation of an output file.\n", cyan(m.output.CallingModule))
@@ -90,7 +92,7 @@ func (m *AccessKeysModule) PrintAccessKeys(filter string, outputFormat string, o
}
func (m *AccessKeysModule) writeLoot(outputDirectory string, verbosity int) {
path := filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile, "loot")
path := filepath.Join(outputDirectory, "loot")
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
m.modLog.Error(err.Error())
+22 -9
View File
@@ -25,6 +25,7 @@ type BucketsModule struct {
Caller sts.GetCallerIdentityOutput
AWSRegions []string
OutputFormat string
Goroutines int
AWSProfile string
// Main module data
@@ -49,13 +50,15 @@ func (m *BucketsModule) PrintBuckets(outputFormat string, outputDirectory string
m.modLog = utils.TxtLogger.WithFields(logrus.Fields{
"module": m.output.CallingModule,
})
if m.AWSProfile == "" {
m.AWSProfile = fmt.Sprintf("%s-%s", aws.ToString(m.Caller.Account), aws.ToString(m.Caller.UserId))
m.AWSProfile = utils.BuildAWSPath(m.Caller)
}
fmt.Printf("[%s] Enumerating buckets for account %s.\n", cyan(m.output.CallingModule), aws.ToString(m.Caller.Account))
wg := new(sync.WaitGroup)
semaphore := make(chan struct{}, m.Goroutines)
// Create a channel to signal the spinner aka task status goroutine to finish
spinnerDone := make(chan bool)
@@ -71,7 +74,7 @@ func (m *BucketsModule) PrintBuckets(outputFormat string, outputDirectory string
wg.Add(1)
m.CommandCounter.Pending++
go m.executeChecks(wg, dataReceiver)
go m.executeChecks(wg, semaphore, dataReceiver)
wg.Wait()
// Send a message to the spinner goroutine to close the channel and stop
@@ -100,7 +103,7 @@ func (m *BucketsModule) PrintBuckets(outputFormat string, outputDirectory string
m.output.FilePath = filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile)
////m.output.OutputSelector(outputFormat)
utils.OutputSelector(verbosity, outputFormat, m.output.Headers, m.output.Body, m.output.FilePath, m.output.CallingModule, m.output.CallingModule)
m.writeLoot(outputDirectory, verbosity, m.AWSProfile)
m.writeLoot(m.output.FilePath, verbosity, m.AWSProfile)
fmt.Printf("[%s] %s buckets found.\n", cyan(m.output.CallingModule), strconv.Itoa(len(m.output.Body)))
} else {
@@ -122,18 +125,18 @@ func (m *BucketsModule) Receiver(receiver chan Bucket, receiverDone chan bool) {
}
}
func (m *BucketsModule) executeChecks(wg *sync.WaitGroup, dataReceiver chan Bucket) {
func (m *BucketsModule) executeChecks(wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Bucket) {
defer wg.Done()
m.CommandCounter.Total++
m.CommandCounter.Pending--
m.CommandCounter.Executing++
m.getBuckets(m.output.Verbosity, dataReceiver)
wg.Add(1)
m.getBuckets(m.output.Verbosity, wg, semaphore, dataReceiver)
m.CommandCounter.Executing--
m.CommandCounter.Complete++
}
func (m *BucketsModule) writeLoot(outputDirectory string, verbosity int, profile string) {
path := filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile, "loot")
path := filepath.Join(outputDirectory, "loot")
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
m.modLog.Error(err.Error())
@@ -176,7 +179,17 @@ func (m *BucketsModule) writeLoot(outputDirectory string, verbosity int, profile
}
func (m *BucketsModule) getBuckets(verbosity int, dataReceiver chan Bucket) {
func (m *BucketsModule) getBuckets(verbosity int, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Bucket) {
defer func() {
m.CommandCounter.Executing--
m.CommandCounter.Complete++
wg.Done()
}()
semaphore <- struct{}{}
defer func() {
<-semaphore
}()
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
var r string = "Global"
var name string
+21 -11
View File
@@ -26,6 +26,7 @@ type ECRModule struct {
Caller sts.GetCallerIdentityOutput
AWSRegions []string
OutputFormat string
Goroutines int
AWSProfile string
// Main module data
@@ -55,12 +56,13 @@ func (m *ECRModule) PrintECR(outputFormat string, outputDirectory string, verbos
"module": m.output.CallingModule,
})
if m.AWSProfile == "" {
m.AWSProfile = fmt.Sprintf("%s-%s", aws.ToString(m.Caller.Account), aws.ToString(m.Caller.UserId))
m.AWSProfile = utils.BuildAWSPath(m.Caller)
}
fmt.Printf("[%s] Enumerating container repositories for account %s.\n", cyan(m.output.CallingModule), aws.ToString(m.Caller.Account))
wg := new(sync.WaitGroup)
semaphore := make(chan struct{}, m.Goroutines)
// Create a channel to signal the spinner aka task status goroutine to finish
spinnerDone := make(chan bool)
@@ -77,7 +79,7 @@ func (m *ECRModule) PrintECR(outputFormat string, outputDirectory string, verbos
for _, region := range m.AWSRegions {
wg.Add(1)
m.CommandCounter.Pending++
go m.executeChecks(region, wg, dataReceiver)
go m.executeChecks(region, wg, semaphore, dataReceiver)
}
@@ -120,7 +122,7 @@ func (m *ECRModule) PrintECR(outputFormat string, outputDirectory string, verbos
m.output.FilePath = filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile)
//m.output.OutputSelector(outputFormat)
utils.OutputSelector(verbosity, outputFormat, m.output.Headers, m.output.Body, m.output.FilePath, m.output.CallingModule, m.output.CallingModule)
m.writeLoot(outputDirectory, verbosity)
m.writeLoot(m.output.FilePath, verbosity)
fmt.Printf("[%s] %s repositories found.\n", cyan(m.output.CallingModule), strconv.Itoa(len(m.output.Body)))
} else {
fmt.Printf("[%s] No repositories found, skipping the creation of an output file.\n", cyan(m.output.CallingModule))
@@ -128,14 +130,12 @@ func (m *ECRModule) PrintECR(outputFormat string, outputDirectory string, verbos
}
func (m *ECRModule) executeChecks(r string, wg *sync.WaitGroup, dataReceiver chan Repository) {
func (m *ECRModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Repository) {
defer wg.Done()
m.CommandCounter.Total++
m.CommandCounter.Pending--
m.CommandCounter.Executing++
m.getECRRecordsPerRegion(r, dataReceiver)
m.CommandCounter.Executing--
m.CommandCounter.Complete++
wg.Add(1)
m.getECRRecordsPerRegion(r, wg, semaphore, dataReceiver)
}
func (m *ECRModule) Receiver(receiver chan Repository, receiverDone chan bool) {
@@ -152,7 +152,7 @@ func (m *ECRModule) Receiver(receiver chan Repository, receiverDone chan bool) {
}
func (m *ECRModule) writeLoot(outputDirectory string, verbosity int) {
path := filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile, "loot")
path := filepath.Join(outputDirectory, "loot")
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
m.modLog.Error(err.Error())
@@ -197,7 +197,17 @@ func (m *ECRModule) writeLoot(outputDirectory string, verbosity int) {
}
func (m *ECRModule) getECRRecordsPerRegion(r string, dataReceiver chan Repository) {
func (m *ECRModule) getECRRecordsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Repository) {
defer func() {
m.CommandCounter.Executing--
m.CommandCounter.Complete++
wg.Done()
}()
semaphore <- struct{}{}
defer func() {
<-semaphore
}()
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
var PaginationControl *string
var PaginationControl2 *string
+6 -5
View File
@@ -55,6 +55,7 @@ type EndpointsModule struct {
Caller sts.GetCallerIdentityOutput
AWSRegions []string
OutputFormat string
Goroutines int
AWSProfile string
// Main module data
@@ -87,7 +88,7 @@ func (m *EndpointsModule) PrintEndpoints(outputFormat string, outputDirectory st
"module": m.output.CallingModule,
})
if m.AWSProfile == "" {
m.AWSProfile = fmt.Sprintf("%s-%s", aws.ToString(m.Caller.Account), aws.ToString(m.Caller.UserId))
m.AWSProfile = utils.BuildAWSPath(m.Caller)
}
fmt.Printf("[%s] Enumerating endpoints for account %s.\n", cyan(m.output.CallingModule), aws.ToString(m.Caller.Account))
@@ -95,7 +96,7 @@ func (m *EndpointsModule) PrintEndpoints(outputFormat string, outputDirectory st
fmt.Printf("[%s] \t\t\tLambda, MQ, OpenSearch, Redshift, RDS\n", cyan(m.output.CallingModule))
wg := new(sync.WaitGroup)
semaphore := make(chan struct{}, 50)
semaphore := make(chan struct{}, m.Goroutines)
// Create a channel to signal the spinner aka task status goroutine to finish
spinnerDone := make(chan bool)
//fire up the the task status spinner/updated
@@ -168,8 +169,8 @@ func (m *EndpointsModule) PrintEndpoints(outputFormat string, outputDirectory st
m.output.FilePath = filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile)
//m.output.OutputSelector(outputFormat)
utils.OutputSelector(verbosity, outputFormat, m.output.Headers, m.output.Body, m.output.FilePath, m.output.CallingModule, m.output.CallingModule)
m.writeLoot(outputDirectory, verbosity)
fmt.Printf("[%s] %s endpoints enumerated.\n", cyan(m.output.CallingModule), strconv.Itoa(len(m.output.Body)))
m.writeLoot(m.output.FilePath, verbosity)
fmt.Printf("[%s] %s endpoints found.\n", cyan(m.output.CallingModule), strconv.Itoa(len(m.output.Body)))
} else {
fmt.Printf("[%s] No endpoints found, skipping the creation of an output file.\n", cyan(m.output.CallingModule))
}
@@ -259,7 +260,7 @@ func (m *EndpointsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore
}
func (m *EndpointsModule) writeLoot(outputDirectory string, verbosity int) {
path := filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile, "loot")
path := filepath.Join(outputDirectory, "loot")
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
m.modLog.Error(err.Error())
+3 -2
View File
@@ -37,6 +37,7 @@ type EnvsModule struct {
AWSRegions []string
AWSProfile string
OutputFormat string
Goroutines int
// Service Clients
ECSClient *ecs.Client
@@ -71,14 +72,14 @@ func (m *EnvsModule) PrintEnvs(outputFormat string, outputDirectory string, verb
"module": m.output.CallingModule,
})
if m.AWSProfile == "" {
m.AWSProfile = fmt.Sprintf("%s-%s", aws.ToString(m.Caller.Account), aws.ToString(m.Caller.UserId))
m.AWSProfile = utils.BuildAWSPath(m.Caller)
}
fmt.Printf("[%s] Enumerating environment variables in all regions for account %s.\n", cyan(m.output.CallingModule), aws.ToString(m.Caller.Account))
fmt.Printf("[%s] Supported Services: App Runner, Elastic Container Service, Lambda, Lightsail Containers, Sagemaker \n", cyan(m.output.CallingModule))
wg := new(sync.WaitGroup)
semaphore := make(chan struct{}, 50)
semaphore := make(chan struct{}, m.Goroutines)
// Create a channel to signal the spinner aka task status goroutine to finish
spinnerDone := make(chan bool)
+11 -9
View File
@@ -31,6 +31,7 @@ type FilesystemsModule struct {
Caller sts.GetCallerIdentityOutput
AWSRegions []string
OutputFormat string
Goroutines int
AWSProfile string
// Main module data
@@ -62,7 +63,7 @@ func (m *FilesystemsModule) PrintFilesystems(outputFormat string, outputDirector
"module": m.output.CallingModule,
})
if m.AWSProfile == "" {
m.AWSProfile = fmt.Sprintf("%s-%s", aws.ToString(m.Caller.Account), aws.ToString(m.Caller.UserId))
m.AWSProfile = utils.BuildAWSPath(m.Caller)
}
//populate region in the filesystems module struct
for i, region := range m.AWSRegions {
@@ -73,6 +74,7 @@ func (m *FilesystemsModule) PrintFilesystems(outputFormat string, outputDirector
fmt.Printf("[%s] Supported Services: EFS, FSx \n", cyan(m.output.CallingModule))
wg := new(sync.WaitGroup)
semaphore := make(chan struct{}, m.Goroutines)
// Create a channel to signal the spinner aka task status goroutine to finish
spinnerDone := make(chan bool)
@@ -89,7 +91,7 @@ func (m *FilesystemsModule) PrintFilesystems(outputFormat string, outputDirector
//execute regional checks
for _, region := range m.AWSRegions {
wg.Add(1)
m.executeChecks(region, wg, dataReceiver)
m.executeChecks(region, wg, semaphore, dataReceiver)
}
wg.Wait()
@@ -135,7 +137,7 @@ func (m *FilesystemsModule) PrintFilesystems(outputFormat string, outputDirector
m.output.FilePath = filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile)
//m.output.OutputSelector(outputFormat)
utils.OutputSelector(verbosity, outputFormat, m.output.Headers, m.output.Body, m.output.FilePath, m.output.CallingModule, m.output.CallingModule)
m.writeLoot(outputDirectory, verbosity)
m.writeLoot(m.output.FilePath, verbosity)
fmt.Printf("[%s] %s filesystems found.\n", cyan(m.output.CallingModule), strconv.Itoa(len(m.output.Body)))
} else {
@@ -157,16 +159,16 @@ func (m *FilesystemsModule) Receiver(receiver chan FilesystemObject, receiverDon
}
}
func (m *FilesystemsModule) executeChecks(r string, wg *sync.WaitGroup, dataReceiver chan FilesystemObject) {
func (m *FilesystemsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan FilesystemObject) {
defer wg.Done()
wg.Add(1)
go m.getEFSSharesPerRegion(r, wg, dataReceiver)
go m.getEFSSharesPerRegion(r, wg, semaphore, dataReceiver)
wg.Add(1)
go m.getFSxSharesPerRegion(r, wg, dataReceiver)
go m.getFSxSharesPerRegion(r, wg, semaphore, dataReceiver)
}
func (m *FilesystemsModule) writeLoot(outputDirectory string, verbosity int) {
path := filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile, "loot")
path := filepath.Join(outputDirectory, "loot")
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
m.modLog.Error(err.Error())
@@ -219,7 +221,7 @@ func (m *FilesystemsModule) writeLoot(outputDirectory string, verbosity int) {
}
func (m *FilesystemsModule) getEFSSharesPerRegion(r string, wg *sync.WaitGroup, dataReceiver chan FilesystemObject) {
func (m *FilesystemsModule) getEFSSharesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan FilesystemObject) {
defer func() {
m.CommandCounter.Executing--
m.CommandCounter.Complete++
@@ -322,7 +324,7 @@ func (m *FilesystemsModule) getEFSSharesPerRegion(r string, wg *sync.WaitGroup,
}
func (m *FilesystemsModule) getFSxSharesPerRegion(r string, wg *sync.WaitGroup, dataReceiver chan FilesystemObject) {
func (m *FilesystemsModule) getFSxSharesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan FilesystemObject) {
defer func() {
m.CommandCounter.Executing--
m.CommandCounter.Complete++
+7 -7
View File
@@ -25,6 +25,7 @@ type IamSimulatorModule struct {
Caller sts.GetCallerIdentityOutput
AWSRegions []string
OutputFormat string
Goroutines int
AWSProfile string
// Main module data
@@ -84,9 +85,8 @@ func (m *IamSimulatorModule) PrintIamSimulator(principal string, action string,
var pmapperOutFileName string
if m.AWSProfile == "" {
m.AWSProfile = fmt.Sprintf("%s-%s", aws.ToString(m.Caller.Account), aws.ToString(m.Caller.UserId))
m.AWSProfile = utils.BuildAWSPath(m.Caller)
}
wg := new(sync.WaitGroup)
// Create a channel to signal the spinner aka task status goroutine to finish
spinnerDone := make(chan bool)
@@ -125,7 +125,7 @@ func (m *IamSimulatorModule) PrintIamSimulator(principal string, action string,
m.getIAMUsers(wg, actionList, resource, dataReceiver)
wg.Add(1)
m.getIAMRoles(wg, actionList, resource, dataReceiver)
pmapperOutFileName = filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile, "loot", fmt.Sprintf("pmapper-output-%s.txt", action))
pmapperOutFileName = filepath.Join(m.output.FullFilename, "loot", fmt.Sprintf("pmapper-output-%s.txt", action))
pmapperCommands = append(pmapperCommands, fmt.Sprintf("pmapper --profile %s query \"who can do %s with %s\" | tee %s\n", m.AWSProfile, action, resource, pmapperOutFileName))
} else {
// Both --principal and --action are empty. Run in default mode!
@@ -133,7 +133,7 @@ func (m *IamSimulatorModule) PrintIamSimulator(principal string, action string,
m.output.FullFilename = m.output.CallingModule
m.executeChecks(wg, resource, dataReceiver)
for _, action := range defaultActionNames {
pmapperOutFileName = filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile, "loot", fmt.Sprintf("pmapper-output-%s.txt", action))
pmapperOutFileName = filepath.Join(m.output.FullFilename, "loot", fmt.Sprintf("pmapper-output-%s.txt", action))
pmapperCommands = append(pmapperCommands, fmt.Sprintf("pmapper --profile %s query \"who can do %s with %s\" | tee %s\n", m.AWSProfile, action, resource, pmapperOutFileName))
}
@@ -175,14 +175,14 @@ func (m *IamSimulatorModule) PrintIamSimulator(principal string, action string,
}
if len(m.output.Body) > 0 {
//m.output.OutputSelector(outputFormat)
m.output.FilePath = filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile)
utils.OutputSelector(verbosity, outputFormat, m.output.Headers, m.output.Body, m.output.FilePath, m.output.FullFilename, m.output.CallingModule)
fmt.Printf("[%s] We suggest running the pmapper commands in the loot file to get the same information but taking privesc paths into account.\n", cyan(m.output.CallingModule))
// fmt.Printf("[%s]\t\tpmapper --profile %s graph create\n", cyan(m.output.CallingModule), m.AWSProfile)
// for _, line := range pmapperCommands {
// fmt.Printf("[%s]\t\t%s", cyan(m.output.CallingModule), line)
// }
m.writeLoot(outputDirectory, verbosity, pmapperCommands)
m.writeLoot(m.output.FilePath, verbosity, pmapperCommands)
} else if principal != "" || action != "" {
fmt.Printf("[%s] No allowed permissions identified, skipping the creation of an output file.\n", cyan(m.output.CallingModule))
@@ -191,7 +191,7 @@ func (m *IamSimulatorModule) PrintIamSimulator(principal string, action string,
}
func (m *IamSimulatorModule) writeLoot(outputDirectory string, verbosity int, pmapperCommands []string) {
path := filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile, "loot")
path := filepath.Join(outputDirectory, "loot")
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
m.modLog.Error(err.Error())
+8 -6
View File
@@ -25,6 +25,7 @@ type InstancesModule struct {
Caller sts.GetCallerIdentityOutput
AWSRegions []string
OutputFormat string
Goroutines int
UserDataAttributesOnly bool
AWSProfile string
@@ -58,9 +59,8 @@ func (m *InstancesModule) Instances(filter string, outputFormat string, outputDi
"module": m.output.CallingModule,
})
if m.AWSProfile == "" {
m.AWSProfile = fmt.Sprintf("%s-%s", aws.ToString(m.Caller.Account), aws.ToString(m.Caller.UserId))
m.AWSProfile = utils.BuildAWSPath(m.Caller)
}
// regions, errtemp := m.EC2Client.DescribeRegions(
// context.TODO(),
// &ec2.DescribeRegionsInput{})
@@ -142,7 +142,6 @@ func (m *InstancesModule) printInstancesUserDataAttributesOnly(outputFormat stri
m.output.CallingModule = "instance-userdata"
path := filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile, "loot")
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
m.modLog.Error(err.Error())
@@ -170,7 +169,8 @@ func (m *InstancesModule) printInstancesUserDataAttributesOnly(outputFormat stri
}
}
}
if len(m.MappedInstances) > 0 {
// only create a file if if there is at least one instance AND at least one instance had user-data.
if (len(m.MappedInstances) > 0) && (userDataOut != "=============================================\n") {
if m.output.Verbosity > 1 {
fmt.Printf("%s", userDataOut)
}
@@ -180,6 +180,8 @@ func (m *InstancesModule) printInstancesUserDataAttributesOnly(outputFormat stri
m.CommandCounter.Error++
}
fmt.Printf("[%s] Loot written to [%s]\n", cyan(m.output.CallingModule), userDataFileName)
} else {
fmt.Printf("[%s] No user data found, skipping the creation of an output file\n", cyan(m.output.CallingModule))
}
}
@@ -218,7 +220,7 @@ func (m *InstancesModule) printGeneralInstanceData(outputFormat string, outputDi
////m.output.OutputSelector(outputFormat)
utils.OutputSelector(m.output.Verbosity, outputFormat, m.output.Headers, m.output.Body, m.output.FilePath, m.output.CallingModule, m.output.CallingModule)
m.writeLoot(outputDirectory)
m.writeLoot(m.output.FilePath)
fmt.Printf("[%s] %s instances found.\n", cyan(m.output.CallingModule), strconv.Itoa(len(m.output.Body)))
} else {
@@ -227,7 +229,7 @@ func (m *InstancesModule) printGeneralInstanceData(outputFormat string, outputDi
}
func (m *InstancesModule) writeLoot(outputDirectory string) {
path := filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile, "loot")
path := filepath.Join(outputDirectory, "loot")
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
m.modLog.Error(err.Error())
+288 -9
View File
@@ -16,11 +16,13 @@ import (
"github.com/aws/aws-sdk-go-v2/service/apprunner"
"github.com/aws/aws-sdk-go-v2/service/cloudformation"
"github.com/aws/aws-sdk-go-v2/service/cloudfront"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/ecs"
"github.com/aws/aws-sdk-go-v2/service/eks"
"github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing"
"github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2"
"github.com/aws/aws-sdk-go-v2/service/glue"
"github.com/aws/aws-sdk-go-v2/service/grafana"
"github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/aws/aws-sdk-go-v2/service/lambda"
@@ -31,6 +33,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/redshift"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
"github.com/aws/aws-sdk-go-v2/service/sns"
"github.com/aws/aws-sdk-go-v2/service/sqs"
"github.com/aws/aws-sdk-go-v2/service/ssm"
"github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/sirupsen/logrus"
@@ -59,10 +63,15 @@ type Inventory2Module struct {
CloudfrontClient *cloudfront.Client
AppRunnerClient *apprunner.Client
LightsailClient *lightsail.Client
GlueClient *glue.Client
SNSClient *sns.Client
SQSClient *sqs.Client
DynamoDBClient *dynamodb.Client
Caller sts.GetCallerIdentityOutput
AWSRegions []string
OutputFormat string
Goroutines int
AWSProfile string
// Main module data
@@ -97,12 +106,12 @@ func (m *Inventory2Module) PrintInventoryPerRegion(outputFormat string, outputDi
},
)
// def change this to build dynamically in the future.
m.services = []string{"total", "APIGateway RestAPIs", "APIGatewayv2 APIs", "AppRunner Services", "CloudFormation Stacks", "Cloudfront Distributions", "EC2 Instances", "ECS Tasks", "EKS Clusters", "ELB Load Balancers", "ELBv2 Load Balancers", "Grafana Workspaces", "Lambda Functions", "Lightsail Instances/Containers", "MQ Brokers", "OpenSearch DomainNames", "RDS DB Instances", "SecretsManager Secrets", "SSM Parameters"}
m.services = []string{"total", "APIGateway RestAPIs", "APIGatewayv2 APIs", "AppRunner Services", "CloudFormation Stacks", "Cloudfront Distributions", "DynamoDB Tables", "EC2 Instances", "ECS Tasks", "EKS Clusters", "ELB Load Balancers", "ELBv2 Load Balancers", "Glue Dev Endpoints", "Glue Jobs", "Grafana Workspaces", "Lambda Functions", "Lightsail Instances/Containers", "MQ Brokers", "OpenSearch DomainNames", "RDS DB Instances", "SecretsManager Secrets", "SNS Topics", "SQS Queues", "SSM Parameters"}
m.serviceMap = map[string]map[string]int{}
m.totalRegionCounts = map[string]int{}
if m.AWSProfile == "" {
m.AWSProfile = fmt.Sprintf("%s-%s", aws.ToString(m.Caller.Account), aws.ToString(m.Caller.UserId))
m.AWSProfile = utils.BuildAWSPath(m.Caller)
}
//initialize servicemap and total
@@ -115,11 +124,11 @@ func (m *Inventory2Module) PrintInventoryPerRegion(outputFormat string, outputDi
}
fmt.Printf("[%s] Enumerating selected services in all regions for account %s.\n", cyan(m.output.CallingModule), aws.ToString(m.Caller.Account))
fmt.Printf("[%s] Supported Services: ApiGateway, ApiGatewayv2, AppRunner, CloudFormation, Cloudfront, EC2, ECS, EKS, \n", cyan(m.output.CallingModule))
fmt.Printf("[%s] \t\t\tELB, ELBv2, Grafana, IAM, Lambda, Lightsail, MQ, OpenSearch, RDS, S3, SecretsManager, SSM\n", cyan(m.output.CallingModule))
fmt.Printf("[%s] Supported Services: ApiGateway, ApiGatewayv2, AppRunner, CloudFormation, Cloudfront, DynamoDB, EC2, ECS, EKS, \n", cyan(m.output.CallingModule))
fmt.Printf("[%s] \t\t\tELB, ELBv2, Glue, Grafana, IAM, Lambda, Lightsail, MQ, OpenSearch, RDS, S3, SecretsManager, SNS, SQS, SSM\n", cyan(m.output.CallingModule))
wg := new(sync.WaitGroup)
semaphore := make(chan struct{}, 50)
semaphore := make(chan struct{}, m.Goroutines)
// Create a channel to signal the spinner aka task status goroutine to finish
spinnerDone := make(chan bool)
@@ -269,8 +278,6 @@ func (m *Inventory2Module) PrintGlobalResources(outputFormat string, outputDirec
//m.globalOutput.CallingModule = fmt.Sprintf("%s-global", m.globalOutput.CallingModule)
m.globalOutput.FilePath = filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile)
m.globalOutput.Headers = []string{
"Resource Type",
"Total",
@@ -377,6 +384,25 @@ func (m *Inventory2Module) executeChecks(r string, wg *sync.WaitGroup, semaphore
wg.Add(1)
go m.getSSMParametersPerRegion(r, wg, semaphore)
m.CommandCounter.Total++
wg.Add(1)
go m.getGlueDevEndpointsPerRegion(r, wg, semaphore)
m.CommandCounter.Total++
wg.Add(1)
go m.getGlueJobsPerRegion(r, wg, semaphore)
m.CommandCounter.Total++
wg.Add(1)
go m.getSNSTopicsPerRegion(r, wg, semaphore)
m.CommandCounter.Total++
wg.Add(1)
go m.getSQSQueuesPerRegion(r, wg, semaphore)
m.CommandCounter.Total++
wg.Add(1)
go m.getDynamoDBTablesPerRegion(r, wg, semaphore)
}
func (m *Inventory2Module) getLambdaFunctionsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}) {
@@ -1321,6 +1347,259 @@ func (m *Inventory2Module) getEcsTasksPerRegion(r string, wg *sync.WaitGroup, se
}
}
func (m *Inventory2Module) getGlueDevEndpointsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}) {
// Don't use this method as a template for future ones. There is a one off in the way the NextToken is handled.
defer func() {
wg.Done()
m.CommandCounter.Executing--
m.CommandCounter.Complete++
}()
semaphore <- struct{}{}
defer func() {
<-semaphore
}()
// m.CommandCounter.Total++
m.CommandCounter.Pending--
m.CommandCounter.Executing++
var totalCountThisServiceThisRegion = 0
var service = "Glue Dev Endpoints"
var PaginationControl *string
for {
ListDevEndpoints, err := m.GlueClient.ListDevEndpoints(
context.TODO(),
&(glue.ListDevEndpointsInput{
NextToken: PaginationControl,
}),
func(o *glue.Options) {
o.Region = r
},
)
if err != nil {
m.modLog.Error(err.Error())
m.CommandCounter.Error++
break
}
// Add this page of resources to the total count
totalCountThisServiceThisRegion = totalCountThisServiceThisRegion + len(ListDevEndpoints.DevEndpointNames)
// This next line is non-standard. For some reason this next token is an empty string instead of nil, so
// as a result we had to change the comparison.
if aws.ToString(ListDevEndpoints.NextToken) != "" {
PaginationControl = ListDevEndpoints.NextToken
} else {
PaginationControl = nil
m.mu.Lock()
m.serviceMap[service][r] = totalCountThisServiceThisRegion
m.totalRegionCounts[r] = m.totalRegionCounts[r] + totalCountThisServiceThisRegion
m.serviceMap["total"][r] = m.serviceMap["total"][r] + totalCountThisServiceThisRegion
m.mu.Unlock()
break
}
}
}
func (m *Inventory2Module) getGlueJobsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}) {
defer func() {
wg.Done()
m.CommandCounter.Executing--
m.CommandCounter.Complete++
}()
semaphore <- struct{}{}
defer func() {
<-semaphore
}()
// m.CommandCounter.Total++
m.CommandCounter.Pending--
m.CommandCounter.Executing++
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
var PaginationControl *string
var totalCountThisServiceThisRegion = 0
var service = "Glue Jobs"
for {
m.modLog.Info(fmt.Sprintf("Getting jobs %v\n", PaginationControl))
ListJobs, err := m.GlueClient.ListJobs(
context.TODO(),
&(glue.ListJobsInput{
NextToken: PaginationControl,
}),
func(o *glue.Options) {
o.Region = r
},
)
if err != nil {
m.modLog.Error(err.Error())
m.CommandCounter.Error++
break
}
// Add this page of resources to the total count
totalCountThisServiceThisRegion = totalCountThisServiceThisRegion + len(ListJobs.JobNames)
// The "NextToken" value is nil when there's no more data to return.
if ListJobs.NextToken != nil {
PaginationControl = ListJobs.NextToken
} else {
PaginationControl = nil
// No more pages, update the module's service map
m.mu.Lock()
m.serviceMap[service][r] = totalCountThisServiceThisRegion
m.totalRegionCounts[r] = m.totalRegionCounts[r] + totalCountThisServiceThisRegion
m.serviceMap["total"][r] = m.serviceMap["total"][r] + totalCountThisServiceThisRegion
m.mu.Unlock()
break
}
}
}
func (m *Inventory2Module) getSNSTopicsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}) {
defer func() {
wg.Done()
m.CommandCounter.Executing--
m.CommandCounter.Complete++
}()
semaphore <- struct{}{}
defer func() {
<-semaphore
}()
// m.CommandCounter.Total++
m.CommandCounter.Pending--
m.CommandCounter.Executing++
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
var PaginationControl *string
var totalCountThisServiceThisRegion = 0
var service = "SNS Topics"
for {
ListTopics, err := m.SNSClient.ListTopics(
context.TODO(),
&(sns.ListTopicsInput{
NextToken: PaginationControl,
}),
func(o *sns.Options) {
o.Region = r
},
)
if err != nil {
m.modLog.Error(err.Error())
m.CommandCounter.Error++
break
}
// Add this page of resources to the total count
totalCountThisServiceThisRegion = totalCountThisServiceThisRegion + len(ListTopics.Topics)
// The "NextToken" value is nil when there's no more data to return.
if ListTopics.NextToken != nil {
PaginationControl = ListTopics.NextToken
} else {
PaginationControl = nil
// No more pages, update the module's service map
m.mu.Lock()
m.serviceMap[service][r] = totalCountThisServiceThisRegion
m.totalRegionCounts[r] = m.totalRegionCounts[r] + totalCountThisServiceThisRegion
m.serviceMap["total"][r] = m.serviceMap["total"][r] + totalCountThisServiceThisRegion
m.mu.Unlock()
break
}
}
}
func (m *Inventory2Module) getSQSQueuesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}) {
defer func() {
wg.Done()
m.CommandCounter.Executing--
m.CommandCounter.Complete++
}()
semaphore <- struct{}{}
defer func() {
<-semaphore
}()
// m.CommandCounter.Total++
m.CommandCounter.Pending--
m.CommandCounter.Executing++
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
var PaginationControl *string
var totalCountThisServiceThisRegion = 0
var service = "SQS Queues"
for {
ListQueues, err := m.SQSClient.ListQueues(
context.TODO(),
&(sqs.ListQueuesInput{
NextToken: PaginationControl,
}),
func(o *sqs.Options) {
o.Region = r
},
)
if err != nil {
m.modLog.Error(err.Error())
m.CommandCounter.Error++
break
}
// Add this page of resources to the total count
totalCountThisServiceThisRegion = totalCountThisServiceThisRegion + len(ListQueues.QueueUrls)
// The "NextToken" value is nil when there's no more data to return.
if ListQueues.NextToken != nil {
PaginationControl = ListQueues.NextToken
} else {
PaginationControl = nil
// No more pages, update the module's service map
m.mu.Lock()
m.serviceMap[service][r] = totalCountThisServiceThisRegion
m.totalRegionCounts[r] = m.totalRegionCounts[r] + totalCountThisServiceThisRegion
m.serviceMap["total"][r] = m.serviceMap["total"][r] + totalCountThisServiceThisRegion
m.mu.Unlock()
break
}
}
}
func (m *Inventory2Module) getDynamoDBTablesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}) {
defer func() {
wg.Done()
m.CommandCounter.Executing--
m.CommandCounter.Complete++
}()
semaphore <- struct{}{}
defer func() {
<-semaphore
}()
// m.CommandCounter.Total++
m.CommandCounter.Pending--
m.CommandCounter.Executing++
var totalCountThisServiceThisRegion = 0
var service = "DynamoDB Tables"
ListTables, err := m.DynamoDBClient.ListTables(
context.TODO(),
&(dynamodb.ListTablesInput{}),
func(o *dynamodb.Options) {
o.Region = r
},
)
if err != nil {
m.modLog.Error(err.Error())
m.CommandCounter.Error++
return
}
// Add this page of resources to the total count
totalCountThisServiceThisRegion = totalCountThisServiceThisRegion + len(ListTables.TableNames)
// No more pages, update the module's service map
m.mu.Lock()
m.serviceMap[service][r] = totalCountThisServiceThisRegion
m.totalRegionCounts[r] = m.totalRegionCounts[r] + totalCountThisServiceThisRegion
m.serviceMap["total"][r] = m.serviceMap["total"][r] + totalCountThisServiceThisRegion
m.mu.Unlock()
}
// Global Resources
func (m *Inventory2Module) getBuckets(verbosity int, dataReceiver chan GlobalResourceCount2) {
@@ -1468,8 +1747,8 @@ func (m *Inventory2Module) PrintTotalResources(outputFormat string) {
}
}
for i, _ := range m.GlobalResourceCounts {
for i := range m.GlobalResourceCounts {
totalResources = totalResources + m.GlobalResourceCounts[i].count
}
fmt.Printf("[%s] %d resources enumerated in the services we looked at. This is NOT the total number of resources in the account.\n", cyan(m.output.CallingModule), totalResources)
fmt.Printf("[%s] %d resources found in the services we looked at. This is NOT the total number of resources in the account.\n", cyan(m.output.CallingModule), totalResources)
}
+19 -10
View File
@@ -25,6 +25,7 @@ type OutboundAssumedRolesModule struct {
Caller sts.GetCallerIdentityOutput
AWSRegions []string
OutputFormat string
Goroutines int
AWSProfile string
// Main module data
@@ -121,15 +122,15 @@ func (m *OutboundAssumedRolesModule) PrintOutboundRoleTrusts(days int, outputFor
"module": m.output.CallingModule,
})
if m.AWSProfile == "" {
m.AWSProfile = fmt.Sprintf("%s-%s", aws.ToString(m.Caller.Account), aws.ToString(m.Caller.UserId))
m.AWSProfile = utils.BuildAWSPath(m.Caller)
}
m.Days = days
fmt.Printf("[%s] Enumerating outbound assumed role entries in cloudtrail for account %s.\n", cyan(m.output.CallingModule), aws.ToString(m.Caller.Account))
fmt.Printf("[%s] Going back through %d days of cloudtrail events. (This command can be pretty slow, FYI)\n", cyan(m.output.CallingModule), days)
wg := new(sync.WaitGroup)
semaphore := make(chan struct{}, m.Goroutines)
// Create a channel to signal the spinner aka task status goroutine to finish
spinnerDone := make(chan bool)
@@ -146,7 +147,7 @@ func (m *OutboundAssumedRolesModule) PrintOutboundRoleTrusts(days int, outputFor
for _, region := range m.AWSRegions {
wg.Add(1)
m.CommandCounter.Pending++
go m.executeChecks(region, wg, dataReceiver)
go m.executeChecks(region, wg, semaphore, dataReceiver)
}
@@ -213,17 +214,25 @@ func (m *OutboundAssumedRolesModule) Receiver(receiver chan OutboundAssumeRoleEn
}
}
func (m *OutboundAssumedRolesModule) executeChecks(r string, wg *sync.WaitGroup, dataReceiver chan OutboundAssumeRoleEntry) {
func (m *OutboundAssumedRolesModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan OutboundAssumeRoleEntry) {
defer wg.Done()
m.CommandCounter.Total++
m.CommandCounter.Pending--
m.CommandCounter.Executing++
m.getAssumeRoleLogEntriesPerRegion(r, dataReceiver)
m.CommandCounter.Executing--
m.CommandCounter.Complete++
m.getAssumeRoleLogEntriesPerRegion(r, wg, semaphore, dataReceiver)
}
func (m *OutboundAssumedRolesModule) getAssumeRoleLogEntriesPerRegion(r string, dataReceiver chan OutboundAssumeRoleEntry) {
func (m *OutboundAssumedRolesModule) getAssumeRoleLogEntriesPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan OutboundAssumeRoleEntry) {
defer func() {
m.CommandCounter.Executing--
m.CommandCounter.Complete++
wg.Done()
}()
semaphore <- struct{}{}
defer func() {
<-semaphore
}()
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
var PaginationControl *string
//var LookupAttributes []types.LookupAttributes
+2 -1
View File
@@ -26,6 +26,7 @@ type IamPermissionsModule struct {
Caller sts.GetCallerIdentityOutput
AWSRegions []string
OutputFormat string
Goroutines int
AWSProfile string
// Main module data
@@ -91,7 +92,7 @@ func (m *IamPermissionsModule) PrintIamPermissions(outputFormat string, outputDi
"module": m.output.CallingModule,
})
if m.AWSProfile == "" {
m.AWSProfile = fmt.Sprintf("%s-%s", aws.ToString(m.Caller.Account), aws.ToString(m.Caller.UserId))
m.AWSProfile = utils.BuildAWSPath(m.Caller)
}
m.output.FilePath = filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile)
fmt.Printf("[%s] Enumerating IAM permissions for account %s.\n", cyan(m.output.CallingModule), aws.ToString(m.Caller.Account))
+2 -1
View File
@@ -22,6 +22,7 @@ type IamPrincipalsModule struct {
Caller sts.GetCallerIdentityOutput
AWSRegions []string
OutputFormat string
Goroutines int
AWSProfile string
// Main module data
@@ -71,7 +72,7 @@ func (m *IamPrincipalsModule) PrintIamPrincipals(outputFormat string, outputDire
"module": m.output.CallingModule,
})
if m.AWSProfile == "" {
m.AWSProfile = fmt.Sprintf("%s-%s", aws.ToString(m.Caller.Account), aws.ToString(m.Caller.UserId))
m.AWSProfile = utils.BuildAWSPath(m.Caller)
}
fmt.Printf("[%s] Enumerating IAM Users and Roles for account %s.\n", cyan(m.output.CallingModule), aws.ToString(m.Caller.Account))
+3 -5
View File
@@ -22,6 +22,7 @@ type RAMModule struct {
Caller sts.GetCallerIdentityOutput
AWSRegions []string
OutputFormat string
Goroutines int
AWSProfile string
// Main module data
@@ -48,7 +49,7 @@ func (m *RAMModule) PrintRAM(outputFormat string, outputDirectory string, verbos
"module": m.output.CallingModule,
})
if m.AWSProfile == "" {
m.AWSProfile = fmt.Sprintf("%s-%s", aws.ToString(m.Caller.Account), aws.ToString(m.Caller.UserId))
m.AWSProfile = utils.BuildAWSPath(m.Caller)
}
fmt.Printf("[%s] Enumerating shared resources for account %s.\n", cyan(m.output.CallingModule), aws.ToString(m.Caller.Account))
@@ -87,10 +88,7 @@ func (m *RAMModule) PrintRAM(outputFormat string, outputDirectory string, verbos
"Service",
"Region",
"Name",
"URI",
"PushedAt",
"ImageTags",
"ImageSize",
"Owner",
}
// Table rows
+2 -1
View File
@@ -24,6 +24,7 @@ type RoleTrustsModule struct {
IAMClient iam.ListRolesAPIClient
Caller sts.GetCallerIdentityOutput
AWSProfile string
Goroutines int
CommandCounter console.CommandCounter
// Main module data
@@ -43,7 +44,7 @@ func (m *RoleTrustsModule) PrintRoleTrusts(outputFormat string, outputDirectory
"module": m.output.CallingModule,
})
if m.AWSProfile == "" {
m.AWSProfile = fmt.Sprintf("%s-%s", aws.ToString(m.Caller.Account), aws.ToString(m.Caller.UserId))
m.AWSProfile = utils.BuildAWSPath(m.Caller)
}
fmt.Printf("[%s] Enumerating role trusts for account %s.\n", cyan(m.output.CallingModule), aws.ToString(m.Caller.Account))
+4 -3
View File
@@ -25,6 +25,7 @@ type Route53Module struct {
Caller sts.GetCallerIdentityOutput
AWSRegions []string
OutputFormat string
Goroutines int
AWSProfile string
CommandCounter console.CommandCounter
@@ -54,7 +55,7 @@ func (m *Route53Module) PrintRoute53(outputFormat string, outputDirectory string
"module": m.output.CallingModule,
})
if m.AWSProfile == "" {
m.AWSProfile = fmt.Sprintf("%s-%s", aws.ToString(m.Caller.Account), aws.ToString(m.Caller.UserId))
m.AWSProfile = utils.BuildAWSPath(m.Caller)
}
fmt.Printf("[%s] Enumerating Route53 for account %s.\n", cyan(m.output.CallingModule), aws.ToString(m.Caller.Account))
@@ -88,7 +89,7 @@ func (m *Route53Module) PrintRoute53(outputFormat string, outputDirectory string
m.output.FilePath = filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile)
//m.output.OutputSelector(outputFormat)
utils.OutputSelector(verbosity, outputFormat, m.output.Headers, m.output.Body, m.output.FilePath, m.output.CallingModule, m.output.CallingModule)
m.writeLoot(outputDirectory, verbosity)
m.writeLoot(m.output.FilePath, verbosity)
fmt.Printf("[%s] %s DNS records found.\n", cyan(m.output.CallingModule), strconv.Itoa(len(m.output.Body)))
} else {
@@ -98,7 +99,7 @@ func (m *Route53Module) PrintRoute53(outputFormat string, outputDirectory string
}
func (m *Route53Module) writeLoot(outputDirectory string, verbosity int) {
path := filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile, "loot")
path := filepath.Join(outputDirectory, "loot")
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
m.modLog.Error(err.Error())
+44 -14
View File
@@ -25,6 +25,7 @@ type SecretsModule struct {
Caller sts.GetCallerIdentityOutput
AWSRegions []string
AWSProfile string
Goroutines int
// Main module data
Secrets []Secret
@@ -52,18 +53,19 @@ func (m *SecretsModule) PrintSecrets(outputFormat string, outputDirectory string
"module": m.output.CallingModule,
})
if m.AWSProfile == "" {
m.AWSProfile = fmt.Sprintf("%s-%s", aws.ToString(m.Caller.Account), aws.ToString(m.Caller.UserId))
m.AWSProfile = utils.BuildAWSPath(m.Caller)
}
fmt.Printf("[%s] Enumerating secrets for account %s.\n", cyan(m.output.CallingModule), aws.ToString(m.Caller.Account))
fmt.Printf("[%s] Supported Services: SecretsManager, SSM Parameters\n", cyan(m.output.CallingModule))
wg := new(sync.WaitGroup)
semaphore := make(chan struct{}, m.Goroutines)
// Create a channel to signal the spinner aka task status goroutine to finish
spinnerDone := make(chan bool)
//fire up the the task status spinner/updated
go console.SpinUntil(m.output.CallingModule, &m.CommandCounter, spinnerDone, "regions")
go console.SpinUntil(m.output.CallingModule, &m.CommandCounter, spinnerDone, "tasks")
//create a channel to receive the objects
dataReceiver := make(chan Secret)
@@ -75,7 +77,7 @@ func (m *SecretsModule) PrintSecrets(outputFormat string, outputDirectory string
for _, region := range m.AWSRegions {
wg.Add(1)
m.CommandCounter.Pending++
go m.executeChecks(region, wg, dataReceiver)
go m.executeChecks(region, wg, semaphore, dataReceiver)
}
wg.Wait()
@@ -113,7 +115,7 @@ func (m *SecretsModule) PrintSecrets(outputFormat string, outputDirectory string
m.output.FilePath = filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile)
//m.output.OutputSelector(outputFormat)
utils.OutputSelector(m.output.Verbosity, outputFormat, m.output.Headers, m.output.Body, m.output.FilePath, m.output.CallingModule, m.output.CallingModule)
m.writeLoot(outputDirectory, verbosity)
m.writeLoot(m.output.FilePath, verbosity)
fmt.Printf("[%s] %s secrets found.\n", cyan(m.output.CallingModule), strconv.Itoa(len(m.output.Body)))
} else {
@@ -135,19 +137,21 @@ func (m *SecretsModule) Receiver(receiver chan Secret, receiverDone chan bool) {
}
}
func (m *SecretsModule) executeChecks(r string, wg *sync.WaitGroup, dataReceiver chan Secret) {
func (m *SecretsModule) executeChecks(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Secret) {
defer wg.Done()
m.CommandCounter.Total++
m.CommandCounter.Pending--
m.CommandCounter.Executing++
m.getSecretsManagerSecretsPerRegion(r, dataReceiver)
m.getSSMParametersPerRegion(r, dataReceiver)
m.CommandCounter.Executing--
m.CommandCounter.Complete++
wg.Add(1)
go m.getSecretsManagerSecretsPerRegion(r, wg, semaphore, dataReceiver)
m.CommandCounter.Total++
wg.Add(1)
go m.getSSMParametersPerRegion(r, wg, semaphore, dataReceiver)
}
func (m *SecretsModule) writeLoot(outputDirectory string, verbosity int) {
path := filepath.Join(outputDirectory, "cloudfox-output", "aws", m.AWSProfile, "loot")
path := filepath.Join(outputDirectory, "loot")
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
m.modLog.Error(err.Error())
@@ -189,7 +193,20 @@ func (m *SecretsModule) writeLoot(outputDirectory string, verbosity int) {
}
func (m *SecretsModule) getSecretsManagerSecretsPerRegion(r string, dataReceiver chan Secret) {
func (m *SecretsModule) getSecretsManagerSecretsPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Secret) {
defer func() {
m.CommandCounter.Executing--
m.CommandCounter.Complete++
wg.Done()
}()
semaphore <- struct{}{}
defer func() {
<-semaphore
}()
// m.CommandCounter.Total++
m.CommandCounter.Pending--
m.CommandCounter.Executing++
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
var PaginationControl *string
for {
@@ -234,7 +251,20 @@ func (m *SecretsModule) getSecretsManagerSecretsPerRegion(r string, dataReceiver
}
}
func (m *SecretsModule) getSSMParametersPerRegion(r string, dataReceiver chan Secret) {
func (m *SecretsModule) getSSMParametersPerRegion(r string, wg *sync.WaitGroup, semaphore chan struct{}, dataReceiver chan Secret) {
defer func() {
m.CommandCounter.Executing--
m.CommandCounter.Complete++
wg.Done()
}()
semaphore <- struct{}{}
defer func() {
<-semaphore
}()
// m.CommandCounter.Total++
m.CommandCounter.Pending--
m.CommandCounter.Executing++
// "PaginationMarker" is a control variable used for output continuity, as AWS return the output in pages.
var PaginationControl *string
+625 -363
View File
File diff suppressed because it is too large. Load diff
+6 -2
View File
@@ -10,7 +10,7 @@ require (
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/aquasecurity/table v1.8.0
github.com/aws/aws-sdk-go-v2 v1.16.11
github.com/aws/aws-sdk-go-v2 v1.16.16
github.com/aws/aws-sdk-go-v2/config v1.13.0
github.com/aws/aws-sdk-go-v2/service/apigateway v1.15.6
github.com/aws/aws-sdk-go-v2/service/apigatewayv2 v1.12.5
@@ -18,6 +18,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/cloudformation v1.18.0
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.18.2
github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.16.1
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.17.1
github.com/aws/aws-sdk-go-v2/service/ec2 v1.26.0
github.com/aws/aws-sdk-go-v2/service/ecr v1.17.5
github.com/aws/aws-sdk-go-v2/service/ecs v1.18.7
@@ -26,6 +27,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.14.5
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.18.5
github.com/aws/aws-sdk-go-v2/service/fsx v1.24.1
github.com/aws/aws-sdk-go-v2/service/glue v1.32.0
github.com/aws/aws-sdk-go-v2/service/grafana v1.9.1
github.com/aws/aws-sdk-go-v2/service/iam v1.18.5
github.com/aws/aws-sdk-go-v2/service/lambda v1.23.1
@@ -39,9 +41,11 @@ require (
github.com/aws/aws-sdk-go-v2/service/s3 v1.24.0
github.com/aws/aws-sdk-go-v2/service/sagemaker v1.32.1
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.13.0
github.com/aws/aws-sdk-go-v2/service/sns v1.18.1
github.com/aws/aws-sdk-go-v2/service/sqs v1.19.10
github.com/aws/aws-sdk-go-v2/service/ssm v1.27.1
github.com/aws/aws-sdk-go-v2/service/sts v1.14.0
github.com/aws/smithy-go v1.12.1
github.com/aws/smithy-go v1.13.3
github.com/fatih/color v1.13.0
github.com/go-openapi/strfmt v0.21.3 // indirect
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
+20 -5
View File
@@ -75,8 +75,9 @@ github.com/aws/aws-sdk-go-v2 v1.11.2/go.mod h1:SQfA+m2ltnu1cA0soUkj4dRSsmITiVQUJ
github.com/aws/aws-sdk-go-v2 v1.13.0/go.mod h1:L6+ZpqHaLbAaxsqV0L4cvxZY7QupWJB4fhkf8LXvC7w=
github.com/aws/aws-sdk-go-v2 v1.16.4/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU=
github.com/aws/aws-sdk-go-v2 v1.16.5/go.mod h1:Wh7MEsmEApyL5hrWzpDkba4gwAPc5/piwLVLFnCxp48=
github.com/aws/aws-sdk-go-v2 v1.16.11 h1:xM1ZPSvty3xVmdxiGr7ay/wlqv+MWhH0rMlyLdbC0YQ=
github.com/aws/aws-sdk-go-v2 v1.16.11/go.mod h1:WTACcleLz6VZTp7fak4EO5b9Q4foxbn+8PIz3PmyKlo=
github.com/aws/aws-sdk-go-v2 v1.16.16 h1:M1fj4FE2lB4NzRb9Y0xdWsn2P0+2UHVxwKyOa4YJNjk=
github.com/aws/aws-sdk-go-v2 v1.16.16/go.mod h1:SwiyXi/1zTUZ6KIAmLK5V5ll8SiURNUYOqTerZPaF9k=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.2.0 h1:scBthy70MB3m4LCMFaBcmYCyR2XWOz6MxSfdSu/+fQo=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.2.0/go.mod h1:oZHzg1OVbuCiRTY0oRPM+c2HQvwnFCGJwKeSqqAJ/yM=
github.com/aws/aws-sdk-go-v2/config v1.13.0 h1:1ij3YPk13RrIn1h+pH+dArh3lNPD5JSAP+ifOkNhnB0=
@@ -89,14 +90,16 @@ github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.2/go.mod h1:SgKKNBIoDC/
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.4/go.mod h1:XHgQ7Hz2WY2GAn//UXHofLfPXWh+s62MbMOijrg12Lw=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.11/go.mod h1:tmUB6jakq5DFNcXsXOA/ZQ7/C8VnSKYkx58OI7Fh79g=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.12/go.mod h1:Afj/U8svX6sJ77Q+FPWMzabJ9QjbwP32YlopgKALUpg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.18 h1:OmiwoVyLKEqqD5GvB683dbSqxiOfvx4U2lDZhG2Esc4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.18/go.mod h1:348MLhzV1GSlZSMusdwQpXKbhD7X2gbI/TxwAPKkYZQ=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23 h1:s4g/wnzMf+qepSNgTvaQQHNxyMLKSawNhKCPNy++2xY=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23/go.mod h1:2DFxAQ9pfIRy0imBCJv+vZ2X6RKxves6fbnEuSry6b4=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.0.2/go.mod h1:xT4XX6w5Sa3dhg50JrYyy3e4WPYo/+WjY/BXtqXVunU=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.2.0/go.mod h1:BsCSJHx5DnDXIrOcqB8KN1/B+hXLG/bi4Y6Vjcx/x9E=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.5/go.mod h1:fV1AaS2gFc1tM0RCb015FJ0pvWVUfJZANzjwoO4YakM=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.6/go.mod h1:FwpAKI+FBPIELJIdmQzlLtRe8LQSOreMcM2wBsPMvvc=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.12 h1:5mvQDtNWtI6H56+E4LUnLWEmATMB7oEh+Z9RurtIuC0=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.12/go.mod h1:ckaCVTEdGAxO6KwTGzgskxR1xM+iJW4lxMyDFVda2Fc=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17 h1:/K482T5A3623WJgWT8w1yRAFK4RzGzEl7y39yhtn9eA=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17/go.mod h1:pRwaTYCJemADaqCbUAxltMoHKata7hmB5PjEXeu0kfg=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.4 h1:0NrDHIwS1LIR750ltj6ciiu4NZLpr9rgq8vHi/4QD4s=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.4/go.mod h1:R3sWUqPcfXSiF/LSFJhjyJmpg9uV6yP2yv3YZZjldVI=
github.com/aws/aws-sdk-go-v2/service/apigateway v1.15.6 h1:GWrNZ3xtblr+J+oCYtJY67YPqR5nzObOaXWWKplDMag=
@@ -111,6 +114,8 @@ github.com/aws/aws-sdk-go-v2/service/cloudfront v1.18.2 h1:uhpcmaxRKPt/VHGmzJ4aa
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.18.2/go.mod h1:TRXCqcApTM1LhOJcLNolqFEDr3InJoYBieb1qqPcCko=
github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.16.1 h1:1dALZzDXZmZIgVX6N+oOSwSBH+BBWC1RCjUCvgQUQfs=
github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.16.1/go.mod h1:ddq0bwfwHQ0QxV6vU9arKg31qfmg8fjcYZt/ef01AcI=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.17.1 h1:1QpTkQIAaZpR387it1L+erjB5bStGFCJRvmXsodpPEU=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.17.1/go.mod h1:BZhn/C3z13ULTSstVi2Kymc62bgjFh/JwLO9Tm2OFYI=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.26.0 h1:Q++veaxis1Dg7is9yi+aEPsIBRAgdkUxoIvyud7jOyo=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.26.0/go.mod h1:cIbz+b70nxJafXf9lT07Xj03pef6CsVdYTCCR0DQEQc=
github.com/aws/aws-sdk-go-v2/service/ecr v1.17.5 h1:W9vzPbvX7rOa/FacbQIDfnNrwxHkn5O+DdfmiIS4cHc=
@@ -127,12 +132,17 @@ github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.18.5 h1:OR1FrOPrN
github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.18.5/go.mod h1:v+U73J9xr5bwN0IYXIUlQnfWGVrcIGlY1wuq+vDsCyg=
github.com/aws/aws-sdk-go-v2/service/fsx v1.24.1 h1:VKo7a7XJbqzzELwaLzjpGm7uPPtO+ZKCs/oHonYk7lE=
github.com/aws/aws-sdk-go-v2/service/fsx v1.24.1/go.mod h1:MQeMcZxXxWr8VHc/8f8Hspg5D5dCm7wchXHYaiBntDw=
github.com/aws/aws-sdk-go-v2/service/glue v1.32.0 h1:7Kacs7LCbDmMSwW5nHk4OExP9JlYK5P9r3iQ853yoOE=
github.com/aws/aws-sdk-go-v2/service/glue v1.32.0/go.mod h1:aupHsCJmK66t1MQ542c6qBSuJYEA2IwKmwi4M3jdT1M=
github.com/aws/aws-sdk-go-v2/service/grafana v1.9.1 h1:qrd9evf91w8s7TIIvJakNzRJLW7HRiMDpQ9nmI7cOgo=
github.com/aws/aws-sdk-go-v2/service/grafana v1.9.1/go.mod h1:ckNYD0WIFojv8yOx5QiTp2tg64kTJdMFDPjR4y4mboY=
github.com/aws/aws-sdk-go-v2/service/iam v1.18.5 h1:NUT8HreIoLAfPAP7eEYN8jwidvY+4TjZ86xqS/iGDQI=
github.com/aws/aws-sdk-go-v2/service/iam v1.18.5/go.mod h1:0YrrF3m5qWi/EItvW0wmZCydlgr8lzJ882P2mXoHo7U=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.7.0 h1:F1diQIOkNn8jcez4173r+PLPdkWK7chy74r3fKpDrLI=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.7.0/go.mod h1:8ctElVINyp+SjhoZZceUAZw78glZH6R8ox5MVNu5j2s=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.9 h1:Lh1AShsuIJTwMkoxVCAYPJgNG5H+eN6SmoUn8nOZ5wE=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.9/go.mod h1:a9j48l6yL5XINLHLcOKInjdvknN+vWqPBxqeIDw7ktw=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.17 h1:o0Ia3nb56m8+8NvhbCDiSBiZRNUwIknVWobx5vks0Vk=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.17/go.mod h1:WJD9FbkwzM2a1bZ36ntH6+5Jc+x41Q4K2AcLeHDLAS8=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.5.2/go.mod h1:FgR1tCsn8C6+Hf+N5qkfrE4IXvUL1RgW87sunJ+5J4I=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0 h1:4QAOB3KrvI1ApJK14sliGr3Ie2pjyvNypn/lfzDHfUw=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0/go.mod h1:K/qPe6AP2TGYv4l6n7c88zh9jWBDf6nHhvg1fx/EWfU=
@@ -160,6 +170,10 @@ github.com/aws/aws-sdk-go-v2/service/sagemaker v1.32.1 h1:nOjJ5PevzE1ymPIYnJk6X/
github.com/aws/aws-sdk-go-v2/service/sagemaker v1.32.1/go.mod h1:nF9ZnJeXLYw8PPRAtsBbG3aY1P8c5bUH2zqA/IAzaks=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.13.0 h1:VKvs4yx3nrcyBJcj4iSy5UI/Awdsa0fbDKesiNwPuZY=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.13.0/go.mod h1:5Oibvfj4kc6CE70qamrlOU+KSO/JWANgxIVbesvSMCE=
github.com/aws/aws-sdk-go-v2/service/sns v1.18.1 h1:nxfBH9r3VUyybIOWdbIBJ/d5I1wdG7FwIoZ/BH/EhS8=
github.com/aws/aws-sdk-go-v2/service/sns v1.18.1/go.mod h1:sIIc12m8ASRbCgOERccSSkTFeekFfHKEM4TKAvzJpG0=
github.com/aws/aws-sdk-go-v2/service/sqs v1.19.10 h1:Y4civ9pg5cbQkSf/YGMfFZaIPAAAK61JV+NIzO8Ri4k=
github.com/aws/aws-sdk-go-v2/service/sqs v1.19.10/go.mod h1:65Z/rmGw/6usiOFI0Tk4ddNUmPbjjPER1WLZwnFqxFM=
github.com/aws/aws-sdk-go-v2/service/ssm v1.27.1 h1:w/HlW+NGK5EU5jf/qekDZ56kg9jhvP/1Egh3bMRTdgo=
github.com/aws/aws-sdk-go-v2/service/ssm v1.27.1/go.mod h1:Ej87mQA2lDTOyPL/ZCjoChhTCU/fwPKg5Em62pOIqVc=
github.com/aws/aws-sdk-go-v2/service/sso v1.9.0 h1:1qLJeQGBmNQW3mBNzK2CFmrQNmoXWrscPqsrAaU1aTA=
@@ -170,8 +184,9 @@ github.com/aws/smithy-go v1.9.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAm
github.com/aws/smithy-go v1.10.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aws/smithy-go v1.11.2/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM=
github.com/aws/smithy-go v1.11.3/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/aws/smithy-go v1.12.1 h1:yQRC55aXN/y1W10HgwHle01DRuV9Dpf31iGkotjt3Ag=
github.com/aws/smithy-go v1.12.1/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/aws/smithy-go v1.13.3 h1:l7LYxGuzK6/K+NzJ2mC+VvLUbae0sL3bXU//04MkmnA=
github.com/aws/smithy-go v1.13.3/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+1 -1
View File
@@ -11,7 +11,7 @@ import (
var (
rootCmd = &cobra.Command{
Use: os.Args[0],
Version: "1.7.0",
Version: "1.7.2",
}
)
+9 -3
View File
@@ -9,6 +9,7 @@
"cloudformation:ListStacks",
"cloudtrail:LookupEvents",
"cloudfront:ListDistributions",
"dynamodb:ListTables",
"ec2:DescribeInstanceAttribute",
"ec2:DescribeInstances",
"ec2:DescribeRegions",
@@ -27,6 +28,8 @@
"elasticloadbalancing:DescribeListeners",
"fsx:DescribeFileSystems",
"fsx:DescribeVolumes",
"glue:ListDevEndpoints",
"glue:ListJobs",
"grafana:ListWorkspaces",
"iam:GetAccountAuthorizationDetails",
"iam:ListAccessKeys",
@@ -57,11 +60,14 @@
"sagemaker:DescribeTrainingJob",
"sagemaker:ListModels",
"sagemaker:DescribeModel",
"secretsmanager:ListSecrets"
"secretsmanager:ListSecrets",
"sns:ListTopics",
"sqs:ListQueues"
],
"Resource": "*",
"Effect": "Allow",
"Sid": "AllowMoreReadForCloudFox"
"Sid": "AllowMoreReadForCloudFox171"
}
]
}
}
+144 -42
View File
@@ -1,27 +1,30 @@
package utils
import (
"bufio"
"context"
"fmt"
"log"
"os"
"regexp"
"strings"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/aws/retry"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/ssm"
"github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/aws/smithy-go/ptr"
"github.com/kyokomi/emoji"
"github.com/sirupsen/logrus"
"github.com/spf13/afero"
)
var (
TxtLoggerName = "root"
TxtLogger = txtLogger()
UtilsFs = afero.NewOsFs()
)
func AWSConfigFileLoader(AWSProfile string) aws.Config {
func AWSConfigFileLoader(AWSProfile string, version string) aws.Config {
// Ensures the profile in the aws config file meets all requirements (valid keys and a region defined). I noticed some calls fail without a default region.
cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithSharedConfigProfile(AWSProfile), config.WithDefaultRegion("us-east-1"), config.WithRetryer(
func() aws.Retryer {
@@ -29,66 +32,65 @@ func AWSConfigFileLoader(AWSProfile string) aws.Config {
}))
if err != nil {
fmt.Println(err)
log.Fatal(err)
TxtLogger.Println(err)
}
_, err = cfg.Credentials.Retrieve(context.TODO())
if err != nil {
fmt.Printf("[%s] Error retrieving credentials from the specified profile, environment variables, or the instance metadata service.\n", cyan(emoji.Sprintf(":fox:cloudfox :fox:")))
log.Fatalf("Could not retrieve the specified profile name %s", err)
fmt.Printf("[%s] Error retrieving credentials from the [%s] profile, environment variables, or the instance metadata service.\n", cyan(emoji.Sprintf(":fox:cloudfox v%s :fox:", version)), AWSProfile)
TxtLogger.Printf("Could not retrieve the specified profile name %s", err)
}
return cfg
}
func AWSWhoami(awsProfile string) sts.GetCallerIdentityOutput {
func AWSWhoami(awsProfile string, version string) (*sts.GetCallerIdentityOutput, error) {
// Connects to STS and checks caller identity. Same as running "aws sts get-caller-identity"
//fmt.Printf("[%s] Retrieving caller's identity\n", cyan(emoji.Sprintf(":fox:cloudfox v%s :fox:", version)))
STSService := sts.NewFromConfig(AWSConfigFileLoader(awsProfile))
STSService := sts.NewFromConfig(AWSConfigFileLoader(awsProfile, version))
CallerIdentity, err := STSService.GetCallerIdentity(context.TODO(), &sts.GetCallerIdentityInput{})
if err != nil {
fmt.Printf("[%s] Could not get caller's identity\n\nError: %s", cyan(emoji.Sprintf(":fox:cloudfox :fox:")), err)
log.Fatalf("Could not get caller's identity: %s", err)
fmt.Printf("[%s] Could not get caller's identity for the [%s] profile\n\nError: %s\n\n", cyan(emoji.Sprintf(":fox:cloudfox v%s :fox:", version)), awsProfile, err)
TxtLogger.Printf("Could not get caller's identity: %s", err)
return CallerIdentity, err
}
return *CallerIdentity
return CallerIdentity, err
}
func GetRegionsForService(awsProfile string, service string) []string {
SSMClient := ssm.NewFromConfig(AWSConfigFileLoader(awsProfile))
var PaginationControl *string
var supportedRegions []string
path := fmt.Sprintf("/aws/service/global-infrastructure/services/%s/regions", service)
// func GetRegionsForService(awsProfile string, service string) []string {
// SSMClient := ssm.NewFromConfig(AWSConfigFileLoader(awsProfile))
// var PaginationControl *string
// var supportedRegions []string
// path := fmt.Sprintf("/aws/service/global-infrastructure/services/%s/regions", service)
ServiceRegions, err := SSMClient.GetParametersByPath(
context.TODO(),
&(ssm.GetParametersByPathInput{
NextToken: PaginationControl,
Path: &path,
}),
)
if err != nil {
fmt.Println(err.Error())
// ServiceRegions, err := SSMClient.GetParametersByPath(
// context.TODO(),
// &(ssm.GetParametersByPathInput{
// NextToken: PaginationControl,
// Path: &path,
// }),
// )
// if err != nil {
// fmt.Println(err.Error())
}
// }
if ServiceRegions.Parameters != nil {
for _, region := range ServiceRegions.Parameters {
name := *region.Value
supportedRegions = append(supportedRegions, name)
}
// if ServiceRegions.Parameters != nil {
// for _, region := range ServiceRegions.Parameters {
// name := *region.Value
// supportedRegions = append(supportedRegions, name)
// }
// The "NextToken" value is nil when there's no more data to return.
if ServiceRegions.NextToken != nil {
PaginationControl = ServiceRegions.NextToken
} else {
PaginationControl = nil
}
}
return supportedRegions
}
// // The "NextToken" value is nil when there's no more data to return.
// if ServiceRegions.NextToken != nil {
// PaginationControl = ServiceRegions.NextToken
// } else {
// PaginationControl = nil
// }
// }
// return supportedRegions
// }
// txtLogger - Returns the txt logger
func txtLogger() *logrus.Logger {
@@ -103,3 +105,103 @@ func txtLogger() *logrus.Logger {
return txtLogger
}
func CheckErr(e error, msg string) {
if e != nil {
TxtLogger.Printf("[-] Error %s", msg)
}
}
func GetAllAWSProfiles(AWSConfirm bool) []string {
credentialsFile, err := UtilsFs.Open(config.DefaultSharedCredentialsFilename())
CheckErr(err, "could not open default AWS credentials file")
defer credentialsFile.Close()
configFile, err := UtilsFs.Open(config.DefaultSharedConfigFilename())
CheckErr(err, "could not open default AWS credentials file")
defer configFile.Close()
var AWSProfiles []string
scanner := bufio.NewScanner(credentialsFile)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
text := strings.TrimSpace(scanner.Text())
if strings.HasPrefix(text, "[") && strings.HasSuffix(text, "]") {
text = strings.TrimPrefix(text, "[")
text = strings.TrimSuffix(text, "]")
if !Contains(text, AWSProfiles) {
AWSProfiles = append(AWSProfiles, text)
}
}
}
scanner2 := bufio.NewScanner(configFile)
scanner2.Split(bufio.ScanLines)
for scanner2.Scan() {
text := strings.TrimSpace(scanner2.Text())
if strings.HasPrefix(text, "[") && strings.HasSuffix(text, "]") {
text = strings.TrimPrefix(text, "[profile ")
text = strings.TrimPrefix(text, "[")
text = strings.TrimSuffix(text, "]")
if !Contains(text, AWSProfiles) {
AWSProfiles = append(AWSProfiles, text)
}
}
}
if !AWSConfirm {
result := ConfirmSelectedProfiles(AWSProfiles)
if !result {
os.Exit(1)
}
}
return AWSProfiles
}
func ConfirmSelectedProfiles(AWSProfiles []string) bool {
reader := bufio.NewReader(os.Stdin)
fmt.Printf("[ %s] Identified profiles:\n\n", cyan(emoji.Sprintf(":fox:cloudfox :fox:")))
for _, profile := range AWSProfiles {
fmt.Printf("\t* %s\n", profile)
}
fmt.Printf("\n[ %s] Are you sure you'd like to run this command against the [%d] listed profile(s)? (Y\\n): ", cyan(emoji.Sprintf(":fox:cloudfox :fox:")), len(AWSProfiles))
text, _ := reader.ReadString('\n')
switch text {
case "\n", "Y\n", "y\n":
return true
}
return false
}
func GetSelectedAWSProfiles(AWSProfilesListPath string) []string {
AWSProfilesListFile, err := UtilsFs.Open(AWSProfilesListPath)
CheckErr(err, fmt.Sprintf("could not open given file %s", AWSProfilesListPath))
defer AWSProfilesListFile.Close()
var AWSProfiles []string
scanner := bufio.NewScanner(AWSProfilesListFile)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
profile := strings.TrimSpace(scanner.Text())
if len(profile) != 0 {
AWSProfiles = append(AWSProfiles, profile)
}
}
return AWSProfiles
}
func removeBadPathChars(receivedPath *string) string {
var path string
var bannedPathChars *regexp.Regexp = regexp.MustCompile(`[<>:"'|?*]`)
path = bannedPathChars.ReplaceAllString(aws.ToString(receivedPath), "_")
return path
}
func BuildAWSPath(Caller sts.GetCallerIdentityOutput) string {
var callerAccount = removeBadPathChars(Caller.Account)
var callerUserID = removeBadPathChars(Caller.UserId)
return fmt.Sprintf("%s-%s", callerAccount, callerUserID)
}
+77
View File
@@ -0,0 +1,77 @@
package utils
import (
"fmt"
"testing"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/spf13/afero"
)
func compareSlice(a []string, b []string) bool {
if len(a) != len(b) {
return false
}
for idx, elem := range a {
if elem != b[idx] {
return false
}
}
return true
}
// Case empty file.
// Case expected format.
func TestGetAllAWSProfiles(t *testing.T) {
var tests = []struct {
fileData []byte
expectedOutput []string
AWSConfirm bool
caseName string
}{
{[]byte(""), []string{}, true, "empty file"},
{[]byte("[default]\naws_access_key=abc\naws_secret_access_key=123\n[123]\naws_access_key=abc\naws_secret_access_key=123"), []string{"default", "123"}, true, "expected format"},
}
credentialsFile := config.DefaultSharedCredentialsFilename()
for _, test := range tests {
UtilsFs = afero.NewMemMapFs()
fmt.Printf("[*] Testing %s\n", test.caseName)
UtilsFs.Create(credentialsFile)
afero.WriteFile(UtilsFs, credentialsFile, test.fileData, 0755)
output := GetAllAWSProfiles(test.AWSConfirm)
if !compareSlice(output, test.expectedOutput) {
t.Errorf("Test Failed: %v inputted, %v expected, received: %v", test.fileData, test.expectedOutput, output)
}
}
}
// Case empty file.
// Case special characters \r \n \t.
// Case with extra new lines at the end.
// Case expected format.
func TestGetSelectedAWSProfiles(t *testing.T) {
var tests = []struct {
fileData []byte
expectedOutput []string
AWSConfirm bool
caseName string
}{
{[]byte(""), []string{}, true, "empty file"},
{[]byte("abcd\r\nxyz\t\n123\r\t"), []string{"abcd", "xyz", "123"}, true, "special characters \\r \\n \\t"},
{[]byte("qwerty\nxyz\n456\n\n\n"), []string{"qwerty", "xyz", "456"}, true, "extra new lines at the end"},
{[]byte("nmhj\nyuioy\n098"), []string{"nmhj", "yuioy", "098"}, true, "expected format"},
}
for _, test := range tests {
UtilsFs = afero.NewMemMapFs()
fmt.Printf("[*] Testing %s\n", test.caseName)
UtilsFs.Create("/tmp/myfile.txt")
afero.WriteFile(UtilsFs, "/tmp/myfile.txt", test.fileData, 0755)
output := GetSelectedAWSProfiles("/tmp/myfile.txt")
if !compareSlice(output, test.expectedOutput) {
t.Errorf("Test Failed: %v inputted, %v expected, recieved: %v", test.fileData, test.expectedOutput, output)
}
}
}