Skip to content
This repository was archived by the owner on Aug 19, 2026. It is now read-only.

Latest commit

 

History

History
297 lines (238 loc) · 15.4 KB

File metadata and controls

297 lines (238 loc) · 15.4 KB

Stage 1R runtime validation

Scope and safety boundary

Stage 1R was performed on 2026-08-09 against one authorized MegaRAC SP lab BMC corresponding to the supplied JViewer artifacts. It answers the bounded questions that static bytecode could not answer before Stage 2. Results in this document are firmware/session-specific observations, not universal MegaRAC guarantees.

The initial Stage 1R probes used only the normal web login, fresh JNLP acquisition, the legacy KVM authentication/start/pause/resume/stop messages, and one virtual-media authentication exchange. A later Stage 4 follow-up sent one exact JViewer resume-time relative mouse report whose buttons, X/Y deltas, and wheel were all zero. A second Stage 4 control run relayed an otherwise unaltered original JViewer connection solely to count its plaintext IVTP packet types. No key, click, nonzero movement, configuration change, power action, media mount, SCSI service loop, or unknown opcode was sent.

Raw application streams contain credentials, session material, and potentially screen data. They remain below Git-ignored docs/captures/private/ with directory mode 0700 and file mode 0600. Only categorical or structural results are recorded here. Packet capture through BPF was unavailable to the unprivileged research account, so the probes captured the plaintext bytes at the application socket boundary.

Reproducible research helpers

The stdlib-only helpers created for this validation are:

  • research/scripts/megarac_auth.py: normal login, cookie jar, validation, bounded JNLP fetch, and redacted summary;
  • research/scripts/dump_jnlp.py: secret-safe structural JNLP inspection;
  • research/scripts/session_lifecycle_probe.py: independent issuance and silent-idle observations;
  • research/scripts/kvm_probe.py: bounded IVTP handshake, raw capture, fragment/frame telemetry, refresh, and idle scenarios;
  • research/scripts/vmedia_probe.py: one CD-class IUSB authentication frame and one bounded acknowledgement, with no media source or SCSI loop.

Offline regression tests cover truncated GoAhead responses, negative HAPI status values, XML hardening, secret redaction, exact type-18 construction, fragmented TCP reads, IVTP limits/state transitions, frame telemetry, capture permissions, the IUSB two-pass checksum, and acknowledgement bounds.

Symbolic examples:

python3 research/scripts/megarac_auth.py \
  --host HOST --user USER --scheme http

python3 research/scripts/kvm_probe.py \
  --host HOST --user USER --scheme http --mode baseline

python3 research/scripts/session_lifecycle_probe.py idle-expiry \
  --host HOST --user USER --scheme http --delay-seconds 300

python3 research/scripts/vmedia_probe.py \
  --host HOST --user USER --scheme http --mode valid

LUNARVIEW_TEST_PASSWORD was used only for the authorized automated run. The password, web-session value, and KVM token are never printed by these tools.

Web authentication and fresh JNLP

The live web assets identify and the BMC confirms this normal flow:

  1. POST /rpc/WEBSES/create.asp with WEBVAR_USERNAME followed by WEBVAR_PASSWORD.
  2. Parse JavaScript data, not JSON, for exactly one HAPI_STATUS and one quoted SESSION_COOKIE.
  3. Insert SessionCookie into a real cookie jar. The server itself sets only an unrelated test cookie; the browser JavaScript performs this insertion.
  4. GET /rpc/WEBSES/validate.asp and require status zero.
  5. GET /Java/jviewer.jnlp with the UI's two query parameters and parse exactly 15 direct application arguments.

The RPC bodies have no reliable HTTP length delimiter and end at the semantic marker //Dynamic data end. The captured JNLP declared a longer Content-Length than its complete XML body; bounded reading through </jnlp> was therefore required. HTTP/1.1 plus Connection: close worked on this BMC; the HTTP/1.0 compatibility switch was not needed. The lab endpoint used plaintext HTTP, so the research helper reports that credential-transport warning rather than hiding it.

One nonexistent username attempt and one wrong-password attempt produced byte-identical bodies with HAPI_STATUS=-6 and no usable session value. This firmware does not expose enough information to distinguish those two causes; the correct diagnostic is “invalid username or password.” A valid login immediately afterwards succeeded, so the bounded negative tests did not lock the account.

The fresh JNLP result confirmed all 15 positions documented in jviewer-arguments.md. Its final argument exactly matched the login web-session value, while argument 3 was a separate KVM token. The JNLP codebase scheme differed from the acquisition scheme, so scheme equality is not a valid parser requirement.

Issuance, refresh, expiry, and logout

Experiment Runtime observation Bounded conclusion
Three independent login/JNLP/logout flows All three 35-byte ASCII web-session values were distinct; all three 16-byte ASCII KVM tokens were distinct; nonsensitive endpoint/count/flag arguments were stable Both secret values are newly issued per login/JNLP flow on this firmware
Two deliberate JNLP GETs in one web session KVM tokens were distinct; the web-session value and all public arguments were unchanged A new JNLP refreshes the KVM token without replacing the web cookie
Silent web session for 300 seconds A single validation after the silent interval returned status zero; logout then succeeded Web-session validity is at least 300 seconds; exact TTL is not determined
Validate immediately after explicit logout Validation returned nonzero status 2 Logout invalidates the web session for the validation endpoint
KVM immediately after web logout KVM type 19 accepted the already issued token Web logout does not immediately revoke that KVM token
KVM after web logout plus 300 seconds of silence KVM type 19 still accepted KVM-token validity after logout is at least 300 seconds; exact TTL is not determined

A finite successful idle interval can establish only a lower bound. No claim of “no expiry” is made, and the validation endpoint was not polled during the silent interval because polling could refresh server-side activity.

IVTP handshake and type 18

The baseline capture observed:

server 23
client 21 (raw web-session body)
client 18 (132 transmitted bytes; header pktSize 140)
client 6
server 19 (one nonzero byte)
server 20, 10, 20, 4099
client 8 during bounded cleanup

This proves that the exact legacy type-18 inconsistency is accepted by this BMC: the 8-byte header declares 140 body bytes, while the client transmits only 124 body bytes. The next 8-byte type-6 header was not consumed as missing type-18 payload; all later control packets remained correctly framed.

A split experiment held type 6 for two seconds after the last type-18 byte. No type-19 response arrived during the delay. A nonzero type 19 arrived about two milliseconds after type 6. The observable rule is therefore:

  • the BMC preserves the 132-byte type-18 boundary despite pktSize=140;
  • this firmware does not acknowledge validation independently during the observed delay;
  • type 6 triggers or gates the validation response.

The capture cannot reveal the internal implementation or why the historical length field is 140. LunarView must preserve the proven 132-byte wire form.

What the KVM server validates

Each mutation below was a single controlled attempt with a freshly issued session; there was no guessing or retry loop.

Type-21/type-18 material Type-19 result
Matching fresh web-session value and KVM token accepted
Web-session value from fresh session A plus KVM token from fresh session B accepted
One-character mutation of an otherwise fresh KVM token rejected
One-character mutation of an otherwise fresh web-session value accepted
Matching credentials with an all-zero textual embedded local-IP field accepted

For this firmware, the primary KVM token is the value whose exact content is validated. The captured KVM path did not require the type-21 body to match a live web-session value, did not pair it with a particular KVM token, and did not require the embedded textual IP to equal the TCP source address. This does not prove that type 21 may be omitted or malformed; only an exact-value change was tested while preserving its framing.

Network-source binding could not be tested because the workstation had only one usable on-link source address. No interface alias or routing change was made solely to manufacture another source.

Server control state, input mode, and video silence

The accepted session returned:

  • type 10 with value 1, selecting the documented relative mouse mode;
  • type 20 with the same one-byte LED state twice;
  • type 4099 with eight configuration bytes. Compression mode was zero (documented YUV 4:2:0), and the VGA-DAC/host-video-output field was zero.

The initial captures sent no HID. The later refresh-hid follow-up waited for type 10 value 1, then reproduced the otherwise-missing JViewer resume behavior: type 4, type 6, and one 45-byte plaintext type-1 relative mouse report with sequence zero and all four report bytes zero. The server kept the session open and continued with normal type 20/4099 control state, but there is no explicit HID acknowledgement, so this is not proof of general generated-input support.

No type-25 video fragment arrived in either of two captures:

  • 180 seconds after a normal accepted baseline handshake;
  • 120 seconds after an accepted handshake followed by the same type-4 then type-6 refresh sequence used by the JViewer UI.

Nor did a fragment arrive during the 20-second refresh-hid follow-up after the exact type-4, type-6, zero-relative-HID resume sequence. Thus the earlier video silence is not explained by omission of that JViewer-generated report.

Finally, a fresh original JViewer process was run for 60 seconds through a bounded localhost TCP relay. The normal standalone launcher first had to be given a private test-only JNI shim for its keyboard-layout query: Java Web Start would have loaded the historical native bundle, while the local launcher only placed that bundle on java.library.path; loading the original library itself is no longer possible on this host because it names removed X11/libstdc++ paths. The shim returned only the US keyboard-layout identifier and did not intercept or alter KVM traffic. The legacy process remained healthy and the BMC accepted type 19. Its complete client stream was exactly types 21, 18, and 6; the server stream was types 23, 19, 10, 20 (twice), and 4099. It contained zero type-25 packets and parsed without trailing or malformed bytes. Raw relay streams remain private and ignored.

This control establishes that the supplied original viewer also received no video from the current lab state. The missing frame is therefore not evidence of a LunarView/Python handshake divergence.

The read-only web power-status endpoint reported the host as powered on, and the eighth type-4099 field was zero. JViewer names that field vga_dac and maps it to the Host Video Output menu item, but does not use it to gate streaming or decoding. Its zero value therefore does not establish the cause of video silence. Changing that setting or sending host input would have exceeded the capture-only boundary, so neither was done. Consequently this environment did not yield runtime evidence for fragment markers, start code, frame-number lifecycle, compressed-size agreement, resolution, RC4 flags, keyframe cadence, or damaged-frame recovery. The transport and authentication are proven; the remaining gap is the unexplained absence of type-25 packets, not an observed IVTP framing failure.

Liveness observation

After the initial state packets, the baseline KVM socket remained connected without any client transmission for the rest of the 180-second observation. The server sent no periodic packet after its final configuration response near the start of the session. Type 8 and web logout then completed normally.

This gives a KVM idle-tolerance lower bound of 180 seconds and confirms no server-originated periodic heartbeat in that window. It does not establish an infinite timeout or the configured disconnect threshold. A paused-video idle comparison could not reach its required first-frame boundary because no video frame arrived.

Virtual-media authentication and checksum

The valid CD-class probe reproduced the Java client's fresh zeroed 131,134-byte buffer and both aliased IUSBHeader.write passes. For device index zero, the transmitted 160-byte auth frame has an additive byte sum of 0xDA, not zero. The BMC returned a 55-byte-payload 0xF1 acknowledgement with connection status 1.

The server acknowledgement itself used zero for major, minor, and the packet header-length field. The Java reader stores but does not validate those fields; the initial strict runtime parser exposed this difference and was narrowed to the two observed/constructed header-length values without weakening the signature or payload bounds.

A second probe changed only the low bit of final wire offset 11 after both legacy checksum passes. Its transmitted additive sum changed from 0xDA to 0xDB, yet the BMC returned the same 0xF1, status-1 acceptance. Therefore this firmware does not enforce the IUSB header checksum on the initial virtual-media authentication frame.

Both sockets were closed immediately after the single acknowledgement. No local source was opened, no media-state notification was sent on KVM, and no SCSI request was serviced. Checksum handling on later SCSI traffic, opaque native envelope fields, sequence policy, and the hard-disk 0xF4 extension remain untested; resolving them requires an explicitly approved nonsensitive test medium.

Serial/SOL evidence

The authenticated live navigation bundle contains Console Redirection and Virtual Media pages but no serial, terminal, COM-redirection, or SOL entry. This strengthens the artifact-level negative result for this web/JViewer bundle. It still does not test the BMC's separate IPMI SOL capability, which would require a different client/protocol and authorization scope.

Final confidence and residual limits

Stage 1R resolves or bounds the practical static unknowns as follows:

  • exact legacy web login and malformed-length JNLP acquisition: confirmed;
  • type-18 wire acceptance and next-packet framing: confirmed;
  • KVM token validation versus cookie/IP fields: experimentally separated;
  • token/cookie issuance and same-session JNLP refresh: confirmed;
  • web-session and post-logout KVM-token lifetime: at least 300 seconds;
  • KVM idle tolerance: at least 180 seconds, with no periodic server packet;
  • initial virtual-media checksum enforcement: absent on this firmware;
  • live relative-mouse mode and video configuration response: confirmed; one zero-state resume report caused no disconnect but has no positive input ACK;
  • original JViewer control: accepted for 60 seconds and likewise received no type-25 fragment, isolating the video gap to the current lab/BMC state;
  • video-fragment/header/RC4 runtime values: still not observable without a producing video source;
  • later virtual-media SCSI semantics: intentionally not exercised without a user-approved benign medium;
  • true network-source binding, exact expiry thresholds, IPMI SOL, and cross-firmware behavior: not determinable from this single lab path.

These bounded residuals should remain explicit during Stage 2 comparison; a third-party implementation must not be used to rewrite observed facts from this BMC or the supplied JViewer bytecode.