Introduction

I’m not a party guy, but I think we’ve all wondered what’s up with girls paying less than guys… but that’s a conversation for another day, because that won’t be a problem anymore.

Now, everyone pays the same: nothing!

This will be a short post about a pretty cool vulnerability I found on a web application that sells tickets for college parties in Aveiro. Before you ask, there is no vulnerability disclosure program but I’m a professor there, so technically I already work for the University.

It’s not an extremely technical vulnerability, but it was one of those simple but exciting “a-ha!” moments. It’s perfect to show to students and newbies in cybersecurity since I don’t think it’s very hard to understand, you just have to be extra creative. That comes with practice and experience.

Exploring the application

This is a pretty straightforward app. You have a tab for your profile and settings, which we don’t really care about right now. Here’s the wallet tab, notice my balance is at 0€:

The tickets tab, showing that I have not purchased any tickets:

And the active events list. At the time I took the screenshots, tickets were being sold for the valentine’s day party on the 13th:

One more introduction before we get into the fun stuff. If we try to buy a ticket, the “wallet” option is unavailable since we have 0€. The complete flow isn’t shown but there’s nothing interesting, just a standard add to cart flow.

The fun stuff

I started messing around with the buying flow and looking at the requests. Initially I just tried to set the “qtd” parameter to 0 on this request, hoping that it would only affect the price - same number of tickets for the price of 0 tickets.

It turns out the price did change, I’m not sure if the number of tickets did too, but either way this is a dead end. I cannot use my wallet. And I won’t touch the other two methods since they’re external components, developed by third parties.

Okay, let’s go for round two. What if I change the “qtd” parameter to a negative number? Surely the application will be ready for this…

This is where everything gets weird. The total price is -4.00€ (one ticket was priced at 4€). It actually accepted the negative value. And notice how I can pay with the wallet again.

What I think is happening here is that the application allows paying with the wallet since WalletBalance > CartPrice. The flaw here is likely due to weak and/or bad logic in the security checks.

For example, let’s say you check for the following things: 1) Is the wallet’s balance > 0€? If not, you can’t buy; 2) Is the wallet’s balance greater than the cart’s cost? If not, you can’t buy. If yes, proceed.

This would work. Now let’s say the checks are performed in reverse order. It first checks if my wallet’s balance (0€) is greater than the cart’s balance (-4€). It is, so I proceed without ever checking if my wallet has more than 0€. This probably isn’t the logic behind the application, but my point is that they never check for negative values.

After getting over the problem of the wallet not being displayed, I noticed another request being sent… On this request the “qtd” parameter is carried over and set with the value -1. I did not edit it.

Let’s leave it like that for now… The application displays a message - “Ticket bought successfully”. But… I still have no tickets. My guess is that the application is trying to give me -1 tickets.

Okay, cool. We haven’t really exploited anything, but we’re figuring how things work. Just because we don’t have a finding yet, doesn’t mean we’re not making progress.

Okay sorry, time for the actual fun stuff

Let’s do the exact same flow - try to buy a ticket, change the “qtd” parameter on that first request so the wallet is available to us (0€ > -4€ so it lets us use the wallet) and intercept that second request.

It turns out this request controls the number of tickets we will be getting. So we can just change it to 1.

In sum, we paid the price of -1 tickets for 1 ticket. Here it is, in our tickets tab!

If I press the ticket, it shows me the QR code that will be scanned at the entrance.

The end

As I mentioned, I’m not a party guy, but I did want to make sure this worked. Let’s say someone might or might not have gotten into a party for free once or twice. Perks of being a hacker’s friend.

Jokes aside, this was reported and fixed pretty quickly. The quantity parameters are now validated on server side and will throw an error if you try to get cute with them, so don’t bother trying to smuggle yourself into college parties.

Here’s me on 1.25x speed trying to explain this in video format. I did mix things up a bit with the logic of the payments so there’s a little correction halfway through the video. In my defense, that was take #1!

Updated: