BA Cybersecurity

Exposed endpoint? Assess it. Don't scope it out.

Sooner or later an assessment turns up an endpoint that is not on the client's list: an admin console on a hostname nobody mentioned, an old API version that still answers, a test environment with a public address. The tempting sentence is "out of scope." I think it is the wrong one. This post is about the sentence I try to write instead, how I earn it with requests that only read, and where the answer goes in the report.

A scope list is not a map

A scope list records what I was asked to test. It does not record what is reachable, and attackers do not read it. Unlisted endpoints are easy to create and easy to forget: a migration leaves the old hostname alive, a debug route ships to production, a staging copy gets a public address.

If my report says "out of scope" about an endpoint I watched answer from the internet, the reader cannot tell "we looked and it is fine" from "nobody looked." The two look identical on the page, and only one of them is reassuring. The phrase also says nothing about the endpoint itself that anyone can check.

So I do two things, in this order. First I establish that the endpoint belongs to the client: the domain registration, the certificate, the client's own confirmation. If I cannot establish that, I report that I found it and stop, because it may be somebody else's. Second, if it is theirs, I assess it using only requests that any anonymous visitor could send.

Confirm with requests that only read

Read-only is a discipline about what I ask, not just which verb I use. I stay with HEAD, GET and OPTIONS, which HTTP defines (along with TRACE) as safe methods with essentially read-only semantics (RFC 9110, section 9.2.1). The RFC is careful to say that this describes what the client requests, not everything a server might do. So I also stay on routes the endpoint itself offers, send no payloads, try no credentials and create no load.

Three questions turn "unknown" into "assessed":

  • Is authentication required? A plain success, a challenge, or a redirect to a login page.
  • What comes back? A login form, API data, a directory listing, a file, a default page.
  • How does it fail? A generic error, or a stack trace, a version string, an internal name.

The OWASP Web Security Testing Guide covers how such endpoints are found: other base paths, non-standard ports and virtual hosts (WSTG-INFO-04), and administrative interfaces (WSTG-CONF-05). The admin-interface test also lists active techniques, such as guessing paths and credentials. For an endpoint outside the agreed list, I stop at the read-only ones. If they cannot settle the question, I do not guess "vulnerable" or "safe." I write "not confirmed," list what I checked, and say what would settle it. Going further is the owner's decision.

On AWS, the penetration testing policy is worth rereading first. It lets customers test their own resources for the listed services without prior approval, and it prohibits a short list of activities. The list includes request flooding, S3 bucket takeover and subdomain takeover. That fits the read-only rule. For a dangling name (a DNS record that still points at a deleted resource), my evidence stops at the record and what the endpoint answers. I do not recreate the missing resource to prove the point.

A detection is not a confirmation. A WAF rule match, a scanner finding and a banner that resembles a vulnerable version each say that something looked like something. None says what the application did. Before I write "confirmed," I check the route the request really took (CDN, WAF, load balancer, application), whether the rule blocked or only counted (on AWS WAF, a Count rule counts the request and leaves the allow-or-block decision to other rules), and what the endpoint returned. Addresses get the same treatment. I do not call one a scanner or an attacker because of its reputation. I look up who holds the address range (WHOIS), watch what it actually did, and make the claim after the facts.

Put one yes-or-no question first

The first line of every finding answers one question: can someone on the internet exploit this right now, without credentials or prior access? Yes or no, then the evidence in a sentence or two. For example:

Assessed with read-only requests. Exploitable from the internet now: no. Every route the endpoint offers answers with an authentication challenge, unknown routes return a generic error, and the responses name no product, version or internal host.

Version matches and CVE numbers come after that, and severity follows the answer. A vulnerable-looking version with no reachable path is not urgent. A misconfiguration with no CVE at all can be.

CVSS already holds the ingredients. Attack Vector asks how far away the attacker can be; Network means the possible attackers extend up to the entire internet. Privileges Required asks what the attacker must hold first; None means the attacker is unauthorized before the attack. (That is the v3.1 wording; v4.0 keeps both metrics.) My question is roughly those two in a plain sentence, placed ahead of the number, because a score hides which assumption produced it. FIRST's v4.0 guidance also says the base score measures severity and should not be used alone to assess risk.

Risks that matter only after someone is already inside go in a separate table, which I call level-up proposals: a cloud role with far more permissions than its job needs, a flat internal network, no alert on privileged actions. They are worth fixing, but they are not findings about today's exposure, and one mixed table makes everything look equally urgent, which means nothing stands out. The split follows evidence, not mood. If I can show an external path to the precondition, the item moves up to the first table, with the path attached.

Two habits keep the report honest about whom it serves. Recommendations must fit the reader's authority: if the reader cannot change the infrastructure, a recommendation to change it is noise, so I write "this environment cannot detect X" and offer the best option inside their reach. And when I answer a vendor's report or a client's question, I never ask them to scope the item out. I give the facts and the options (fix, restrict, accept and record, retest), and they decide.

Keep the routine boring, and client data out of your tools

I work alone, so anything repeatable should not depend on my attention. The collection step is a read-only script and a checklist. For each endpoint, it sends a fixed set of safe requests, saves the request and response pairs, and fills in a record:

endpoint:         <ENDPOINT_1>
requests_sent:    HEAD, GET, OPTIONS only
auth_required:    yes | no | partial
response_kind:    login form | API data | listing | file | default page | error
error_style:      generic | verbose
evidence:         <saved request/response pairs>
--- human decides ---
exploitable_now:  yes | no | not confirmed
recommendation:   <within the reader's authority>

The machine fills in everything above the line. I decide the rest. That is where my attention belongs.

The same discipline applies when I use an AI tool to draft or tidy report text: it should see structure, not the client's environment.

  • Environment values (host names, addresses, account IDs, people's names, system names) become placeholders such as <HOST_1> or <ACCOUNT_1>.
  • Attack payloads become a description of kind and structure, for example "a boolean-based SQL injection attempt in a login field, URL-encoded," never the string.
  • Raw logs are never pasted whole. I extract the few fields the point needs.

Nothing is lost. The reader of a report needs to understand the technique, not copy the string.

"Out of scope" makes no claim anyone can check. "Assessed: not exploitable from the internet, because of X" can be wrong, which is exactly why it is worth writing down.

Disclosure

This article was drafted with an AI assistant (Claude). The practices described are my own, from running these systems in production. Product statements were checked against the documentation listed under References. I reviewed and approved the final text and am responsible for it.

References

  1. OWASP WSTG v4.2, WSTG-INFO-04 Enumerate Applications on Webserver: https://wstg.owasp.org/v4.2/4-Web_Application_Security_Testing/01-Information_Gathering/04-Enumerate_Applications_on_Webserver
  2. OWASP WSTG v4.2, WSTG-CONF-05 Enumerate Infrastructure and Application Admin Interfaces: https://wstg.owasp.org/v4.2/4-Web_Application_Security_Testing/02-Configuration_and_Deployment_Management_Testing/05-Enumerate_Infrastructure_and_Application_Admin_Interfaces
  3. FIRST, CVSS v3.1 Specification Document: https://www.first.org/cvss/v3.1/specification-document
  4. FIRST, CVSS v4.0 Specification Document: https://www.first.org/cvss/v4.0/specification-document
  5. FIRST, CVSS v4.0 User Guide: https://www.first.org/cvss/v4.0/user-guide
  6. AWS, Customer Support Policy for Penetration Testing: https://aws.amazon.com/security/penetration-testing/
  7. AWS WAF Developer Guide, Using rule actions in AWS WAF: https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-action.html
  8. IETF, RFC 9110 HTTP Semantics, section 9.2.1 Safe Methods: https://www.rfc-editor.org/rfc/rfc9110.html