mirror of
https://github.com/anotherhadi/github-recon.git
synced 2026-10-05 19:08:24 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c460e5c24f | ||
|
|
e572d1326a | ||
|
|
0017d649d3 | ||
|
|
c163fb4ecd | ||
|
|
325397dfef | ||
|
|
dab0ba2b38 | ||
|
|
98769561c3 | ||
|
|
c76953882c | ||
|
|
94fd83ecce |
+10
-2
@@ -1,6 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
recon_email "github.com/anotherhadi/github-recon/github-recon/email"
|
||||
recon_username "github.com/anotherhadi/github-recon/github-recon/username"
|
||||
github_recon_settings "github.com/anotherhadi/github-recon/settings"
|
||||
@@ -8,7 +10,10 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
settings := github_recon_settings.GetSettings()
|
||||
settings, err := github_recon_settings.GetSettings()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if !settings.Silent {
|
||||
utils.Header()
|
||||
@@ -23,7 +28,10 @@ func main() {
|
||||
}
|
||||
|
||||
if settings.TargetType == github_recon_settings.TargetUsername {
|
||||
result := recon_username.Username(settings)
|
||||
result, err := recon_username.Username(settings)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
writeJson(settings, result)
|
||||
} else {
|
||||
result := recon_email.Email(settings)
|
||||
|
||||
@@ -13,7 +13,7 @@ type EmailResult struct {
|
||||
TargetType github_recon_settings.TargetType
|
||||
|
||||
Commits CommitsResult
|
||||
Spoofing SpoofingResult
|
||||
Spoofing *SpoofingResult
|
||||
}
|
||||
|
||||
func Email(settings github_recon_settings.Settings) EmailResult {
|
||||
@@ -33,7 +33,7 @@ func Email(settings github_recon_settings.Settings) EmailResult {
|
||||
} else {
|
||||
utils.PrintTitle(settings.Silent, "🎭 Spoofing test")
|
||||
result.Spoofing = Spoofing(settings)
|
||||
if result.Spoofing.AvatarURL != "" {
|
||||
if result.Spoofing != nil && result.Spoofing.AvatarURL != "" {
|
||||
utils.PrintAvatar(settings, result.Spoofing.AvatarURL)
|
||||
}
|
||||
utils.PrintStruct(settings, result.Spoofing, 0)
|
||||
|
||||
@@ -2,6 +2,7 @@ package recon
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
github_recon_settings "github.com/anotherhadi/github-recon/settings"
|
||||
"github.com/anotherhadi/github-recon/utils"
|
||||
@@ -26,7 +27,8 @@ func RandomString(n int) string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func Spoofing(s github_recon_settings.Settings) (response SpoofingResult) {
|
||||
func Spoofing(s github_recon_settings.Settings) (response *SpoofingResult) {
|
||||
response = &SpoofingResult{}
|
||||
name := "gh-recon-spoofing-" + RandomString(8)
|
||||
private := true
|
||||
autoInit := true
|
||||
@@ -41,6 +43,13 @@ func Spoofing(s github_recon_settings.Settings) (response SpoofingResult) {
|
||||
}
|
||||
utils.WaitForRateLimit(s, resp)
|
||||
|
||||
defer func() {
|
||||
_, err = s.Client.Repositories.Delete(s.Ctx, repo.Owner.GetLogin(), name)
|
||||
if err != nil {
|
||||
s.Logger.Error("Error while deleting repo", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
branch := repo.GetDefaultBranch()
|
||||
if branch == "" {
|
||||
branch = "main"
|
||||
@@ -57,7 +66,6 @@ func Spoofing(s github_recon_settings.Settings) (response SpoofingResult) {
|
||||
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)
|
||||
@@ -65,7 +73,6 @@ func Spoofing(s github_recon_settings.Settings) (response SpoofingResult) {
|
||||
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)
|
||||
@@ -81,7 +88,6 @@ func Spoofing(s github_recon_settings.Settings) (response SpoofingResult) {
|
||||
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)
|
||||
@@ -90,31 +96,35 @@ func Spoofing(s github_recon_settings.Settings) (response SpoofingResult) {
|
||||
_, 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)
|
||||
|
||||
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
|
||||
const maxRetries = 5
|
||||
const retryDelay = 2 * time.Second
|
||||
for i := 0; i < maxRetries; i++ {
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
if len(commits) > 1 {
|
||||
last := commits[0]
|
||||
response.Username = last.GetAuthor().GetLogin()
|
||||
response.Name = last.GetAuthor().GetName()
|
||||
response.Email = last.GetAuthor().GetEmail()
|
||||
response.Url = last.GetAuthor().GetHTMLURL()
|
||||
response.AvatarURL = last.GetAuthor().GetAvatarURL()
|
||||
break
|
||||
}
|
||||
|
||||
s.Logger.Info("Only one commit found, retrying...", "attempt", i+1)
|
||||
time.Sleep(retryDelay)
|
||||
}
|
||||
|
||||
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()
|
||||
response.AvatarURL = last.GetAuthor().GetAvatarURL()
|
||||
if response.Username == "" && response.Name == "" && response.Email == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = s.Client.Repositories.Delete(s.Ctx, repo.Owner.GetLogin(), name)
|
||||
if err != nil {
|
||||
s.Logger.Error("Error while deleting repo", "err", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ func DeepScan(s github_recon_settings.Settings) (response DeepScanResult) {
|
||||
}
|
||||
|
||||
repositories = append(repositories, Repositorie{
|
||||
Repository: repo.GetCloneURL(),
|
||||
Repository: repo.GetHTMLURL(),
|
||||
Owner: repo.GetOwner().GetLogin(),
|
||||
Name: repo.GetName(),
|
||||
Size: repo.GetSize(),
|
||||
@@ -139,6 +139,11 @@ func DeepScan(s github_recon_settings.Settings) (response DeepScanResult) {
|
||||
}
|
||||
s.Logger.Info("Cloned all repositories", "path", tmp_folder)
|
||||
|
||||
if len(repositories) == 0 {
|
||||
s.Logger.Info("No repositories found for the user, skipping deep scan.")
|
||||
return
|
||||
}
|
||||
|
||||
authorOccurrences := Authors{}
|
||||
mapAuthorToIndex := make(map[string]int)
|
||||
for _, repo := range repositories {
|
||||
@@ -202,7 +207,7 @@ func DeepScan(s github_recon_settings.Settings) (response DeepScanResult) {
|
||||
Name: authorName,
|
||||
Email: authorEmail,
|
||||
FoundIn: []string{repoIdentifier},
|
||||
Levenshtein: utils.LevenshteinDistance(s.Target, authorName),
|
||||
Levenshtein: levenshteinDistanceAuthor(s.Target, authorName, authorEmail),
|
||||
})
|
||||
mapAuthorToIndex[trimmedLine] = len(authorOccurrences) - 1
|
||||
}
|
||||
@@ -380,3 +385,10 @@ func findEmailsAndOccurrencesInDir(rootPath string, username string) (Emails, er
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func levenshteinDistanceAuthor(target, name, email string) int {
|
||||
if strings.Contains(email, "@") {
|
||||
email = strings.SplitN(email, "@", 2)[0]
|
||||
}
|
||||
return slices.Min([]int{utils.LevenshteinDistance(target, name), utils.LevenshteinDistance(target, email)})
|
||||
}
|
||||
|
||||
@@ -27,16 +27,21 @@ type UsernameResult struct {
|
||||
DeepScan DeepScanResult
|
||||
}
|
||||
|
||||
func Username(settings github_recon_settings.Settings) UsernameResult {
|
||||
|
||||
result := UsernameResult{
|
||||
func Username(settings github_recon_settings.Settings) (result UsernameResult, err error) {
|
||||
result = UsernameResult{
|
||||
Target: settings.Target,
|
||||
TargetType: settings.TargetType,
|
||||
DateTime: time.Now().String(),
|
||||
}
|
||||
|
||||
utils.PrintTitle(settings.Silent, "👤 User informations")
|
||||
result.User = User(settings)
|
||||
result.User, err = User(settings)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if result.User == (UserResult{}) {
|
||||
return
|
||||
}
|
||||
utils.PrintAvatar(settings, result.User.AvatarURL)
|
||||
utils.PrintStruct(settings, result.User, 0)
|
||||
|
||||
@@ -74,5 +79,5 @@ func Username(settings github_recon_settings.Settings) UsernameResult {
|
||||
utils.PrintStruct(settings, result.DeepScan, 0)
|
||||
}
|
||||
|
||||
return result
|
||||
return
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func Orgs(s github_recon_settings.Settings) (response OrgsResult) {
|
||||
for _, org := range orgs {
|
||||
o := OrgResult{
|
||||
Name: org.GetLogin(),
|
||||
URL: org.GetURL(),
|
||||
URL: org.GetHTMLURL(),
|
||||
Description: org.GetDescription(),
|
||||
}
|
||||
response = append(response, o)
|
||||
|
||||
@@ -31,13 +31,13 @@ type UserResult struct {
|
||||
Plan string
|
||||
}
|
||||
|
||||
func User(s github_recon_settings.Settings) (response UserResult) {
|
||||
func User(s github_recon_settings.Settings) (response UserResult, err error) {
|
||||
user, resp, err := s.Client.Users.Get(s.Ctx, s.Target)
|
||||
if resp.StatusCode == 404 {
|
||||
s.Logger.Fatal("User not found with username")
|
||||
return UserResult{}, nil
|
||||
}
|
||||
if err != nil {
|
||||
s.Logger.Fatal("Failed to fetch user's information", "err", err)
|
||||
return UserResult{}, fmt.Errorf("failed to fetch user's information")
|
||||
}
|
||||
|
||||
u := UserResult{
|
||||
@@ -65,5 +65,5 @@ func User(s github_recon_settings.Settings) (response UserResult) {
|
||||
}
|
||||
|
||||
utils.WaitForRateLimit(s, resp)
|
||||
return u
|
||||
return u, nil
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ func GetDefaultSettings() Settings {
|
||||
}
|
||||
}
|
||||
|
||||
func GetSettings() (settings Settings) {
|
||||
func GetSettings() (settings Settings, err error) {
|
||||
settings = GetDefaultSettings()
|
||||
//// Flag settings
|
||||
flag.Usage = func() {
|
||||
@@ -152,7 +152,7 @@ func GetSettings() (settings Settings) {
|
||||
settings.Target = strings.TrimPrefix(settings.Target, "@") // Remove the @ of the username
|
||||
|
||||
if strings.Contains(settings.Target, " ") {
|
||||
settings.Logger.Fatal("Target cannot contain spaces")
|
||||
err = fmt.Errorf("target cannot contain spaces")
|
||||
}
|
||||
|
||||
if strings.Contains(settings.Target, "@") {
|
||||
|
||||
+3
-1
@@ -57,7 +57,9 @@ func PrintStruct(settings github_recon_settings.Settings, s any, indent int) {
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
field := t.Field(i).Name
|
||||
value := v.Field(i)
|
||||
|
||||
if !value.CanInterface() {
|
||||
continue
|
||||
}
|
||||
if !value.IsValid() || (value.Kind() == reflect.String && value.String() == "") {
|
||||
continue
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user