diff --git a/cmd/email.go b/cmd/email.go index d6cfb8e..e934230 100644 --- a/cmd/email.go +++ b/cmd/email.go @@ -10,7 +10,8 @@ type EmailResult struct { Target string TargetType github_recon_settings.TargetType - Commit recon.CommitsResult + Commits recon.CommitsResult + Spoofing recon.SpoofingResult } func email(settings github_recon_settings.Settings, datetime string) { @@ -21,6 +22,10 @@ func email(settings github_recon_settings.Settings, datetime string) { } printTitle(settings.Silent, "👤 Commits author") - result.Commit = recon.Email(settings) - printStruct(settings, result.Commit, 0) + result.Commits = recon.Commits(settings) + printStruct(settings, result.Commits, 0) + + printTitle(settings.Silent, "🎭 Spoofing test") + result.Spoofing = recon.Spoofing(settings) + printStruct(settings, result.Spoofing, 0) } diff --git a/cmd/print.go b/cmd/print.go index f2ccd10..3b78db9 100644 --- a/cmd/print.go +++ b/cmd/print.go @@ -42,6 +42,8 @@ func printStruct(settings github_recon_settings.Settings, s any, indent int) { return } + printed := 0 + for i := 0; i < v.NumField(); i++ { field := t.Field(i).Name value := v.Field(i) @@ -55,6 +57,7 @@ func printStruct(settings github_recon_settings.Settings, s any, indent int) { if (field == "FirstFoundIn" || field == "FoundIn") && !settings.ShowSource { continue } + printed++ switch value.Kind() { case reflect.Struct, reflect.Slice, reflect.Array, reflect.Ptr: @@ -66,6 +69,9 @@ func printStruct(settings github_recon_settings.Settings, s any, indent int) { fmt.Printf("%s%s %s\n", prefix, greyStyle.Render(field+":"), greenStyle.Render(fmt.Sprintf("%v", value.Interface()))) } } + if printed == 0 { + fmt.Println(prefix + greyStyle.Render("No data found")) + } fmt.Println("") case reflect.Slice, reflect.Array: diff --git a/github-recon/email/spoofing.go b/github-recon/email/spoofing.go index 5c98f35..58b9375 100644 --- a/github-recon/email/spoofing.go +++ b/github-recon/email/spoofing.go @@ -1 +1,120 @@ package recon + +import ( + "math/rand" + + github_recon_settings "github.com/anotherhadi/github-recon/settings" + "github.com/anotherhadi/github-recon/utils" + "github.com/google/go-github/v72/github" +) + +type SpoofingResult struct { + Username string + Name string + Email string + Url string +} + +func RandomString(n int) string { + letters := []rune("abcdefghijklmnopqrstuvwxyz") + + b := make([]rune, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return string(b) +} + +func Spoofing(s github_recon_settings.Settings) (response SpoofingResult) { + // 1) Create repo (auto-init pour avoir une branche par défaut) + name := "gh-recon-spoofing-" + RandomString(8) + private := true + autoInit := true + repo, resp, err := s.Client.Repositories.Create(s.Ctx, "", &github.Repository{ + Name: &name, + Private: &private, + AutoInit: &autoInit, + }) + if err != nil { + s.Logger.Error("Error while creating repo", "err", err) + return + } + utils.WaitForRateLimit(s, resp) + + // branche par défaut (fallback "main") + branch := repo.GetDefaultBranch() + if branch == "" { + branch = "main" + } + refName := "heads/" + branch + + // 2) Commit vide avec auteur spoofé (tree identique au parent) + author := &github.CommitAuthor{ + Name: github.String("Spoofed Name"), + Email: github.String(s.Target), + } + + ref, resp, err := s.Client.Git.GetRef(s.Ctx, repo.Owner.GetLogin(), name, refName) + if err != nil { + s.Logger.Error("Error while getting ref", "err", err) + s.Logger.Warn("The temp repo was left undeleted", "repo", repo.GetHTMLURL()) + return + } + utils.WaitForRateLimit(s, resp) + + parentCommit, resp, err := s.Client.Git.GetCommit(s.Ctx, repo.Owner.GetLogin(), name, ref.GetObject().GetSHA()) + if err != nil { + s.Logger.Error("Error while getting parent commit", "err", err) + s.Logger.Warn("The temp repo was left undeleted", "repo", repo.GetHTMLURL()) + return + } + utils.WaitForRateLimit(s, resp) + + commit := &github.Commit{ + Author: author, + Message: github.String("Spoofed empty commit"), + Tree: &github.Tree{SHA: parentCommit.Tree.SHA}, // même tree => commit vide + Parents: []*github.Commit{parentCommit}, + } + + newCommit, resp, err := s.Client.Git.CreateCommit(s.Ctx, repo.Owner.GetLogin(), name, commit, nil) + if err != nil { + s.Logger.Error("Error while creating spoofed empty commit", "err", err) + s.Logger.Warn("The temp repo was left undeleted", "repo", repo.GetHTMLURL()) + return + } + utils.WaitForRateLimit(s, resp) + + ref.Object.SHA = newCommit.SHA + _, resp, err = s.Client.Git.UpdateRef(s.Ctx, repo.Owner.GetLogin(), name, ref, false) + if err != nil { + s.Logger.Error("Error while updating ref to spoofed commit", "err", err) + s.Logger.Warn("The temp repo was left undeleted", "repo", repo.GetHTMLURL()) + return + } + utils.WaitForRateLimit(s, resp) + + // 3) Get user + commits, _, err := s.Client.Repositories.ListCommits(s.Ctx, repo.Owner.GetLogin(), name, nil) + if err != nil { + s.Logger.Error("Error while listing commits", "err", err) + s.Logger.Warn("The temp repo was left undeleted", "repo", repo.GetHTMLURL()) + return + } + + if len(commits) > 0 { + last := commits[0] + response.Username = last.GetAuthor().GetLogin() + response.Name = last.GetAuthor().GetName() + response.Email = last.GetAuthor().GetEmail() + response.Url = last.GetAuthor().GetHTMLURL() + } + + // 4) Cleanup + _, err = s.Client.Repositories.Delete(s.Ctx, repo.Owner.GetLogin(), name) + if err != nil { + s.Logger.Error("Error while deleting repo", "err", err) + } + + return +} diff --git a/utils/utils.go b/utils/utils.go index b597859..bb4a4f6 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -110,7 +110,7 @@ func LevenshteinDistance(s1, s2 string) int { } func SkipResult(name, email string) bool { - if name == "github-actions[bot]" || name == "dependabot[bot]" || name == "github-actions" { + if name == "github-actions[bot]" || name == "dependabot[bot]" || name == "github-actions" || name == "GitHub Actions" { return true } if email == "github-actions[bot]@users.noreply.github.com" || email == "noreply@github.com" ||