DEV Community

Cover image for Client-Side Price Manipulation: Pay Whatever You Want at Checkout
Oopssec Store
Oopssec Store

Posted on • Originally published at koadt.github.io on

Client-Side Price Manipulation: Pay Whatever You Want at Checkout

Exploiting a server-side validation failure in OopsSec Store's checkout process to purchase products at arbitrary prices.

OopsSec Store's checkout sends the order total straight from the browser. The server saves whatever it receives without recalculating from actual product prices. Change it to a penny, the order goes through at a penny.

Table of contents

Lab setup

From an empty directory:

npx create-oss-store oss-store
cd oss-store
npm start
Enter fullscreen mode Exit fullscreen mode

Or with Docker (no Node.js required):

docker run -p 3000:3000 leogra/oss-oopssec-store
Enter fullscreen mode Exit fullscreen mode

The app runs at http://localhost:3000.

Vulnerability overview

When you buy something on OopsSec Store, the browser sends a POST to /api/orders with the cart items and a total field. That total is calculated by frontend JavaScript. The server takes it at face value and creates the order.

The product prices are in the database. The server could look them up and do the math itself. It doesn't.

Locating the attack surface

Add some products to your cart and go through checkout. The payment page shows your order summary with the total.

Checkout page displaying order summary and payment button

Click "Complete Payment" and the browser fires off a POST with the order details, including the total the frontend calculated.

Exploitation

Configuring the proxy

Set up Burp Suite as an intercepting proxy (browser traffic through 127.0.0.1:8080). Leave interception off for now.

Preparing the order

Add products to your cart. Higher-priced items make the result more obvious. Go through checkout until you hit the payment page.

Product page showing item to be added to cart

Intercepting the request

Turn on interception in Burp, then click "Complete Payment". Burp catches the POST to /api/orders before it hits the server.

Burp Suite intercept toggle enabled

Looking at the request

The request body is JSON with the order details:

Intercepted POST request showing order JSON with total field

The total field is the price the frontend calculated. The server uses this number directly.

Modifying the price

Change total to whatever you want. 0.1 works:

Modified request with total changed to 0.1

Completing the attack

Forward the modified request and turn off interception. The server processes the order at your price.

Capturing the flag

The order confirmation shows the purchase at the modified total. The server notices the mismatch and returns the flag:

OSS{cl13nt_s1d3_pr1c3_m4n1pul4t10n}
Enter fullscreen mode Exit fullscreen mode

Order confirmation showing manipulated price and captured flag

Vulnerable code analysis

The checkout handler pulls total straight out of the request body and saves it:

const { total } = await request.json();

const order = await prisma.order.create({
  data: {
    userId: user.id,
    total: total, // Client-provided value used directly
  },
});
Enter fullscreen mode Exit fullscreen mode

The frontend does calculate the right number. But the server never checks it. Anyone with a proxy, devtools, or curl can send whatever total they want.

The product prices and cart quantities are right there in the database. The server just doesn't use them.

Remediation

Recalculate the total server-side

Pull the cart from the database and compute the total from actual prices:

const cart = await prisma.cart.findFirst({
  where: { userId: user.id },
  include: {
    cartItems: {
      include: { product: true },
    },
  },
});

const calculatedTotal = cart.cartItems.reduce(
  (sum, item) => sum + item.product.price * item.quantity,
  0
);

const order = await prisma.order.create({
  data: {
    userId: user.id,
    total: calculatedTotal, // Server-calculated value
  },
});
Enter fullscreen mode Exit fullscreen mode

Detect tampering

If you still want the client total for logging or display, compare it against the server calculation:

const clientTotal = requestBody.total;
const serverTotal = calculateTotalFromCart(cart);

if (Math.abs(clientTotal - serverTotal) > 0.01) {
  return NextResponse.json(
    { error: "Price validation failed" },
    { status: 400 }
  );
}
Enter fullscreen mode Exit fullscreen mode

The frontend total is fine for UX. The backend should never trust it for the actual charge.

Lab

GitHub logo kOaDT / oss-oopssec-store

Security training for the apps you actually ship. Open your browser and start hacking.

OSS - OopsSec Store

An intentionally vulnerable e-commerce app for learning web security.
Master real-world attack vectors through a realistic CTF platform.
Hunt for flags, exploit vulnerabilities, and level up your security skills

Docker Hub · npm · Walkthroughs · Contributing · Good first issues

GitHub license PRs Welcome Good first issues Intentionally Vulnerable
GitHub stars GitHub forks

   ____  ____ ____     ____                  ____            ____  _
  / __ \/ __// __/    / __ \ ___   ___  ___ / __/ ___  ____ / __/ / /_ ___   ____ ___
 / /_/ /\ \ _\ \     / /_/ // _ \ / _ \(_-<_\ \  / -_)/ __/_\ \  / __// _ \ / __// -_)
 \____/___//___/     \____/ \___// .__/___/___/  \__/ \__//___/  \__/ \___//_/   \__/
                                /_/

# Node.js
npx create-oss-store my-ctf-lab && cd my-ctf-lab && npm start

# Docker
docker run -p 3000:3000 leogra/oss-oopssec-store

# Then open http://localhost:3000 and start hacking
Enter fullscreen mode Exit fullscreen mode

Further reading

Standards and classifications

Tools

Stack documentation

Lab source


Disclaimers

Do not deploy OopsSec Store on a production server. This application is intentionally vulnerable and should only be used in isolated, local environments for educational purposes.

Do not exploit vulnerabilities on systems you don’t have explicit authorization to test. Unauthorized access to computer systems is illegal. Always obtain proper permission before performing security testing.

Feedback & Support

Having trouble following this writeup? Found a typo or have suggestions for improvement?

Feel free to open an issue or start a discussion on GitHub.

Top comments (0)