Versions Affected: All versions of Solidus
Not affected: NONE
Fixed Versions: 4.7.2, 4.6.4
Impact
------
An authenticated customer can cancel another customer's in-progress return.
`POST /api/orders/:order_id/return_authorizations` accepts
`return_items_attributes[][inventory_unit_id]` values from any order, and
creating a return item for an inventory unit cancels every other active return
item for that unit.
Nothing ties the inventory units in the request to the order in the URL:
* `Spree::Api::ReturnAuthorizationsController#create` authorizes against the
`ReturnAuthorization` class, so the ownership block in
`Spree::PermissionSets::DefaultCustomer` is never evaluated. `load_order`
only checks access to the parent order.
* `inventory_unit_id` is a permitted nested attribute for every caller.
* `Spree::ReturnItem#belongs_to_same_customer_order` returns early when there
is no customer return, which is always the case when a return authorization
is created.
The return authorization saves with a return item for another order's unit,
and the `after_create :cancel_others` callback on `Spree::ReturnItem` cancels
the other order's return items for that unit.
Any customer with a shipped order can cancel other customers' pending return
items. The victim's return has to be re-authorized, and a stray return item is
left on the victim's inventory unit.
No data is disclosed and the attacker gains nothing financially:
reimbursements take their order from the inventory unit, so any refund goes to
the victim's order.
Patches
-------
Fixed in `solidus_core` 4.7.2 and 4.6.4. Older release series will not be
patched and should upgrade.
`Spree::ReturnItem` now validates that its inventory unit belongs to the
return authorization's order. Code or specs that attach a return item to a
return authorization on a different order will get a validation error.
Workarounds
-----------
If you cannot upgrade, add the same validation in an initializer:
# config/initializers/solidus_return_item_order_validation.rb
Rails.application.config.to_prepare do
Spree::ReturnItem.validate do
next unless inventory_unit_id_changed? || return_authorization_id_changed?
next unless return_authorization && inventory_unit
if return_authorization.order_id != inventory_unit.order_id
errors.add(:base, I18n.t("spree.return_items_cannot_be_associated_with_multiple_orders"))
end
end
end
Neither the patch nor the workaround repairs existing data. To find return
items on a different order than their return authorization:
Spree::ReturnItem
.joins(:return_authorization, inventory_unit: :shipment)
.where("spree_return_authorizations.order_id != spree_shipments.order_id")
References
----------
*
https://github.com/solidusio/solidus/security/advisories/GHSA-6p7r-vx57-9gw3If you have any questions about how to secure your site please stop by our
Slack:
http://slack.solidus.io/