Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
moodle-enrol-database-sqli — Moodle 5.2.3 - High - Remote - SQLi - enrol_database concatenates course idnumber into SQL | Kitploit
Tools/GitHubGitHub/abraxas/moodle-enrol-database-sqli
Vulnerability AnalysisExploitationWeb Application ExploitationWeb SecurityPenetration TestingDatabase SecurityLabs & Practice
GitHub
abraxas/moodle-enrol-database-sqli

moodle-enrol-database-sqli

Moodle 5.2.3 - High - Remote - SQLi - enrol_database concatenates course idnumber into SQL

View Repository
94 days agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

Abraxas Labs - moodle-enrol-database-sqli

abraxaslabs.tech  ·  github.com/abraxas  ·  @abraxas_null  ·  [email protected]  ·  moodle-enrol-database-sqli

moodle-enrol-database-sqli

Moodle LMS 5.2.3 - Moodle

Class: SQLi Reach: Remote

enrol_database::db_get_sql still slashes and concatenates. MSA-26-0005 / MDL-88138 parameterized auth_db as Execute($sql, [$value]). This plugin did not copy that shape. Defaults in settings: dbsybasequoting=0, localcoursefield=idnumber. The sink is the external enrolment database. Moodle core $DB is not on this path.

An editing teacher on a site with enrol_database enabled sets the course idnumber. Cron interpolates it. Extra rows come back from the enrol DB.

IDno CVE yet
CWECWE-89
CVSSHigh: 8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
ProductMoodle
Affected5.2.3 tag v5.2.3 (344232c15336c71b80f9aca8359ce0e0a9f3d116). Plugin enrol_database must be on.
Authediting teacher (course idnumber, default local map)
LicenseGNU Affero GPL v3.0
Lab127.0.0.1 only

What an attacker can do

An editing teacher on a site with enrol_database enabled injects SQL on the external enrolment database by setting the course idnumber. Cron sync_enrolments then runs that SQL.

PrivilegeMethod / sinkRecon / integrity / availabilityLab-proven or inferred
Splice a SQL fragment into the external enrol SELECTCron sync_enrolments -> db_get_sql($table, array($coursefield=>$course->mapping), ...) -> $extdb->Execute($sql)Integrity of the query stringLab-proven. Concat SQL is WHERE courseid = 'LABCOURSE\'' OR 1=1 -- '
Read other courses' enrol rows on that external DBSame SELECT after the string closesReconLab-proven. Clean: 1 row teacher-local. Inject: 2 rows including MOODLE-ENROL-DATABASE-SQLI-WITNESS
Skip the query when the plugin is offSame gadget, plugin=offn/aLab-proven closed. executed=false, empty SQL, 0 rows
Hold the same payload as a bound parameterauth_db leftover of MSA-26-0005: Execute($sql, [$value])n/aLab-proven closed. style=param returned 0 rows, SQL contains ?
Enrol / unenrol / role-assign Moodle users named in extra rowsAfter cron applies requestedenrolsIntegrity of enrolmentsInferred. Lab stops at extra rows on the enrol DB
FILE / INTO OUTFILE as the enrol-DB userSame sink if that account has FILEIntegrity / availability of that DB hostInferred. Not demonstrated. Not OS LPE
Student-controlled pathOnly if admin maps localuserfield=email (or similar)n/aInferred. Default path is teacher idnumber

Closed on this leftover: Moodle core $DB; plugin disabled (no query); unauthenticated access; OS shell; stacked queries (this mysqli gadget is single-statement). Default Moodle without enrol_database is not this bug.

Same pin leftovers: Blackboard img file read, grades feedback IDOR, hidden grade items.

How I found it

I pinned moodle/moodle v5.2.3 after MSA-26-0042/0043. MSA-26-0005 closed auth_db with a placeholder. I opened enrol_database because it talks to the same kind of external DB and still has db_addslashes.

dbsybasequoting defaults to 0, so only ' is doubled. A backslash-quote in the idnumber closes the MySQL string.

Wrong turns already recorded: treating Moodle core $DB as the sink (it is not); calling this OS LPE because FILE exists as a MySQL privilege (not demonstrated, not this leftover); student email as the default map (admin has to change localuserfield). A reverse shell. Theatre.

Lab

Faithful gadget of the pin builders, not a full Moodle PHP app. php:8.3-cli serves the copied db_get_sql / db_addslashes / db_encode. mysql:8.0 holds two enrol rows.

python3 moodle-enrol-database-sqli-Abraxas-Labs.py

or

cd lab
./run.sh

Target only http://127.0.0.1:19570. Compose project moodle-enrol-database-sqli. Stack tears down after the run.

SUCCESS moodle-enrol-database-sqli how=gadget plugin-off=no-query clean-rows=1 inject-rows=2 sql-concat=yes param-rows=0 extra=yes sink=enrol-db MOODLE-ENROL-DATABASE-SQLI-WITNESS

The fix

Copy auth_db's parameterized queries (MDL-88138). Pass the mapped course / user value as Execute($sql, [$value]). Do not concatenate db_addslashes into '$value'. Add a test that a quote in course idnumber does not return rows from other courses.

References

  • github.com/moodle/moodle tag v5.2.3
  • lib.php (db_get_sql, db_addslashes, sync_enrolments)
  • Nearby patched (not this plugin): MSA-26-0005 / MDL-88138 auth_db
  • Same pin: moodle-blackboard-img · moodle-grades-feedback-idor · moodle-grades-gradeitems
  • CWE-89

License

GNU Affero GPL v3.0. See LICENSE. Loopback lab only. No warranty.

Download Tool