Severity: MEDIUM (CVSS 5.3)
Affected: yamcs-core < 5.12.7
Fixed in: yamcs-core 5.12.7
Advisory: GHSA-w5r6-mcgq-7pq4

YAMCS is an open-source mission control framework used in real space missions ESA’s OPS-SAT, various ground station deployments. If you’ve spent any time in the space software world, you’ve probably run into it.

The issue

POST /auth/token accepts unlimited login attempts. No rate limiting, no lockout, no throttling. Every failed attempt returns HTTP 401. It never returns HTTP 429.

Spin up a YAMCS instance and try it:

for i in $(seq 1 20); do
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
    -X POST "http://localhost:8090/auth/token" \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "grant_type=password&username=operator&password=wrongpass$i")
  echo "Attempt $i: HTTP $STATUS"
done

No 429. Nothing. You can go as fast as your network allows.

CVE-2026-44596 proof of concept HTTP 401 no rate limiting

Why it matters

YAMCS operators can have direct access to spacecraft commanding. A compromised account in a mission operations environment is not just a data breach it’s potential access to a spacecraft.

In practice, YAMCS instances often sit on internal networks, but that’s not always the case. And «internal network» is not a security boundary.

Tested on yamcs-core 5.12.6. Same behavior on all versions before 5.12.7 no IP throttling, no account lockout.

Root cause

In AuthHandler.java, handleToken() called getSecurityStore().login(token) directly with no prior checks. No counter, no cache, no throttle, every request was processed immediately regardless of how many failures had come before it from the same IP.

The fix (yamcs-core 5.12.7)

A Guava Cache was added to track requests per IP with a 1-second sliding window:

// Limit auth requests by IP, expires after 1 min of inactivity
private static final Cache IP_LIMIT_CACHE = CacheBuilder.newBuilder()
.expireAfterAccess(1, TimeUnit.MINUTES)
.build();

private void checkRateLimit(HandlerContext ctx) throws TooManyRequestsException {
var ip = ctx.getOriginalHostAddress();
var ipLimit = IP_LIMIT_CACHE.get(ip, () -> new IpLimit());
var now = System.currentTimeMillis();

if (now - ipLimit.timestamp > 1000) { // Reset every second
    ipLimit.count.set(0);
    ipLimit.timestamp = now;
}

if (ipLimit.count.incrementAndGet() > maxAuthRequestsPerSecond) {
    throw new TooManyRequestsException("Too many login attempts");
}

}

Per-IP rate limiting, configurable via maxAuthRequestsPerSecond. Exceed the limit and you get HTTP 429. Cache resets after 1 minute of inactivity.

PoC

github.com/ex-cal1bur/CVE-2026-44596


Daniel Miranda Barcelona (Excal1bur) — GitHub · LinkedIn · thedumpster · portfolio