| Gói / sản phẩm | Phiên bản bị ảnh hưởng | Phiên bản đã vá |
|---|---|---|
| openmage/magento-lts | < 20.17.0 | 20.17.0 |
| magento | < 20.17.0 | 20.17.0 |
The 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:
to 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.
In app/code/core/Mage/Wishlist/controllers/SharedController.php, the shared flow does:
$item = Mage::getModel('wishlist/item')->load($itemId);
$wishlist = Mage::getModel('wishlist/wishlist')->loadByCode($code);
...
$item->addToCart($cart);
Relevant lines:
SharedController.php:86 loads the wishlist item by global IDSharedController.php:87 loads the wishlist by shared codeSharedController.php:99 imports the item into cartThere is no check that:
$item->getWishlistId() == $wishlist->getId()
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:
$cart->addProduct($product, $buyRequest);
An attacker can import another user's private wishlist item into the attacker's own cart, using an unrelated shared wishlist code.
Bạn đang dùng một trong các sản phẩm này? Quét site để biết có đang lộ phiên bản có lỗ hổng không.
Quét site miễn phí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.
If the victim item contains a custom option of type file, the imported quote item preserves file metadata such as:
quote_pathorder_pathsecret_keyThe file option renderer in app/code/core/Mage/Catalog/Model/Product/Option/Type/File.php:547-552 generates a download URL from:
sales/quote_item_option IDsecret_keyThe downloader in app/code/core/Mage/Sales/controllers/DownloadController.php:150-185:
secret_keyorder_path or quote_pathIt 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.
wishlist_id = 1customer_id = 2sharing_code = 6376bb8c37a09c2de3664bd8cdc16412wishlist_id = 2customer_id = 3wishlist_item_id = 1wishlist_id = 2product_id = 2VICTIM-MARKER-49040822Send:
GET /wishlist/shared/cart/?code=6376bb8c37a09c2de3664bd8cdc16412&item=1
Where:
code belongs to shared wishlist Aitem=1 belongs to victim wishlist BThe request should be rejected because the item does not belong to the shared wishlist referenced by the sharing_code.
The application imports victim item 1 into the attacker's quote anyway.
Previously verified at quote/option level in lab:
option_1 = VICTIM-MARKER-49040822
This shows that the attacker's cart received victim-private custom-option data from another user's wishlist item.
Previously verified in lab after importing a victim file-option payload:
/sales/download/downloadCustomOption/id/9/key/86fca9b61c0b891b52fb/
This URL was generated from imported quote item option data containing the victim file metadata and secret key.
This is not a timing issue and does not depend on non-default security settings.
The bug is a direct authorization failure:
That is a broken object-level authorization issue with clear cross-user impact.
In SharedController::cartAction(), reject any request where the loaded item does not belong to the wishlist loaded from the share code:
$item = Mage::getModel('wishlist/item')->load($itemId);
$wishlist = Mage::getModel('wishlist/wishlist')->loadByCode($code);
if (!$item->getId() || !$wishlist->getId() || (int) $item->getWishlistId() !== (int) $wishlist->getId()) {
return $this->_forward('noRoute');
}
Defense in depth:
sales/download/downloadCustomOption to the current quote/order owner instead of trusting only id + secret_key