Moodle: possible to bypass timer in timed assignments
Description
An issue in Moodle’s timed assignment feature allowed students to bypass the time restriction, potentially giving them more time than allowed to complete an assessment.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Moodle's timed assignment feature allowed students to bypass time restrictions, potentially gaining extra time to complete assessments.
The timed assignment feature in Moodle failed to properly enforce time limits, allowing students to bypass the restriction and potentially gain additional time to complete assessments [1][2]. The root cause was that the timer did not correctly check whether a student had clicked the 'Begin assignment' button to start the timer; the timer could start automatically when the page was loaded, or the student could manipulate the request to delay or avoid starting the timer [3].
To exploit this vulnerability, an authenticated student with access to a timed assignment could send a request that omits the begin parameter, preventing the timer from starting. The fix adds a check for this parameter, ensuring the timer only starts when the student explicitly clicks the 'Begin assignment' button [3]. No further authentication or network position is required beyond a standard student account.
An attacker could gain additional time to complete an assessment, potentially exceeding the allowed time limit and gaining an unfair advantage over other students. This could affect the integrity of assessments and grading [1][2].
Moodle has addressed this issue in a commit that modifies the get_group_submission and get_user_submission functions to require the begin parameter [3]. Administrators should update Moodle to the latest version that includes this patch. No workarounds other than updating are recommended.
AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
moodle/moodlePackagist | >= 5.0.0-beta, < 5.0.3 | 5.0.3 |
moodle/moodlePackagist | >= 4.5.0-beta, < 4.5.7 | 4.5.7 |
moodle/moodlePackagist | >= 4.2.0-beta, < 4.4.11 | 4.4.11 |
moodle/moodlePackagist | < 4.1.21 | 4.1.21 |
Affected products
2Patches
178a3fe6c6186MDL-75087 mod_assign: Respect assignment timer
3 files changed · +131 −10
public/mod/assign/classes/output/user_submission_actionmenu.php+16 −6 modified@@ -89,6 +89,16 @@ protected function get_current_status(): string { } } + /** + * Has the submission started. + * + * @return bool The status of the submission; true if started, otherwise false. + */ + protected function is_submission_started(): bool { + return (is_object($this->teamsubmission) && isset($this->teamsubmission->timestarted)) + || (is_object($this->submission) && isset($this->submission->timestarted)); + } + /** * Export the submission buttons for the page. * @@ -130,20 +140,20 @@ public function export_for_template(\renderer_base $output): array { $data['edit']['help'] = $newattempthelp->export_for_template($output); } if ($status === ASSIGN_SUBMISSION_STATUS_NEW) { - $timelimitenabled = get_config('assign', 'enabletimelimit'); - if ($timelimitenabled && $this->timelimit && empty($this->submission->timestarted)) { + if ($timelimitenabled && $this->timelimit && !$this->is_submission_started()) { $confirmation = new \confirm_action( get_string('confirmstart', 'assign', format_time($this->timelimit)), null, get_string('beginassignment', 'assign') ); - $urlparams = array('id' => $this->cmid, 'action' => 'editsubmission'); + // The 'begin' flag indicates that the user is starting a timed assignment. + $urlparams = ['id' => $this->cmid, 'action' => 'editsubmission', 'begin' => 1]; $beginbutton = new \action_link( new moodle_url('/mod/assign/view.php', $urlparams), - get_string('beginassignment', 'assign'), - $confirmation, - ['class' => 'btn btn-primary'] + get_string('beginassignment', 'assign'), + $confirmation, + ['class' => 'btn btn-primary'] ); $data['edit']['button'] = $beginbutton->export_for_template($output); $data['edit']['begin'] = true;
public/mod/assign/locallib.php+31 −4 modified@@ -3353,7 +3353,9 @@ public function get_group_submission($userid, $groupid, $create, $attemptnumber= if ($create) { $action = optional_param('action', '', PARAM_TEXT); if ($action == 'editsubmission') { - if (empty($submission->timestarted) && $this->get_instance()->timelimit) { + $starttimer = optional_param('begin', 0, PARAM_INT); + // Only start the timer if the user has clicked the 'Begin assignment' button. + if (empty($submission->timestarted) && $this->get_instance()->timelimit && $starttimer) { $submission->timestarted = \core\di::get(\core\clock::class)->time(); $DB->update_record('assign_submission', $submission); } @@ -3866,7 +3868,9 @@ public function get_user_submission($userid, $create, $attemptnumber=-1) { if ($create) { $action = optional_param('action', '', PARAM_TEXT); if ($action == 'editsubmission') { - if (empty($submission->timestarted) && $this->get_instance()->timelimit) { + $starttimer = optional_param('begin', 0, PARAM_INT); + // Only start the timer if the user has clicked the 'Begin assignment' button. + if (empty($submission->timestarted) && $this->get_instance()->timelimit && $starttimer) { $submission->timestarted = \core\di::get(\core\clock::class)->time(); $DB->update_record('assign_submission', $submission); } @@ -4878,7 +4882,7 @@ public function fullname($user) { * @return string The page output. */ protected function view_edit_submission_page($mform, $notices) { - global $CFG, $USER, $DB, $PAGE; + global $CFG, $USER, $DB, $PAGE, $OUTPUT; $o = ''; require_once($CFG->dirroot . '/mod/assign/submission_form.php'); @@ -4963,7 +4967,30 @@ protected function view_edit_submission_page($mform, $notices) { $o .= $this->get_renderer()->notification($notice); } - $o .= $this->get_renderer()->render(new assign_form('editsubmissionform', $mform)); + if ( + $submission->status == ASSIGN_SUBMISSION_STATUS_NEW && $this->get_instance()->timelimit && + empty($submission->timestarted) + ) { + // Timed assignment should always get a confirmation that the user wants to start it. + $confirmation = new \confirm_action( + get_string('confirmstart', 'assign', format_time($this->get_instance()->timelimit)), + null, + get_string('beginassignment', 'assign') + ); + // The 'begin' flag indicates that the user is starting a timed assignment. + $urlparams = ['id' => $this->get_course_module()->id, 'action' => 'editsubmission', 'begin' => 1]; + $beginbutton = new \action_link( + new moodle_url('/mod/assign/view.php', $urlparams), + get_string('beginassignment', 'assign'), + $confirmation, + ['class' => 'btn btn-primary'] + ); + + $o .= $OUTPUT->render($beginbutton); + } else { + $o .= $this->get_renderer()->render(new assign_form('editsubmissionform', $mform)); + } + $o .= $this->view_footer(); \mod_assign\event\submission_form_viewed::create_from_user($this, $user)->trigger();
public/mod/assign/tests/behat/timed_assignment.feature+84 −0 added@@ -0,0 +1,84 @@ +@mod @mod_assign +Feature: In a timed assignment, students should confirm before starting the timer + In order to submit a timed assignment + As a student + I need to confirm to begin the assignment before the timer starts + + Background: + Given the following config values are set as admin: + | config | value | plugin | + | enabletimelimit | 1 | assign | + And the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And the following "users" exist: + | username | firstname | lastname | email | + | student1 | Student | 1 | student1@example.com | + | student2 | Student | 2 | student2@example.com | + And the following "course enrolments" exist: + | user | course | role | + | student1 | C1 | student | + | student2 | C1 | student | + And the following "activities" exist: + | activity | course | name | assignsubmission_file_enabled | assignsubmission_file_maxfiles | assignsubmission_file_maxsizebytes | teamsubmission | duedate | timelimit | + | assign | C1 | Timed assignment 1 | 1 | 1 | 2097152 | 0 | ##+1 hours 30 minutes## | 60 | + | assign | C1 | Group assignment 2 | 1 | 1 | 2097152 | 1 | ##+1 hours 30 minutes## | 60 | + + Scenario: Access a timed assignment from the course page + Given I am on the "Timed assignment 1" Activity page logged in as student1 + And "Begin assignment" "link" should exist + When I reload the page + Then "Begin assignment" "link" should exist + And "#mod_assign_timelimit_block" "css_element" should not exist + + @javascript + Scenario: Access a timed assignment from the Dashboard + Given I am logged in as student1 + When I click on "Timed assignment 1" "link" in the "Calendar" "block" + And I click on "Add submission" "link" in the ".modal-footer" "css_element" + Then "Begin assignment" "link" should exist + And I reload the page + And "Begin assignment" "link" should exist + And "#mod_assign_timelimit_block" "css_element" should not exist + # Repeat the steps to confirm timer doesn't start automatically. + And I select "Dashboard" from primary navigation + And I click on "Timed assignment 1" "link" in the "Calendar" "block" + And I click on "Add submission" "link" in the ".modal-footer" "css_element" + And "Begin assignment" "link" should exist + And "#mod_assign_timelimit_block" "css_element" should not exist + # Now start the timer. + And I click on "Begin assignment" "link" + And I click on "Begin assignment" "button" in the ".modal-footer" "css_element" + And "#mod_assign_timelimit_block" "css_element" should exist + + @javascript + Scenario: Access a timed group assignment from the Dashboard + Given the following "groups" exist: + | name | course | idnumber | + | Group 1 | C1 | CG1 | + And the following "group members" exist: + | user | group | + | student1 | CG1 | + | student2 | CG1 | + And I am logged in as student1 + When I click on "Group assignment 2" "link" in the "Calendar" "block" + And I click on "Add submission" "link" in the ".modal-footer" "css_element" + Then "Begin assignment" "link" should exist + And I reload the page + And "Begin assignment" "link" should exist + And "#mod_assign_timelimit_block" "css_element" should not exist + # Repeat the steps to confirm timer doesn't start automatically. + And I select "Dashboard" from primary navigation + And I click on "Group assignment 2" "link" in the "Calendar" "block" + And I click on "Add submission" "link" in the ".modal-footer" "css_element" + And "Begin assignment" "link" should exist + And "#mod_assign_timelimit_block" "css_element" should not exist + # Now start the timer. + And I click on "Begin assignment" "link" + And I click on "Begin assignment" "button" in the ".modal-footer" "css_element" + And "#mod_assign_timelimit_block" "css_element" should exist + And I log out + # Now check the submission has started for the other group member too. + And I am on the "Group assignment 2" "assign activity" page logged in as student2 + And "Begin assignment" "link" should not exist + And "Add submission" "button" should exist
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-w29j-8phw-ffjfghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-62401ghsaADVISORY
- access.redhat.com/security/cve/CVE-2025-62401ghsavdb-entryx_refsource_REDHATWEB
- bugzilla.redhat.com/show_bug.cgighsaissue-trackingx_refsource_REDHATWEB
- github.com/moodle/moodle/commit/78a3fe6c618676dfc53ea538abbfe35e60674eebghsaWEB
- moodle.org/mod/forum/discuss.phpghsaWEB
News mentions
0No linked articles in our index yet.