GHSA-665x-ppc4-685w OpenMage LTS: Cross-user wishlist import leads to private option & file disclosure
Cross-user wishlist item import via shared wishlist code, leading to private option disclosure and file-disclosure variantSummaryThe shared wishlist add-to-cart endpoint authorizes access with a public `sharing_code`, but loads the acted-on wishlist item by a separate global `wishlist_item_id` and never verifies that the item belongs to the shared wishlist referenced by that code.This lets an attacker use:a valid shared wishlist code for wishlist Aa wishlist item ID belonging to victim wishlist Bto import victim item B into the attacker's cart through the shared wishlist flow for wishlist A.Because the victim item's stored `buyRequest` is reused during cart import, the victim's private custom-option data is copied into the attacker's quote. If the product uses a file custom option, this can be elevated to cross-user file disclosure because the imported file metadata is preserved and the download endpoint is not ownership-bound.Vulnerability TypeBroken object-level authorization / IDORCross-user data disclosureCross-user file disclosure variantRoot CauseIn `app/code/core/Mage/Wishlist/controllers/SharedController.php`, the shared flow does:Relevant lines:`SharedController.php:86` loads the wishlist item by global ID`SharedController.php:87` loads the wishlist by shared code`SharedController.php:99` imports the item into cartThere is no check that:The safe owner flow in `app/code/core/Mage/Wishlist/controllers/IndexController.php:521-528` does preserve this binding by deriving the wishlist from `item->getWishlistId()`.The imported item keeps its original `buyRequest` because `app/code/core/Mage/Wishlist/Model/Item.php:370-372` passes that stored request directly into:Security ImpactBaseline impactAn attacker can import another user's private wishlist item into the attacker's own cart, using an unrelated shared wishlist code.This is a clear cross-user authorization bypass. The victim item's private configuration is copied into the attacker's quote, including custom-option values such as personalized text.Stronger variant: cross-user file disclosureIf the victim item contains a custom option of type `file`, the imported quote item preserves file metadata such as:`quote_path``order_path``secret_key`The file option renderer in `app/code/core/Mage/Catalog/Model/Product/Option/Type/File.php:547-552` generates a download URL from:the imported `sales/quote_item_option` IDthe preserved `secret_key`The downloader in `app/code/core/Mage/Sales/controllers/DownloadController.php:150-185`:loads quote item option by global IDverifies only product option type and `secret_key`reads the file from `order_path` or `quote_path`It does not verify ownership of the quote item, order, or original wishlist item. This creates a cross-user file disclosure path once victim file metadata has been imported.Steps To ReproduceLab datashared wishlist A:`wishlist_id = 1``customer_id = 2``sharing_code = 6376bb8c37a09c2de3664bd8cdc16412`victim wishlist B:`wishlist_id = 2``customer_id = 3`victim item:`wishlist_item_id = 1``wishlist_id = 2``product_id = 2`victim private text option marker:`VICTIM-MARKER-49040822`ReproductionSend:Where:`code` belongs to shared wishlist A`item=1` belongs to victim wishlist BExpected resultThe request should be rejected because the item does not belong to the shared wishlist referenced by the `sharing_code`.Actual resultThe application imports victim item `1` into the attacker's quote anyway.Verified EvidenceBaseline variantPreviously verified at quote/option level in lab:This shows that the attacker's cart received victim-private custom-option data from another user's wishlist item.File-disclosure variantPreviously verified in lab after importing a victim file-option payload:This URL was generated from imported quote item option data containing the victim file metadata and secret key.Why This Is A Valid BugThis is not a timing issue and does not depend on non-default security settings.The bug is a direct authorization failure:authorization is based on wishlist A's share codethe acted-on object is item B from another wishlistthere is no item-to-wishlist binding checkvictim-controlled item state is then copied into attacker-controlled cart stateThat is a broken object-level authorization issue with clear cross-user impact.RemediationIn `SharedController::cartAction()`, reject any request where the loaded item does not belong to the wishlist loaded from the share code:Defense in depth:bind `sales/download/downloadCustomOption` to the current quote/order owner instead of trusting only `id + secret_key`
CVSS v4.0 score: 5.3
CVSS v3.1 score: 5.4