-
Notifications
You must be signed in to change notification settings - Fork 125
feat: Adding federated identity credentials #164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
dadab60
e013481
b043349
9a124ca
a45f5dc
f1b9e39
4b30027
e8c5062
506de58
0332e9a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| // Copyright (C) 2022 Specter Ops, Inc. | ||
| // | ||
| // This file is part of AzureHound. | ||
| // | ||
| // AzureHound is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, either version 3 of the License, or | ||
| // (at your option) any later version. | ||
| // | ||
| // AzureHound is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License for more details. | ||
| // | ||
| // You should have received a copy of the GNU General Public License | ||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
|
||
| package cmd | ||
|
|
||
| import ( | ||
| "context" | ||
| "os" | ||
| "os/signal" | ||
| "sync" | ||
| "time" | ||
|
Comment on lines
+20
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: On Unix-like systems, Also, in Go What to do instead (graceful shutdown)Use a catchable signal like ctx, stop := signal.NotifyContext(context.Background(),
os.Interrupt, syscall.SIGTERM,
)
defer stop()
<-ctx.Done() // start shutdown/cleanupThen, if you need a “hard stop” fallback, do it from outside the process (service manager / supervisor): send Sources: Go [1] https://pkg.go.dev/os/signal 🌐 Web query:
💡 Result: In Go, you typically implement “graceful shutdown” by catching Why this works
Minimal pattern (recommended):
|
||
|
|
||
| "github.com/bloodhoundad/azurehound/v2/client" | ||
| "github.com/bloodhoundad/azurehound/v2/client/query" | ||
| "github.com/bloodhoundad/azurehound/v2/config" | ||
| "github.com/bloodhoundad/azurehound/v2/enums" | ||
| "github.com/bloodhoundad/azurehound/v2/models" | ||
| "github.com/bloodhoundad/azurehound/v2/panicrecovery" | ||
| "github.com/bloodhoundad/azurehound/v2/pipeline" | ||
| "github.com/spf13/cobra" | ||
| ) | ||
|
|
||
| func init() { | ||
| listRootCmd.AddCommand(listAppFICCmd) | ||
| } | ||
|
|
||
| var listAppFICCmd = &cobra.Command{ | ||
| Use: "appfics", | ||
| Long: "Lists Entra ID Application Federated Identity Credentials", | ||
| Run: listAppFICsCmdImpl, | ||
| SilenceUsage: true, | ||
| } | ||
|
|
||
| func listAppFICsCmdImpl(cmd *cobra.Command, args []string) { | ||
| ctx, stop := signal.NotifyContext(cmd.Context(), os.Interrupt, os.Kill) | ||
| defer gracefulShutdown(stop) | ||
|
|
||
| log.V(1).Info("testing connections") | ||
| azClient := connectAndCreateClient() | ||
| log.Info("collecting azure app federated identity credentials...") | ||
| start := time.Now() | ||
| stream := listAppFICs(ctx, azClient, listApps(ctx, azClient)) | ||
| panicrecovery.HandleBubbledPanic(ctx, stop, log) | ||
| outputStream(ctx, stream) | ||
| duration := time.Since(start) | ||
| log.Info("collection completed", "duration", duration.String()) | ||
| } | ||
|
|
||
| func listAppFICs(ctx context.Context, client client.AzureClient, apps <-chan azureWrapper[models.App]) <-chan azureWrapper[models.AppFICs] { | ||
| var ( | ||
| out = make(chan azureWrapper[models.AppFICs]) | ||
| streams = pipeline.Demux(ctx.Done(), apps, config.ColStreamCount.Value().(int)) | ||
| wg sync.WaitGroup | ||
| params = query.GraphParams{} | ||
| ) | ||
|
|
||
| wg.Add(len(streams)) | ||
| for i := range streams { | ||
| stream := streams[i] | ||
| go func() { | ||
| defer panicrecovery.PanicRecovery() | ||
| defer wg.Done() | ||
| for app := range stream { | ||
| var ( | ||
| data = models.AppFICs{ | ||
| AppId: app.Data.AppId, | ||
| } | ||
| count = 0 | ||
| ) | ||
| for item := range client.ListAzureADAppFICs(ctx, app.Data.Id, params) { | ||
| if item.Error != nil { | ||
| log.Error(item.Error, "unable to continue processing federated identity credentials for this app", "appId", app.Data.AppId) | ||
| } else { | ||
| appFIC := models.AppFIC{ | ||
| FIC: item.Ok, | ||
| AppId: app.Data.AppId, | ||
| } | ||
| log.V(2).Info("found app FIC", "appFic", appFIC) | ||
| count++ | ||
| data.FICs = append(data.FICs, appFIC) | ||
| } | ||
| } | ||
|
|
||
| if data.FICs != nil { | ||
| if ok := pipeline.Send(ctx.Done(), out, NewAzureWrapper( | ||
| enums.KindAZFederatedIdentityCredential, | ||
| data, | ||
| )); !ok { | ||
| return | ||
| } | ||
| } | ||
| log.V(1).Info("finished listing app fics", "appId", app.Data.AppId, "count", count) | ||
| } | ||
| }() | ||
| } | ||
|
|
||
| go func() { | ||
| wg.Wait() | ||
| close(out) | ||
| log.Info("finished listing all app fics") | ||
| }() | ||
|
|
||
| return out | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,48 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Copyright (C) 2022 Specter Ops, Inc. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This file is part of AzureHound. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // AzureHound is free software: you can redistribute it and/or modify | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // it under the terms of the GNU General Public License as published by | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // the Free Software Foundation, either version 3 of the License, or | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // (at your option) any later version. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // AzureHound is distributed in the hope that it will be useful, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // GNU General Public License for more details. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // You should have received a copy of the GNU General Public License | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| package models | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "encoding/json" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| type AppFIC struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| FIC json.RawMessage `json:"fic"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| AppId string `json:"appId"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (s *AppFIC) MarshalJSON() ([]byte, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| output := make(map[string]any) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| output["appId"] = s.AppId | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if s.FIC == nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil, nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if fic, err := OmitEmpty(s.FIC); err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil, err | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| output["fic"] = fic | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return json.Marshal(output) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
29
to
42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard empty
🛠️ Proposed fix func (s *AppFIC) MarshalJSON() ([]byte, error) {
output := make(map[string]any)
output["appId"] = s.AppId
+ if len(s.FIC) == 0 {
+ return json.Marshal(output)
+ }
+
if fic, err := OmitEmpty(s.FIC); err != nil {
return nil, err
} else {
output["fic"] = fic
return json.Marshal(output)
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| type AppFICs struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| FICs []AppFIC `json:"fics"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| AppId string `json:"appId"` | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.