
Cloud Vulnerability DB
A community-led vulnerabilities database
?set_locale= wrote the request's value into Avo.configuration.locale, a setting shared by every request the Ruby process serves. It did so before Avo's authenticate block ran.
?set_locale=zz) was stored, and every later Avo request from every user raised I18n::InvalidLocale (HTTP 500) until the process restarted.config.authenticate_with rather than a route constraint, both worked without signing in.Fixed in avo 4.3.1 and avo 3.33.1.
Avo 3 apps that use avo-pro or avo-advanced also need 3.33.1 of avo-pro, avo-advanced, avo-dashboards, avo-menu and avo-dynamic_filters, because they pin the avo version exactly. Run bundle update avo together with those gems.
Unknown locales in set_locale and force_locale are now ignored, and set_locale is stored only after the authenticate block lets the request through: avo-hq/avo#4886 and avo-hq/avo#4893 (4.x), avo-hq/avo#4894 (3.x).
authenticate :user do ... end), so signed-out requests never reach Avo's controllers. Signed-in users can still trigger the bug until you upgrade.Avo.configuration.locale mutation via params[:set_locale]Avo::BaseApplicationController#set_avo_locale is registered as an around_action and writes Avo.configuration.locale from params[:set_locale]:
# app/controllers/avo/base_application_controller.rb
around_action :set_avo_locale
...
def set_avo_locale(&action)
locale = Avo.configuration.default_locale
if params[:set_locale].present?
locale = params[:set_locale]
Avo.configuration.locale = locale # <-- mutates global config
end
I18n.with_locale(locale, &action)
endAvo.configuration is the application-wide singleton built in Avo::Configuration and reused across every request handled by the same Ruby process. Writing to Avo.configuration.locale therefore persists the value globally — it affects every other in-flight and future request, served to every other user, until the process is restarted or a subsequent request explicitly resets it.
A second issue is callback ordering: set_avo_locale is an around_action registered before _authenticate! in the BaseApplicationController callback chain, so the mutation happens before authentication. Whether it is reachable pre-auth depends on the host application's Avo.configuration.authenticate proc — anything that renders before redirecting (or any host that allows public access to a single avo URL such as a status / health endpoint) gives unauthenticated attackers a write primitive against Avo.configuration.locale.
All current 3.x releases up to 3.31.2 and the v4.0.0 betas are affected.
set_avo_locale around_action (i.e. anything inheriting BaseApplicationController — effectively the whole engine).authenticate proc lets the around_action complete before redirecting (the around_action runs to its first yield before _authenticate!'s before_action is invoked, so the mutation always lands).A reproducible RSpec request spec is provided at spec/requests/avo/locale_global_state_spec.rb:
it "persists the attacker-supplied locale on the global Avo.configuration" do
original = Avo.configuration.locale
get "/admin?set_locale=xx-poisoned"
expect(Avo.configuration.locale).to eq("xx-poisoned")
Avo.configuration.locale = original
endEnd-to-end with curl:
curl -b session.txt "https://target/admin?set_locale=xx-poisoned"
# every subsequent request served by the same Puma worker now starts
# with Avo.configuration.locale == "xx-poisoned" (until another set_locale
# overrides it or the worker restarts).Avo.configuration.locale (rather than per-request I18n.locale) return missing-translation strings or fall back unpredictably for every user.Avo.configuration.locale (e.g. for caching keys, log routing, default behavior) inherits the corruption automatically.params[:force_locale] (set_force_locale) and other I18n.with_locale paths can be used to force missing-translation behavior for other admins (denial-of-service of the UI for the attacker's chosen scope).
CVSS v3.1 estimate: 3.7 / Low — AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N. Higher if reachable pre-auth.Treat params[:set_locale] as request-local input, never write to Avo.configuration:
def set_avo_locale(&action)
locale = params[:set_locale].presence || Avo.configuration.default_locale
I18n.with_locale(locale, &action)
endIf a deployment legitimately needs to "remember" a user's locale across requests, that belongs in the user record or the session — not in a process-global singleton.
Optionally, restrict the value against I18n.available_locales so a malformed locale doesn't poison I18n.with_locale:
def set_avo_locale(&action)
requested = params[:set_locale].to_s
locale = if I18n.available_locales.map(&:to_s).include?(requested)
requested
else
Avo.configuration.default_locale
end
I18n.with_locale(locale, &action)
endA regression test is included with the PoC above.
Reported privately via GitHub Security Advisory.
Source: NVD
Free Vulnerability Assessment
Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.
Get a personalized demo
"Best User Experience I have ever seen, provides full visibility to cloud workloads."
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
"We know that if Wiz identifies something as critical, it actually is."