Skip to content

e107 v2.3.8

Choose a tag to compare

@Deltik Deltik released this 21 Jul 18:40
v2.3.8

Caution

v2.3.8 is a security release for sites on v2.3.7 or earlier.
Upgrade from any 2.x at or below v2.3.7. If your site tracks the master branch, you are already past v2.3.8, so installing it would be a downgrade. v2.4.x is planned to be the next forward step.

Important

Upgrade promptly. v2.3.8 closes an unauthenticated SQL injection reachable on a default site, finishes the core-wide SQL-injection cleanup begun in v2.3.7, and removes an open redirect.

  • Unauthenticated SQL injection in the news item view (GHSA-fr8h-3vfv-9q98, CVSS 8.6). A crafted news address let request data reach a database query without being made safe, with no login and no user interaction, on an ordinary front-end page. Any public-facing site is exposed. (d11929a0ff)
  • All audited SQL-injection sinks closed across the core. v2.3.7 hardened many SQL injection risks, whether exploitable or not. v2.3.8 works through more risks that a follow-up audit found across admin, user, installer, and additional front-end code. There is no specific vulnerability mitigation but to upgrade.
  • Code execution from a poisoned preferences value (GHSA-568x-w5qj-vr7c, CVE-2026-57859, CVSS 7.2). A stored user-preferences value was rebuilt with eval(), so a hostile value written into the database out of band (for example through a separate injection) could run as PHP when that account's preferences were loaded. It is now parsed safely and rejects anything that is not plain stored data, which closes the second stage an injection would otherwise chain into. (59cf3aaaa0)
  • Open redirect in the download handler (GHSA-wcc5-8jrf-6q26, CVSS 6.1). A crafted download link bounced a visitor to any external site under the trust of your domain. (b1def30489)

You must upgrade to mitigate the numerous vulnerability risks.

Highlights

  • [Security] Unauthenticated SQL injection through the news pages (GHSA-fr8h-3vfv-9q98). A single news item address carried a value straight into a database query without being made safe, so an attacker could run their own database commands with no login. It is now read as a number, as the surrounding queries already were. (d11929a0ff)
  • [Security] SQL injection audit cleanup. v2.3.7 hardened many but not all SQL injection risks. A follow-up audit of the whole v2.3.x tree found more of the same pattern in admin, user-account, installer, and other front-end code. Those are now fixed too, so the same class of attack is closed off at all known areas of the e107 core code. Plugins should be audited separately and are not in the scope of fixes in this release. There is nothing for you to configure. (32940962e3, fb8fe63e9e)
  • [Security] Stored preferences can no longer run as code (GHSA-568x-w5qj-vr7c, CVE-2026-57859). e107 was rebuilding a saved user-preferences value by running it through PHP's eval(), with only a loose check on the front of the string. A hostile value planted in the database could therefore run as code the next time that account's preferences loaded. The value is now read with a strict parser that only accepts ordinary stored data and refuses anything else, so it can no longer be turned into code, even if something else managed to write into the database. (59cf3aaaa0)
  • [Security] Open redirect removed from the download handler (GHSA-wcc5-8jrf-6q26, CVSS 6.1). A download link could be crafted to bounce a visitor off to any other website while appearing to come from your domain, which is a common trick for phishing. That redirect is gone; a link that is not a real download now lands on the ordinary file-not-found page. (b1def30489)
  • [Fix] Blank pages and emails when a theme has no override. Some templates (including the "notify" e-mail and the default page fallback) could come out empty because the template file was only read once but looked up under two names. The content is now captured reliably. (4b68ed8b40)
  • [Fix] A leftover notification could take the whole site down. If a saved notification pointed at a plugin that had since been removed or renamed, every page that triggered an event (such as saving content) could go blank on PHP 7 and later. e107 now skips and logs the stale notification instead of stopping. (40e73cefde)

For Administrators

Changed

  • Deep audit SQL injection risks closed across the core (security). The following vulnerable areas are now closed off to SQL injection:

    • the news item view (reachable with no login, GHSA-fr8h-3vfv-9q98),
    • the admin batch-edit and search-filter machinery,
    • user-account management,
    • the plugin install/upgrade path,
    • the installer's database-name and table-prefix handling,
    • user-extended field definitions,
    • emoticon pack names,
    • poll previews,
    • the display-name save, and
    • the related-news block.

    Nothing changes in how the site behaves and there is nothing to configure. Upgrading is critical to protect your site from potential exploitation. (d11929a0ff, 32940962e3, fb8fe63e9e)

  • The plugin/core update screen now carries a security token (hardening). The action to update your plugins/database could previously be triggered by a forged request if a logged-in main administrator was lured to a malicious page (a CSRF). It now requires e107's own one-time form token before any update runs, so only a genuine click on the real update button can start it. Sites that have deliberately turned off token protection are unaffected. (23fa132aee)

  • A saved value that could run as code is now read safely (security). e107 stores some data (such as a user's preferences) as text and reads it back when needed. One of those readers was rebuilding the value by running it through PHP's code evaluator, so a value tampered with directly in the database could execute the next time it was loaded. That reader now uses a strict parser that only understands ordinary saved data and rejects anything else, so a poisoned value fails safely instead of running. There is nothing to configure. Ref GHSA-568x-w5qj-vr7c, CVE-2026-57859. (59cf3aaaa0)

Fixed

  • Open redirect in the download handler (security). download/request.php would redirect to any web address placed in the link's query string, letting a crafted request.php?https://… link bounce a visitor off to another site under the trust of your domain (a phishing aid). Real downloads are served by their numeric ID and are unaffected; an address that is not a registered download now falls through to the normal file-not-found page. Ref GHSA-wcc5-8jrf-6q26. (b1def30489)
  • Blank template output when a theme provides no override. With no theme override in play, a template file that e107 looks up under two internal names was only read the first time, so the second lookup found nothing and rendered an empty result for the rest of the page load. This showed up on the "notify" administrator e-mail and the default page fallback, and on any merged template request. The template content is now captured once per file and reused, so those outputs come through. (4b68ed8b40)
  • Stale notification could blank the site (non-security). A saved notification pointing at a plugin class that had since been removed or renamed threw an uncatchable error on PHP 7 and later, so every page that fired an event (for example saving content) could go blank. e107 now checks the class exists, skips and logs the stale entry, and carries on. Fixes #5604. (40e73cefde)
  • News grid crash on PHP 8. A News grid menu whose caption is stored as a per-language array with no entry for the active language could stop the page with a TypeError, because the array was handed to defined(). The caption is now checked before that call, so the grid renders. Fixes #5649. (ddf6726c1a)
  • Editor page-break button. In the TinyMCE editor the page-break button inserted a marker e107's pagination never recognized, so it did nothing. It now inserts e107's own [newpage] marker, so a page break splits the content as intended. Fixes #5792. (1ef5658a52)
  • Site disclaimer year. The site disclaimer showed a literal "YYYY" to anything that read it directly (for example the RSS feed copyright) instead of the current year. The year is now substituted where the value is defined, matching the shortcode. Fixes #5714. (9d1258e696)
  • Admin Help panels on PHP 8. The Log, Users and Upload admin Help panels emitted "Undefined array key" warnings while parsing the page address. They no longer reach for a value that is not there. Fixes discussion #5762. (c1545b1665)
  • Log noise without the optional social-login integration. On sites that do not have the optional social-login plugin installed, e107 wrote a "failed to open stream" warning to the PHP error log on every page. The include is now guarded, so the log stays quiet; behavior is unchanged where the plugin is present. Fixes #5683. (79ce2dfed1)

For Developers

Added

  • Regression coverage for the SQL-injection fixes. SqlInjectionFixesTest pins the news item integer cast, the admin batch field allowlist (reject and accept paths), the user-model ID cast, the user-extended partial-update and create validation, and the installer name checks. The probe controller is built via eval so the unit loader's include-time class scan does not pull in admin_ui.php. (181eeba90e)
  • e107TemplateSourceTest. Pins the capture-once-per-physical-file behavior so a template backing two registry paths cannot regress to caching an empty array for the second one. (4b68ed8b40)
  • DownloadOpenRedirectCest + seeNoRedirectTo acceptance helper. Asserts request.php does not emit an off-site Location redirect. (b1def30489)
  • e_array::unserialize() parser coverage. New e_arrayTest cases pin the sink and the live e_user_pref::load() path, a var_export() round-trip, an edge-form round-trip (NUL / INF / NAN / PHP_INT_MIN), and an injection-rejection case; validated against the var_export()/eval() baseline on PHP 5.6 through 8.5. (59cf3aaaa0)
  • Backport regression coverage. news_classTest pins the news-grid array-caption guard; e107TinyMceParserTest and wysiwygTest pin the [newpage] pagebreak separator. (ddf6726c1a, 1ef5658a52)

Changed

  • SQL-injection remediation techniques (2.3.x). The e107 v2.3.x branch predates the future e_db prepared-statement and query builder API, so tainted values are escaped inside their SQL quotes with escape(), numeric / LIMIT / OFFSET positions are (int) cast, and dynamic table/column identifiers are confined to a strict [A-Za-z0-9_] allowlist (escaping cannot protect an identifier). The news item action reuses the integer cast it already computed; admin_ui / model_class validate the column against the controller's declared fields before it reaches the SET/WHERE clause, both at dispatch and unconditionally in batchUpdate(); e_user_model::load() int-casts the ID and the unadmin path moves its permission gate ahead of the query. If you maintain a plugin that builds raw SQL from request input, mirror these practices! (32940962e3)
  • _getFieldValue() safestr now escapes. The safestr field type was serialized into single quotes without escape(), unlike the sibling str branch; its filter() pre-processing encodes quotes but leaves the backslash intact, so a trailing backslash could escape its own closing quote. It now calls escape() to match str, and the stored bytes are unchanged for any value without a backslash. xmlClass::e107Export() validates each requested table against the live table list before it reaches the FROM identifier position. (fb8fe63e9e)
  • CSRF check at the e107Update choke point. e107Update::__construct() reads key($_POST['update']) / key($_POST['update_core']) and runs the upgrade routines; both callers (db.php's db_update branch and e107_update.php) are gated only by getperms('0'). A valid session token is now required at the shared constructor choke point before any update runs, validated through e107::getSession()->check(false) (fail-closed, honors e_SECURITY_LEVEL); the real form emitted by renderForm() now includes the e-token hidden field. (23fa132aee)
  • e_array::unserialize() no longer uses eval(). It rebuilt the var_export() array-storage format by prepending $data = and calling eval(), guarded only by a substr(...,0,5) === 'array' check, so a stored value beginning with array that carried a PHP expression executed on load (second-order, reachable through e_user_pref::load() from a poisoned user_prefs column). It is replaced by a token_get_all() parser that accepts only the literal grammar var_export() emits (array structure with string / int / float / bool / null / nested-array leaves) and fails closed otherwise. The multi-token edge forms (NUL via 'a' . "\0" . 'b', INF/-INF/NAN, PHP_INT_MIN) are read faithfully, and the existing $data = strip, stripslashes fixups and PDO => fix are retained, so legacy and addslash'd values still round-trip; stored bytes are unchanged for well-formed data. Ref CVE-2026-57859 / GHSA-568x-w5qj-vr7c. (59cf3aaaa0)
  • Template source captured once per physical path. e107::_getTemplate() caches each template under an override (/ext) and a non-override registry path; with no theme override both resolve to the same file, but include_once() only populated the template variable on the first inclusion, so the second flavor cached array() and every later lookup on it returned false. The file's definitions are now captured once under the templates/source/ registry namespace and re-read for each registry path the file backs. Template PHP still executes exactly once per request and theme overrides keep their distinct content. This clears the mail.php "notify" FIXME (2f9dae5, 2014) and un-deadens the page.php "default" fallback (46b2518, 2016). (4b68ed8b40)

Fixed

  • e107_event::trigger() tolerates stale handlers. A notify_prefs entry whose class no longer exists is now skipped with a logged EVENT_01 warning behind a class_exists() guard, and the handler catch is widened to also cover \Throwable, so an \Error raised inside a handler on PHP 7+ is logged rather than left to fatal. The widening is a second catch(\Throwable) after catch(\Exception) so the code stays parseable and correct on PHP 5.6, where \Throwable is undefined and \Exception already catches every throwable. (40e73cefde)

Acknowledgements

Our thanks to Aastha Aggarwal (@Aastha2602) for responsibly disclosing the open redirect (GHSA-wcc5-8jrf-6q26), and to @Jimmi08, whose report prompted the wider SQL-injection audit behind this release (GHSA-fr8h-3vfv-9q98), both through GitHub Security Advisories. We also thank Saidakbarxon Maxsudxonov (@sermikr0) for reporting the preferences code-execution issue (GHSA-568x-w5qj-vr7c, CVE-2026-57859), coordinated through VulnCheck.