Hi people,
File import features are some of my favorite places to slow down and inspect the request. The interface may show an upload box, but the API sometimes receives a URL and asks a backend worker to collect the file later.
That difference matters. A browser fetching a URL uses my network position. A server fetching it uses the application's network position and may return content I could not otherwise reach.
Publication note: This story is reconstructed from private research. The organization, product, routes, identifiers, dates, and UI labels were removed or replaced. Every example below is synthetic. I am not claiming a deployed fix, completed retest, or program acceptance.

Looking behind the upload control
I was using a privileged controlled role in a document-import workflow. The normal interface first uploaded files and then passed their locations to the application. When I inspected the API request, I saw that those locations were still ordinary HTTPS strings.
So I replaced them with unique paths on a listener I controlled. Each path returned a tiny harmless HTML marker instead of a document. I did not use loopback, private addresses, link-local ranges, metadata services, internal hostnames, or third-party systems.
The callback proved who made the request
My listener received requests for the exact unique paths I had supplied. Because those paths had never appeared anywhere else, the callbacks linked directly to the import action.
A callback alone would prove server-side interaction, but it would not show whether I could read the response. Blind server-side requests and response-readable requests have very different impact, so I kept following the workflow.
POST /imports HTTP/1.1
Host: app.research.example
Authorization: Bearer [REDACTED]
Content-Type: application/json
{"source":"https://listener.research.example/[UNIQUE_PATH]"}
GET /[UNIQUE_PATH] HTTP/1.1
Host: listener.research.example
HTTP/1.1 200 OK
Content-Type: text/html
SAFE-RESEARCH-MARKER
The exchange above is illustrative. It is not an original endpoint, schema, listener, header set, or response body.
The stored object returned my marker
The import response created an object containing a download location. I opened that location through the same controlled workflow and received the exact marker bytes served by my listener.
The content type was not the expected document type, yet the application had fetched and stored it. Matching the body and length showed more than a DNS callback: the worker had persisted the response and made it available to the authorized caller.
I also tested the role boundary. A lower-privileged controlled identity received a denial on the import operation, and an unauthenticated request was rejected. That kept the conclusion narrow. I had demonstrated a privileged response-readable URL fetcher, not an anonymous or zero-permission primitive.
What I could safely claim
I could prove that the backend fetched a researcher-owned HTTPS resource, stored arbitrary harmless bytes, and returned them through the import object. I did not probe internal services and I did not retrieve secrets.
That boundary is important. The possibility of internal reachability is a reason to fix the design, but it is not evidence that internal access already happened. A strong report separates the observed primitive from the larger impact that safe testing deliberately leaves untested.
Open-source workflow companion: Interactsh
For a URL-fetching workflow, the open-source tool I would reach for is ProjectDiscovery Interactsh. It issues unique out-of-band addresses and records DNS and HTTP interactions, so each import attempt can have its own correlation point. I would place one unique address in the approved URL field, record the callback, and then pair that evidence with a separate controlled marker response to show whether the application only connected or also consumed and stored the returned bytes. Those are different claims, and the evidence should keep them separate. I would self-host when an assessment needs tighter control over logs and retention. This is a workflow recommendation for an authorized lab, not a claim that Interactsh was used in the original private submission.
Takeaway
The cleanest repair is to avoid accepting arbitrary absolute URLs. A server-issued upload identifier gives the backend something it can resolve without becoming a general-purpose fetcher.
If remote retrieval is truly required, I would expect several layers:
- Allowlist approved source hosts and schemes.
- Resolve and reject loopback, private, link-local, reserved, and internal ranges.
- Repeat validation after every redirect and DNS resolution.
- Verify file type, signature, size, and upload ownership before storage.
- Keep the existing role check and test it at both request and worker time.
Redirects deserve their own test. A permitted public URL can redirect to a forbidden address after the first validation step, so the destination must be checked again on every hop rather than only before the initial request.
The lesson for me was to treat every import URL as a network boundary, even when the UI makes it look like an ordinary file upload.
That is all for this write-up. Hope it gives you another place to look when an application says, "import from file."
For a different server-owned-input lesson, read When a validation helper became a data query, or browse the full Bug Bounty Archive.
Test what the server fetches, stores, and returns.
Balhence tests API inputs, worker behavior, cloud boundaries, and the evidence needed to prove impact safely.
Research topics: SSRF / URL import / server-side fetch / response readback