Reverse engineering the Costa App bean QR codes

Reverse engineering the Costa App bean QR codes was always going to be a challenge but here goes!
What we know
- That this exercise is highly unlikely to lead to free Coffee as the QR codes are likely generated at the point of sale and stored in a database centrally somewhere as the machines are Internet-enabled
- In any event it does lead to free Coffee this would be reported to Costa as a bug immediately and any further investigation would cease.
- That the price of Medium or Large drinks varies from machine to machine
- That the QR codes continue to work days and even weeks after the initial purchase even if they have been collected, meaning the app must be checking either that the data held on the QR codes matched given criteria, a checksum for example, or more likely that the QR codes are being matched against an entry in a database which doesn’t clear out used QR codes.
- That when ordering the same drink from the same location there are similarities between the QR codes as shown below.
The Green shaded areas are the same with the differences shown in light brown.


- That the data held on the QR codes look to possibly be some form of base64 encoded string separated by full stops with = as padding.
- That when the data from the QR Code is split up by full stop and is passed through a base 64 decoder you get back JS Data as below, JS Data then formatted with https://www.prettifyjs.net/
6VHKntKNERSLLW8qMw+eTUqZyvG6Ii7B8EbnIK0aL8Y=.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtYWNoaW5lTm8iOiIxMzI2NTEwOSIsInNpdGVJZCI6IjYyNTU4MDAxIiwia2V5SWQiOiIxMDkzMDEwMTgxMjM4MjgiLCJkcmlua0lkIjoiTEFUVC1MQzEtSC0xQ0FSLUMtU0MwMSIsImFtb3VudCI6MjgwLCJjdXJyZW5jeSI6IkdCUCIsInRpbWVzdGFtcCI6IjIwMjItMDEtMDZUMTY6MDE6MjgiLCJndWlkIjoiNzk1ZTQwNDMtZTkzNy0zMWU3LTJhNWMtZGM5MjZjMTBlYjhmIn0=.DlUEnnCuK+4qdy7MIFWG7i5u4dErBeUEIh55wsVOgNk=
{
“alg”: “HS256”,
“typ”: “JWT”
}
{
“machineNo”: “13265109”,
“siteId”: “62558001”,
“keyId”: “109301018123828”,
“drinkId”: “LATT-LC1-H-1CAR-C-SC01”,
“amount”: 280,
“currency”: “GBP”,
“timestamp”: “2022-01-06T16:01:28”,
“guid”: “795e4043-e937-31e7-2a5c-dc926c10eb8f”
}
The JS Code looks to contain all the information from the point of sale, i.e. the machine unique ID, the unique ID of the site, the drink chosen, the price paid, currency paid in, a date/time and a GUID that I would suspect would be unique to each drink purchase.
The drink in question looks to be a large ( “amount”: 280 (Large drinks sell at either £2.75 or £2.80 and Medium drinks sell at either £2.40 or £2.50 from the machine I have used)) Caramel Latte ( “drinkId”: “LATT-LC1-H-1CAR-C-SC01″ )
It crossed my mind to wonder how many drinks could be sold using the GUID and for them to not have duplicated. It turns out its a LOT. Providing Costa are doing some form of checking when they create the GUID to ensure a duplicate value is not generated on the off chance the total number of unique GUIDs calculated as there are 122 random bits (128 – 2 for variant – 4 for version) so this is 2^122 or 5,316,911,983,139,663,491,615,228,241,121,400,000 possible combinations or five undecillion three hundred sixteen decillion nine hundred eleven nonillion nine hundred eighty-three octillion one hundred thirty-nine septillion six hundred sixty-three sextillion four hundred ninety-one quintillion six hundred fifteen quadrillion two hundred twenty-eight trillion two hundred forty-one billion one hundred twenty-one million four hundred thousand cups of Java!
- That when looking at the first few lines of the JS Code we can see that part (or more likely all) of the data held on the QR Code looks to be a JSON Web Token (with bits being base64 encoded). We can bob this code into a JSON Web Token decoded and get the same info as we get from decoding the separate base64 parts, this can be seen here.
- Unfortunately as we do not have the 256bit HMACSHA256 secret Costa used to sign the QR Code data we aren’t able to re-sign new payload data so that the Costa App recognises it as being a legitimate claim. Well done Costa on a truly ingenious way of passing data in plan sight but fully secure 10/10!

- This still leaves the first part of the QR Code data shown in green, it cannot be decoded from base64 so unsure as to what this part is. Investigations will continue….
6VHKntKNERSLLW8qMw+eTUqZyvG6Ii7B8EbnIK0aL8Y=.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtYWNoaW5lTm8iOiIxMzI2NTEwOSIsInNpdGVJZCI6IjYyNTU4MDAxIiwia2V5SWQiOiIxMDkzMDEwMTgxMjM4MjgiLCJkcmlua0lkIjoiTEFUVC1MQzEtSC0xQ0FSLUMtU0MwMSIsImFtb3VudCI6MjgwLCJjdXJyZW5jeSI6IkdCUCIsInRpbWVzdGFtcCI6IjIwMjItMDEtMDZUMTY6MDE6MjgiLCJndWlkIjoiNzk1ZTQwNDMtZTkzNy0zMWU3LTJhNWMtZGM5MjZjMTBlYjhmIn0=.DlUEnnCuK+4qdy7MIFWG7i5u4dErBeUEIh55wsVOgNk=
Known URLs / Ports
https://costa-platform.com / 23.55.6.191 (Akamai Technologies, Inc.)
Collected QR Codes
19/01/2022 - Costa Jct 41 M1 - Regular Mocha
iWgoFdiNztSrkda1LdV8OrkpUjzF0YZgYLpuNLUkPPI=.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtYWNoaW5lTm8iOiIxNTQ4MTA4MiIsInNpdGVJZCI6IjYwNDk3MDA2Iiwia2V5SWQiOiIxMTMyNjAzMTkxNDM3NTYiLCJkcmlua0lkIjoiTU9DQy1NQzEtSC0wMDAwLUMtQ0IwMSIsImFtb3VudCI6MjUwLCJjdXJyZW5jeSI6IkdCUCIsInRpbWVzdGFtcCI6IjIwMjItMDEtMThUMTY6MDE6MjEiLCJndWlkIjoiZGNiMTQzOGMtZTNiYS02NzNhLTVhYmEtYTVhNjZhMGZlMGU2In0=.L0NOQpu27V/KGHagEp+g5oD+ZqF11Pv0vItTHAktdKI=
{
“alg”: “HS256”,
“typ”: “JWT”
}
{
“machineNo”: “15481082”,
“siteId”: “60497006”,
“keyId”: “113260319143756”,
“drinkId”: “MOCC-MC1-H-0000-C-CB01”,
“amount”: 250,
“currency”: “GBP”,
“timestamp”: “2022-01-18T16:01:21”,
“guid”: “dcb1438c-e3ba-673a-5aba-a5a66a0fe0e6”
}
06/01/2022 - Costa at end of parkway - medium caramel latte
6VHKntKNERSLLW8qMw+eTUqZyvG6Ii7B8EbnIK0aL8Y=.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtYWNoaW5lTm8iOiIxMzI2NTEwOSIsInNpdGVJZCI6IjYyNTU4MDAxIiwia2V5SWQiOiIxMDkzMDEwMTgxMjM4MjgiLCJkcmlua0lkIjoiTEFUVC1MQzEtSC0xQ0FSLUMtU0MwMSIsImFtb3VudCI6MjgwLCJjdXJyZW5jeSI6IkdCUCIsInRpbWVzdGFtcCI6IjIwMjItMDEtMDZUMTY6MDE6MjgiLCJndWlkIjoiNzk1ZTQwNDMtZTkzNy0zMWU3LTJhNWMtZGM5MjZjMTBlYjhmIn0=.DlUEnnCuK+4qdy7MIFWG7i5u4dErBeUEIh55wsVOgNk=
{
“alg”: “HS256”,
“typ”: “JWT”
}
{
“machineNo”: “13265109”,
“siteId”: “62558001”,
“keyId”: “109301018123828”,
“drinkId”: “LATT-LC1-H-1CAR-C-SC01”,
“amount”: 280,
“currency”: “GBP”,
“timestamp”: “2022-01-06T16:01:28”,
“guid”: “795e4043-e937-31e7-2a5c-dc926c10eb8f”
}

04/01/2022 - Rontec garage near Kings School - caramel latte
RuxGSlzz1dNziTyuCZ0iKF4pMR8WxzJld1r+U/ZxjkA=.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtYWNoaW5lTm8iOiI4MjAwNTMyNCIsInNpdGVJZCI6IjYzMDg2MDQ5Iiwia2V5SWQiOiIxMTQxNDEyMjAxMjUyMzIiLCJkcmlua0lkIjoiTEFUVC1NQzEtSC0xQ0FSLUMtVFAwMSIsImFtb3VudCI6Mjc1LCJjdXJyZW5jeSI6IkdCUCIsInRpbWVzdGFtcCI6IjIwMjItMDEtMDRUMDc6MjA6MjYiLCJndWlkIjoiMDM5OGZkZmQtZTYyYS04ZDg4LWI2M2QtNTA1OTY5NGNlODRhIn0=.0uRep1JpShAubc2zelgAcYx0PEwbwlIBYl4y/1NSs8c=
{
“alg”: “HS256”,
“typ”: “JWT”
}
{
“machineNo”: “82005324”,
“siteId”: “63086049”,
“keyId”: “114141220125232”,
“drinkId”: “LATT-MC1-H-1CAR-C-TP01”,
“amount”: 275,
“currency”: “GBP”,
“timestamp”: “2022-01-04T07:20:26”,
“guid”: “0398fdfd-e62a-8d88-b63d-5059694ce84a”
}

11/1/2022 - Spar Manvers - 2 x caramel latte
sf12FtQsb0281Lb3Hkr8rh9ORBDdNroAQ/mI/oC3HdY=.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtYWNoaW5lTm8iOiIxNDA0NzU5OSIsInNpdGVJZCI6IjYwMDA4MDcxIiwia2V5SWQiOiIxMDcyMTA5MjAxMDQ1NTMiLCJkcmlua0lkIjoiTEFUVC1NQzEtSC0xQ0FSLUMtU0MwMSIsImFtb3VudCI6MjUwLCJjdXJyZW5jeSI6IkdCUCIsInRpbWVzdGFtcCI6IjIwMjItMDEtMTFUMDc6MTU6MzMiLCJndWlkIjoiYTA2MDIyYTktMmQ5NS1lMTc4LTUwZWMtNDk0ZjY1ZTVmMmM5In0=.Y4b55t5X4z3p/0Jt4HqbKFZ2JkKOKoNvdsrCI4kCieo=
{
“alg”: “HS256”,
“typ”: “JWT”
}
{
“machineNo”: “14047599”,
“siteId”: “60008071”,
“keyId”: “107210920104553”,
“drinkId”: “LATT-MC1-H-1CAR-C-SC01”,
“amount”: 250,
“currency”: “GBP”,
“timestamp”: “2022-01-11T07:15:33”,
“guid”: “a06022a9-2d95-e178-50ec-494f65e5f2c9”
}

1ChphnZRKePjf14USDRICuEABbI8AN0O3iuHnki97yQ=.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtYWNoaW5lTm8iOiIxNDA0NzU5OSIsInNpdGVJZCI6IjYwMDA4MDcxIiwia2V5SWQiOiIxMDcyMTA5MjAxMDQ1NTMiLCJkcmlua0lkIjoiTEFUVC1NQzEtSC0xQ0FSLUMtU0MwMSIsImFtb3VudCI6MjUwLCJjdXJyZW5jeSI6IkdCUCIsInRpbWVzdGFtcCI6IjIwMjItMDEtMTFUMDc6MTc6MDEiLCJndWlkIjoiZmI0ZWIwZTQtOTQ3Ny03ZjNjLTQyM2UtOWZjOTE0ZGM2MGU5In0=.b3Fb+SkuCqTjcFQ5BXC+4YqEa1dWdloi4FifBiDNMiA=
{
“alg”: “HS256”,
“typ”: “JWT”
}
{
“machineNo”: “14047599”,
“siteId”: “60008071”,
“keyId”: “107210920104553”,
“drinkId”: “LATT-MC1-H-1CAR-C-SC01”,
“amount”: 250,
“currency”: “GBP”,
“timestamp”: “2022-01-11T07:17:01”,
“guid”: “fb4eb0e4-9477-7f3c-423e-9fc914dc60e9”
}
