Three weeks ago I went out to a pub for dinner. Due to covid restrictions there are no paper menus anymore and the waitress gave me a card to place my order.
The card she gave me had a QR code and a 5-digit number. I scanned the QR code and opened the website it pointed to. To login I used that 5-digit number. I placed my order. So far so good.
When suddenly a hamburger button caught my attention. I pressed it, but mostly I clicked on the first item in the menu because, judging by its text, it seemed “nice” to have a look at the order I had just placed:
Uh?! 4751€?! Definitely not me! To my surprice that page listed many orders, not just mine, and they were also old. That’s interesting.
Once back home, I wanted to understand it more. I opened the website in my browser, but I failed to login because my 5-digit number “expired”, then I gave it a few tries by increasing it and it worked :-)
I took a look at the JavaScript files to find the one that makes the request to retrieve the orders:
$.ajax({
type: "POST",
url: '/include/ajax.php?f=getlist&t=orders',
data: {
src:[
{
name:"self_cart_id",
value:app.table_id,
compare:"equal"
}
],
orderby: "id DESC"
}
Let’s do the same request, changing the value
(app.table_id
) parameter and see what happens:
curl 'https://$HOST/include/ajax.php?f=getlist&t=orders' -X POST --data-raw "src%5B0%5D%5Bname%5D=self_cart_id&src%5B0%5D%5Bvalue%5D=1&src%5B0%5D%5Bcompare%5D=equal&orderby=id+DESC"
I got fewer orders. Then I increase the table_id
and I got even less orders. Mmm, I take a second look at the parameters and then I realize that’s a query statement! At this point I played a bit with the parameters until I removed the value
parameter completly. Well, now I got 347752 orders and they are even paginated:
"success": 1,
"pag": "1",
"per_pag": 500,
"total_records": 347752,
"total_pages": 696,
Fortunately, there was no sensitive information. I got all the orders made in the last ~2 years from all the pubs scattered around italy (the pub is part of a franchising). There was some Deliveroo/UberEats/Glovo id, but nothing sensitive. Not yet.
Back to the JavaScript file, there were few interesting calls:
url: '/include/ajax.php?f=get&t=customers&id='+app.customer_id,
url: '/include/ajax.php?f=edit_customer&t=self_cart&id='+app.table_id,
url: '/include/ajax.php?f=getlist&t=categories',
url: '/include/ajax.php?f=getlist&t=products',
url: '/include/ajax.php?f=get&t=products&id='+$(this).attr("data-id"),
url: '/include/ajax.php?f=edit_product&t=self_cart&id='+app.table_id,
I tried with the most tempting, customers
, and here we go:
curl 'https://$HOST/include/ajax.php?f=getlist&t=customers'
"success": 1,
"pag": 1,
"per_pag": 500,
"total_records": 11928,
"total_pages": 24,
"rows": [
{
"surname": "<REDACTED>",
"name": "<REDACTED>",
"email": "<REDACTED>",
"mobile": "<REDACTED>",
"addresses": [
{
"name": "<REDACTED>",
"surname": "",
"address": "<REDACTED>",
"zipcode": "<REDACTED>",
"city": "<REDACTED>",
"province": "<REDACTED>",
"coord": "44.6<REDACTED>, 10.6<REDACTED>",
"doorphone": "<REDACTED>",
}
]
"barcode": "https:\/\/api.$ANOTHER_HOST\/include\/barcode.php?f=png&s=code-128&d=1",
That single request returned 500 out of 11928 results that include full names, phone numbers and addresses of real persons who placed their orders through one of those food delivery apps.
Back to the JavaScript file, the edit_product
call is also very tempting (what if I change the price of a product, place my order, and then restore the original price?), but I had already eaten dinner and didn’t try it.
Finally, the $ANOTHER_HOST
domain got my attention because it points to a different domain. I googled it and I then realized that this pub was using an e-commerce made by a company that claims on their website that they serve 570 restaurants in Italy. Which makes that 11928 way larger.
To confirm this, I first googled the footer text in the e-commerce and actually found ~100 other websites using it that are affected by the same issue. Then, I found others using DNS enumeration targeting the $ANOTHER_HOST domain.
I warned the company about the unauthenticated endpoints and the possible data leak affecting them and their customers. They politely replied that they don’t provide bug bounties and the endpoints have been patched.
Tags: coordinated vulnerability disclosure, security, privacy