BASETIDE The Meridian Leak SQL client Scoreboard

Incident briefing

Open the SQL client Everything below is done there.

The situation

On Monday morning a file was posted to a public forum. It holds several hundred thousand records: names, addresses, IBANs and meter readings for Meridian Energy customers.

Our security team has compared it against our own customer table. It matches, row for row. This did not come from a partner, a supplier or a scraper. Somebody inside Meridian took it out of the database.

You have been brought in to establish who, and to make it stick.

What is established

  • The file came from our own customer table. Security matched it record for record.
  • It contains bank details, so it is not one of the routine marketing extracts.
  • Customers have come and gone since. Some signed up after that night; some exercised their right to erasure and are no longer in the table at all.
  • Twelve database accounts had read access to that table at the time.
  • Every statement run against the database is recorded in the audit log, with the account that ran it and how many rows it returned.

What is not known

  • When the data was taken.
  • Which of the twelve accounts was used.
  • How many records were in the file. The audit trail does not record a row count for a bulk copy, and the customer table has changed since that night.
  • Who was sitting behind that account.

What you have

A read-only copy of Meridian's systems. The customer table itself, the database audit log, session records, badge readings at the doors, leave records, helpdesk tickets and personnel notes.

Start by looking around:

SELECT table_name, column_name, data_type
  FROM information_schema.columns
 WHERE table_schema = 'meridian'
 ORDER BY table_name, ordinal_position;

What we need from you

#FindingPoints
01 When did the bulk export run? a timestamp, like 2025-11-30 23:05 100
02 Which account still had read access to the customer table on the night of the export, while the person it belongs to had already left Meridian? an account name, as in meridian.db_accounts 300
03 How many customer records were exposed? a plain number 800
04 Which helpdesk ticket explains how that account was compromised? a ticket reference, HD-#### 450
05 Who actually took the data out? an employee number, EMP-#### 700
06 Why did he do it? Meridian had already done something to him.
  • ahe was passed over for promotion
  • bhe was about to be dismissed for misconduct
  • cMeridian had cut off his own electricity supply
  • da competitor paid him for the file
  • ehe owed Meridian money he could not pay
  • fhis job was about to be outsourced
a single letter, like c
300

This list also lives in the database: SELECT * FROM game.findings_list;

Bonus round

For teams who have finished the case. These count on the scoreboard like any other finding, but nobody is expected to reach them in two hours, and the case does not need them.

#Bonus findingPoints
07 Meridian raises an alert whenever a single statement returns more than 5,000 rows. In the days before the export, one account never tripped it - and still returned more than 60,000 rows within seven days. On which day did it first pass that total? a date, like 2026-01-31 400
08 One account was connected from two different places at the same moment - which one person cannot do. For how many minutes in total did that happen? a whole number of minutes 400
09 Meridian must warn the customers in the leaked file, and the ones whose home looks empty are the ones most at risk. Among the homes in that file that were read in every month of the year, how many used more than three standard deviations less electricity between the readings of 1 January and 1 February 2026 than their own year predicts? a whole number 500
10 Sander learned from the audit log itself that a COPY leaves no row count. On the night of the export, how many accounts could read meridian.query_log - through a grant of their own, or through roles granted to roles? a whole number of accounts 600

How to report a finding

Submit each finding as you establish it, using the same team name every time:

SELECT game.submit('Your team name', 1, 'your answer');

The first team to establish a finding earns a quarter extra. Stuck?

SELECT game.hint('Your team name', 4);

Two hints per finding, each costing a quarter of that finding's value. A wrong answer costs a quarter as well, taken off your score straight away but never below zero. An answer in the wrong format, or one you already tried, costs nothing. All the tables are open from the start; there is no fixed order.

One warning

Some of what you will find looks damning and proves nothing. A grievance is not a confession. Being in the building is not the same as being at the keyboard. And an account is not a person: somebody has to have been sitting behind it.

Before you name anyone, check whether they could actually have done it.

This briefing is also readable from your SQL client: SELECT * FROM game.briefing ORDER BY ord;