How I Hacked TicketMaster Barcodes

I recently bought concert tickets from TicketMaster. If they had sent me a regular printable PDF ticket that I could save offline on my phone, this article would never have existed. But it's 2024: everything done online is no longer easy.

After completing the purchase, TicketMaster informed me that I would not be able to print tickets for the event. The site issues tickets using the Mobile Entry system, aka SafeTix. They come in the form of an updatable barcode displayed in the TicketMaster web app or Android/iOS app.

“Screenshots Won't Let You Pass,” But Chrome Developer Tools Will

Maybe I'm getting old, but I still remember the days when printed tickets were everywhere. You could print out your tickets after purchasing them online or even at the box office (wow!) and bring those paper tickets to the entrance of the event. They could be saved as PDFs and viewed on almost any device. PDF tickets can be used even when your phone doesn't have an internet connection. Paper tickets can be used even when you don't have a phone. If I bought a ticket from an official company that sells them (and not from a shady middleman), I knew for sure that it was real. There was no risk of being denied entry with it. You could safely send them to friends via WhatsApp, iMessage, Signal, email, or even hand-deliver printed tickets.


And these updatable barcodes are far from perfect. I personally encountered this last year when I went to very popular concert that used a similar system with tickets based on updatable QR codes. Many people, including me and my friends, got stuck at the turnstile due to a bunch of problems with broken barcodes. The main problem:

The phone does not have an internet connection, so the QR code does not load.

The event was so crowded that cell towers and WiFi were overloaded. Internet access was spotty and intermittent.

Apparently the company that created this ticketing nightmare (I can't remember the name) didn't have a helpline, and even if they did, it probably wouldn't work. There was nothing the event staff could do. All we could do was wave our phones in the air and hope that the ticketing app would update sooner or later.

In the end, I luckily managed to “catch” a mobile operator and download the QR codes for the tickets. We left a bunch of other ticket holders waving their phones at the turnstiles. I have no idea if they managed to get in.

I paid three hundred dollars for the experience of using this high-tech system.

Marketing

TicketMaster advertises its SafeTix technology as a panacea for scammers and scalpers.

SafeTix™ uses a new unique barcode that automatically updates every few seconds to ensure it cannot be stolen or copied, keeping your tickets secure and safe.

Ticketmaster SafeTix uses a new, unique barcode system that automatically updates every 15 seconds, greatly reducing the risk of fraud with stolen or counterfeit tickets.

Source

Our secure ticketing technology reduces the risk of ticket fraud by eliminating theft and counterfeiting. When you buy mobile tickets from Ticketmaster, you can rest assured that you will get the seats you paid for.

Source

And there is also this masterpiece:

If you look closely at the ticket, you will notice that it has a moving strip, making it in some sense alive. This is the same technology that works every second to protect you.

Oh, don't push it, TicketMaster. It's just CSS animation, have some sense.

This is what made me wary:

The barcode on the mobile ticket includes security technology, meaning that screenshots and printouts of the ticket cannot be scanned.

It gave me flashbacks to last year's concert, and I imagined myself waving my phone around even more frantically, praying for a cell connection more than Saul Goodman in the desert.

But TicketMaster was ready to allay my worries:

Worried about cellular coverage at an event? A ticket will solve that problem. If you view it in the app, the ticket will be automatically saved and always ready.

Great, if I trust the app and it doesn't break on the day of the concert, then everything will be fine. Unfortunately, I don't trust it, and I don't want to install this spyware on my phone.

Motivation

The reasons for TicketMaster to promote this technology are quite obvious:

  • SafeTix makes it difficult to resell tickets outside of its closed, high-margin resale marketplace, where the company can buy cheap and sell high to people with no alternative.
  • It forces users to install TicketMaster's proprietary, closed-source app, which gives the company more information about users' devices and behavior.
  • Buyers cannot save or transfer tickets outside of Ticketmaster. This forces ticket holders to share friends' contact information with TicketMaster, which can be used to create social graphs and reduce privacy.

TicketMaster would never admit to such a motivation, but these consequences certainly occur even if the company had no such intentions; and all of this pleases TicketMaster's co-owners, but not its customers.

Contradiction

If you have experience with computers and software, you may have the same question I had after reading TicketMaster's marketing statements.

How can tickets be saved offline if they cannot be transferred outside the TicketMaster system?

The ticket is digital. Saving data offline is similar to copying it to a hard drive. If the data can be copied, then it can be transferred. And if it can be transferred, then you can also sell it/

This contradicts TicketMaster's claims. You can't create reliable DRM for tickets if those tickets can be viewed offline.

So what does TicketMaster actually do to create these updatable barcodes?

The first thing I had to do was study the barcodes themselves to see what I could glean from them. Their format is fairly simple. These are barcodes

PDF417

encoding text in

UTF-8

. As mentioned above, that blue stripe floating across the barcode is simply useless.

CSS animation

: it doesn't actually interfere with scanning barcode screenshots because PDF417 has built-in error correction properties.

It looks like some older barcodes encoded other text formats, but the barcodes in my TicketMaster web app generated data like this embed:

B4cq2BdFCpFl90TDuYD3pWfRDSO6eQ3bR0YQqsDnyfciuVFkKp+m0zI+a2lgfonY::140013::481994::1707070843

Note: The data is not taken from a real SafeTix barcode. I do not want TicketMaster to be able to identify and track me.

It looks like four blocks of data separated by colons. First comes some data, encoded Base64followed by two six-digit numbers, and at the end is indicated Unix timestamp.

When the barcode changes every 15 seconds, its contents change slightly. The base64 data remains static, only the six-digit numbers and the timestamp change.

B4cq2BdFCpFl90TDuYD3pWfRDSO6eQ3bR0YQqsDnyfciuVFkKp+m0zI+a2lgfonY::358190::038184::1707070859

These six-digit numbers behave in many ways like

Time-based One-Time Password (TOTP)

used in 2FA applications such as

Authy

And

Google Authenticator

. These are rolling six-digit numbers that can be generated from a shared secret and a timestamp.

My gut feeling was that the first two numbers would indeed be TOTP, generated from different secrets based on a Unix timestamp appended to the end of the barcode data. This makes sense: TicketMaster wouldn't want to reinvent the wheel with this system, so they used a cryptographic tool as part of it.

The base64 data was still a mystery. After decoding the data into its 48 bytes, I couldn't discern any meaningful data structures from it. It looked more or less like random data, and since it didn't change when the barcode was updated, it was probably some random bearer token identifying the ticket owner and the ticket itself.

When a ticket is scanned at the turnstile, TicketMaster (or perhaps the event organizer) looks for ticket metadata using that bearer token, and then validates the two OTPs with two secrets stored in its database. If both steps are successful, the ticket is authentic and security can let you through.

Little secret

TOTP can be flexibly configured, but in general the software industry has chosen a certain set of parameters to standardize TOTP. Two parts are sufficient to generate TOTP:

  • Shared secret (just a byte array)
  • Working hours

If you have both, you can generate any amount of TOTP while you are

completely offline

.

There are two TOTPs in the barcode data, so I probably need to find two shared secrets. If I have both, plus the bearer token, I can generate any number of valid barcodes.

That is, my task became much clearer: I need to find out where these tokens and secrets come from.

Debugging a web application

I turned on my Android phone and connected its Chrome browser to Chrome DevTools on my desktop. This gave me access to the TicketMaster API and source code.

Recording network requests While loading the TicketMaster barcode viewer, I came across one particularly curious query:

POST /api/render-ticket/secure-barcode?time=1707071877481&amid=XXXXXXXXXXXXXXX&_format=json

The response data to it:

{
  "deviceId": "8f651107-acad-42a4-b3a6-019aaac41960",
  "deviceType": "WEB",
  "deviceOs": "ANDROID",
  "userAgent": "Mozilla/5.0 (Linux; Android 10; K) XXXXXXXXXXXXXXX",
  "nfcCapableDevice": true,
  "tickets": [
    {
      "eventId": "myevent.50.38991943985838B9",
      "section": "3",
      "row": "A",
      "seat": "1",
      "barcode": "481848590102K",
      "addedValue": false,
      "generalAdmission": false,
      "fan": null,
      "token": "eyJiIjoiNDgxODQ4NTkwMTAySyIsInQiOiJUR1JMWUNxQWYyQ1MvQmxILzh5dThZdkhoV055TW8xUW9CYTI5UTVqVkN4V2xBcE5NbnczSlJkeU9UcFVVWUFDIiwiY2siOiJiOTg0MzJlZDIzYjhmMmJkYTgyMzQ4MjE2MjI5ZjRkMjdjZTlkMDYzIiwiZWsiOiJiMzUxOTM2NGUwYzc5MTRjMWY5ZDU5ZDM1NjUyYTA0MDY3ZDJmNjQ3IiwicnQiOiJyb3RhdGluZ19zeW1ib2xvZ3kifQ==",
      "renderType": "rotating_symbology",
      "passData": {
        "android": {
          "jwt": "eyJhbGciOiJSUzI1NiJ9.XXXXXXXXXXXXXXXXXX.YYYYYYYYYYYYYYYYYYYYYYYYYY"
        }
      },
      "bindingRequired": true,
      "deviceKeyBindingRequired": false,
      "deviceSignatureRequired": false,
      "segmentType": "NFC_ROTATING_SYMBOLOGY",
      "ticketId": "50.3.A.1"
    }
  ],
  "globalUserId": "k39Fj4lNfOS4Zq481bxIWg"
}

Note: I have scrambled identifying information to avoid punishment.

Please note the property token object in array tickets. I decoded it from base64 and found that it was another JSON object:

{
  "b": "481848590102K",
  "t": "TGRLYCqAf2CS/BlH/8yu8YvHhWNyMo1QoBa29Q5jVCxWlApNMnw3JRdyOTpUUYAC",
  "ck": "b98432ed23b8f2bda82348216229f4d27ce9d063",
  "ek": "b3519364e0c7914c1f9d59d35652a04067d2f647",
  "rt": "rotating_symbology"
}

  • It seems to be a property b coincides with the property barcode tickets object.
  • Property rt coincides with the property renderType tickets object.
  • Property t — is a base64 encoded byte array of length 48 bytes.
  • Properties ck And ek — these are arrays of bytes in hexadecimal encoding, each 20 bytes long.

I scanned the last barcode displayed in the TicketMaster web app again:

TGRLYCqAf2CS/BlH/8yu8YvHhWNyMo1QoBa29Q5jVCxWlApNMnw3JRdyOTpUUYAC::492436::240860::1707074879

Excellent. That is to say.

t

— it is a static bearer token. I wonder if there will be

ck

And

ek

those TOTP secrets I'm looking for?

Upon further examination of the minified source code of the TicketMaster website, I found in the file presence-secure-entry.js function generateSignedTokenwhich the web application uses to generate barcode data.

key: "generateSignedToken",
value: function
    var e = arguments.length > 1 && void 0 !== arguments[1] && arguments[1];
    if (this.displayType === l.ROTATING) {
        var n = [this.eventKey, this.customerKey]
          , a = t;
        if (this.eventKey) {
            var u = new Date(a);
            a = u instanceof Date && "Invalid Date" !== "".concat(u) ? u : new Date
        }
        var A = n.reduce((function(t, n) {
            if (n) {
                var u;
                try {
                    u = i.b32encode(o.a.hexToByteString(n))
                } catch 
                    u = ""
                }
                var A = r.a(u, 15).now(a, e);
                t.push(A)
            }
            return t
        }
        ), [this.rawToken]);
        if (this.eventKey) {
            var s = Math.floor(a.getTime() / 1e3);
            A.push(s)
        }
        return A.join("::")
    }
    return this.barcode
}

Minification makes the code harder to read, but it seems that

ek

And

ck

are deciphered as

eventKey

And

customerKey

and the bearer token

t

is called in the code above

rawToken

.

The two TOTPs appear to be generated in 15 second increments, but are otherwise created in the same way as the industry standard SHA-1 TOTPs found in any mobile 2FA app. The first is generated using eventKeyand the second one – with the help of customerKey. Next, the Unix timestamp used to generate both TOTPs is added to the end to help with server-side verification.

To test my interpretation, I installed a command line tool for working with TOTP oathtoolThen I substituted ck, ek and the Unix timestamp into the TOTP SHA-1 generator with an interval of 15 seconds:

$ sudo apt install oathtool -y
...
$ date=$(python3 -c 'import datetime; print(datetime.datetime.fromtimestamp(1707074879).isoformat())')
$ oathtool --totp --time-step-size 15s -N "$date" b3519364e0c7914c1f9d59d35652a04067d2f647
492436
$ oathtool --totp --time-step-size 15s -N "$date" b98432ed23b8f2bda82348216229f4d27ce9d063
240860

Excellent! This matches the two TOTPs in the barcode:

TGRLYCqAf2CS/BlH/8yu8YvHhWNyMo1QoBa29Q5jVCxWlApNMnw3JRdyOTpUUYAC::492436::240860::1707074879

Probably,

eventKey

unique to the specific event for which tickets are purchased, and

customerKey

probably unique to the ticket holder. They don't seem to change at all, unlike

rawToken

which is updated every time the TicketMaster web application is updated. However, if you leave the page alone for a few hours,

rawToken

will not change, meaning it should presumably remain valid even after the web application is closed.

What about the field? passData.android.jwt? Does it do anything? I'll save you some time: it turns out it's not needed to verify the ticket at all; it's probably just an authentication token used to store the ticket in Google Wallet user. I don't use Google Wallet because I care a lot about privacy and try to stay as far away from Google services as possible.

Pirate tickets

Now I know everything I need to duplicate TicketMaster barcodes in my own app or even resell a ticket outside of TicketMaster's fenced marketplace. All I need to do is extract the base64 encrypted property

token

from API endpoint

/api/render-ticket/secure-barcode

or develop a way to dynamically obtain this token using TicketMaster session credentials.

This line token in base64 encoding and there is ticket, at least for the guards at the entrance. If you have valid rawToken, eventKey And customerKeythen you can generate valid PDF417 barcodesindistinguishable from the barcodes from the official TicketMaster application. Without the ability to check a document with a photo at the entrance, the employees of the organizing company will not be able to understand whether the person at the turnstile is the same as the person to whom the ticket is registered in TicketMaster.

The funny thing is that TicketMaster itself has simplified the process of extracting tokens: when a barcode renderer component is placed on a web page, token is automatically output to the browser console.

r.a.log(
  "'render' called on '".concat(
    "pseview-".concat(J.get(this)),
    "' with token '", this.token, "'"
  )
)

This means that in order to obtain

token

we don't even have to bother with injecting our own custom scripts into the page. Simply open the SafeTix barcode in the TicketMaster web app,

connect chrome instance on phone to chrome devtools on laptop

and open the console. It will display

token

. You can copy it and use it however you like.

Lifespans

The only unknown factor here is lifespan.

rawToken

. It is difficult to know exactly how TicketMaster's backend server uses

rawToken

to search for a ticket. Probably new

rawToken

generated every time a client accesses an endpoint

/api/render-ticket/secure-barcode

.

I have no idea how long rawToken remains valid. It is possible that only one TicketMaster account can be valid at a time. rawToken. TicketMaster developers could have designed the system in such a way as to prevent multiple valid tickets from being retrieved at the same time.

If a set is simultaneously valid tokenthen one person could buy dozens of tickets, extract the amount he needs token and resell them “under the counter”. It would be great if TicketMaster didn't think about this, because then I could extract token tickets for friends and distribute them without having to communicate with TicketMaster's data collection pipeline.

The only authoritative source I could find on this topic is mysterious document on TicketMaster Developer API Documentation website.

Partners must refresh the token 20 hours before the event and every time the ticket is displayed in the app.

FAQ

  1. How often should the token be refreshed? You need to refresh the token every time a viewer opens and views a ticket in your app, and 20 hours before the event. If we can't refresh the token when the viewer opens the ticket at the turnstile, the SDK will try to use the token that was refreshed 20 hours before. The token should still be valid. You don't need to refresh the token every 20 hours.

Based on this, it would be reasonable to assume that

rawToken

is only valid for 20 hours, which should mean you need to get

rawToken

no later than 20 hours before the event to resell or transfer it without TicketMaster's permission. However, if you just want to keep the ticket offline, then this is more than an adequate solution. I even collected on

Expo

a small application that I called

TicketGimp

; if you pass it on to him

token

it renders SafeTix barcodes.

I want to test it when my concert date comes.

Conclusion

I think we all agree with the motto

Fuck TicketMaster

. I hope its worthless product managers and senior management read this and freak out. I hope its developers read it and feel ashamed. I rarely feel genuine hostility toward other developers, but to the designers of this system, I say:

“Shame on you”

.

Shame on you for wasting your skills on limiting the opportunities of people who do not have modern technologies.

Shame on you for allowing the marketing team use this dark pattern as a protective measure.

Shame on you for supporting a company with such vicious business practices.

Software developers are the wizards and shamans of the modern era. We must use our powers with the care and responsibility that these powers imply. And you use these powers to keep people out of entertainment.

Good luck with refactoring your ticket verification system.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *