Detection notes from a Graylog Log4Shell intrusion: JNDI in the sessions API, class-init execution, and why the staging log beats the JNDI log
The durable signatures here are in payload content and request patterns, not infrastructure. On the application: POST to /api/system/sessions carrying a ${jndi: substring in the username field, paired with the X-Requested-By: cli header the app enforces as a CSRF control. On the host: a child process spawned from the Java service, since the command fires from class initialization, not a method call site. Content match on the template name ysoserial/payloads/util/Gadgets$StubTransletPayload, which is stable across payloads built this way.
One hunting note that saved us: for this technique, use the staging server's access log, not the JNDI listener log. The listener log was only scanner noise. The access log is what dated the campaign and proved execution.
Detection section and IOCs: https://hunt.io/blog/ukraine-state-data-exchange-graylog-log4shell