Files

146 lines
6.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SSL hook for VDDK protocol capture
VDDK’s NBD path is TLS end to end: SOAP to vCenter on 443, then authd/NFC
to ESXi on 902. Packet captures on those ports are ciphertext, so they
cannot show command names, tickets, or NFC frames.
This project used a small `LD_PRELOAD` library (`sslhook.c`, built to
`sslhook.so`) to log OpenSSL plaintext while a ctypes wrapper ran
`VixDiskLib_ConnectEx` / `VixDiskLib_Open`. The authd sequence in
`docs/nfc_auth.md` was recovered from that log, not from VDDK source.
The hook is a reverse-engineering aid. It is not part of OpenVixDiskLib.
## Why not tcpdump or VDDK logs
| Approach | What it shows | Gap |
| -------------------------------- | -------------------------------------------------- | ------------------------------------------------ |
| tcpdump on 443 / 902 | TLS records | No SOAP bodies, no authd lines, no NFC frames |
| `strace` on `write` / `send*` | Plaintext NFC **after** PROXY (`useSSL=0`) | TLS still opaque; `-s` truncates large extras |
| `vixDiskLib.nfc.LogLevel=4` | Function names, `opId` / `type` / `size` | Not the bytes on the wire |
| Strings in `libvixDiskLib.so` | Command tokens (`SESSION`, `PROXY`, `BANNER`) | Not order, spacing, or replies |
| SSL hook on `SSL_write`/`read` | Exact buffers before encrypt / after decrypt | Must split connections and reassemble 1-byte I/O |
VDDK logs were still useful to *name* AIO message types after the hex
dump showed `type` and `size`. The hook supplied the actual framing.
## How `LD_PRELOAD` interposition works
The hook exports `SSL_write` and `SSL_read` with OpenSSL’s signatures.
When the process starts with `LD_PRELOAD=/path/sslhook.so`, the dynamic
linker binds VDDK’s calls to those symbols instead of `libssl`.
Each wrapper:
1. Resolves the real OpenSSL function with `dlsym(RTLD_NEXT, ...)`.
2. Logs the plaintext buffer.
3. Calls the real function so the session is unchanged.
```
VixDiskLib --> SSL_write (hook) --> log hex --> SSL_write (libssl)
VixDiskLib <-- SSL_read (hook) <-- log hex <-- SSL_read (libssl)
```
`SSL_write` logs **before** encryption. `SSL_read` calls OpenSSL first,
then logs `n` decrypted bytes when `n > 0`.
## Implementation notes
The working copy lived under `/tmp` during capture (`/tmp/sslhook.c`).
Behavior that mattered for parsing:
- Log path from `SSLHOOK_LOG`, default `/tmp/sslhook-open.log`.
- Unbuffered writes (`_IONBF`) so a crash still leaves a complete file.
- Each record tagged with the `SSL *` pointer so vCenter HTTPS and
ESXi:902 are separable. An earlier version omitted the pointer and
mixed both streams into one timeline.
- Payload stored as hex, not mixed ASCII, so binary NFC frames stay
unambiguous.
Record layout:
```
==== W 0x7f8a1234 46 ====
53455353494f4e2035326364656263352d...0d0a
```
| Field | Meaning |
| ------- | ---------------------------------------------------- |
| `W`/`R` | Write (plaintext to encrypt) or read (decrypted) |
| `%p` | `SSL *` for this socket |
| length | Byte count of this OpenSSL call |
| hex | Buffer contents |
`SSL_read` is often **one byte per call**. A 220 banner is therefore
dozens of `R 1` records. Adjacent records with the same `SSL *` and
direction must be concatenated before parsing lines or NFC headers.
OpenSSL 3 also has `SSL_write_ex` / `SSL_read_ex`. This VDDK 8.0.2
build still used `SSL_write` / `SSL_read`, so those two symbols were
enough. If a later library switches APIs, the hook would need matching
wrappers.
## How it was used for authd
A minimal ctypes program loaded `libvixDiskLib.so`, called
`VixDiskLib_InitEx`, `ConnectEx` (UID to vCenter, `nbd`), and `Open` on
the lab VMDK. The process was started as:
```bash
export LD_LIBRARY_PATH=/home/ubuntu/workspace/vmware_nbd_tests/.vddk
export LD_PRELOAD=/tmp/sslhook.so
export SSLHOOK_LOG=/tmp/sslhook-open.log
python /tmp/vddk_open_trace.py
```
`LD_LIBRARY_PATH` is required so VDDK uses its bundled `libssl.so.3`.
`LD_PRELOAD` still interposes that copy.
After the run, records were grouped by `SSL *`. The ESXi connection is
the one whose writes contain `SESSION ` and `PROXY `. Concatenating
that stream after the TLS handshake produced:
```
C -> SESSION <sessionId>\r\n
C -> BANNER \r\n
S -> 220 VMware Authentication Daemon Version 1.10: ...\r\n
C -> THUMBPRINT_SHA2 PlainText\r\n
S -> 200 <SHA-256 thumbprint>\r\n
C -> PROXY vpxa-nfc\r\n
S -> 200 Connect ha-nfc\r\n
```
The same log also showed the SOAP `NfcGetVmFiles` body on the vCenter
`SSL *` (`xmlns="urn:vim25"`, moref `nfcService`). That is how the
ticket call was identified as `NfcGetVmFiles` rather than guessing
from `libvim-types` strings alone.
Details that only the hex dump made obvious:
- `BANNER` includes a trailing space (`BANNER \r\n`).
- `THUMBPRINT_SHA2` argument is the literal `PlainText`, not the
ticket SHA-1 thumbprint.
- `SESSION` has no reply; waiting for a line after it looks like a hang.
- Ticket `sessionId` is the UUID string on the `SESSION` line.
Those facts are written up in `docs/nfc_auth.md`. OpenVixDiskLib
(`openvixdisklib/nfc_auth.py`) replays this sequence; it does not use
the hook.
After `200 Connect`, NFC is **not** on `SSL_write`. VDDK uses
`write`/`read` on `SSL_get_fd` (`useSSL=0`). A later hook that also
interposed those syscalls, filtered to peer port 902, recovered the
264-byte handshake and AIO frames in `docs/nfc_open.md`. TLS record
bytes (`16 03` / `17 03`) on that fd are the authd phase and must be
skipped.
## Limits
- The hook sees every OpenSSL client in the process (VDDK and, if the
same interpreter is used, anything else linked to OpenSSL). Filter by
`SSL *`.
- It does not decode TLS handshakes, certificates, or SOAP envelopes;
that is done offline on the hex log.
- It must not ship in OpenVixDiskLib. Keep it out of
the library path used by `openvixdisklib/nfc_auth.py`.