Write-up of a reMarkable security vulnerability
- Write Ups
- by Jacob Riggs
- 30-10-2023
This is a write-up on an integer overflow vulnerability that enabled me to zero balance any order at reMarkable. I was looking to buy a reMarkable tablet as a gift to my boss (for putting up with me) and noticed they had a VDP. Unfortunately, they don’t offer bug bounty rewards, but curious about their ecommerce setup, I figured I’d take a look at the application checkout logic to see how it works.
The first step was to create a new order:
Observing the raw HTTP traffic I could see that adding this to cart naturally creates a PUT request to the https://api.store.remarkable.com/v2/carts/77b5a997-500d-4ea6-9f53-2958a9c99cb2/items endpoint (where 77b5a997-500d-4ea6-9f53-2958a9c99cb2 represents a uniquely generated 128-bit value UUID for the cart):
In the request body we can see the sku value represents the item code and the quantity value for the selected item is represented as an integer.
We can tell this is an integer by the fact it’s not encased in quotation marks. Nothing unusual about this, it’s just important to note the difference between an integer and a string, as depending on how the backend handles these values there might be differences in how the application behaves.
Where the quantity value is a string:
Where the quantity value is an integer:
When the natural request is processed by the server, we observe a 200 response where we can see the arithmetic operations performed on the quantity are consistent with what we expect. They’re clearly being stored in the database as an integer.
Now let’s try modifying the request integer value to 999999999999999 (15 digits):
We observe another 200 response:
And here is the bottom part of the same response:
And if we refresh the checkout page in the browser, we can see the quantity has been updated.
The backend has processed the given quantity integer value with the expected arithmetic operations, which calculates the total value amount by simply multiplying the price of an item by its represented quantity.
Why is this interesting?
Well firstly, it’s possible that adding this many items to a unique cart ID could temporarily affect stock analytics. For example, if there are only 1,000 units of a particular item in stock, an attacker adding an amount beyond this to their basket may render an “out-of-stock” error for other legitimate customers attempting to make purchases, but this really depends on how the application is configured to handle stock control.
The more interesting element is the fact that there doesn’t appear to be any server-side input validation limiting the quantity integer in requests to a maximum value.
Remember we submitted 15 digits before? What response might we get if we submit 16 digits?
And if we now refresh the checkout page in the browser?
So to recap:
-
999999999999999 (15 digits) produces a total of £33,597,677,788,712,864
-
9999999999999999 (16 digits) produces a total of £0
To understand why this happened, it’s important to understand how integer values are stored by a database. Integer values are stored in databases using a specific data type that determines the range of values the integer can hold. The storage size and range depends on the database system and the specific integer type chosen.
In this example, the backend is likely using a 64-bit signed integer to store the quantity values. When the quantity value is increased to 16 digits, it exceeds the maximum value that can be stored in a 64-bit integer, resulting in an integer overflow.
This vulnerability exists because the server is attempting to store 16 digits inside an integer variable value which is higher than the maximum value the variable can hold. When an arithmetic operation results in a value that exceeds the maximum value the integer type can store, the value wraps around to the minimum value and continues counting up from there. Similar to how a seven-digit odometer within a car works to display the mileage - it can only store seven-digits, so if the counter hits 9999999 miles then the next integer up will roll it back to 0.
To mitigate this vulnerability, the application should ideally perform input validation checks to ensure that submitted quantities are within acceptable ranges and use appropriate data types for database objects that can handle the range of values the application expects to process.
This was reported in-gratis and in good faith to reMarkable within 48 hours of discovery, in accordance with their VDP. My report was well received and they have since provided recognition by linking to my blog from their website at the bottom of the Vulnerability Disclosure Policy page.
I have since purchased my boss a Type Folio bundle (which was wrapped spectacularly I might add).
He takes a lot of notes in the course of his work, so expect it will serve him well, and perhaps persuade him to read more of my write-ups!
ABOUT THE AUTHOR