TL;DR: Two generations of one platform on one domain: the modern one was current, the legacy one defined the risk, and the parent cookie scope tied them together. I show how I mapped both estates and how to retire the archive.
The scenario
I ran this as a black-box review: no credentials, only the publicly visible surface. Here is what I saw before I touched anything.
A community and events platform with a current content management system on the main domain and a legacy installation of the same platform serving an archived section on a subdomain, fronted by a caching layer. Scope covered both estates, the shared cookie scope, client library versions and subdomain exposure.
I removed all identifying information. The target is described only by sector and technology class so the pattern can be reused.
The pattern
The pattern I recognised — and that I keep finding in similar estates:
Coexisting generations is normal and it is where technical debt becomes an attack surface. When a current platform handles the site and a legacy platform handles an archive, both are reachable, both share a domain, and both receive requests from the same audience. The legacy one has no maintainers watching it, no patch cadence and a smaller user base to notice a breach, which is precisely why it is not upgraded.
Two amplifiers make this worse. First, the cache: a layer that stores and serves responses can hide an old platform from casual inspection, so the estate looks current to everyone including the team. Second, the cookie scope: a cookie set on the parent domain is sent to every host beneath it, so an attacker who obtains a session cookie from the legacy host can present it to the modern one, and a session established on the legacy host is trusted by shared middleware.
The third amplifier is the client library. Long-dead platforms ship long-dead libraries that other sites still use, so their end-of-life status affects third parties who never chose it. That is why an unmaintained installation is a supply chain problem, not only a hygiene problem.
Findings
| Severity | Finding | Evidence |
|---|---|---|
| HOCH | Legacy platform still served on a subdomain | Archived section on a platform release past end of life |
| HOCH | Client library version past end of life | Affects third-party sites that did not choose the dependency |
| MEDIUM | Cookie scope shared across both platforms | Session from the legacy host valid on the modern host |
| MEDIUM | Caching layer hides the legacy platform from inspection | Estate appears current to automated checks |
| LOW | Enumerated subdomains with distinct stacks | Several technologies on one domain |
| LOW | Administrative paths reachable on both estates | Consistent attack surface instead of one hardened point |
The attack path
Reproduction in my lab
Every command below targets a lab container I control. Nothing here is aimed at a live system.
Map the generations on your own estate
# what answers where, including archived sections
$ for h in example.org www.example.org archive.example.org old.example.org legacy.example.org; do
printf '%-28s %s %s\n' "$h" \
"$(curl -so /dev/null -w '%{http_code}' https://$h/)" \
"$(curl -sI https://$h/ | grep -i '^x-powered-by\|^x-generator\|^server:' | head -1 | tr -d '\r')"
done
# is the parent cookie shared
$ curl -sI https://example.org/ | grep -i '^set-cookie'
Detection in your own estate
Check what your own client libraries have reached end of life
$ npm outdated --json 2>/dev/null | python3 -c '
import json,sys
d = json.load(sys.stdin)
for name, info in d.items():
cur, want = info.get("current"), info.get("wanted")
print(f"{name}: {cur} -> {want}")' | head -30
# and list every host you actually own
$ for h in $(cat hosts.txt); do
printf '%s %s\n' "$h" "$(dig +short "$h" | tr '\n' ' ')"
done
Remediation
Scope the session cookie to the host that needs it
# no session cookie for the whole estate
proxy_cookie_path / /;
proxy_cookie_domain example.org ~^([a-z0-9-]+\.)?example\.org$ localhost;
# better: set the cookie host-only by never setting a domain attribute
add_header Set-Cookie "sessionid=; Path=/; Secure; HttpOnly; SameSite=Lax" always;
Once an attacker gains access, they can perform the following actions:
Takeaways
- Two generations on one domain means two attack surfaces and one cookie scope.
- A cache that hides an old platform hides it from your own audits too.
- An unmaintained platform is a third-party dependency problem, not just yours.
- Retire the archive by redirecting it; every year it runs is a year of exposure.