mirror of
https://github.com/BishopFox/cloudfox.git
synced 2026-11-07 09:56:01 -10:00
Have global data in dom's graph format now. just need to write the table creation code
This commit is contained in:
5 files changed
+600
-52
No files matched your search
+197
-8
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
ingestor "github.com/BishopFox/cloudfox/aws/graph/ingester"
|
||||
@@ -12,8 +13,10 @@ import (
|
||||
"github.com/BishopFox/cloudfox/internal"
|
||||
"github.com/BishopFox/cloudfox/internal/aws/policy"
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/iam/types"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sts"
|
||||
"github.com/bishopfox/knownawsaccountslookup"
|
||||
"github.com/dominikbraun/graph"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -45,6 +48,28 @@ type GraphCommand struct {
|
||||
modLog *logrus.Entry
|
||||
}
|
||||
|
||||
type GraphCommand2 struct {
|
||||
|
||||
// General configuration data
|
||||
Caller sts.GetCallerIdentityOutput
|
||||
AWSRegions []string
|
||||
Goroutines int
|
||||
AWSProfile string
|
||||
WrapTable bool
|
||||
AWSOutputType string
|
||||
AWSTableCols string
|
||||
Verbosity int
|
||||
AWSOutputDirectory string
|
||||
AWSConfig aws.Config
|
||||
Version string
|
||||
SkipAdminCheck bool
|
||||
|
||||
GlobalGraph graph.Graph[string, string]
|
||||
|
||||
output internal.OutputData2
|
||||
modLog *logrus.Entry
|
||||
}
|
||||
|
||||
func (m *GraphCommand) RunGraphCommand() {
|
||||
|
||||
// These struct values are used by the output module
|
||||
@@ -167,6 +192,57 @@ func (m *GraphCommand) RunGraphCommand() {
|
||||
|
||||
}
|
||||
|
||||
func (m *GraphCommand2) RunGraphCommand2() {
|
||||
|
||||
// These struct values are used by the output module
|
||||
m.output.Verbosity = m.Verbosity
|
||||
m.output.Directory = m.AWSOutputDirectory
|
||||
m.output.CallingModule = "graph"
|
||||
m.modLog = internal.TxtLog.WithFields(logrus.Fields{
|
||||
"module": m.output.CallingModule,
|
||||
})
|
||||
if m.AWSProfile == "" {
|
||||
m.AWSProfile = internal.BuildAWSPath(m.Caller)
|
||||
}
|
||||
m.output.FilePath = filepath.Join(m.AWSOutputDirectory, "cloudfox-output", "aws", fmt.Sprintf("%s-%s", m.AWSProfile, aws.ToString(m.Caller.Account)))
|
||||
|
||||
m.output.Headers = []string{
|
||||
"Account",
|
||||
"Principal Arn",
|
||||
"IsAdmin?",
|
||||
"CanPrivEscToAdmin?",
|
||||
}
|
||||
|
||||
// If the user specified table columns, use those.
|
||||
// If the user specified -o wide, use the wide default cols for this module.
|
||||
// Otherwise, use the hardcoded default cols for this module.
|
||||
var tableCols []string
|
||||
// If the user specified table columns, use those.
|
||||
if m.AWSTableCols != "" {
|
||||
// If the user specified wide as the output format, use these columns.
|
||||
// remove any spaces between any commas and the first letter after the commas
|
||||
m.AWSTableCols = strings.ReplaceAll(m.AWSTableCols, ", ", ",")
|
||||
m.AWSTableCols = strings.ReplaceAll(m.AWSTableCols, ", ", ",")
|
||||
tableCols = strings.Split(m.AWSTableCols, ",")
|
||||
} else if m.AWSOutputType == "wide" {
|
||||
tableCols = []string{
|
||||
"Account",
|
||||
"Principal Arn",
|
||||
"IsAdmin?",
|
||||
"CanPrivEscToAdmin?",
|
||||
}
|
||||
// Otherwise, use the default columns.
|
||||
} else {
|
||||
tableCols = []string{
|
||||
"Principal Arn",
|
||||
"IsAdmin?",
|
||||
"CanPrivEscToAdmin?",
|
||||
}
|
||||
}
|
||||
internal.TxtLog.Debug("tableCols: ", tableCols)
|
||||
|
||||
}
|
||||
|
||||
func (m *GraphCommand) collectAccountDataForGraph() []models.Account {
|
||||
//OrganizationsCommandClient := InitOrgClient(m.AWSConfig)
|
||||
var accounts []models.Account
|
||||
@@ -257,9 +333,9 @@ func (m *GraphCommand) collectAccountDataForGraph() []models.Account {
|
||||
func (m *GraphCommand) collectRoleDataForGraph() []models.Role {
|
||||
var isAdmin, canPrivEscToAdmin string
|
||||
|
||||
iamClient := InitIAMClient(m.AWSConfig)
|
||||
iamSimClient := InitIamCommandClient(iamClient, m.Caller, m.AWSProfile, m.Goroutines)
|
||||
localAdminMap := make(map[string]bool)
|
||||
// iamClient := InitIAMClient(m.AWSConfig)
|
||||
// iamSimClient := InitIamCommandClient(iamClient, m.Caller, m.AWSProfile, m.Goroutines)
|
||||
// localAdminMap := make(map[string]bool)
|
||||
|
||||
var roles []models.Role
|
||||
IAMCommandClient := InitIAMClient(m.AWSConfig)
|
||||
@@ -276,11 +352,11 @@ func (m *GraphCommand) collectRoleDataForGraph() []models.Role {
|
||||
break
|
||||
}
|
||||
|
||||
if m.pmapperError == nil {
|
||||
isAdmin, canPrivEscToAdmin = GetPmapperResults(m.SkipAdminCheck, m.pmapperMod, role.Arn)
|
||||
} else {
|
||||
isAdmin, canPrivEscToAdmin = GetIamSimResult(m.SkipAdminCheck, role.Arn, iamSimClient, localAdminMap)
|
||||
}
|
||||
// if m.pmapperError == nil {
|
||||
// isAdmin, canPrivEscToAdmin = GetPmapperResults(m.SkipAdminCheck, m.pmapperMod, role.Arn)
|
||||
// } else {
|
||||
// isAdmin, canPrivEscToAdmin = GetIamSimResult(m.SkipAdminCheck, role.Arn, iamSimClient, localAdminMap)
|
||||
// }
|
||||
|
||||
// for _, row := range m.PermissionRowsFromAllProfiles {
|
||||
// if row.Arn == aws.ToString(role.Arn) {
|
||||
@@ -414,3 +490,116 @@ func (m *GraphCommand) collectRoleDataForGraph() []models.Role {
|
||||
}
|
||||
return roles
|
||||
}
|
||||
|
||||
func ConvertIAMRoleToModelRole(role types.Role, vendors *knownawsaccountslookup.Vendors) models.Role {
|
||||
var isAdmin, canPrivEscToAdmin string
|
||||
|
||||
accountId := strings.Split(aws.ToString(role.Arn), ":")[4]
|
||||
trustsdoc, err := policy.ParseRoleTrustPolicyDocument(role)
|
||||
if err != nil {
|
||||
internal.TxtLog.Error(err.Error())
|
||||
return models.Role{}
|
||||
}
|
||||
|
||||
var TrustedPrincipals []models.TrustedPrincipal
|
||||
var TrustedServices []models.TrustedService
|
||||
var TrustedFederatedProviders []models.TrustedFederatedProvider
|
||||
//var TrustedFederatedSubjects string
|
||||
var trustedProvider string
|
||||
var trustedSubjects string
|
||||
var vendorName string
|
||||
|
||||
for _, statement := range trustsdoc.Statement {
|
||||
for _, principal := range statement.Principal.AWS {
|
||||
if strings.Contains(principal, ":root") {
|
||||
//check to see if the accountID is known
|
||||
accountID := strings.Split(principal, ":")[4]
|
||||
vendorName = vendors.GetVendorNameFromAccountID(accountID)
|
||||
}
|
||||
|
||||
TrustedPrincipals = append(TrustedPrincipals, models.TrustedPrincipal{
|
||||
TrustedPrincipal: principal,
|
||||
ExternalID: statement.Condition.StringEquals.StsExternalID,
|
||||
VendorName: vendorName,
|
||||
//IsAdmin: false,
|
||||
//CanPrivEscToAdmin: false,
|
||||
})
|
||||
|
||||
}
|
||||
for _, service := range statement.Principal.Service {
|
||||
TrustedServices = append(TrustedServices, models.TrustedService{
|
||||
TrustedService: service,
|
||||
AccountID: accountId,
|
||||
//IsAdmin: false,
|
||||
//CanPrivEscToAdmin: false,
|
||||
})
|
||||
|
||||
}
|
||||
for _, federated := range statement.Principal.Federated {
|
||||
if statement.Condition.StringLike.TokenActionsGithubusercontentComAud != "" || len(statement.Condition.StringLike.TokenActionsGithubusercontentComSub) > 0 {
|
||||
trustedProvider = "GitHub"
|
||||
trustedSubjects := strings.Join(statement.Condition.StringLike.TokenActionsGithubusercontentComSub, ",")
|
||||
if trustedSubjects == "" {
|
||||
trustedSubjects = "ALL REPOS!!!"
|
||||
} else {
|
||||
trustedSubjects = "Repos: " + trustedSubjects
|
||||
}
|
||||
|
||||
} else if statement.Condition.StringEquals.SAMLAud == "https://signin.aws.amazon.com/saml" {
|
||||
if strings.Contains(statement.Principal.Federated[0], "AWSSSO") {
|
||||
trustedProvider = "AWS SSO" // (" + statement.Principal.Federated[0] + ")"
|
||||
} else if strings.Contains(statement.Principal.Federated[0], "Okta") {
|
||||
trustedProvider = "Okta" // (" + statement.Principal.Federated[0] + ")"
|
||||
}
|
||||
trustedSubjects = "Not applicable"
|
||||
} else if statement.Condition.StringEquals.OidcEksAud != "" || statement.Condition.StringEquals.OidcEksSub != "" || statement.Condition.StringLike.OidcEksAud != "" || statement.Condition.StringLike.OidcEksSub != "" {
|
||||
trustedProvider = "EKS" // (" + statement.Principal.Federated[0] + ")"
|
||||
if statement.Condition.StringEquals.OidcEksSub != "" {
|
||||
trustedSubjects = statement.Condition.StringEquals.OidcEksSub
|
||||
} else if statement.Condition.StringLike.OidcEksSub != "" {
|
||||
trustedSubjects = statement.Condition.StringLike.OidcEksSub
|
||||
} else {
|
||||
trustedSubjects = "ALL SERVICE ACCOUNTS!"
|
||||
}
|
||||
} else if statement.Principal.Federated[0] == "cognito-identity.amazonaws.com" {
|
||||
trustedProvider = "Cognito" // (" + statement.Principal.Federated[0] + ")"
|
||||
if statement.Condition.ForAnyValueStringLike.CognitoAMR != "" {
|
||||
trustedSubjects = statement.Condition.ForAnyValueStringLike.CognitoAMR
|
||||
}
|
||||
} else {
|
||||
if trustedProvider == "" && strings.Contains(statement.Principal.Federated[0], "oidc.eks") {
|
||||
trustedProvider = "EKS" // (" + statement.Principal.Federated[0] + ")"
|
||||
trustedSubjects = "ALL SERVICE ACCOUNTS!"
|
||||
} else if trustedProvider == "" && strings.Contains(statement.Principal.Federated[0], "AWSSSO") {
|
||||
trustedProvider = "AWS SSO" // (" + statement.Principal.Federated[0] + ")"
|
||||
}
|
||||
trustedSubjects = "Not applicable"
|
||||
}
|
||||
|
||||
TrustedFederatedProviders = append(TrustedFederatedProviders, models.TrustedFederatedProvider{
|
||||
TrustedFederatedProvider: federated,
|
||||
ProviderShortName: trustedProvider,
|
||||
TrustedSubjects: trustedSubjects,
|
||||
//IsAdmin: false,
|
||||
//CanPrivEscToAdmin: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//create new object of type models.Role
|
||||
cfRole := models.Role{
|
||||
Id: aws.ToString(role.Arn),
|
||||
AccountID: accountId,
|
||||
ARN: aws.ToString(role.Arn),
|
||||
Name: aws.ToString(role.RoleName),
|
||||
TrustsDoc: trustsdoc,
|
||||
TrustedPrincipals: TrustedPrincipals,
|
||||
TrustedServices: TrustedServices,
|
||||
TrustedFederatedProviders: TrustedFederatedProviders,
|
||||
CanPrivEscToAdmin: canPrivEscToAdmin,
|
||||
IsAdmin: isAdmin,
|
||||
}
|
||||
//roles = append(roles, role)
|
||||
|
||||
return cfRole
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/BishopFox/cloudfox/aws/graph/ingester/schema"
|
||||
"github.com/BishopFox/cloudfox/internal/aws/policy"
|
||||
"github.com/BishopFox/cloudfox/internal/common"
|
||||
"github.com/dominikbraun/graph"
|
||||
)
|
||||
|
||||
type Role struct {
|
||||
@@ -22,7 +23,8 @@ type Role struct {
|
||||
IsAdmin string
|
||||
IdValue string
|
||||
IsAdminP bool
|
||||
PathToAdmin bool
|
||||
PathToAdminSameAccount bool
|
||||
PathToAdminCrossAccount bool
|
||||
}
|
||||
|
||||
type TrustedPrincipal struct {
|
||||
@@ -113,6 +115,7 @@ func (a *Role) MakeRelationships() []schema.Relationship {
|
||||
TargetLabel: schema.Role,
|
||||
RelationshipType: schema.CanAssume,
|
||||
})
|
||||
|
||||
// make a CAN_BE_ASSUMED_BY relationship between this role and the trusted principal
|
||||
// relationships = append(relationships, schema.Relationship{
|
||||
// SourceNodeID: a.Id,
|
||||
@@ -326,3 +329,246 @@ func (a *Role) MakeRelationships() []schema.Relationship {
|
||||
|
||||
return relationships
|
||||
}
|
||||
|
||||
// func (a *Role) MakeVertex() {
|
||||
|
||||
// // make a vertex for this role as populate all of the data in the Role struct as attributes
|
||||
// err := GlobalGraph.AddVertex(
|
||||
// a.Id,
|
||||
// graph.VertexAttribute("Name", a.Name),
|
||||
// graph.VertexAttribute("Type", "Role"),
|
||||
// graph.VertexAttribute("AccountID", a.AccountID),
|
||||
// graph.VertexAttribute("ARN", a.ARN),
|
||||
// graph.VertexAttribute("CanPrivEscToAdmin", a.CanPrivEscToAdmin),
|
||||
// graph.VertexAttribute("IsAdmin", a.IsAdmin),
|
||||
// graph.VertexAttribute("IdValue", a.IdValue),
|
||||
// graph.VertexAttribute("IsAdminP", a.IsAdminP),
|
||||
// graph.VertexAttribute("PathToAdminSameAccount", a.PathToAdminSameAccount),
|
||||
// graph.VertexAttribute("PathToAdminCrossAccount", a.PathToAdminCrossAccount),
|
||||
// )
|
||||
|
||||
// }
|
||||
|
||||
func (a *Role) MakeEdges(GlobalGraph graph.Graph[string, string]) []schema.Relationship {
|
||||
var relationships []schema.Relationship
|
||||
|
||||
// get thisAccount id from role arn
|
||||
var thisAccount string
|
||||
if len(a.ARN) >= 25 {
|
||||
thisAccount = a.ARN[13:25]
|
||||
} else {
|
||||
fmt.Sprintf("Could not get account number from this role arn%s", a.ARN)
|
||||
}
|
||||
|
||||
for _, TrustedPrincipal := range a.TrustedPrincipals {
|
||||
//get account id from the trusted principal arn
|
||||
var trustedPrincipalAccount string
|
||||
if len(TrustedPrincipal.TrustedPrincipal) >= 25 {
|
||||
trustedPrincipalAccount = TrustedPrincipal.TrustedPrincipal[13:25]
|
||||
} else {
|
||||
fmt.Sprintf("Could not get account number from this TrustedPrincipal%s", TrustedPrincipal.TrustedPrincipal)
|
||||
}
|
||||
var PermissionsRowAccount string
|
||||
|
||||
// if the role trusts a principal in this same account explicitly, then the principal can assume the role
|
||||
if thisAccount == trustedPrincipalAccount {
|
||||
// make a CAN_ASSUME relationship between the trusted principal and this role
|
||||
|
||||
err := GlobalGraph.AddEdge(
|
||||
TrustedPrincipal.TrustedPrincipal,
|
||||
a.Id,
|
||||
graph.EdgeAttribute("AssumeRole", "Same account explicit trust"),
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println(TrustedPrincipal.TrustedPrincipal + a.Id + "Same account explicit trust")
|
||||
}
|
||||
}
|
||||
|
||||
// If the role trusts a principal in this account or another account using the :root notation, then we need to iterate over all of the rows in AllPermissionsRows to find the principals that have sts:AssumeRole permissions on this role
|
||||
// if the role we are looking at trusts root in it's own account
|
||||
|
||||
if strings.Contains(TrustedPrincipal.TrustedPrincipal, fmt.Sprintf("%s:root", thisAccount)) {
|
||||
err := GlobalGraph.AddVertex(
|
||||
a.Id,
|
||||
graph.VertexAttribute("Name", a.Name),
|
||||
graph.VertexAttribute("Type", "Account"),
|
||||
graph.VertexAttribute("AccountID", a.AccountID),
|
||||
)
|
||||
if err != nil {
|
||||
if err == graph.ErrVertexAlreadyExists {
|
||||
fmt.Println(a.Id + " already exists")
|
||||
}
|
||||
}
|
||||
// iterate over all rows in AllPermissionsRows
|
||||
for _, PermissionsRow := range common.PermissionRowsFromAllProfiles {
|
||||
// but we only care about the rows that have arns that are in this account
|
||||
|
||||
if len(PermissionsRow.Arn) >= 25 {
|
||||
PermissionsRowAccount = PermissionsRow.Arn[13:25]
|
||||
} else {
|
||||
fmt.Sprintf("Could not get account number from this PermissionsRow%s", PermissionsRow.Arn)
|
||||
}
|
||||
|
||||
if PermissionsRowAccount == thisAccount {
|
||||
// lets only look for rows that have sts:AssumeRole permissions
|
||||
if strings.EqualFold(PermissionsRow.Action, "sts:AssumeRole") ||
|
||||
strings.EqualFold(PermissionsRow.Action, "*") ||
|
||||
strings.EqualFold(PermissionsRow.Action, "sts:Assume*") ||
|
||||
strings.EqualFold(PermissionsRow.Action, "sts:*") {
|
||||
// lets only focus on rows that have an effect of Allow
|
||||
if strings.EqualFold(PermissionsRow.Effect, "Allow") {
|
||||
// if the resource is * or the resource is this role arn, then this principal can assume this role
|
||||
if PermissionsRow.Resource == "*" || strings.Contains(PermissionsRow.Resource, a.ARN) {
|
||||
// make a CAN_ASSUME relationship between the trusted principal and this role
|
||||
//evalutate if the princiapl is a user or a role and set a variable accordingly
|
||||
//var principalType schema.NodeLabel
|
||||
if strings.EqualFold(PermissionsRow.Type, "User") {
|
||||
err := GlobalGraph.AddEdge(
|
||||
PermissionsRow.Arn,
|
||||
a.Id,
|
||||
graph.EdgeAttribute("AssumeRole", "Same account root trust and trusted principal has permission to assume role"),
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println(PermissionsRow.Arn + a.Id + "Same account root trust and trusted principal has permission to assume role")
|
||||
}
|
||||
} else if strings.EqualFold(PermissionsRow.Type, "Role") {
|
||||
err := GlobalGraph.AddEdge(
|
||||
PermissionsRow.Arn,
|
||||
a.Id,
|
||||
graph.EdgeAttribute("AssumeRole", "Same account root trust and trusted principal has permission to assume role"),
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println(PermissionsRow.Arn + a.Id + "Same account root trust and trusted principal has permission to assume role")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if strings.Contains(TrustedPrincipal.TrustedPrincipal, ":root") && TrustedPrincipal.VendorName != "" {
|
||||
err := GlobalGraph.AddVertex(
|
||||
a.Id,
|
||||
graph.VertexAttribute("Name", a.Name),
|
||||
graph.VertexAttribute("Type", "Account"),
|
||||
graph.VertexAttribute("AccountID", a.AccountID),
|
||||
graph.VertexAttribute("VendorName", TrustedPrincipal.VendorName),
|
||||
)
|
||||
if err != nil {
|
||||
if err == graph.ErrVertexAlreadyExists {
|
||||
fmt.Println(a.Id + " already exists")
|
||||
}
|
||||
}
|
||||
// If the role trusts :root in another account and the trusted principal is a vendor, we will make a relationship between our role and a vendor node instead of a principal node
|
||||
relationships = append(relationships, schema.Relationship{
|
||||
SourceNodeID: TrustedPrincipal.TrustedPrincipal,
|
||||
TargetNodeID: a.Id,
|
||||
SourceLabel: schema.Vendor,
|
||||
TargetLabel: schema.Role,
|
||||
RelationshipType: schema.CanAssume,
|
||||
})
|
||||
err = GlobalGraph.AddEdge(
|
||||
//TrustedPrincipal.TrustedPrincipal,
|
||||
TrustedPrincipal.VendorName,
|
||||
a.Id,
|
||||
graph.EdgeAttribute("VendorAssumeRole", "Cross account root trust and trusted principal is a vendor"),
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println(TrustedPrincipal.VendorName + a.Id + "Cross account root trust and trusted principal is a vendor")
|
||||
|
||||
}
|
||||
} else if strings.Contains(TrustedPrincipal.TrustedPrincipal, fmt.Sprintf("%s:root", trustedPrincipalAccount)) {
|
||||
err := GlobalGraph.AddVertex(
|
||||
a.Id,
|
||||
graph.VertexAttribute("Name", a.Name),
|
||||
graph.VertexAttribute("Type", "Account"),
|
||||
graph.VertexAttribute("AccountID", a.AccountID),
|
||||
)
|
||||
if err != nil {
|
||||
if err == graph.ErrVertexAlreadyExists {
|
||||
fmt.Println(a.Id + " already exists")
|
||||
}
|
||||
}
|
||||
// iterate over all rows in AllPermissionsRows
|
||||
for _, PermissionsRow := range common.PermissionRowsFromAllProfiles {
|
||||
// but we only care about the rows that have arns that are in this other account
|
||||
if len(PermissionsRow.Arn) >= 25 {
|
||||
PermissionsRowAccount = PermissionsRow.Arn[13:25]
|
||||
} else {
|
||||
fmt.Sprintf("Could not get account number from this PermissionsRow%s", PermissionsRow.Arn)
|
||||
}
|
||||
if PermissionsRowAccount == trustedPrincipalAccount {
|
||||
// lets only look for rows that have sts:AssumeRole permissions
|
||||
if strings.EqualFold(PermissionsRow.Action, "sts:AssumeRole") ||
|
||||
strings.EqualFold(PermissionsRow.Action, "*") ||
|
||||
strings.EqualFold(PermissionsRow.Action, "sts:Assume*") ||
|
||||
strings.EqualFold(PermissionsRow.Action, "sts:*") {
|
||||
// lets only focus on rows that have an effect of Allow
|
||||
if strings.EqualFold(PermissionsRow.Effect, "Allow") {
|
||||
// if the resource is * or the resource is this role arn, then this principal can assume this role
|
||||
if PermissionsRow.Resource == "*" || strings.Contains(PermissionsRow.Resource, a.ARN) {
|
||||
// make a CAN_ASSUME relationship between the trusted principal and this role
|
||||
|
||||
if strings.EqualFold(PermissionsRow.Type, "User") {
|
||||
err := GlobalGraph.AddEdge(
|
||||
PermissionsRow.Arn,
|
||||
a.Id,
|
||||
graph.EdgeAttribute("CrossAccountAssumeRole", "Cross account root trust and trusted principal has permission to assume role"),
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println(PermissionsRow.Arn + a.Id + "Cross account root trust and trusted principal has permission to assume role")
|
||||
}
|
||||
|
||||
} else if strings.EqualFold(PermissionsRow.Type, "Role") {
|
||||
err := GlobalGraph.AddEdge(
|
||||
PermissionsRow.Arn,
|
||||
a.Id,
|
||||
graph.EdgeAttribute("CrossAccountAssumeRole", "Cross account root trust and trusted principal has permission to assume role"),
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println(PermissionsRow.Arn + a.Id + "Cross account root trust and trusted principal has permission to assume role")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// pmapper takes care of this part so commenting out for now - but leaving as a placeholder
|
||||
|
||||
// for _, TrustedService := range a.TrustedServices {
|
||||
// // make relationship from trusted service to this role of type can assume
|
||||
// // make relationship from this role to trusted service of type can be assumed by
|
||||
// }
|
||||
|
||||
for _, TrustedFederatedProvider := range a.TrustedFederatedProviders {
|
||||
// make relationship from trusted federated provider to this role of type can assume
|
||||
|
||||
GlobalGraph.AddVertex(
|
||||
TrustedFederatedProvider.TrustedFederatedProvider,
|
||||
graph.VertexAttribute("Type", "FederatedIdentity"),
|
||||
)
|
||||
err := GlobalGraph.AddEdge(
|
||||
TrustedFederatedProvider.TrustedFederatedProvider,
|
||||
a.Id,
|
||||
graph.EdgeAttribute("FederatedAssumeRole", "Trusted federated provider"),
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println(TrustedFederatedProvider.TrustedFederatedProvider + a.Id + "Trusted federated provider")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return relationships
|
||||
}
|
||||
+33
-1
@@ -115,7 +115,39 @@ func (m *PmapperModule) createAndPopulateGraph() graph.Graph[string, string] {
|
||||
}
|
||||
|
||||
for _, edge := range m.Edges {
|
||||
_ = pmapperGraph.AddEdge(edge.Source, edge.Destination)
|
||||
err := pmapperGraph.AddEdge(
|
||||
edge.Source,
|
||||
edge.Destination,
|
||||
graph.EdgeAttribute(edge.ShortReason, edge.Reason),
|
||||
)
|
||||
if err != nil {
|
||||
if err == graph.ErrEdgeAlreadyExists {
|
||||
// update the ege by copying the existing graph.Edge with attributes and add the new attributes
|
||||
//fmt.Println("Edge already exists, but adding a new one!")
|
||||
|
||||
// get the existing edge
|
||||
existingEdge, _ := pmapperGraph.Edge(edge.Source, edge.Destination)
|
||||
// get the map of attributes
|
||||
existingProperties := existingEdge.Properties
|
||||
// add the new attributes to attributes map within the properties struct
|
||||
// Check if the Attributes map is initialized, if not, initialize it
|
||||
if existingProperties.Attributes == nil {
|
||||
existingProperties.Attributes = make(map[string]string)
|
||||
}
|
||||
|
||||
// Add or update the attribute
|
||||
existingProperties.Attributes[edge.ShortReason] = edge.Reason
|
||||
//Update the edge
|
||||
pmapperGraph.UpdateEdge(
|
||||
edge.Source,
|
||||
edge.Destination,
|
||||
graph.EdgeAttributes(existingProperties.Attributes),
|
||||
)
|
||||
|
||||
}
|
||||
//fmt.Println(edge.Reason)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//internal.Cache.Set(cacheKey, pmapperGraph, cache.DefaultExpiration)
|
||||
|
||||
+123
-38
@@ -8,6 +8,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/BishopFox/cloudfox/aws"
|
||||
"github.com/BishopFox/cloudfox/aws/graph/ingester/schema/models"
|
||||
"github.com/BishopFox/cloudfox/aws/sdk"
|
||||
"github.com/BishopFox/cloudfox/internal"
|
||||
"github.com/BishopFox/cloudfox/internal/common"
|
||||
@@ -60,6 +61,8 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/sqs"
|
||||
"github.com/aws/aws-sdk-go-v2/service/ssm"
|
||||
"github.com/aws/smithy-go/ptr"
|
||||
"github.com/bishopfox/knownawsaccountslookup"
|
||||
"github.com/dominikbraun/graph"
|
||||
"github.com/fatih/color"
|
||||
"github.com/kyokomi/emoji"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -932,31 +935,12 @@ func runFilesystemsCommand(cmd *cobra.Command, args []string) {
|
||||
}
|
||||
|
||||
func runGraphCommand(cmd *cobra.Command, args []string) {
|
||||
for _, profile := range AWSProfiles {
|
||||
//var AWSConfig = internal.AWSConfigFileLoader(profile, cmd.Root().Version)
|
||||
caller, err := internal.AWSWhoami(profile, cmd.Root().Version, AWSMFAToken)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
//instantiate a permissions client and populate the permissions data
|
||||
fmt.Println("Getting GAAD for " + profile)
|
||||
PermissionsCommandClient := aws.InitPermissionsClient(*caller, profile, cmd.Root().Version, Goroutines, AWSMFAToken)
|
||||
PermissionsCommandClient.GetGAAD()
|
||||
PermissionsCommandClient.ParsePermissions("")
|
||||
common.PermissionRowsFromAllProfiles = append(common.PermissionRowsFromAllProfiles, PermissionsCommandClient.Rows...)
|
||||
}
|
||||
|
||||
// for _, profile := range AWSProfiles {
|
||||
// caller, err := internal.AWSWhoami(profile, cmd.Root().Version, AWSMFAToken)
|
||||
// if err != nil {
|
||||
// continue
|
||||
// }
|
||||
|
||||
// fmt.PrintLn("Importing Pmapper for " + profile)
|
||||
|
||||
// }
|
||||
GlobalGraph := graph.New(graph.StringHash, graph.Directed())
|
||||
//var PermissionRowsFromAllProfiles []common.PermissionsRow
|
||||
var GlobalRoles []models.Role
|
||||
|
||||
vendors := knownawsaccountslookup.NewVendorMap()
|
||||
vendors.PopulateKnownAWSAccounts()
|
||||
for _, profile := range AWSProfiles {
|
||||
var AWSConfig = internal.AWSConfigFileLoader(profile, cmd.Root().Version, AWSMFAToken)
|
||||
caller, err := internal.AWSWhoami(profile, cmd.Root().Version, AWSMFAToken)
|
||||
@@ -964,22 +948,123 @@ func runGraphCommand(cmd *cobra.Command, args []string) {
|
||||
continue
|
||||
}
|
||||
|
||||
graphCommandClient := aws.GraphCommand{
|
||||
Caller: *caller,
|
||||
AWSProfile: profile,
|
||||
Goroutines: Goroutines,
|
||||
AWSRegions: internal.GetEnabledRegions(profile, cmd.Root().Version, AWSMFAToken),
|
||||
WrapTable: AWSWrapTable,
|
||||
AWSOutputType: AWSOutputType,
|
||||
AWSTableCols: AWSTableCols,
|
||||
AWSOutputDirectory: AWSOutputDirectory,
|
||||
Verbosity: Verbosity,
|
||||
AWSConfig: AWSConfig,
|
||||
Version: cmd.Root().Version,
|
||||
SkipAdminCheck: AWSSkipAdminCheck,
|
||||
//Gather all Permissions data
|
||||
fmt.Println("Getting GAAD for " + profile)
|
||||
PermissionsCommandClient := aws.InitPermissionsClient(*caller, profile, cmd.Root().Version, Goroutines, AWSMFAToken)
|
||||
PermissionsCommandClient.GetGAAD()
|
||||
PermissionsCommandClient.ParsePermissions("")
|
||||
common.PermissionRowsFromAllProfiles = append(common.PermissionRowsFromAllProfiles, PermissionsCommandClient.Rows...)
|
||||
|
||||
// Gather all Pmapper data
|
||||
fmt.Println("Importing Pmapper for " + profile)
|
||||
pmapperMod, pmapperError := aws.InitPmapperGraph(*caller, AWSProfile, Goroutines)
|
||||
if pmapperError != nil {
|
||||
fmt.Println("Error importing pmapper data: " + pmapperError.Error())
|
||||
}
|
||||
graphCommandClient.RunGraphCommand()
|
||||
for _, node := range pmapperMod.Nodes {
|
||||
GlobalGraph.AddVertex(node.Arn)
|
||||
}
|
||||
for _, edge := range pmapperMod.Edges {
|
||||
err := GlobalGraph.AddEdge(
|
||||
edge.Source,
|
||||
edge.Destination,
|
||||
graph.EdgeAttribute(edge.ShortReason, edge.Reason),
|
||||
)
|
||||
if err != nil {
|
||||
if err == graph.ErrEdgeAlreadyExists {
|
||||
// update the ege by copying the existing graph.Edge with attributes and add the new attributes
|
||||
//fmt.Println("Edge already exists")
|
||||
|
||||
// get the existing edge
|
||||
existingEdge, _ := GlobalGraph.Edge(edge.Source, edge.Destination)
|
||||
// get the map of attributes
|
||||
existingProperties := existingEdge.Properties
|
||||
// add the new attributes to attributes map within the properties struct
|
||||
// Check if the Attributes map is initialized, if not, initialize it
|
||||
if existingProperties.Attributes == nil {
|
||||
existingProperties.Attributes = make(map[string]string)
|
||||
}
|
||||
|
||||
// Add or update the attribute
|
||||
existingProperties.Attributes[edge.ShortReason] = edge.Reason
|
||||
GlobalGraph.UpdateEdge(
|
||||
edge.Source,
|
||||
edge.Destination,
|
||||
graph.EdgeAttributes(existingProperties.Attributes),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Gather all role data
|
||||
fmt.Println("Getting Roles for " + profile)
|
||||
IAMCommandClient := aws.InitIAMClient(AWSConfig)
|
||||
ListRolesOutput, err := sdk.CachedIamListRoles(IAMCommandClient, ptr.ToString(caller.Account))
|
||||
if err != nil {
|
||||
internal.TxtLog.Error(err)
|
||||
}
|
||||
for _, role := range ListRolesOutput {
|
||||
modelRole := aws.ConvertIAMRoleToModelRole(role, vendors)
|
||||
GlobalRoles = append(GlobalRoles, modelRole)
|
||||
}
|
||||
//making edges
|
||||
fmt.Println("Making edges for " + profile)
|
||||
for _, role := range GlobalRoles {
|
||||
role.MakeEdges(GlobalGraph)
|
||||
}
|
||||
|
||||
// print how many nodes and edges are in the graph to the screen and exit
|
||||
|
||||
for _, profile := range AWSProfiles {
|
||||
var AWSConfig = internal.AWSConfigFileLoader(profile, cmd.Root().Version, AWSMFAToken)
|
||||
caller, err := internal.AWSWhoami(profile, cmd.Root().Version, AWSMFAToken)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// graphCommandClient := aws.GraphCommand{
|
||||
// Caller: *caller,
|
||||
// AWSProfile: profile,
|
||||
// Goroutines: Goroutines,
|
||||
// AWSRegions: internal.GetEnabledRegions(profile, cmd.Root().Version, AWSMFAToken),
|
||||
// WrapTable: AWSWrapTable,
|
||||
// AWSOutputType: AWSOutputType,
|
||||
// AWSTableCols: AWSTableCols,
|
||||
// AWSOutputDirectory: AWSOutputDirectory,
|
||||
// Verbosity: Verbosity,
|
||||
// AWSConfig: AWSConfig,
|
||||
// Version: cmd.Root().Version,
|
||||
// SkipAdminCheck: AWSSkipAdminCheck,
|
||||
// }
|
||||
// graphCommandClient.RunGraphCommand()
|
||||
|
||||
graphCommandClient := aws.GraphCommand2{
|
||||
Caller: *caller,
|
||||
AWSProfile: profile,
|
||||
Goroutines: Goroutines,
|
||||
AWSRegions: internal.GetEnabledRegions(profile, cmd.Root().Version, AWSMFAToken),
|
||||
WrapTable: AWSWrapTable,
|
||||
AWSOutputType: AWSOutputType,
|
||||
AWSTableCols: AWSTableCols,
|
||||
AWSOutputDirectory: AWSOutputDirectory,
|
||||
Verbosity: Verbosity,
|
||||
AWSConfig: AWSConfig,
|
||||
Version: cmd.Root().Version,
|
||||
SkipAdminCheck: AWSSkipAdminCheck,
|
||||
GlobalGraph: GlobalGraph,
|
||||
}
|
||||
|
||||
graphCommandClient.RunGraphCommand2()
|
||||
}
|
||||
|
||||
}
|
||||
// // print the edges to the screen
|
||||
// edges, _ := GlobalGraph.Edges()
|
||||
// for _, edge := range edges {
|
||||
// fmt.Println(edge)
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
func runIamSimulatorCommand(cmd *cobra.Command, args []string) {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package common
|
||||
|
||||
import "github.com/dominikbraun/graph"
|
||||
|
||||
type PermissionsRow struct {
|
||||
AWSService string
|
||||
Type string
|
||||
@@ -17,5 +15,3 @@ type PermissionsRow struct {
|
||||
}
|
||||
|
||||
var PermissionRowsFromAllProfiles []PermissionsRow
|
||||
|
||||
var GlobalGraph graph.Graph[string, string]
|
||||
Reference in new issue
Block a user