Verder naar navigatie Doorgaan naar hoofdinhoud Ga naar de voettekst

A bankruptcy level event

(Treat your points as cash)

Companies can use “points” to help retain customers and this is especially true for industries where the product is often seen as a commodity and there is very little friction for a customer to change, such as in airlines, credit cards and restaurants. The more money a customer spends, the more points they earn. They can even get points through other activities such as referring friends. A company may give more points for specially purchased items, tying them to national holidays and even the customer’s birthday. Ultimately, these points can be redeemed for discounts on future purchases.

A partnership can be made between companies so points may be redeemed elsewhere. In a contrived example, for every “Waterproof Sponge” purchased, the customer earns 10 points. That company has partnered with Widget, Inc. and every 1000 points can be redeemed for a bag of Widget’s “Fire-resistant Marshmallows”.

One day, an irate customer calls Helpdesk to complain that their sponge actually retained water. Helpdesk can try to make things right through a variety of means and one can be to give the customer extra points to “apologize for the inconvenience.” Behind the scenes, this is locating the customer in the database and see that they are active and have converted points in the past. They can be granted 1000 points with just the click of the button - happy customer.

When we do a pentest of such an environment, we instinctively seize on this concept and ask, “What happens if I submit a very large or negative number?” The web front-end for Helpdesk won’t typically allow typing in an arbitrary value and is more likely to have a couple buttons with pre-programmed numbers. However, this is often turned into a simple API call where the numbers can be modified before being sent to the server.

In a well-designed system, the server would only allow the specific values that the front-end specified. At the very least, most servers will check an upper limit so putting in a large value is usually caught as an error. Sometimes this can be bypassed by sending a negative number, as that can be converted to a massive positive number later on and this has the effect of giving a customer a massive number of points.

[Side technical explanation - everything in a computer is just a sequence of bits. In a 32 bit signed integer, the very first bit is 0 to indicate a positive number and a 1 means a negative number. The remaining 31 bits are the value. However, when the computer is told to interpret this as an “unsigned” integer, it treats all 32 bits as a positive integer. So a -1 is reinterpreted as over four billion!]

Now a customer might have limits to how many points may be redeemed at a time, so even with billions of points, the damage can be limited. However, an attacker can create a script to run through all the customers and give each one of them nearly unlimited points. A large number of customers may convert these points and get an insane number of Fire-resistant Marshmallows which the original company would be responsible for paying.

A coworker and I once showcased a particularly nasty chain where a disgruntled Helpdesk employee can give billions of points to every customer and even make it appear that the attack was from a different employee.

The underlying problem was allowing negative numbers for points, and there were others:

  • A member of Helpdesk could use a single API call to retrieve the ID of every customer in the system. 
  • There was no Cross-Site Request Forgery token with any API call. This is typical when an API might be called from one program to another but not for a human-facing interaction.
  • The points API took the employee’s ID as a parameter to show who authorized the transfer and it didn’t validate that the employee ID matched the caller.
  • Finding out another person’s employee ID was helpfully provided by a name search feature.
  • There was no rate limiting on the API calls.

This scenario could have played out with a disgruntled or otherwise compromised account in this way:

  • First, search for names of other Helpdesk employees. Even using common names is likely to quickly find a match.
  • Get a list of every customer in the database.
  • Loop over them all and give them -1 points each and be sure to use the employee ID of the other user.
  • Customers who accidentally discover these points will redeem them, especially for expensive tangible products at other companies. Social media platforms can spread this hack far and wide without any assistance from the disgruntled Helpdesk worker.

Points that are redeemed via a third party, especially for expensive tangible goods, would require the original company to send a significant amount of cash to pay for those goods, or to lose their reputation by refusing to pay. Completing this on a three day weekend such as a busy national holiday with already heavy media promotions would create what we labeled in the report as a “bankruptcy level event.”

While there is an obvious one-line fix to ensure that server verifies the points amount, this is an opportunity to take a big step back to think of a comprehensive solution. Nonsensical values can get into the database through a wide variety of bugs and malicious actions such as corrupting a backup file and then causing that to be restored. What if points were treated the same as cash? Now we can introduce a concept that there are limited number of points which exist in the entire ecosystem. They are only created once in the beginning and simple double-entry bookkeeping makes things almost effortless.

To fix the above scenario: at the very beginning, a number of points can be put aside for Helpdesk support. When a Helpdesk worker signs in for the day, a fixed amount is transferred from the support bucket and put into the worker’s account. During the day, they may be authorized to transfer up to a certain number of points per customer based on their discretion. At the end of the day, remaining points are put back in the support bucket. The points move from one place to another and are never created or destroyed.

Periodically, the system can query the overall number of points and would see that the total always remains fixed system-wide. If that’s violated, alerts can be raised well in advance. While not a perfect fix in the face of deliberate attacks, the point is to detect and act well before significant damage happens.

Takeaways:

  • If you use a point system, especially where they may be redeemed for tangible goods or with third parties, strongly consider using a double-entry bookkeeping methodology to ensure that points cannot be created.
  • Consider how you might detect issues before significant damage can be done: Do you have sanity checking on builds or overnight stress testing? Can you quickly find out how many points were redeemed over the past few hours, such as via a dashboard, and is that consistent with the past?
  • What would be the warning signs that a disgruntled employee is looking for vulnerabilities? Can you automate that detection? 

Pro tip for pentesters: don’t simply check for -1 as a value, try several. I had a different client who prevented small negative numbers and the use of -1000 led to compromise.

Disclaimer: This post was written entirely without ChatGPT or other related tools.