mirror of
https://github.com/BishopFox/cloudfox.git
synced 2026-11-07 17:05:53 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16fcbc2fdd | ||
|
|
7cafe1f066 | ||
|
|
59b082a40b | ||
|
|
26dbb56d60 | ||
|
|
58b9c7306e | ||
|
|
8cdb854740 | ||
|
|
dfe2fc1037 | ||
|
|
e35aa612ba | ||
|
|
89c6c0ddb3 | ||
|
|
6aee4780b3 | ||
|
|
351a9cc724 | ||
|
|
b3531faead | ||
|
|
7ad15cc599 | ||
|
|
95d1fbc621 | ||
|
|
91ad816b75 | ||
|
|
cd3a4be1ff | ||
|
|
b05254d434 | ||
|
|
79dbe13468 | ||
|
|
a63c62d5b1 | ||
|
|
a342c4da04 | ||
|
|
9a7ecc5a0f | ||
|
|
8dd7fba172 | ||
|
|
bc72d939f3 | ||
|
|
fe96654ae7 | ||
|
|
0e2a03385a | ||
|
|
b18d1a29ed | ||
|
|
a8d086bf2a | ||
|
|
752833f11d | ||
|
|
8b83d5b1bc | ||
|
|
a5f6317c6d | ||
|
|
8859c62013 | ||
|
|
95b237177d | ||
|
|
ee89a2dc67 | ||
|
|
f7c0e91f0e | ||
|
|
a9f2c5c25f | ||
|
|
ea852070b3 | ||
|
|
bf2e5aa75d | ||
|
|
fb85cb8ccb | ||
|
|
d3ac589701 | ||
|
|
31d8c8efd6 | ||
|
|
664569b70b |
No files matched your search
@@ -11,7 +11,7 @@ CloudFox helps you gain situational awareness in unfamiliar cloud environments.
|
||||
* What endpoints/hostnames/IPs can I attack from an internal starting point (assumed breach within the VPC)?
|
||||
* What filesystems can I potentially mount from a compromised resource inside the VPC?
|
||||
|
||||
## Quickstart
|
||||
## Quick Start
|
||||
CloudFox is modular (you can run one command at a time), but there is an aws `all-checks` command that will run the other aws commands for you with sane defaults:
|
||||
|
||||
`cloudfox aws --profile [profile-name] all-checks`
|
||||
@@ -28,15 +28,16 @@ For the full documentation please refer to our [wiki](https://github.com/BishopF
|
||||
| Provider| CloudFox Commands |
|
||||
| - | - |
|
||||
| AWS | 15 |
|
||||
| Azure | 2 |
|
||||
| GCP | Support Planned |
|
||||
| Azure | 2 (alpha) |
|
||||
| GCP | Support Planned |
|
||||
| Kubernetes | Support Planned |
|
||||
|
||||
|
||||
# Install
|
||||
|
||||
**Option 1:** Download the [latest binary release](https://github.com/BishopFox/cloudfox/releases) for your platform.
|
||||
|
||||
**Option 2:** [Install Go](https://golang.org/doc/install), clone the cloudfox repository and compile from source
|
||||
**Option 2:** [Install Go](https://golang.org/doc/install), clone the CloudFox repository and compile from source
|
||||
```
|
||||
# git clone https://github.com/BishopFox/cloudfox.git
|
||||
...omitted for brevity...
|
||||
@@ -51,14 +52,18 @@ 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)
|
||||
* A principal with one recommended policies attacehd (described below)
|
||||
* Reccommended attached policies: **`SecurityAudit` + [CloudFox custom policy](./misc/aws/cloudfox-policy.json)**
|
||||
* Additional policy notes (as of 09/2022):
|
||||
* [The cloudfox policy we created](./misc/aws/cloudfox-policy.json) has a complete list of every permission cloudfox uses and nothing else
|
||||
* `arn:aws:iam::aws:policy/SecurityAudit` - Covers most cloudfox checks but is missing newer services or permissions like apprunner:\*, grafana:\*, lambda:GetFunctionURL, lightsail:GetContainerServices
|
||||
* `arn:aws:iam::aws:policy/job-function/ViewOnlyAccessetcchecks` - Covers most cloudfox checks but is missing newer services or permissions like AppRunner:\*, grafana:\*, lambda:GetFunctionURL, lightsail:GetContainerServices - and is also missing iam:SimulatePrincipalPolicy.
|
||||
* `arn:aws:iam::aws:policy/ReadOnlyAccess` - Only missing AppRunner, but also grants things like "s3:Get*" which can be overly permissive.
|
||||
* `arn:aws:iam::aws:policy/AdministratorAccess` - This will work just fine with CloudFox, but if you were given this level of access as a penetration tester, that should probably be a finding in itself :)
|
||||
* A principal with one recommended policies attached (described below)
|
||||
* Recommended attached policies: **`SecurityAudit` + [CloudFox custom policy](./misc/aws/cloudfox-policy.json)**
|
||||
|
||||
Additional policy notes (as of 09/2022):
|
||||
|
||||
| Policy | Notes |
|
||||
| - | - |
|
||||
| [CloudFox custom policy](./misc/aws/cloudfox-policy.json) | Has a complete list of every permission cloudfox uses and nothing else |
|
||||
| `arn:aws:iam::aws:policy/SecurityAudit` | Covers most cloudfox checks but is missing newer services or permissions like apprunner:\*, grafana:\*, lambda:GetFunctionURL, lightsail:GetContainerServices |
|
||||
| `arn:aws:iam::aws:policy/job-function/ViewOnlyAccess` | Covers most cloudfox checks but is missing newer services or permissions like AppRunner:\*, grafana:\*, lambda:GetFunctionURL, lightsail:GetContainerServices - and is also missing iam:SimulatePrincipalPolicy.
|
||||
| `arn:aws:iam::aws:policy/ReadOnlyAccess` | Only missing AppRunner, but also grants things like "s3:Get*" which can be overly permissive. |
|
||||
| `arn:aws:iam::aws:policy/AdministratorAccess` | This will work just fine with CloudFox, but if you were handed this level of access as a penetration tester, that should probably be a finding in itself :) |
|
||||
|
||||
### Azure
|
||||
* Viewer or similar permissions applied.
|
||||
@@ -97,6 +102,19 @@ For the full documentation please refer to our [wiki](https://github.com/BishopF
|
||||
* AWS - Add support for hardcoded region (which would override the default of looking in every region)
|
||||
|
||||
|
||||
# FAQ
|
||||
|
||||
**How does CloudFox compare with ScoutSuite, Prowler, Steampipe's AWS Compliance Module, AWS Security Hub, etc.**
|
||||
|
||||
CloudFox doesn't create any alerts or findings, and doesn't check your environment for compliance to a baseline or benchmark. Instead, it simply enables you to be more efficient during your manual penetration testing activities. If gives you the information you'll likely need to validate whether an attack path is possible or not.
|
||||
|
||||
**Why do I see errors in some CloudFox commands?**
|
||||
|
||||
* Services that don't exist in all regions - CloudFox currently makes the same API calls to every region. However, not all regions support all services. For instance, services like Appstream and AWS Grafana are only supported in a subset of the total regions. In the future, we plan to make CloudFox aware of which services run in each region.
|
||||
* You don't have permission - Another reason you might see errors if you don't have permissions to make calls that CloudFox is making. Either because the policy doesn't allow it (e.g., SecurityAudit doesn't allow all of the permissions CloudFox needs. Or, it might be an SCP that is blocking you.
|
||||
|
||||
You can always look in the ~/.cloudfox/cloudfox-error.log file to get more information on errors.
|
||||
|
||||
# Prior work and other related projects
|
||||
* [SmogCloud](https://github.com/BishopFox/smogcloud) - Inspiration for the `endpoints` command
|
||||
* [SummitRoute's AWS Exposable Resources](https://github.com/SummitRoute/aws_exposable_resources) - Inspiration for the `endpoints` command
|
||||
|
||||
+5
-3
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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++
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
+41
-23
@@ -8,15 +8,15 @@ import (
|
||||
"github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute"
|
||||
"github.com/Azure/azure-sdk-for-go/profiles/latest/network/mgmt/network"
|
||||
"github.com/BishopFox/cloudfox/utils"
|
||||
"github.com/alexeyco/simpletable"
|
||||
"github.com/aws/smithy-go/ptr"
|
||||
)
|
||||
|
||||
const instancesCallingModuleName = "instances"
|
||||
|
||||
type InstancesMapModule struct {
|
||||
// Tenants, Subs and RGs: map[TenantID]map[SubscriptionID][]ResourceGroups
|
||||
Scope map[string]map[string][]string
|
||||
// Used to store output data for pretty printing
|
||||
output utils.OutputData
|
||||
|
||||
// This module queries 3 different resource types (Compute, NIC and PublicIP), thus 3 different clients.
|
||||
computeClient compute.VirtualMachinesClient
|
||||
nicClient network.InterfacesClient
|
||||
@@ -35,7 +35,7 @@ type vmRelevantInformation struct {
|
||||
externalIPs []string
|
||||
}
|
||||
|
||||
func (m *InstancesMapModule) InstancesMap(outputFormat string, resourceGroupFilter string) {
|
||||
func (m *InstancesMapModule) InstancesMap(verbosity int, outputFormat string, outputDirectory string, resourceGroupFilter string) {
|
||||
for tenantID, subscriptionsMap := range m.Scope {
|
||||
fmt.Printf("[*] Started tenant: %s\n", tenantID)
|
||||
for subscriptionID := range subscriptionsMap {
|
||||
@@ -47,25 +47,27 @@ func (m *InstancesMapModule) InstancesMap(outputFormat string, resourceGroupFilt
|
||||
|
||||
fmt.Printf("\n[*] Preparing output...\n\n")
|
||||
// Prepare table headers
|
||||
m.output.Headers = []*simpletable.Cell{
|
||||
{Text: "RESOURCE_GROUP"},
|
||||
{Text: "NAME"},
|
||||
{Text: "OS"},
|
||||
{Text: "ADMIN_USERNAME"},
|
||||
{Text: "INTERNAL_IPS"},
|
||||
{Text: "EXTERNAL_IPS"},
|
||||
header := []string{
|
||||
"RESOURCE_GROUP",
|
||||
"NAME",
|
||||
"OS",
|
||||
"ADMIN_USERNAME",
|
||||
"INTERNAL_IPS",
|
||||
"EXTERNAL_IPS",
|
||||
}
|
||||
|
||||
// Prepare table body
|
||||
var body [][]string
|
||||
for _, result := range m.results {
|
||||
m.output.Body = append(
|
||||
m.output.Body,
|
||||
[]interface{}{
|
||||
body = append(
|
||||
body,
|
||||
[]string{
|
||||
result.resourceGroup,
|
||||
result.name,
|
||||
result.operatingSystem,
|
||||
result.adminUsername,
|
||||
result.internalIPs,
|
||||
result.externalIPs,
|
||||
strings.Join(result.internalIPs, ", "),
|
||||
strings.Join(result.externalIPs, ", "),
|
||||
//Use the format below if you don't like the array format on output
|
||||
//strings.Join(result.internalIPs, ", "),
|
||||
//strings.Join(result.externalIPs, ", "),
|
||||
@@ -73,7 +75,16 @@ func (m *InstancesMapModule) InstancesMap(outputFormat string, resourceGroupFilt
|
||||
)
|
||||
}
|
||||
// Pretty prints output
|
||||
m.output.OutputSelector(outputFormat)
|
||||
//m.output.OutputSelector(outputFormat)
|
||||
utils.OutputSelector(
|
||||
verbosity,
|
||||
outputFormat,
|
||||
header,
|
||||
body,
|
||||
outputDirectory,
|
||||
"instances",
|
||||
instancesCallingModuleName,
|
||||
)
|
||||
}
|
||||
|
||||
func (m *InstancesMapModule) setNecessaryClients(subscriptionID string) {
|
||||
@@ -141,13 +152,20 @@ func (m *InstancesMapModule) getVMsDataPerResourceGroup(subscriptionID string, r
|
||||
vmData.name = ptr.ToString(vm.Name)
|
||||
vmData.adminUsername = ptr.ToString(vm.VirtualMachineProperties.OsProfile.AdminUsername)
|
||||
vmData.operatingSystem = ptr.ToString(vm.VirtualMachineProperties.StorageProfile.ImageReference.Offer) + " " + ptr.ToString(vm.VirtualMachineProperties.StorageProfile.ImageReference.Sku)
|
||||
for _, nic := range *vm.VirtualMachineProperties.NetworkProfile.NetworkInterfaces {
|
||||
internalIP, externalIP := m.getNICInternalAndExternalIPs(nic, resourceGroup)
|
||||
vmData.internalIPs = append(vmData.internalIPs, internalIP)
|
||||
if externalIP != "" {
|
||||
vmData.externalIPs = append(vmData.externalIPs, externalIP)
|
||||
|
||||
if vm.VirtualMachineProperties.NetworkProfile.NetworkInterfaces != nil {
|
||||
for _, nic := range *vm.VirtualMachineProperties.NetworkProfile.NetworkInterfaces {
|
||||
internalIP, externalIP := m.getNICInternalAndExternalIPs(nic, resourceGroup)
|
||||
vmData.internalIPs = append(vmData.internalIPs, internalIP)
|
||||
if externalIP != "" {
|
||||
vmData.externalIPs = append(vmData.externalIPs, externalIP)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
vmData.internalIPs = append(vmData.internalIPs, "Error: NICnotFound")
|
||||
vmData.externalIPs = append(vmData.externalIPs, "Error: NICnotFound")
|
||||
}
|
||||
|
||||
m.results = append(m.results, vmData)
|
||||
vmData.internalIPs = nil
|
||||
vmData.externalIPs = nil
|
||||
@@ -162,7 +180,7 @@ func (m *InstancesMapModule) getNICInternalAndExternalIPs(nic compute.NetworkInt
|
||||
NICName := strings.Split(ptr.ToString(nic.ID), "/")[len(strings.Split(ptr.ToString(nic.ID), "/"))-1]
|
||||
NICExpanded, err := m.nicClient.Get(context.TODO(), resourceGroup, NICName, "")
|
||||
if err != nil {
|
||||
return "\n" + NICName + " not found", "\n" + NICName + "not found"
|
||||
return "Error: NICnotFound", "Error: NICnotFound"
|
||||
}
|
||||
|
||||
if NICExpanded.InterfacePropertiesFormat.IPConfigurations != nil {
|
||||
|
||||
+27
-16
@@ -8,10 +8,11 @@ import (
|
||||
"github.com/Azure/azure-sdk-for-go/profiles/latest/authorization/mgmt/authorization"
|
||||
"github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac"
|
||||
"github.com/BishopFox/cloudfox/utils"
|
||||
"github.com/alexeyco/simpletable"
|
||||
"github.com/aws/smithy-go/ptr"
|
||||
)
|
||||
|
||||
const RBACcallingModuleName = "rbac"
|
||||
|
||||
type RBACMapModule struct {
|
||||
// Tenants, Subs and RGs: map[TenantID]map[SubscriptionID][]ResourceGroups
|
||||
Scope map[string]map[string][]string
|
||||
@@ -20,31 +21,32 @@ type RBACMapModule struct {
|
||||
Roles map[string]roleDefinitionObject
|
||||
RoleAssignments []roleAssignmentObject
|
||||
// Used to store output data for pretty printing
|
||||
output utils.OutputData
|
||||
}
|
||||
|
||||
func (m *RBACMapModule) RBACMapModule(outputFormat string, userFilter string) {
|
||||
func (m *RBACMapModule) RBACMap(verbosity int, outputFormat string, outputDirectory string, userFilter string) {
|
||||
m.getADUsers()
|
||||
m.getRoleDefinitions()
|
||||
m.getRoleAssignments()
|
||||
m.printRoleAssginments(outputFormat, userFilter)
|
||||
m.printRoleAssginments(verbosity, outputFormat, outputDirectory, userFilter)
|
||||
}
|
||||
|
||||
func (m *RBACMapModule) printRoleAssginments(outputFormat string, userFilter string) {
|
||||
func (m *RBACMapModule) printRoleAssginments(verbosity int, outputFormat string, outputDirectory string, userFilter string) {
|
||||
// Prepare table headers
|
||||
m.output.Headers = []*simpletable.Cell{
|
||||
{Text: "PRINCIPAL_NAME"},
|
||||
{Text: "PRINCIPAL_ID"},
|
||||
{Text: "PRINCIPAL_TYPE"},
|
||||
{Text: "ROLE_NAME"},
|
||||
{Text: "SCOPE_LEVEL"},
|
||||
{Text: "SCOPE_NAME"},
|
||||
header := []string{
|
||||
"PRINCIPAL_NAME",
|
||||
"PRINCIPAL_ID",
|
||||
"PRINCIPAL_TYPE",
|
||||
"ROLE_NAME",
|
||||
"SCOPE_LEVEL",
|
||||
"SCOPE_NAME",
|
||||
}
|
||||
|
||||
// Prepare table body
|
||||
var body [][]string
|
||||
for _, result := range m.RoleAssignments {
|
||||
if userFilter == "all" || userFilter == result.principalDisplayName {
|
||||
m.output.Body = append(
|
||||
m.output.Body,
|
||||
[]interface{}{
|
||||
body = append(
|
||||
body, []string{
|
||||
result.principalDisplayName,
|
||||
result.principalID,
|
||||
result.principalType,
|
||||
@@ -56,7 +58,16 @@ func (m *RBACMapModule) printRoleAssginments(outputFormat string, userFilter str
|
||||
}
|
||||
}
|
||||
// Pretty prints output
|
||||
m.output.OutputSelector(outputFormat)
|
||||
//m.output.OutputSelector(outputFormat)
|
||||
utils.OutputSelector(
|
||||
verbosity,
|
||||
outputFormat,
|
||||
header,
|
||||
body,
|
||||
outputDirectory,
|
||||
"rbac",
|
||||
RBACcallingModuleName,
|
||||
)
|
||||
}
|
||||
|
||||
type userObject struct {
|
||||
|
||||
+179
-106
@@ -12,6 +12,7 @@ import (
|
||||
"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/cloudtrail"
|
||||
"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/ecr"
|
||||
"github.com/aws/aws-sdk-go-v2/service/ecs"
|
||||
@@ -20,6 +21,7 @@ import (
|
||||
"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/fsx"
|
||||
"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"
|
||||
@@ -32,6 +34,8 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sagemaker"
|
||||
"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/fatih/color"
|
||||
"github.com/kyokomi/emoji"
|
||||
@@ -47,6 +51,7 @@ var (
|
||||
AWSProfile string
|
||||
AWSOutputFormat string
|
||||
AWSOutputDirectory string
|
||||
Goroutines int
|
||||
Verbosity int
|
||||
AWSCommands = &cobra.Command{
|
||||
Use: "aws",
|
||||
@@ -73,7 +78,8 @@ var (
|
||||
m := aws.RoleTrustsModule{
|
||||
IAMClient: iam.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
AWSProfile: AWSProfile}
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines}
|
||||
m.PrintRoleTrusts(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
},
|
||||
}
|
||||
@@ -96,6 +102,7 @@ var (
|
||||
IAMClient: iam.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
m.PrintAccessKeys(AccessKeysFilter, AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
},
|
||||
@@ -117,6 +124,7 @@ var (
|
||||
S3Client: s3.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
m.PrintBuckets(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
|
||||
@@ -160,32 +168,39 @@ var (
|
||||
fmt.Printf("[%s] AWS Caller Identity: %s\n", cyan(emoji.Sprintf(":fox:cloudfox v%s :fox:", cmd.Root().Version)), *caller.Arn)
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var AWSConfig = utils.AWSConfigFileLoader(AWSProfile)
|
||||
var Caller = utils.AWSWhoami(AWSProfile)
|
||||
m := aws.Inventory2Module{
|
||||
EC2Client: ec2.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
ECSClient: ecs.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
EKSClient: eks.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
S3Client: s3.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
LambdaClient: lambda.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
CloudFormationClient: cloudformation.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
SecretsManagerClient: secretsmanager.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
SSMClient: ssm.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
RDSClient: rds.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
APIGatewayv2Client: apigatewayv2.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
ELBClient: elasticloadbalancing.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
ELBv2Client: elasticloadbalancingv2.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
IAMClient: iam.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
MQClient: mq.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
OpenSearchClient: opensearch.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
GrafanaClient: grafana.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
APIGatewayClient: apigateway.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
RedshiftClient: redshift.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
CloudfrontClient: cloudfront.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
AppRunnerClient: apprunner.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
LightsailClient: lightsail.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
EC2Client: ec2.NewFromConfig(AWSConfig),
|
||||
ECSClient: ecs.NewFromConfig(AWSConfig),
|
||||
EKSClient: eks.NewFromConfig(AWSConfig),
|
||||
S3Client: s3.NewFromConfig(AWSConfig),
|
||||
LambdaClient: lambda.NewFromConfig(AWSConfig),
|
||||
CloudFormationClient: cloudformation.NewFromConfig(AWSConfig),
|
||||
SecretsManagerClient: secretsmanager.NewFromConfig(AWSConfig),
|
||||
SSMClient: ssm.NewFromConfig(AWSConfig),
|
||||
RDSClient: rds.NewFromConfig(AWSConfig),
|
||||
APIGatewayv2Client: apigatewayv2.NewFromConfig(AWSConfig),
|
||||
ELBClient: elasticloadbalancing.NewFromConfig(AWSConfig),
|
||||
ELBv2Client: elasticloadbalancingv2.NewFromConfig(AWSConfig),
|
||||
IAMClient: iam.NewFromConfig(AWSConfig),
|
||||
MQClient: mq.NewFromConfig(AWSConfig),
|
||||
OpenSearchClient: opensearch.NewFromConfig(AWSConfig),
|
||||
GrafanaClient: grafana.NewFromConfig(AWSConfig),
|
||||
APIGatewayClient: apigateway.NewFromConfig(AWSConfig),
|
||||
RedshiftClient: redshift.NewFromConfig(AWSConfig),
|
||||
CloudfrontClient: cloudfront.NewFromConfig(AWSConfig),
|
||||
AppRunnerClient: apprunner.NewFromConfig(AWSConfig),
|
||||
LightsailClient: lightsail.NewFromConfig(AWSConfig),
|
||||
GlueClient: glue.NewFromConfig(AWSConfig),
|
||||
SNSClient: sns.NewFromConfig(AWSConfig),
|
||||
SQSClient: sqs.NewFromConfig(AWSConfig),
|
||||
DynamoDBClient: dynamodb.NewFromConfig(AWSConfig),
|
||||
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSRegions: AWSRegions,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
m.PrintInventoryPerRegion(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
},
|
||||
@@ -202,26 +217,29 @@ var (
|
||||
fmt.Printf("[%s] AWS Caller Identity: %s\n", cyan(emoji.Sprintf(":fox:cloudfox v%s :fox:", cmd.Root().Version)), *caller.Arn)
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var AWSConfig = utils.AWSConfigFileLoader(AWSProfile)
|
||||
var Caller = utils.AWSWhoami(AWSProfile)
|
||||
m := aws.EndpointsModule{
|
||||
EKSClient: eks.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
LambdaClient: lambda.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
MQClient: mq.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
OpenSearchClient: opensearch.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
GrafanaClient: grafana.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
ELBClient: elasticloadbalancing.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
APIGatewayClient: apigateway.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
ELBv2Client: elasticloadbalancingv2.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
APIGatewayv2Client: apigatewayv2.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
RDSClient: rds.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
RedshiftClient: redshift.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
S3Client: s3.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
CloudfrontClient: cloudfront.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
AppRunnerClient: apprunner.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
LightsailClient: lightsail.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
EKSClient: eks.NewFromConfig(AWSConfig),
|
||||
LambdaClient: lambda.NewFromConfig(AWSConfig),
|
||||
MQClient: mq.NewFromConfig(AWSConfig),
|
||||
OpenSearchClient: opensearch.NewFromConfig(AWSConfig),
|
||||
GrafanaClient: grafana.NewFromConfig(AWSConfig),
|
||||
ELBClient: elasticloadbalancing.NewFromConfig(AWSConfig),
|
||||
APIGatewayClient: apigateway.NewFromConfig(AWSConfig),
|
||||
ELBv2Client: elasticloadbalancingv2.NewFromConfig(AWSConfig),
|
||||
APIGatewayv2Client: apigatewayv2.NewFromConfig(AWSConfig),
|
||||
RDSClient: rds.NewFromConfig(AWSConfig),
|
||||
RedshiftClient: redshift.NewFromConfig(AWSConfig),
|
||||
S3Client: s3.NewFromConfig(AWSConfig),
|
||||
CloudfrontClient: cloudfront.NewFromConfig(AWSConfig),
|
||||
AppRunnerClient: apprunner.NewFromConfig(AWSConfig),
|
||||
LightsailClient: lightsail.NewFromConfig(AWSConfig),
|
||||
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSRegions: AWSRegions,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
m.PrintEndpoints(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
},
|
||||
@@ -238,13 +256,16 @@ var (
|
||||
fmt.Printf("[%s] AWS Caller Identity: %s\n", cyan(emoji.Sprintf(":fox:cloudfox v%s :fox:", cmd.Root().Version)), *caller.Arn)
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var AWSConfig = utils.AWSConfigFileLoader(AWSProfile)
|
||||
var Caller = utils.AWSWhoami(AWSProfile)
|
||||
m := aws.SecretsModule{
|
||||
SecretsManagerClient: secretsmanager.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
SSMClient: ssm.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
SecretsManagerClient: secretsmanager.NewFromConfig(AWSConfig),
|
||||
SSMClient: ssm.NewFromConfig(AWSConfig),
|
||||
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSRegions: AWSRegions,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
m.PrintSecrets(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
},
|
||||
@@ -261,12 +282,15 @@ var (
|
||||
fmt.Printf("[%s] AWS Caller Identity: %s\n", cyan(emoji.Sprintf(":fox:cloudfox v%s :fox:", cmd.Root().Version)), *caller.Arn)
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var AWSConfig = utils.AWSConfigFileLoader(AWSProfile)
|
||||
var Caller = utils.AWSWhoami(AWSProfile)
|
||||
m := aws.Route53Module{
|
||||
Route53Client: route53.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
Route53Client: route53.NewFromConfig(AWSConfig),
|
||||
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSRegions: AWSRegions,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
m.PrintRoute53(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
},
|
||||
@@ -283,12 +307,15 @@ var (
|
||||
fmt.Printf("[%s] AWS Caller Identity: %s\n", cyan(emoji.Sprintf(":fox:cloudfox v%s :fox:", cmd.Root().Version)), *caller.Arn)
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var AWSConfig = utils.AWSConfigFileLoader(AWSProfile)
|
||||
var Caller = utils.AWSWhoami(AWSProfile)
|
||||
m := aws.ECRModule{
|
||||
ECRClient: ecr.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
ECRClient: ecr.NewFromConfig(AWSConfig),
|
||||
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSRegions: AWSRegions,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
m.PrintECR(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
},
|
||||
@@ -305,12 +332,15 @@ var (
|
||||
fmt.Printf("[%s] AWS Caller Identity: %s\n", cyan(emoji.Sprintf(":fox:cloudfox v%s :fox:", cmd.Root().Version)), *caller.Arn)
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var AWSConfig = utils.AWSConfigFileLoader(AWSProfile)
|
||||
var Caller = utils.AWSWhoami(AWSProfile)
|
||||
m := aws.OutboundAssumedRolesModule{
|
||||
CloudTrailClient: cloudtrail.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
CloudTrailClient: cloudtrail.NewFromConfig(AWSConfig),
|
||||
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSRegions: AWSRegions,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
m.PrintOutboundRoleTrusts(OutboundAssumedRolesDays, AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
},
|
||||
@@ -327,16 +357,19 @@ var (
|
||||
fmt.Printf("[%s] AWS Caller Identity: %s\n", cyan(emoji.Sprintf(":fox:cloudfox v%s :fox:", cmd.Root().Version)), *caller.Arn)
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var AWSConfig = utils.AWSConfigFileLoader(AWSProfile)
|
||||
var Caller = utils.AWSWhoami(AWSProfile)
|
||||
m := aws.EnvsModule{
|
||||
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSRegions: AWSRegions,
|
||||
AWSProfile: AWSProfile,
|
||||
ECSClient: ecs.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
AppRunnerClient: apprunner.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
LambdaClient: lambda.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
LightsailClient: lightsail.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
SagemakerClient: sagemaker.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
Goroutines: Goroutines,
|
||||
ECSClient: ecs.NewFromConfig(AWSConfig),
|
||||
AppRunnerClient: apprunner.NewFromConfig(AWSConfig),
|
||||
LambdaClient: lambda.NewFromConfig(AWSConfig),
|
||||
LightsailClient: lightsail.NewFromConfig(AWSConfig),
|
||||
SagemakerClient: sagemaker.NewFromConfig(AWSConfig),
|
||||
}
|
||||
m.PrintEnvs(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
},
|
||||
@@ -353,10 +386,13 @@ var (
|
||||
fmt.Printf("[%s] AWS Caller Identity: %s\n", cyan(emoji.Sprintf(":fox:cloudfox v%s :fox:", cmd.Root().Version)), *caller.Arn)
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var AWSConfig = utils.AWSConfigFileLoader(AWSProfile)
|
||||
var Caller = utils.AWSWhoami(AWSProfile)
|
||||
m := aws.IamPrincipalsModule{
|
||||
IAMClient: iam.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
IAMClient: iam.NewFromConfig(AWSConfig),
|
||||
Caller: Caller,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
m.PrintIamPrincipals(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
},
|
||||
@@ -375,10 +411,13 @@ var (
|
||||
fmt.Printf("[%s] AWS Caller Identity: %s\n", cyan(emoji.Sprintf(":fox:cloudfox v%s :fox:", cmd.Root().Version)), *caller.Arn)
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var AWSConfig = utils.AWSConfigFileLoader(AWSProfile)
|
||||
var Caller = utils.AWSWhoami(AWSProfile)
|
||||
m := aws.IamPermissionsModule{
|
||||
IAMClient: iam.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
IAMClient: iam.NewFromConfig(AWSConfig),
|
||||
Caller: Caller,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
m.PrintIamPermissions(AWSOutputFormat, AWSOutputDirectory, Verbosity, PermissionsPrincipal)
|
||||
},
|
||||
@@ -398,11 +437,14 @@ var (
|
||||
fmt.Printf("[%s] AWS Caller Identity: %s\n", cyan(emoji.Sprintf(":fox:cloudfox v%s :fox:", cmd.Root().Version)), *caller.Arn)
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var AWSConfig = utils.AWSConfigFileLoader(AWSProfile)
|
||||
var Caller = utils.AWSWhoami(AWSProfile)
|
||||
m := aws.IamSimulatorModule{
|
||||
IAMClient: iam.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
IAMClient: iam.NewFromConfig(AWSConfig),
|
||||
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
m.PrintIamSimulator(SimulatorPrincipal, SimulatorAction, SimulatorResource, AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
},
|
||||
@@ -419,12 +461,15 @@ var (
|
||||
fmt.Printf("[%s] AWS Caller Identity: %s\n", cyan(emoji.Sprintf(":fox:cloudfox v%s :fox:", cmd.Root().Version)), *caller.Arn)
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var AWSConfig = utils.AWSConfigFileLoader(AWSProfile)
|
||||
var Caller = utils.AWSWhoami(AWSProfile)
|
||||
filesystems := aws.FilesystemsModule{
|
||||
EFSClient: efs.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
FSxClient: fsx.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
EFSClient: efs.NewFromConfig(AWSConfig),
|
||||
FSxClient: fsx.NewFromConfig(AWSConfig),
|
||||
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
AWSRegions: AWSRegions,
|
||||
}
|
||||
filesystems.PrintFilesystems(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
@@ -447,6 +492,7 @@ var (
|
||||
// Caller: utils.AWSWhoami(AWSProfile),
|
||||
// AWSRegions: AWSRegions,
|
||||
// AWSProfile: AWSProfile,
|
||||
// Goroutines: Goroutines,
|
||||
// }
|
||||
// m.PrintRAM(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
// },
|
||||
@@ -461,36 +507,43 @@ var (
|
||||
os.Args[0] + " aws all-checks --profile readonly_profile",
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
var caller = utils.AWSWhoami(AWSProfile)
|
||||
|
||||
fmt.Printf("[%s] AWS Caller Identity: %s\n", cyan(emoji.Sprintf(":fox:cloudfox v%s :fox:", cmd.Root().Version)), *caller.Arn)
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var AWSConfig = utils.AWSConfigFileLoader(AWSProfile)
|
||||
var Caller = utils.AWSWhoami(AWSProfile)
|
||||
|
||||
ec2Client := ec2.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
eksClient := eks.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
s3Client := s3.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
lambdaClient := lambda.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
cloudFormationClient := cloudformation.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
secretsManagerClient := secretsmanager.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
rdsClient := rds.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
apiGatewayv2Client := apigatewayv2.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
apiGatewayClient := apigateway.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
elbClient := elasticloadbalancing.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
elbv2Client := elasticloadbalancingv2.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
iamClient := iam.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
mqClient := mq.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
openSearchClient := opensearch.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
grafanaClient := grafana.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
redshiftClient := redshift.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
cloudfrontClient := cloudfront.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
appRunnerClient := apprunner.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
lightsailClient := lightsail.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
route53Client := route53.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
efsClient := efs.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
fsxClient := fsx.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
ecsClient := ecs.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
sagemakerClient := sagemaker.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
ecrClient := ecr.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
ssmClient := ssm.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile))
|
||||
ec2Client := ec2.NewFromConfig(AWSConfig)
|
||||
eksClient := eks.NewFromConfig(AWSConfig)
|
||||
s3Client := s3.NewFromConfig(AWSConfig)
|
||||
lambdaClient := lambda.NewFromConfig(AWSConfig)
|
||||
cloudFormationClient := cloudformation.NewFromConfig(AWSConfig)
|
||||
secretsManagerClient := secretsmanager.NewFromConfig(AWSConfig)
|
||||
rdsClient := rds.NewFromConfig(AWSConfig)
|
||||
apiGatewayv2Client := apigatewayv2.NewFromConfig(AWSConfig)
|
||||
apiGatewayClient := apigateway.NewFromConfig(AWSConfig)
|
||||
elbClient := elasticloadbalancing.NewFromConfig(AWSConfig)
|
||||
elbv2Client := elasticloadbalancingv2.NewFromConfig(AWSConfig)
|
||||
iamClient := iam.NewFromConfig(AWSConfig)
|
||||
mqClient := mq.NewFromConfig(AWSConfig)
|
||||
openSearchClient := opensearch.NewFromConfig(AWSConfig)
|
||||
grafanaClient := grafana.NewFromConfig(AWSConfig)
|
||||
redshiftClient := redshift.NewFromConfig(AWSConfig)
|
||||
cloudfrontClient := cloudfront.NewFromConfig(AWSConfig)
|
||||
appRunnerClient := apprunner.NewFromConfig(AWSConfig)
|
||||
lightsailClient := lightsail.NewFromConfig(AWSConfig)
|
||||
route53Client := route53.NewFromConfig(AWSConfig)
|
||||
efsClient := efs.NewFromConfig(AWSConfig)
|
||||
fsxClient := fsx.NewFromConfig(AWSConfig)
|
||||
ecsClient := ecs.NewFromConfig(AWSConfig)
|
||||
sagemakerClient := sagemaker.NewFromConfig(AWSConfig)
|
||||
ecrClient := ecr.NewFromConfig(AWSConfig)
|
||||
ssmClient := ssm.NewFromConfig(AWSConfig)
|
||||
glueClient := glue.NewFromConfig(AWSConfig)
|
||||
snsClient := sns.NewFromConfig(AWSConfig)
|
||||
sqsClient := sqs.NewFromConfig(AWSConfig)
|
||||
dynamodbClient := dynamodb.NewFromConfig(AWSConfig)
|
||||
|
||||
fmt.Printf("[%s] %s\n", cyan(emoji.Sprintf(":fox:cloudfox :fox:")), green("Getting a lay of the land, aka \"What regions is this account using?\""))
|
||||
inventory2 := aws.Inventory2Module{
|
||||
@@ -515,10 +568,15 @@ var (
|
||||
CloudfrontClient: cloudfrontClient,
|
||||
AppRunnerClient: appRunnerClient,
|
||||
LightsailClient: lightsailClient,
|
||||
GlueClient: glueClient,
|
||||
SNSClient: snsClient,
|
||||
SQSClient: sqsClient,
|
||||
DynamoDBClient: dynamodbClient,
|
||||
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSRegions: AWSRegions,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
inventory2.PrintInventoryPerRegion(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
//time.Sleep(time.Second * 5)
|
||||
@@ -527,7 +585,7 @@ var (
|
||||
|
||||
instances := aws.InstancesModule{
|
||||
EC2Client: ec2Client,
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSRegions: AWSRegions,
|
||||
|
||||
UserDataAttributesOnly: false,
|
||||
@@ -537,17 +595,19 @@ var (
|
||||
route53 := aws.Route53Module{
|
||||
Route53Client: route53Client,
|
||||
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSRegions: AWSRegions,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
route53.PrintRoute53(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
|
||||
filesystems := aws.FilesystemsModule{
|
||||
EFSClient: efsClient,
|
||||
FSxClient: fsxClient,
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
AWSRegions: AWSRegions,
|
||||
}
|
||||
filesystems.PrintFilesystems(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
@@ -570,9 +630,10 @@ var (
|
||||
AppRunnerClient: appRunnerClient,
|
||||
LightsailClient: lightsailClient,
|
||||
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSRegions: AWSRegions,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
|
||||
endpoints.PrintEndpoints(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
@@ -581,18 +642,20 @@ var (
|
||||
|
||||
ec2UserData := aws.InstancesModule{
|
||||
EC2Client: ec2Client,
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSRegions: AWSRegions,
|
||||
|
||||
UserDataAttributesOnly: true,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
ec2UserData.Instances(InstancesFilter, AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
envsMod := aws.EnvsModule{
|
||||
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSRegions: AWSRegions,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
ECSClient: ecsClient,
|
||||
AppRunnerClient: appRunnerClient,
|
||||
LambdaClient: lambdaClient,
|
||||
@@ -607,16 +670,18 @@ var (
|
||||
|
||||
buckets := aws.BucketsModule{
|
||||
S3Client: s3Client,
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
buckets.PrintBuckets(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
|
||||
ecr := aws.ECRModule{
|
||||
ECRClient: ecrClient,
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSRegions: AWSRegions,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
ecr.PrintECR(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
|
||||
@@ -624,9 +689,10 @@ var (
|
||||
SecretsManagerClient: secretsManagerClient,
|
||||
SSMClient: ssmClient,
|
||||
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSRegions: AWSRegions,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
secrets.PrintSecrets(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
|
||||
@@ -634,32 +700,37 @@ var (
|
||||
fmt.Printf("[%s] %s\n", cyan(emoji.Sprintf(":fox:cloudfox :fox:")), green("IAM is complicated. Complicated usually means misconfigurations. You'll want to pay attention here."))
|
||||
principals := aws.IamPrincipalsModule{
|
||||
IAMClient: iamClient,
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
principals.PrintIamPrincipals(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
permissions := aws.IamPermissionsModule{
|
||||
IAMClient: iamClient,
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
permissions.PrintIamPermissions(AWSOutputFormat, AWSOutputDirectory, Verbosity, PermissionsPrincipal)
|
||||
accessKeys := aws.AccessKeysModule{
|
||||
IAMClient: iam.NewFromConfig(utils.AWSConfigFileLoader(AWSProfile)),
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
IAMClient: iam.NewFromConfig(AWSConfig),
|
||||
Caller: Caller,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
accessKeys.PrintAccessKeys(AccessKeysFilter, AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
inboundRoleTrusts := aws.RoleTrustsModule{
|
||||
IAMClient: iamClient,
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
inboundRoleTrusts.PrintRoleTrusts(AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
iamSimulator := aws.IamSimulatorModule{
|
||||
IAMClient: iamClient,
|
||||
Caller: utils.AWSWhoami(AWSProfile),
|
||||
Caller: Caller,
|
||||
AWSProfile: AWSProfile,
|
||||
Goroutines: Goroutines,
|
||||
}
|
||||
iamSimulator.PrintIamSimulator(SimulatorPrincipal, SimulatorAction, SimulatorResource, AWSOutputFormat, AWSOutputDirectory, Verbosity)
|
||||
|
||||
@@ -697,9 +768,10 @@ func init() {
|
||||
|
||||
// Global flags for the AWS modules
|
||||
AWSCommands.PersistentFlags().StringVarP(&AWSProfile, "profile", "p", "", "AWS CLI Profile Name")
|
||||
AWSCommands.PersistentFlags().StringVarP(&AWSOutputFormat, "output", "o", "all", "[\"table\" | \"csv\"]")
|
||||
AWSCommands.PersistentFlags().StringVarP(&AWSOutputFormat, "output", "o", "all", "[\"table\" | \"csv\" | \"all\" ]")
|
||||
AWSCommands.PersistentFlags().IntVarP(&Verbosity, "verbosity", "v", 1, "1 = Print control messages only\n2 = Print control messages, module output\n3 = Print control messages, module output, and loot file output\n")
|
||||
AWSCommands.PersistentFlags().StringVar(&AWSOutputDirectory, "outdir", ".", "Output Directory ")
|
||||
AWSCommands.PersistentFlags().IntVarP(&Goroutines, "max-goroutines", "g", 30, "Maximum number of concurrent goroutines")
|
||||
|
||||
AWSCommands.AddCommand(
|
||||
AllChecksCommand,
|
||||
@@ -718,6 +790,7 @@ func init() {
|
||||
FilesystemsCommand,
|
||||
BucketsCommand,
|
||||
PermissionsCommand,
|
||||
//RAMCommand,
|
||||
)
|
||||
|
||||
}
|
||||
+15
-9
@@ -3,12 +3,15 @@ package cli
|
||||
import (
|
||||
"github.com/BishopFox/cloudfox/azure"
|
||||
"github.com/BishopFox/cloudfox/utils"
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
AzOutputFormat string
|
||||
AzCommands = &cobra.Command{
|
||||
AzOutputFormat string
|
||||
AzOutputDirectory string
|
||||
AzVerbosity int
|
||||
AzCommands = &cobra.Command{
|
||||
Use: "azure",
|
||||
Aliases: []string{"az"},
|
||||
Long: `
|
||||
@@ -22,14 +25,15 @@ See \"Available Commands\" for Azure Modules`,
|
||||
|
||||
AzInstancesMapRGFilter string
|
||||
AzInstancesMapCommand = &cobra.Command{
|
||||
Use: "instances-map",
|
||||
Aliases: []string{"instances"},
|
||||
Use: "instances",
|
||||
Aliases: []string{"instances-map"},
|
||||
Short: `Enumerates compute instances for specified Resource Group`,
|
||||
Long: `
|
||||
Enumerates compute instances for specified Resource Group`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
color.Red("This subcommand is under development! Use at your own risk!")
|
||||
m := azure.InstancesMapModule{Scope: utils.AzGetScopeInformation()}
|
||||
m.InstancesMap(AzOutputFormat, AzInstancesMapRGFilter)
|
||||
m.InstancesMap(AzVerbosity, AzOutputFormat, AzOutputDirectory, AzInstancesMapRGFilter)
|
||||
},
|
||||
}
|
||||
AzUserFilter string
|
||||
@@ -40,13 +44,18 @@ Enumerates compute instances for specified Resource Group`,
|
||||
Long: `
|
||||
Display all role assignemts for all principals`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
color.Red("This subcommand is under development! Use at your own risk!")
|
||||
m := azure.RBACMapModule{Scope: utils.AzGetScopeInformation()}
|
||||
m.RBACMapModule(AzOutputFormat, AzUserFilter)
|
||||
m.RBACMap(AzVerbosity, AzOutputFormat, AzOutputDirectory, AzUserFilter)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Global flags for the Azure modules
|
||||
AzCommands.PersistentFlags().StringVarP(&AzOutputFormat, "output", "o", "all", "[\"table\" | \"csv\" | \"all\" ]")
|
||||
AzCommands.PersistentFlags().IntVarP(&AzVerbosity, "verbosity", "v", 1, "1 = Print control messages only\n2 = Print control messages, module output\n3 = Print control messages, module output, and loot file output\n")
|
||||
AzCommands.PersistentFlags().StringVar(&AzOutputDirectory, "outdir", "cloudfox-output", "Output Directory ")
|
||||
|
||||
// Instances Map Module Flags
|
||||
AzInstancesMapCommand.Flags().StringVarP(&AzInstancesMapRGFilter, "resource-group", "g", "all", "Name of Resource Group to query")
|
||||
@@ -54,8 +63,5 @@ func init() {
|
||||
// RBAC Map Module Flags
|
||||
AzRBACMapCommand.Flags().StringVarP(&AzUserFilter, "user", "u", "all", "Display name of user to query")
|
||||
|
||||
// Global flags for the AWS modules
|
||||
AzCommands.PersistentFlags().StringVarP(&AzOutputFormat, "output", "o", "table", "[\"table\" | \"csv\"]")
|
||||
|
||||
AzCommands.AddCommand(AzInstancesMapCommand, AzRBACMapCommand)
|
||||
}
|
||||
@@ -9,9 +9,8 @@ require (
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.4 // indirect
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
||||
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
|
||||
github.com/alexeyco/simpletable v1.0.0
|
||||
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.15
|
||||
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
|
||||
@@ -19,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.0
|
||||
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
|
||||
@@ -27,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.31.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
|
||||
@@ -40,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.0
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.19.9
|
||||
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
|
||||
|
||||
@@ -67,8 +67,6 @@ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUM
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/alexeyco/simpletable v1.0.0 h1:ZQ+LvJ4bmoeHb+dclF64d0LX+7QAi7awsfCrptZrpHk=
|
||||
github.com/alexeyco/simpletable v1.0.0/go.mod h1:VJWVTtGUnW7EKbMRH8cE13SigKGx/1fO2SeeOiGeBkk=
|
||||
github.com/aquasecurity/table v1.8.0 h1:9ntpSwrUfjrM6/YviArlx/ZBGd6ix8W+MtojQcM7tv0=
|
||||
github.com/aquasecurity/table v1.8.0/go.mod h1:eqOmvjjB7AhXFgFqpJUEE/ietg7RrMSJZXyTN8E/wZw=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg=
|
||||
@@ -77,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.15 h1:2sInOWGE4HV54R90Pj8QgqBBw3Qf1I0husqbqjPZzys=
|
||||
github.com/aws/aws-sdk-go-v2 v1.16.15/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=
|
||||
@@ -91,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.22 h1:pE27/u2A7JlwICjOvONQDob8PToShRTkuiUE74ymVWg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.22/go.mod h1:/vNv5Al0bpiF8YdX2Ov6Xy05VTiXsql94yUqJMYaj0w=
|
||||
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.16 h1:L5LKGHHXOl4t7+5QZMTl38GIzSAq07XUTRtEquiHGMA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.16/go.mod h1:62dsXI0BqTIGomDl8Hpm33dv0OntGaVblri3ZRParVQ=
|
||||
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=
|
||||
@@ -113,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.0 h1:k0c0qnCgLl42bNH0EAw34grtMGNnHVvWbsp4PtfLZNo=
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.17.0/go.mod h1:LjFcJ+skyeXY5+2SP7hEJ+QT8hA7lrV9dl/Tji14quI=
|
||||
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=
|
||||
@@ -129,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.31.0 h1:/Ap9LnJJzNGiI2zlXNqLoPxHWZoilETnAEEQe5eu6UU=
|
||||
github.com/aws/aws-sdk-go-v2/service/glue v1.31.0/go.mod h1:w+1dZ4Iz1ZwkATSlh9rKscmbQd7bOtNPN3z+ZYHyqjg=
|
||||
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.16 h1:WHwTHJ6MM47naw3C18z2+tg34D8e+cPc21ioyR0QjBQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.16/go.mod h1:KlvKBzHZmhZP7oWyrDy9zRC/PbG4WWGdL89/Tak1DKw=
|
||||
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=
|
||||
@@ -162,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.0 h1:lLluuhi5MhoJXkdbczuvA7sWZ0fUsVL6yw9VUkJW3X8=
|
||||
github.com/aws/aws-sdk-go-v2/service/sns v1.18.0/go.mod h1:eRg+KGyfKJDRMEkqKKRSQPPI4M410dmXV84G32KIILo=
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.19.9 h1:8Ea02xXIVE+N/y5oirWpKvMzFw20jk7mVP1o6nGXzN8=
|
||||
github.com/aws/aws-sdk-go-v2/service/sqs v1.19.9/go.mod h1:Zs7d4VbFN1P8i9PkoH2s5U7/tJVHYGctOn5ax296/CY=
|
||||
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=
|
||||
@@ -172,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=
|
||||
@@ -298,7 +311,6 @@ github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
@@ -314,7 +326,6 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
var (
|
||||
rootCmd = &cobra.Command{
|
||||
Use: os.Args[0],
|
||||
Version: "1.6.0",
|
||||
Version: "1.7.1",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+17
-2
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/aws/retry"
|
||||
@@ -42,10 +43,8 @@ func AWSConfigFileLoader(AWSProfile string) aws.Config {
|
||||
}
|
||||
|
||||
func AWSWhoami(awsProfile string) sts.GetCallerIdentityOutput {
|
||||
|
||||
// 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))
|
||||
CallerIdentity, err := STSService.GetCallerIdentity(context.TODO(), &sts.GetCallerIdentityInput{})
|
||||
if err != nil {
|
||||
@@ -103,3 +102,19 @@ func txtLogger() *logrus.Logger {
|
||||
|
||||
return txtLogger
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
+149
-122
@@ -1,17 +1,32 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/alexeyco/simpletable"
|
||||
"github.com/aquasecurity/table"
|
||||
"github.com/aws/smithy-go/ptr"
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/afero"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
type OutputData struct {
|
||||
Headers []*simpletable.Cell
|
||||
Body [][]interface{}
|
||||
// Used for file system mocking with Afero library. Set:
|
||||
// fileSystem = afero.NewOsFs() if not unit testing (code will use real file system) OR
|
||||
// fileSystem = afero.NewMemMapFs() for a mocked file system (when unit testing)
|
||||
var fileSystem = afero.NewOsFs()
|
||||
|
||||
// Color functions
|
||||
var cyan = color.New(color.FgCyan).SprintFunc()
|
||||
|
||||
// This struct is here to mantain compatibility with legacy cloudfox code
|
||||
type OutputData2 struct {
|
||||
Headers []string
|
||||
Body [][]string
|
||||
FilePath string
|
||||
FullFilename string
|
||||
CallingModule string
|
||||
@@ -19,128 +34,140 @@ type OutputData struct {
|
||||
Directory string
|
||||
}
|
||||
|
||||
func (o *OutputData) tableOutput() {
|
||||
path := filepath.Join(o.FilePath, "table")
|
||||
err := os.MkdirAll(path, os.ModePerm)
|
||||
var fullFilename string
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
if o.FullFilename == "" {
|
||||
fullFilename = filepath.Join(path, fmt.Sprintf("%s.txt", o.CallingModule))
|
||||
} else {
|
||||
fullFilename = o.FullFilename
|
||||
}
|
||||
table := simpletable.New()
|
||||
// verbosity = 1 (Output and loot printed to file).
|
||||
// verbosity = 2 (Output and loot printed to file, output printed screen).
|
||||
// verbosity = 3 (Output and loot printed to file and screen).
|
||||
// outputType = "table", "csv"
|
||||
func OutputSelector(verbosity int, outputType string, header []string, body [][]string, outputDirectory string, fileName string, callingModule string) {
|
||||
|
||||
// Populates table
|
||||
table.Header = &simpletable.Header{Cells: o.Headers}
|
||||
for _, row := range o.Body {
|
||||
var cells []*simpletable.Cell
|
||||
// Cells are built based on the number of table columns. That's why this for loop is necessary, so the columns are adjusted dinamically
|
||||
for i := range o.Headers {
|
||||
cells = append(
|
||||
cells,
|
||||
&simpletable.Cell{
|
||||
Text: fmt.Sprintf("%s", row[i]),
|
||||
Align: simpletable.AlignLeft,
|
||||
Span: 1,
|
||||
},
|
||||
)
|
||||
|
||||
}
|
||||
table.Body.Cells = append(table.Body.Cells, cells)
|
||||
switch verbosity {
|
||||
case 2:
|
||||
printTableToScreen(header, body)
|
||||
case 3:
|
||||
printTableToScreen(header, body)
|
||||
// Add writeLootToScreen function here
|
||||
}
|
||||
|
||||
// Draws table
|
||||
table.SetStyle(simpletable.StyleCompactLite)
|
||||
if o.Verbosity > 1 {
|
||||
fmt.Println()
|
||||
table.Println()
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
tableOut := fmt.Sprintf("%s\n\n", table.String())
|
||||
err = os.WriteFile(fullFilename, []byte(tableOut), 0644)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("[%s] Output written to [%s]\n", cyan(o.CallingModule), fullFilename)
|
||||
}
|
||||
|
||||
func (o *OutputData) csvOutput() {
|
||||
|
||||
path := filepath.Join(o.FilePath, "csv")
|
||||
err := os.MkdirAll(path, os.ModePerm)
|
||||
var fullFilename string
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
if o.FullFilename == "" {
|
||||
fullFilename = filepath.Join(path, fmt.Sprintf("%s.txt", o.CallingModule))
|
||||
} else {
|
||||
fullFilename = o.FullFilename
|
||||
}
|
||||
var out []byte
|
||||
for i, header := range o.Headers {
|
||||
if i == len(o.Headers)-1 {
|
||||
if o.Verbosity > 1 {
|
||||
fmt.Println(header.Text)
|
||||
}
|
||||
out = append(out, []byte(fmt.Sprintf(header.Text))...)
|
||||
} else {
|
||||
if o.Verbosity > 1 {
|
||||
fmt.Printf("%s, ", header.Text)
|
||||
}
|
||||
out = append(out, []byte(fmt.Sprintf("%s,", header.Text))...)
|
||||
}
|
||||
}
|
||||
out = append(out, "\n"...)
|
||||
for _, row := range o.Body {
|
||||
for j, column := range row {
|
||||
if j == len(row)-1 {
|
||||
if o.Verbosity > 1 {
|
||||
fmt.Println(column)
|
||||
}
|
||||
out = append(out, []byte(fmt.Sprintln(column))...)
|
||||
} else {
|
||||
if o.Verbosity > 1 {
|
||||
fmt.Printf("%s, ", column)
|
||||
}
|
||||
out = append(out, []byte(fmt.Sprintf("%s,", column))...)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
out = append(out, "\n"...)
|
||||
if o.Verbosity > 1 {
|
||||
fmt.Println()
|
||||
}
|
||||
err = os.WriteFile(fullFilename, out, 0644)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("[%s] Output written to [%s]\n", cyan(o.CallingModule), fullFilename)
|
||||
}
|
||||
|
||||
func (o *OutputData) OutputSelector(Type string) {
|
||||
|
||||
//fmt.Println()
|
||||
switch Type {
|
||||
switch outputType {
|
||||
case "table":
|
||||
o.tableOutput()
|
||||
outputFileTable := createOutputFile(
|
||||
ptr.String(filepath.Join(outputDirectory, "table")),
|
||||
ptr.String(fmt.Sprintf("%s.txt", fileName)),
|
||||
outputType,
|
||||
callingModule)
|
||||
printTableToFile(header, body, outputFileTable)
|
||||
fmt.Printf("[%s] Output written to [%s]\n", cyan(callingModule), outputFileTable.Name())
|
||||
// Add writeLootToFile function here
|
||||
|
||||
case "csv":
|
||||
o.csvOutput()
|
||||
case "all":
|
||||
o.tableOutput()
|
||||
o.csvOutput()
|
||||
case "both":
|
||||
o.tableOutput()
|
||||
o.csvOutput()
|
||||
outputFileCSV := createOutputFile(
|
||||
ptr.String(filepath.Join(outputDirectory, "csv")),
|
||||
ptr.String(fmt.Sprintf("%s.csv", fileName)),
|
||||
outputType,
|
||||
callingModule)
|
||||
printCSVtoFile(header, body, outputFileCSV)
|
||||
fmt.Printf("[%s] Output written to [%s]\n", cyan(callingModule), outputFileCSV.Name())
|
||||
|
||||
// Add writeLootToFile function here
|
||||
|
||||
default:
|
||||
log.Fatal("Please select a valid output type (\"table\", \"csv\", \"all\")")
|
||||
outputFileTable := createOutputFile(
|
||||
ptr.String(filepath.Join(outputDirectory, "table")),
|
||||
ptr.String(fmt.Sprintf("%s.txt", fileName)),
|
||||
outputType,
|
||||
callingModule)
|
||||
printTableToFile(header, body, outputFileTable)
|
||||
fmt.Printf("[%s] Output written to [%s]\n", cyan(callingModule), outputFileTable.Name())
|
||||
|
||||
outputFileCSV := createOutputFile(
|
||||
ptr.String(filepath.Join(outputDirectory, "csv")),
|
||||
ptr.String(fmt.Sprintf("%s.csv", fileName)),
|
||||
outputType,
|
||||
callingModule)
|
||||
printCSVtoFile(header, body, outputFileCSV)
|
||||
fmt.Printf("[%s] Output written to [%s]\n", cyan(callingModule), outputFileCSV.Name())
|
||||
// Add writeLootToFile function here
|
||||
}
|
||||
//fmt.Println()
|
||||
|
||||
}
|
||||
|
||||
func printCSVtoScreen(header []string, body [][]string) {
|
||||
csvWriter := csv.NewWriter(os.Stdout)
|
||||
csvWriter.Write(header)
|
||||
for _, row := range body {
|
||||
csvWriter.Write(row)
|
||||
}
|
||||
csvWriter.Flush()
|
||||
}
|
||||
|
||||
func printCSVtoFile(header []string, body [][]string, outputFile afero.File) {
|
||||
csvWriter := csv.NewWriter(outputFile)
|
||||
csvWriter.Write(header)
|
||||
for _, row := range body {
|
||||
csvWriter.Write(row)
|
||||
}
|
||||
csvWriter.Flush()
|
||||
}
|
||||
|
||||
func printTableToFile(header []string, body [][]string, outputFile afero.File) {
|
||||
t := table.New(outputFile)
|
||||
t.SetColumnMaxWidth(1000)
|
||||
t.SetHeaders(header...)
|
||||
t.AddRows(body...)
|
||||
t.SetRowLines(false)
|
||||
t.SetDividers(table.UnicodeRoundedDividers)
|
||||
t.Render()
|
||||
}
|
||||
|
||||
func printTableToScreen(header []string, body [][]string) {
|
||||
t := table.New(os.Stdout)
|
||||
// ColumnMaxWidth needs to be set as a large value so the table doesn't wrap.
|
||||
// If the table wraps it's hard to grep the output from the terminal.
|
||||
// TO-DO: add a flag to make this optional.
|
||||
t.SetColumnMaxWidth(1000)
|
||||
t.SetHeaders(header...)
|
||||
t.AddRows(body...)
|
||||
t.SetHeaderStyle(table.StyleBold)
|
||||
t.SetRowLines(false)
|
||||
t.SetLineStyle(table.StyleCyan)
|
||||
t.SetDividers(table.UnicodeRoundedDividers)
|
||||
t.Render()
|
||||
}
|
||||
|
||||
func getTerminalWidth() (int, error) {
|
||||
terminalFileDescriptor := int(os.Stdout.Fd())
|
||||
if term.IsTerminal(terminalFileDescriptor) {
|
||||
width, _, err := term.GetSize(terminalFileDescriptor)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("[-] Valid terminal but failed to read width")
|
||||
}
|
||||
return width, nil
|
||||
}
|
||||
return 0, fmt.Errorf("invalid terminal")
|
||||
}
|
||||
|
||||
// The Afero library enables file system mocking:
|
||||
// fileSystem = afero.NewOsFs() if not unit testing (real file system) OR
|
||||
// fileSystem = afero.NewMemMapFs() for a mocked file system (when unit testing)
|
||||
// outputDirectory = nil (creates the file in the current directory ".")
|
||||
func createOutputFile(outputDirectory *string, fileName *string, outputType string, callingModule string) afero.File {
|
||||
|
||||
if outputDirectory == nil {
|
||||
outputDirectory = ptr.String(".")
|
||||
}
|
||||
|
||||
if _, err := fileSystem.Stat(ptr.ToString(outputDirectory)); os.IsNotExist(err) {
|
||||
err = fileSystem.MkdirAll(ptr.ToString(outputDirectory), 0700)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
if fileName == nil {
|
||||
log.Fatalf("Error creating output file because no file name was specified")
|
||||
}
|
||||
outputFile, err := fileSystem.OpenFile(path.Join(ptr.ToString(outputDirectory), ptr.ToString(fileName)), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("error creating output file: %s", err)
|
||||
}
|
||||
//fmt.Printf("[%s] Creating output file: %s\n", cyan(callingModule), outputFile.Name())
|
||||
return outputFile
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/aquasecurity/table"
|
||||
"github.com/aws/smithy-go/ptr"
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/afero"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// Used for file system mocking with Afero library. Set:
|
||||
// fileSystem = afero.NewOsFs() if not unit testing (code will use real file system) OR
|
||||
// fileSystem = afero.NewMemMapFs() for a mocked file system (when unit testing)
|
||||
var fileSystem = afero.NewOsFs()
|
||||
|
||||
// Color functions
|
||||
var cyan = color.New(color.FgCyan).SprintFunc()
|
||||
|
||||
// This struct is here to mantain compatibility with legacy cloudfox code
|
||||
type OutputData2 struct {
|
||||
Headers []string
|
||||
Body [][]string
|
||||
FilePath string
|
||||
FullFilename string
|
||||
CallingModule string
|
||||
Verbosity int
|
||||
Directory string
|
||||
}
|
||||
|
||||
// verbosity = 1 (Output and loot printed to file).
|
||||
// verbosity = 2 (Output and loot printed to file, output printed screen).
|
||||
// verbosity = 3 (Output and loot printed to file and screen).
|
||||
// outputType = "table", "csv"
|
||||
func OutputSelector(verbosity int, outputType string, header []string, body [][]string, outputDirectory string, fileName string, callingModule string) {
|
||||
|
||||
switch verbosity {
|
||||
case 2:
|
||||
printTableToScreen(header, body)
|
||||
case 3:
|
||||
printTableToScreen(header, body)
|
||||
// Add writeLootToScreen function here
|
||||
}
|
||||
switch outputType {
|
||||
case "table":
|
||||
outputFileTable := createOutputFile(
|
||||
ptr.String(filepath.Join(outputDirectory, "table")),
|
||||
ptr.String(fmt.Sprintf("%s.txt", fileName)),
|
||||
outputType,
|
||||
callingModule)
|
||||
printTableToFile(header, body, outputFileTable)
|
||||
fmt.Printf("[%s] Output written to [%s]\n", cyan(callingModule), outputFileTable.Name())
|
||||
// Add writeLootToFile function here
|
||||
|
||||
case "csv":
|
||||
outputFileCSV := createOutputFile(
|
||||
ptr.String(filepath.Join(outputDirectory, "csv")),
|
||||
ptr.String(fmt.Sprintf("%s.csv", fileName)),
|
||||
outputType,
|
||||
callingModule)
|
||||
printCSVtoFile(header, body, outputFileCSV)
|
||||
fmt.Printf("[%s] Output written to [%s]\n", cyan(callingModule), outputFileCSV.Name())
|
||||
|
||||
// Add writeLootToFile function here
|
||||
|
||||
default:
|
||||
outputFileTable := createOutputFile(
|
||||
ptr.String(filepath.Join(outputDirectory, "table")),
|
||||
ptr.String(fmt.Sprintf("%s.txt", fileName)),
|
||||
outputType,
|
||||
callingModule)
|
||||
printTableToFile(header, body, outputFileTable)
|
||||
fmt.Printf("[%s] Output written to [%s]\n", cyan(callingModule), outputFileTable.Name())
|
||||
|
||||
outputFileCSV := createOutputFile(
|
||||
ptr.String(filepath.Join(outputDirectory, "csv")),
|
||||
ptr.String(fmt.Sprintf("%s.csv", fileName)),
|
||||
outputType,
|
||||
callingModule)
|
||||
printCSVtoFile(header, body, outputFileCSV)
|
||||
fmt.Printf("[%s] Output written to [%s]\n", cyan(callingModule), outputFileCSV.Name())
|
||||
// Add writeLootToFile function here
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func printCSVtoScreen(header []string, body [][]string) {
|
||||
csvWriter := csv.NewWriter(os.Stdout)
|
||||
csvWriter.Write(header)
|
||||
for _, row := range body {
|
||||
csvWriter.Write(row)
|
||||
}
|
||||
csvWriter.Flush()
|
||||
}
|
||||
|
||||
func printCSVtoFile(header []string, body [][]string, outputFile afero.File) {
|
||||
csvWriter := csv.NewWriter(outputFile)
|
||||
csvWriter.Write(header)
|
||||
for _, row := range body {
|
||||
csvWriter.Write(row)
|
||||
}
|
||||
csvWriter.Flush()
|
||||
}
|
||||
|
||||
func printTableToFile(header []string, body [][]string, outputFile afero.File) {
|
||||
t := table.New(outputFile)
|
||||
t.SetColumnMaxWidth(1000)
|
||||
t.SetHeaders(header...)
|
||||
t.AddRows(body...)
|
||||
t.SetRowLines(false)
|
||||
t.SetDividers(table.UnicodeRoundedDividers)
|
||||
t.Render()
|
||||
}
|
||||
|
||||
func printTableToScreen(header []string, body [][]string) {
|
||||
t := table.New(os.Stdout)
|
||||
// ColumnMaxWidth needs to be set as a large value so the table doesn't wrap.
|
||||
// If the table wraps it's hard to grep the output from the terminal.
|
||||
// TO-DO: add a flag to make this optional.
|
||||
t.SetColumnMaxWidth(1000)
|
||||
t.SetHeaders(header...)
|
||||
t.AddRows(body...)
|
||||
t.SetHeaderStyle(table.StyleBold)
|
||||
t.SetRowLines(false)
|
||||
t.SetLineStyle(table.StyleCyan)
|
||||
t.SetDividers(table.UnicodeRoundedDividers)
|
||||
t.Render()
|
||||
}
|
||||
|
||||
func getTerminalWidth() (int, error) {
|
||||
terminalFileDescriptor := int(os.Stdout.Fd())
|
||||
if term.IsTerminal(terminalFileDescriptor) {
|
||||
width, _, err := term.GetSize(terminalFileDescriptor)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("[-] Valid terminal but failed to read width")
|
||||
}
|
||||
return width, nil
|
||||
}
|
||||
return 0, fmt.Errorf("invalid terminal")
|
||||
}
|
||||
|
||||
// The Afero library enables file system mocking:
|
||||
// fileSystem = afero.NewOsFs() if not unit testing (real file system) OR
|
||||
// fileSystem = afero.NewMemMapFs() for a mocked file system (when unit testing)
|
||||
// outputDirectory = nil (creates the file in the current directory ".")
|
||||
func createOutputFile(outputDirectory *string, fileName *string, outputType string, callingModule string) afero.File {
|
||||
|
||||
if outputDirectory == nil {
|
||||
outputDirectory = ptr.String(".")
|
||||
}
|
||||
|
||||
if _, err := fileSystem.Stat(ptr.ToString(outputDirectory)); os.IsNotExist(err) {
|
||||
err = fileSystem.MkdirAll(ptr.ToString(outputDirectory), 0700)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
if fileName == nil {
|
||||
log.Fatalf("Error creating output file because no file name was specified")
|
||||
}
|
||||
outputFile, err := fileSystem.OpenFile(path.Join(ptr.ToString(outputDirectory), ptr.ToString(fileName)), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("error creating output file: %s", err)
|
||||
}
|
||||
//fmt.Printf("[%s] Creating output file: %s\n", cyan(callingModule), outputFile.Name())
|
||||
return outputFile
|
||||
}
|
||||
@@ -64,7 +64,7 @@ func TestOutputSelector(t *testing.T) {
|
||||
outputType: "csv",
|
||||
outputDirectory: "cloudfox-output",
|
||||
fileNameExtension: ".csv",
|
||||
callingModule: "calling_module__6",
|
||||
callingModule: "calling_module_6",
|
||||
},
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user