I had a wonderful time speaking at GISEC and connecting with all of you there. But about two weeks before I took the stage, I found something on the event’s own website that I needed to report.

I’d been poking around the registration platform and ended up with read access to the database behind it, including real visitor and payment records.

The way in was a file-download endpoint. It returned the production web.config without a login, and that file contained the database credentials and the application’s integration secrets. The database credentials worked. I could run queries and read the results.

I reported it on August 31, 2026, with a proof of concept attached. The GISEC team was very thankful that I’d brought it to their attention and acted immediately to fix it. They subsequently confirmed that it was resolved.

It started with an image upload

I’d been looking through the registration frontend and the API calls it made. Several of the data endpoints returned 401 when called without a valid token. Following the frontend’s file-handling code led to an image-upload route that took a folderName parameter.

The upload handler rejected a text file with “Invalid file type.” A small PNG was accepted. There was also a download route, /Upload/File, which took fileName and folderName and returned the uploaded image’s bytes. That download worked without authentication.

So there was a route that would read a file from disk, and the request supplied both the file and its folder. I started checking how far that folder could reach.

403, 404, then 29,887 bytes

Several attempts came back with 403 or 404 responses. Changing the filename, trying different separators, and trying an encoded path did not return the configuration. The request that succeeded put the traversal in folderName and requested web.config as the file.

That one returned HTTP 200 and 29,887 bytes.

A 200 on its own doesn’t tell you much; it could still be an error page. I checked the body. It started with XML, followed by ASP.NET configuration sections, connection strings, and application settings. It was the actual configuration file.

The download handler was allowing path traversal (CWE-22): the supplied folder escaped the directory the endpoint should have been restricted to. No account or password guessing was needed to retrieve the file.

What was sitting in that file

The configuration contained the SQL Server address, database name, username, and password in a connection string. It also contained:

  • The JWT signing secret.
  • Stripe and Tap payment integration keys.
  • A DWTC payment API subscription key.
  • SMTP credentials for registration email.
  • Google and LinkedIn OAuth client secrets.

These were the settings returned by the running registration application. I also checked the exposed API keys separately, and they worked. The Stripe and Tap keys in the saved configuration had test-mode prefixes, so those need to be described as test-environment credentials, even though they were exposed by the production site.

The configuration also had an older, commented-out database connection string. That login failed. The active SQL connection was the one that opened up the live data.

August 31, 2026 · 12 seconds · No audio. The terminal displays the returned configuration file; sensitive values are redacted. Open recording.

I had read access to the database

The SQL Server was reachable over the internet. I connected using the credentials from the active connection string and got a successful login. The server identified itself as SQL Server 2022.

Then I started querying it. I could list tables, inspect their columns, and read rows. The GISEC database had tables for users, bookings, orders, payments, payment transactions, and exhibitor invitations.

An early query failed because I’d asked for a column called Name. The user table used FirstName and LastName instead. After checking the schema, the corrected query returned names, email addresses, phone numbers, companies, job titles, and countries.

I could also read the Payments table. The sample query returned people’s names and email addresses alongside transaction amounts and payment statuses. The exposure included actual registration and transaction data.

In the recorded run below, the table counts were:

  • 21,118 user rows.
  • 28,276 order rows.
  • 620 payment rows.

The proof of concept also returned a 100-record visitor sample. Those are the rows obscured in the lower part of the recording. The counts changed between runs as the database continued to receive registrations.

August 31, 2026 · 7 seconds · No audio. Successful authentication, database enumeration, and GISEC table counts. Credentials, database names, and visitor details are redacted. Open recording.

There were 93 databases on the server

Listing the databases returned 93 names. They included databases associated with GISEC, GITEX, World Government Summit, ADSW, and other events across the DWTC/Evento infrastructure.

I’d reached that server through GISEC’s registration API, but the server was shared with a much larger event-platform estate. The 93 figure is the database inventory returned by the login; the actual row reads and samples described here are from the GISEC database.

Even that database’s name was misleading: it ended in _dev, but the queries were returning real registration and payment records. A development-looking name didn’t make the data behind it test data.

By this point, the chain was straightforward:

  1. Read a file without logging inThe download route let the folder parameter reach outside its intended directory.
  2. Recover the application’s secretsThe returned configuration contained SQL credentials and integration keys.
  3. Connect to SQL ServerThe active credentials authenticated over the internet.
  4. Read visitor and payment rowsSELECT queries returned personal information, transaction amounts, and payment statuses.

The email to GISEC

On August 31, I emailed GISEC to report the vulnerability. I described the unauthenticated file read, the exposed secrets, and the database access, and attached poc.py with instructions for running it.

The script let them check each part separately: retrieve the configuration, parse the database credentials, verify the SQL login, and read a visitor sample. It also had an option to list the exposed configuration entries. That gave the team a way to reproduce the issue and see which secrets needed attention.

I asked them to rotate the exposed credentials and keys immediately, fix the path handling and access controls on the file endpoint, restrict the SQL account’s permissions, and close off unnecessary internet access to the database port. Fixing the file read would stop another download; it wouldn’t invalidate credentials someone had already copied.

I really appreciated how the GISEC team handled the report. They thanked me for flagging it, got straight to work on the fix, and confirmed when it was resolved.

This ended up being a very different kind of preparation for my talk. I was supposed to be going there to discuss security, and two weeks beforehand I was emailing them about their own registration database. The bug itself was a familiar one. What made it serious was what the application kept within reach of it.

More research: How I Found a Critical JEE Advanced 2026 Data Exposure