Security Research Notebook
A structured record of independent security research: the how matters as much as the what.
A structured record of independent security research: the how matters as much as the what.
This notebook collects the research that actually shipped: writeups that went through coordinated disclosure, had the patch land, and cleared the program's embargo window before appearing here. Everything is organized by target class: web application and cloud platform, Aiven managed services (PostgreSQL, Valkey, Dragonfly, Kafka), blockchain consensus nodes, embedded camera firmware (AXIS OS), and cryptographic protocol implementations.
The organizing principle is methodology-first. The writeup leads with how the bug class was identified (the recon pass, the code path, the wrong assumption in the codebase), not just the end result. The 'how' generalizes to future targets; the specific instance usually doesn't.
Managed database services (Aiven). The most technically deep cluster. PostgreSQL's privilege boundary is complex when a managed provider adds its own gatekeeper extension and SECURITY DEFINER chains on top. The interesting cases are where those layers interact unexpectedly. Notable entries: an autovacuum-triggered code execution path (autovacuum evaluates expression index functions under session_user=postgres, which reaches a SECDEF dblink chain not subject to SECURITY_RESTRICTED_OPERATION); a Valkey RESTORE path that plants a corrupt listpack key surviving RDB persistence and producing an infinite crash loop on restart; a Kafka Karapace gzip-bomb that decompresses without bounds.
MPC cryptography (Fireblocks open-source mpc-lib). Eight findings against the Fireblocks MPC-CMP implementation, P1 through P4. The highest-severity entry: MtA batch verification samples a uint8_t as its randomness parameter. When the drawn value is zero, any invalid proof passes the batch check regardless of validity (proven: 200/200 pass rate with a forced-zero gamma). A second memory-safety finding: the destructor calls OPENSSL_cleanse with sizeof(struct) instead of sizeof(k.data), zeroing 320 bytes past the target field.
Embedded firmware (AXIS OS). Five findings covering SSRF via test endpoints that bypass the camera's own validateaddr helper, an IPv6-mapped IPv4 loopback filter bypass on httptest.cgi, and unauthenticated RTSP-over-WebSocket access via ONVIF.
Blockchain consensus (Electroneum). Validation of CVE-2024-32972 against an unpatched etn-sc node: a single unauthenticated TCP packet with Amount=0 causes an integer underflow in serviceContiguousBlockHeaderQuery, triggering a 7.8 GB allocation that OOM-kills the node. Targeting all IBFT validators halts the chain.
def crc64(data):
"""Exact reimplementation of Valkey's _crc64() from src/crc64.c.
Polynomial: 0xad93d23594c935a9 (Jones)
Initial value: 0
Algorithm: bit-by-bit MSB-first with final reflect
Verified against live Valkey DUMP output: matches byte-for-byte.
"""
POLY = 0xad93d23594c935a9
crc = 0
for byte in data:
c = byte
i = 0x01
while i & 0xFF:
bit = 1 if (crc & 0x8000000000000000) else 0
if c & i:
bit = 0 if bit else 1
crc = (crc << 1) & 0xFFFFFFFFFFFFFFFF
if bit:
crc ^= POLY
i <<= 1
return _crc_reflect64(crc)
def make_corrupt_listpack(entry_count=200):
"""Build a listpack whose 'total bytes' header lies about its real size.
The validation check passes (declared size fits); the reader walks off the end.
"""
entries = [struct.pack('<BB', 0x81, 0x80) for _ in range(entry_count)]
payload = b''.join(entries)
# Declare a total-bytes header smaller than the real payload
false_total = len(payload) // 2
return struct.pack('<I', false_total) + struct.pack('<H', entry_count) + payload
Three methodology documents cover a multi-iteration variant-hunting exercise against sequoia-openpgp (recon pass, candidate ranking, and what didn't survive triage), a root-cause walk-through of CVE-2025-47934 in OpenPGP.js (signature-verification bypass via msg.packets mutation and its relationship to the v6.2.0 compression refactor), and a complete audit log of systemd-coredumpd and the systemd-resolved DNS parser that found nothing. That last one is a negative result written up as methodology and dead ends. The systemd audit in particular is useful: it shows what a thorough pass looks like when it doesn't produce a finding.
Disclosure policy: everything in this notebook cleared the program's disclosure window: patch shipped, embargo lifted, or the same bug class disclosed elsewhere with a published CVE. Findings still inside active coordinated-disclosure windows are not included. No customer data was accessed or retained during any of this research.
Each report below went through coordinated disclosure and was already public before appearing here. Click any title for the full writeup: summary, impact, root cause, a sanitized proof of concept, and the fix.