$ cat writeup.md…
$ cat writeup.md…
HackTheBox
Task: Android APK (Secure Cloud Notes) frontend over an Express/Node.js REST API; flag lives in a protected admin note. Solution: TOCTOU on a process-global authorization flag — a legitimate check-permission on an owned note poisons the shared `allowed` state, then an immediate GET of the admin note (numeric id 1) bypasses access control.
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
Secure Cloud Notes — an Android app for storing your private notes in the cloud. Configure your server base URL and keep your secrets safe. (package
htb.d3vnu11.securenotes)
English summary: The challenge ships an Android APK that acts as a thin client over a remote Express/Node.js REST API. The user points the app at http://IP:PORT/api. The flag is stored inside a hidden admin note that a normal user is not authorized to read. The goal is to read that protected note.
Decompiled the APK with jadx and apktool. Package: htb.d3vnu11.securenotes. From the decompiled sources we recover the full API surface:
| Method | Endpoint | Behavior |
|---|---|---|
| POST | /api/auth/register {email, password} | returns HS256 JWT |
| POST | /api/auth/login {email, password} | returns HS256 JWT |
| GET | /api/auth/verify | returns userId |
| GET | /api/notes | list of own notes {id, title, createdAt} |
| GET | /api/notes/:id/check-permission | 200 {success:true} if caller owns note, else 403 Access denied |
| GET | /api/notes/:id | note content if permitted |
| POST | /api/notes {id, title, content} | create note |
| DELETE | /api/notes/:id | delete note |
The decompiled note-open routine (class D0 / h.java) performs the read in two separate server round-trips with a delay in between:
GET /notes/:id/check-permission // authorization check
Thread.sleep(100) // <-- 100 ms gap
GET /notes/:id // actual read
This split between the time-of-check (check-permission) and the time-of-use (GET note) is the structural hint that the authorization decision is not bound to the read request.
The bundled sample token (jwt.txt) decodes to HS256 with payload {id, email: [email protected], iat, exp}.
...
$ grep --similar