How not to do form validation, or breaking QPay's ticketing system

Published on February 10, 2018

QPay, a company that does ticketing, memberships and merchandising for university societies has recently managed to convince various Cambridge societies to use their platform for ticketing. For example, several College Balls such as Churchill’s Spring Ball, Hughes Hall’s May Ball, Trinity Hall’s (canceled) June Event, Christ’s May Ball, Newnham Summer Soirée, Pembroke’s June Event, Sidney Sussex’s June Event, Darwin & St. Edmund’s May Ball and Wolfson’s May Ball are all using QPay for ticketing this year.

These events rely on various mechanisms to check eligibility for various ticket types such as college student, university student, alumni and other special ticket types. These include email address checks and ticket passwords to buy various tickets. And they all fail in spectacular ways, relating to the title of this post, and that is all form validation is done client side, and (almost) none on the server side! This means you can input any data you want, and QPay will happily accept it.

Validation done client-side

Email Addresses

I have to preface this with another flaw, which is even if email address validation is done server-side, QPay does not verify ownership of the email address (via a confirmation email or something similar) and instead uses your phone number as your account, which means there is no need to put your own email address and you can impersonate other people.

QPay has implemented three ways (that I’ve seen) of checking if an email address is on an approved list. Since validation is done client-side, this only serves to provide a hint to the user that the email address entered is not on the approved list.

The typical implementation is that the society maintains a list of email addresses on their management interface, and the browser fires off a request to validate if the email address is on the members list. There is nothing wrong with this, other than the fact this is not enforced on the server. (/ajax_check_payer_email.php and /ajax_check_membership.php)

The other implementation is to take the list of email addresses, and then embed the entire list into the page (implemented by the custom_validate function on the /tickets page). This leaks email addresses of whoever is “allowed” to buy tickets, be it college students or alumni or college staff. Colleges known to be affected by this are:

  • Christ’s College, list of all college students
  • Sidney Sussex College, list of approved alumni
  • Newnham College, list of all college students
  • Trinity Hall, list of all college students

14/02/2018 Update: QPay appears to have scrubbed the emails from the events

function custom_validate(studentnumber) {
    var val = "sa***,gb***,jg***,iv***,sa***,......";
    val = val.toLowerCase();
    var res = val.split(",");
    if (studentnumber.length == 0) {
        return false;
    } else if ($.inArray(studentnumber.toLowerCase(), res) > -1) {
        return true;
    } else if (studentnumber.indexOf("honorarytulsmember") > -1) {
        return true;
    } else {
        return false;

A newer implementation that I just saw implemented (on the Pembroke June Event page) was the landing page ( has the following flow:

  1. Enter email address
  2. Send request to sendaccessemail.php with email address. If email is on member list, send email with an access code, and return said access code in the same request for the client to validate locally
  3. Enter access code, if matches code returned by sendaccessmail.php, redirect to

Or, you could just skip all of that and go straight to

Ticket Passwords

Some events have passwords on certain ticket types. Passwords are embedded in plain text on the page. Simply copy password from page source, or remove validation code.

Known affected events are:

  • Churchill Spring Ball, discounted and committee tickets
  • Christ’s May Ball, alumni and staff tickets
  • Wolfson May Ball, college member early bird tickets
  • Trinity Hall June Event, VIP and alumni tickets
  • Sidney Sussex June Event, college member and alumni tickets

Example using a script I wrote to display ticket info:

Trinity Hall June Event Password

var tickettypesArr = {
    "7259": {
        "custom_fields": {
            "desc": ["Student ID", "Password"],
            "vals": [{
                "mandatory": "false",
                "type": "Text",
                "fieldName": "Student ID"
            }, {
                "mandatory": "rt6-Y4y-2b7-KSP",
                "type": "Text",
                "fieldName": "Password"
        "names": {
            "7259": {
                "price": "120.00",
                "name": "Alumni",
                "membershiptypes": null,
                "starttime": "2018-01-22 11:00:00"
        "quantity": 75,
        "numberleft": 69,
        "starttime": "2018-01-22 11:00:00"
    "7261": {
        "custom_fields": {
            "desc": ["Student ID", "Password"],
            "vals": [{
                "mandatory": "false",
                "type": "Text",
                "fieldName": "Student ID"
            }, {
                "mandatory": "-9-W6r+!pvnfeqZh",
                "type": "Text",
                "fieldName": "Password"
        "names": {
            "7261": {
                "price": "0.00",
                "name": "VIP",
                "membershiptypes": null,
                "starttime": "2018-01-22 11:00:00"
        "starttime": "2018-01-22 11:00:00"

XSS attacks

Input is not sanitized, XSS attacks are possible. Potentially possible to gain access to society portals by stealing cookies.

Churchill Spring Ball XSS demonstration

Permanently reserving tickets

If event has limited number of tickets, it is possible to permanently reserve any number of tickets by changing the ticketholdingtime parameter and adding as many elements to the tickets[] array.

Example of arbitrary ticket holding time

Bypassing waiting list/ticket count restrictions

Simply POST the right details to /tickets to get a ticket id, and then /booking with modified parameters. (eg, dropping the waitinglist parameter, modifying the tickettype parameter, etc)

Example of arbitrary ticket holding time, bypassing closed sales, bypassing email requirements

Event ID Enumeration

Event ids are sequentially numbered. It is possible to enumerate through all ~3100 events as of the writing of this post and discover all of QPay’s events, even though QPay seems to treat the event URLs as a form of access control.

Ticket ID Enumeration

Ticket ids are sequentially numbered. Ticket QR codes are just ticket ids.

15/02/2018 Update:

Disabling campus access to QPay

Since most university networks are behind NATs, if QPay decides to ban your IP address, they may inadvertently ban the entire university/campus from accessing their site.

Various bits of PoC can be found over at, and a userscript for ticket details and removing client form validation.

Experiences with migrating to .NET Core

Published on May 20, 2017

BfUpdater is the backend app that updates the bravefrontier_data repo and /r/bravefrontier’s sidebar, and also acts as an IRC and Discord bot. The entire thing is written in C#, but a constant pain point was deploying to Mono. Take the Discord.Net library for example. WebSockets as used by Discord.Net is broken by newer versions of Mono, requiring a downgrade of Mono, and newer versions of Discord.Net are completely non-functional on Mono regardless of workarounds.

So, I decided on migrating the project to target .NET Core rather than the .NET Framework with Mono, starting off with copying the entire project into a new .NET Core project skeleton. Here were some areas of interest I encountered while migrating the project:


Finding newer versions of libraries that were used that support .NET Core was ok for the most part. The only library that I used without .NET Core support was SmartIrc4Net, but since the #bravefrontier channel was pretty much dead, I just removed the IRC code completely and didn’t worry about switching to a new library.

API Changes

This was where all the fun came from. The code made extensive use of WebClient, which is gone in .NET Core. So, after writing a bunch of extensions for HttpClient and a bit of regex search and replace later all the network related code was running again.

One of the harder things to account for was the removal of System.Diagnostics.StackTrace, which I used for logging method names by looking for the log’s caller. Instead of trying to parse the output from Environment.StackTrace, I decided to make use of Microsoft’s ILogger and deal with the fact that my logs no longer have method-level names, but only class-level names.

Brave Frontier - Update Notes - Tue Mar 22 2016 - Easter Update

Published on March 22, 2016

Official changelog can be found here.

Reddit discussion thread here.

Plasma Blaster Bonnie


Idle Attack

  • Hits/DC: 6/3
  • Cost: 42
  • Stats (L): 6720 HP / 2814 ATK / 2346 DEF / 2934 REC
  • Imps: 1050 HP / 480 ATK / 440 DEF / 420 REC
  • LS: +50% BB Gauge Fill Rate, Reduce BB Cost 25%, +150% BB/SBB/UBB Mod
  • ES: 100% ATK against Statused Targets, +50% ATK when BB Gauge is full
  • BB: Cost: 25BC/2DC - 12 Hits, 300% AoE + Fire/Thunder/Dark Elements, 65% Poison/Curse/Paralyze/Injury/Sick/Weaken, 2 turn Def Ignore Buff
  • SBB: Cost: 25BC/2DC - 15 Hits, 540% AoE + Fire/Thunder/Dark Elements, 65% Poison/Curse/Paralyze/Injury/Sick/Weaken, 3 turn Fire/Thunder/Dark Buff, 3 turn +200% BB/SBB/UBB Mod
  • UBB: Cost: 30BC/2DC - 18 Hits, 1200-2000% AoE depending on HP remaining (ATK+100), 3 turn +500% BB/SBB/UBB Mod, 3 Turn 300% ATK Buff on Statused Targets
  • Arena Type: 2
  • Evo Mat: Thunder Mecha God, Miracle Totem, Metal Mimic, Dark Bulb, Fire Bulb (Not available yet.)
  • Lore: A young girl from the mysterious world of Paskua who looks suspiciously like a rabbit. Bonnie became infamous across Grand Gaia thanks to the immense firepower of her cannon, and her infallible shooting accuracy. Finally having gained the recognition she had so desired as a warrior, instead of being forever branded as just another cute face, she felt so relieved that she no longer felt the need to fight. However, one day as she was passing through a forest, she came across a blonde girl atop a giant rabbit who was fighting single-handedly against a ferocious beast to protect the people of a nearby village. At that moment Bonnie realized that fighting for others was just as important as fighting for herself, leading her to unload her trusty cannon to join the battle. She then modified it with hare-like speed, creating an entirely new machine in a matter of seconds. Legends say that the plasma blast she shot at the menacing beast was so powerful that it completely pulverized the monster, along with one of the mountains beyond the forest.
  • Evo Dialogue: All this time I’ve been fighting to gain a reputation to be proud of. Now the time has come to fight for a good cause!

Eggstra Colorful Carrol


Idle Move Attack

  • Hits/DC: 8/2
  • Cost: 42
  • Stats (L): 6951 HP / 2362 ATK / 2601 DEF / 2123 REC
  • Imps: 1100 HP / 440 ATK / 440 DEF / 440 REC
  • LS: +25% BC/HC Drop Rate, Reduce BB Cost 25%, 4 BC/turn
  • ES: Add Effect To BB/SBB (Cure Status/Debuffs), +15% BB Gauge Fill Rate, +15% HC Effectiveness
  • BB: Cost: 25BC/2DC - 14 Hits, 300% AoE + Water/Earth/Light Elements, 3 Turn +130% ATK/DEF/REC, 3 turn 7 BC/turn
  • SBB: Cost: 25BC/2DC - 16 Hits, 540% AoE + Water/Earth/Light Elements, 3 Turn +130% ATK/DEF/REC, 8% OD Fill, 5-7 BC on Hit for 3 turns, 3 turn Water/Earth/Light Buff
  • UBB: Cost: 30BC/2DC - 18 Hits, 1200% AoE + Water/Earth/Light Elements, 25% OD Fill, Fill 999 BC, 0 Def 20000 Damage light Barrier (Absorb 100% Damage)
  • Arena Type: 2
  • Evo Mat: Light Mecha God, Miracle Totem, Metal Mimic, Water Bulb, Earth Bulb (Not available yet.)
  • Lore: A lovely rabbit tamer from the mysterious world of Paskua. Carrol was in the forest collecting carrots for Bianco when she came across some villagers that lived nearby. As generous as always, she immediately offered them some of the sweets in her basket full of colorful eggs only to hear them scream for help. Puzzled at their reaction, she glanced past them and saw a gigantic hare-like creature covered in a rich brown substance approaching in a frenzy. The sight of the beast surprised Bianco so that he hopped as high as he could, causing Carrol to drop her basket of eggs. All of the mystical colored eggs cracked as they hit the floor, releasing all of the magical force hidden within, and gifting Carrol with unimaginable powers in the process. Fueled by the energy from her precious eggs, Carrol faced the ferocious monster by herself until a girl with rabbit ears wielding an enormous cannon appeared to give her a hand. After they defeated the monster, the villagers showed their gratitude by presenting Carrol and her new friend with two baskets full of the most colorful eggs they had ever seen. The two ladies are believed to then have taken these tokens of appreciation to Paskua to share with their loved ones.
  • Evo Dialogue: Ta-dah! We are back and more colorful than ever! Wow! My pigtails are almost as long as Bianco’s ears!

Lop Blessing

Lop Blessing Thumbnail

Using the essence extracted from Chromatic Blossoms with a strange mechanical contraption Bonnie left behind, the top engineers of Randall crafted an exquisite enhancement charm. It unleashes energy in such great magnitudes that its wearer is said to become able to penetrate any foe’s armor.

  • Atk Boosting
  • +200% BB Mod, +50% Elemental Weakness Dmg
  • Recipe:
    • Chromatic Blossom - A mysterious rainbow colored flower found deep in the forest where Bonnie headed. Rumors say the essence of this flower supplies the luminous energy for Bonnie’s cannon. Chromatic Blossom Thumbnail
    • 60 Choco Bunnies - A pungent chocolate said to be only given to the pure-hearted young of Randall by the guardians in the skies. It is a rare and precious Item that should be cherished. Choco Bunny Thumbnail
    • 75 Rabbit’s Foot
    • 60 Ascension Bells
    • 25 Honor Drops

Easter Madness Dungeon, Easter Fanfare Lv. 4

Furry creatures led to wings of guardian for the kind hearted.

  • 7 Battles
  • 20 EN
  • 2200 XP
  • Clear Rewards:
    • 1 Gem
    • Chromatic Blossom

Addition of various units

  • Immortal Phoenix Levarza (name only)
  • Ocean Conqueror Mega (name only)
  • Paladin Crusader Sodis (name only)
  • Phantom Victor Zephyr (name only)
  • Blazing Dawn Owen (name only)
  • Effulgent Dusk Grahdens (name only)
  • Tartarus Blaze Berdette
  • Tartarus Flood Sareas
  • Tartarus Demolition Dure
  • Tartarus Sacrifice Radia
  • Tartarus Massacre Sirius
  • Tartarus Burial Melina

Other stuff

  • Downloaded Colosseum/Omni Evolution assets.

Brave Frontier - Update Notes - Tue Dec 22 2015 - Christmas Update

Published on December 22, 2015

Official changelog can be found here.

Reddit discussion thread here.

RS Events

Special Summon!

From 23 Dec, 07:00 PST ~ 30 Dec, 06:59 PST, enjoy SPECIAL SUMMON Minimum 5 stars and unlock ONE chance at FROSTY SUMMON for every 5 summons! Get Zeruiah through the Summon Gate for limited time.

Frosty Summon!

From 23 Dec, 7:00 PST ~ 30 Dec, 06:59 PST, enjoy minimum 5 stars and a limited pool of 15 Global Exclusive 7 stars potential units!

Summon now!

New Units: Zeruiah, Piany, Drevas and Reud are available through the Summon Gate. Zeruiah is only available in the Summon Gate for LIMITED TIME.

Enjoy SPECIAL SUMMON for 5 gems!

Christmas Dungeon - Glittering Wintertide

Christmas Dungeon Banner

  • Frozen Dreamland Lv. 2 - In a distant land, bright lights pulsate and melodious songs jingle away.
    • 20 EN, 5 Battles, 1800 XP
  • Frozen Dreamland Lv. 3 - The snowstorm picks up as you trudge your way towards the castle. Curiously, it seems to be nudging you forward.
    • 25 EN, 5 Battles, 2200 XP
  • Frozen Dreamland Lv. 4 - As you approach the artic castle, a shadowy figure watches over you from the top of the tower. What surprises await?.
    • 30 EN, 7 Battles, 2600 XP
    • 1 Gem Clear Bonus

Both Glittering Wintertide and Winter Glades Blitz will be open for 1 week.

Eternal Preserver Zeruiah


Other Illustrations: 5* | 6*

Idle Attack

  • Hits/DC: 10/5
  • Cost: 42
  • Stats (L): 6725 HP / 2510 ATK / 2520 DEF / 2315 REC
  • Imps: 1100 HP / 440 ATK / 440 DEF / 440 REC
  • LS: +12% XP, +24% BC/HC Drop Rate, +7% Item Drop Rate, +15% Karma Drop Rate, +20%% Zel Drop Rate, +60% BB Fill Rate
  • ES: +25% DMG Taken to HP (25% Chance), Reduce DMG 25% (25% Chance)
  • BB: Cost: 25BC/30DC - 30 Hit 300% Earth and Water AoE (ATK+100), 3000-3300 Heal (+31.7% Healer REC), 3 turns +35% BC/HC Drop Rate Buff, Cure Status Ailments
  • SBB: Cost: 40BC/40DC - 40 Hit 540% Earth and Water AoE (ATK+100), Fill Own BB Gauge, 3 turns +35% BC/HC Drop Rate, 3 turns +5% Item Drop Rate, 2 turn All Elements Buff
  • UBB: Cost: 30BC - 3 turns +75% Mitigation, +25% Max HP, 3 turns Convert 100% REC to DEF, 3 turns +100% BC/HC Drop Rate, 3 turns +7% Item Drop Rate
  • Arena Type: 2
  • Evo Mat: (Miracle Totem, 2x Earth Totems, Water Bulb, Earth Bulb) -> (Earth Mecha God, Miracle Totem, Miracle Bulb, Earth Bulb, Legend Stone)
  • 5* Lore: In the snow-bound mountain region of the far North, there exists an impossibly lush and bountiful Vale. Legends tell that in ancient times, a gigantic floating tree drifted in the skies for a hundred years before disappearing. Frozen creeks transformed into flowing rivers, temperate vegetation sprang forth in a mystical miracle. The Vale became a bastion for various wildlife and a host of Fae spirits. The Fae were good-natured, innocent and made the woods ring with merriment everyday. However, a great danger loomed over them in the form of invading goblin hordes that were attracted by the food as well as magical energies of the Vale. The Fae were no match for the savagery of the goblins and were driven back. In their dark hour, they congregated at a massive tree that had sprouted at the center of the Vale and began chanting, thousands of voices soared in harmony. Their myriad of wills and desires for a savior and guardian culminated in a blinding pillar of turquoise light that sundered the clouds high above. The final manifestation of this bewitching event was a mythical deity brought to life. She was known as Zeruiah and all Fae unanimously bowed before her.
  • 6* Lore: Zeruiah was one with the Vale. With the Fae rallied behind, the deity turned wildlife, plants and the very elements against the goblin invaders. Trees uprooted into towering treants that smashed the puny humanoids by the dozens. Stags and boars charged and impaled any greenskins that survived the onslaught of mighty Earth and Water elementals. In one fell swoop, the goblin swarm was defeated and routed out of the Vale. Drawing on her divine powers, Zeruiah erected wards and summoned raging blizzards that would keep out evil monsters. For countless millennia, the Vale flourished and expanded under the protection and rule of Zeruiah. The Fae channeled and constructed majestic Vaults in which they stored all sorts of treasures, tomes and artifacts collected or crafted over the ages. Knowledge of these Vaults gradually leaked to the world outside and many civilizations sought to gain entry into the Vale, fueled by greed and curiosity. From historical records, none made it in.
  • 7* Lore: An Eternal Preserver of all the treasures and knowledge of the Vale in the far North, summoned at the behest of the Fae to rule over and protect the land. One day, Zeruiah discovered that a small girl had somehow gotten pass the wards and gained access to the Vale. After observing the girl for days, Zeruiah grew fascinated at how such a young human was able to command the snow and ice. The Fae and animals initially assisted the girl with sustenance at Zeruiah’s command, but eventually she was brought into the heart of the Vale. Despite her mistrust of humans, Zeruiah grew to love the girl, training and watching her grow up. Through this encounter, Zeruiah saw that not all humans were evil, and began opening up the Vale to druid tribes and scholars sent by Human kingdoms once every year - marked by a Fae festival called Hanukkah.

Frozen Fantasy

Frozen Fantasy Thumbnail

Long deemed unconquerable by mere mortals. The stone was lost to the deep wintertide. In one of the coldest winters known to date, it was discovered by a duo who came across it in a forgotten cave, as if being called out by it. Both were immediately ensnared by its power and a great battle for ownership ensued. In the morning, only one walked out of the cave. The direction taken, however, was not towards home but deeper into the wintertide.

  • Status Boost
  • +30% HP/ATK, +40% DEF/REC, 75% Chance +3-6 BC when Attacked
  • Recipe:
    • Frozen Myth
    • 99 Snow Flakes
    • 65 Snow Crystals
    • 60 Snow Gemstones
    • 80 Miracle Frosts

Grandt and Elaina Buffs


  • Normal hit DC increased from 3 to 8 (Max BC Gen from 12 to 32).
  • BB - Max BC Gen increased from 12 to 48.
  • SBB - ATK Down chance increased from 30% to 50%.
  • SBB - HP% to ATK conversion increased from 10% to 15%.
  • UBB - ATK Down increased from -50% to -75%.


  • Normal hit DC increased from 1 to 2 (Max BC Gen from 10 to 20).
  • BB - BB Fill/turn increased from 4 to 6.
  • SBB - BB Fill/turn increased from 5 to 6.
  • UBB - Attacks are now 1 frame faster to activate.

Brave Frontier - Random Update Notes - Tue Dec 01 2015

Published on December 1, 2015

Random things I found.

Image Image Image Image Image Image Image Image