Skip to content

Latest commit

 

History

History
236 lines (206 loc) · 18.2 KB

logging.md

File metadata and controls

236 lines (206 loc) · 18.2 KB

Logging and producers

Glutton has two output paths: process logs through Go slog, and optional producer events sent to HTTP or hpfeeds sinks. Handlers can emit both, but they're configured separately.

Process logs

producer.NewLogger(...) creates a JSON slog logger that writes every record to:

  • stdout
  • the path configured by --logpath

The file writer uses lumberjack rotation: 200 MB max size, 356 days max age, compression on. It is skipped (stdout only) when --logpath is empty, /dev/null (the default), or an existing non-regular file: lumberjack rotates by renaming the file and creating a new regular one, which on /dev/null replaced the device with a file in RAM-backed devtmpfs that every process on the host wrote into. Every record carries the sensorID attribute (read from / written to <var-dir>/glutton.id).

The --debug flag is parsed but not wired into slog.HandlerOptions, so it does not currently lower the log level.

Producer events

Producer events follow the producer.Event schema:

JSON field Type Meaning
timestamp string (RFC 3339) UTC time the event was produced.
startedAt string (RFC 3339) Start of what the event covers (md.Added): the TCP connection's accept time from the connection table, or the UDP datagram's receive time (for a SIP dialog, its INVITE). Sensors up to v1.0.1+b9e1a04 reused the first datagram seen from the same UDP source address and port, so UDP startedAt could be minutes early. Go does not omit a zero time.Time, so an unknown start is 0001-01-01T00:00:00Z, not absent.
durationMs number, optional Milliseconds from startedAt to produce time. Omitted when 0.
transport string tcp or udp.
srcHost string Source IP.
srcPort string Source port. Unlike dstPort, this is a string.
srcPtr string, optional First reverse-DNS name when a PTR lookup already ran (not on CIDR-matched scanners).
dstHost string, optional Original destination IP (TPROXY LocalAddr / UDP dest). This is the sensor's address; strip it before showing events publicly.
dstPort number Original destination port from metadata.
sensorID string Glutton sensor ID.
sensorVersion string, optional Build version (VERSION / sensor_version).
rule string, optional Rule match string when metadata includes a rule.
ruleName string, optional Optional name from rules.yaml.
handler string Handler name supplied by the protocol handler.
handlers array of strings, optional Protocol stack from the wire up, e.g. ["tcp", "tls", "socks", "tls", "http"]: the transport, a tls layer the sensor terminated before the handler (rule tls), the handler, then layers the handler decoded inside its own stream (a SOCKS tunnel carrying TLS and HTTP). Handlers that decode a nested layer record it: SOCKS (tls, http), the catch-all, RDP and Jabber (tls, from in-band, CredSSP-era or STARTTLS handshakes). When one handler hands the session to another the earlier layer comes before it (["tcp", "http", "mcp"], ["tcp", "tls", "http"] for HTTPS the catch-all decrypted). The last entry is the innermost protocol. Older sensors omit it; fall back to [transport, handler].
payload string (base64), optional First-frame payload bytes.
payloadHash string (hex), optional SHA-256 of the (sanitized) top-level payload.
frameCount number, optional Number of decoded frames when decoded is a slice. Omitted when 0.
endReason string, optional Why the session ended (client_close, client_reset, timeout, handler_close, read_error, write_error, max_frames, evicted). client_reset means the client aborted the connection with a TCP RST instead of closing it. evicted means the handler's session table was full and the least recently active session was flushed early. Omitted by handlers that do not set it.
mptcp boolean, optional true when the TCP connection negotiated Multipath TCP (the client offered MP_CAPABLE and the sensor kernel supports MPTCP). The sensor's TCP listener is MPTCP-enabled (Go 1.24+ default). Omitted for plain TCP and UDP.
tls object, optional Present only when the sensor terminated TLS. See TLS details.
scanner string, optional Scanner classification from scanner.Classify(...), else a payload-hash fleet label, else a JA4 fleet label (from tls.ja4, per handler), else a behavioral label the handler set on the session (ssh-authkeys-implant, ssh-spoofed-openssh). Omitted when empty.
decoded array, object or null, optional Handler-specific decoded data. See Decoded data.

All optional fields use omitempty: an empty string, a zero number or a nil value is left out of the JSON instead of being sent as "", 0 or null.

Events are emitted only when (1) producers.enabled is true so a producer object exists, (2) a handler calls ProduceTCP(...) or ProduceUDP(...), (3) the matched rule does not set produce: false, and (4) at least one sink is enabled. Before output, configured addresses values are scrubbed from payload bytes (ASCII and UTF-16LE) and replaced with 1.2.3.4. The same sanitizer runs on every string and byte slice inside decoded (frame fields such as from, to, endpoint_url, nested structs, maps), so the sensor address never appears in decoded output; fixed-size byte arrays are left alone. Events from sensors built before commit 937dcd8 (after v1.0.1) only scrubbed payload and path.

TLS details

tls is set when the rule has tls: true, or tls: auto and the client opened with a ClientHello, or when the tcp catch-all terminated a complete ClientHello itself (its first decoded frame is then the raw tls-clienthello read, followed by a tls-handshake write for the server flight; if the decrypted traffic is HTTP, the event is an http event instead and has no such frames). decoded and payload then hold the decrypted plaintext protocol, so a display should say that the session was TLS. When the handshake fails mid-flight there is no top-level tls object, but the catch-all still records the tls-handshake write. If the honeypot could not send its flight (endReason write_error), that write has an error field (reply over budget when the tcp_reply_limit guard dropped it, otherwise write failed) and may have no payload.

Field Type Meaning
serverName string, optional SNI from the ClientHello.
alpn array of strings, optional Protocols offered by the client.
version string, optional Negotiated TLS version, e.g. TLS 1.3.
cipher string, optional Negotiated cipher suite. Empty (so omitted) when the handshake failed.
clientHello string (base64), optional Raw ClientHello records, capped at 4 KiB.
truncated boolean, optional Set when clientHello was cut at the cap.
ja3 string, optional JA3 MD5 of the ClientHello. Set whenever the recorded hello parses, also when the handshake failed.
ja3n string, optional JA3 MD5 with extensions sorted, stable for clients that randomize extension order (Chrome).
ja4 string, optional FoxIO JA4 fingerprint, e.g. t13d1516h2_8daaf6152771_e5627efa2ab1.
ja4_r string, optional JA4 raw form: the sorted cipher and extension lists and the signature algorithms in clear instead of hashed.

Decoded data

Value encodings inside decoded follow Go's encoding/json:

  • Byte slices ([]byte) are base64 strings. Frame payload fields are always base64.
  • Fields documented as "(hex)" are hex strings produced by the handler (spi_i, spi_r, token, session_id on openvpn, challenge, and so on).
  • Fixed-size byte arrays are JSON arrays of numbers 0–255. Today these are only the bittorrent fields protocol_identifier, reserved, info_hash and peer_id; show them as hex.
  • Every other array of numbers is a list of IDs, not bytes: cipher_suites and extensions (dtls, uint16), etypes (kerberos), encodings (rfb, int32, can be negative). Show these as lists; joining them as hex gives wrong values.
  • Nested objects (header on smb, mongodb and bgp, open/notification on bgp, message on cassandra, header and options on cql, questions on mdns, submessages on rtps) are JSON objects or arrays of objects.

Array-of-frames decoded entries share these JSON names when the handler fills them: direction, payload, command (leaf operation), path, status (writes), truncated. Handler-specific fields sit beside them. A display that shows only the shared fields hides the most useful data for most handlers, so show every key a frame carries and use the per-handler page linked in the table below to order them. Some fields are easy to miss but matter for reading a session:

  • http: dest_port and src_port on reads, because a session can span connections and ports while the top-level dstPort/srcPort belong to the first connection.
  • rdp: ntlm_domain, ntlm_user, ntlm_workstation and ntlm_version on the NTLMAuthenticate frame, plus netntlmv2 (hashcat -m 5600) for NTLMv2; nla_accepted when a password in rdp.credentials or rdp.guess_passwords matched, then ntlm_user and nla_password from the decrypted authInfo; client_data (client name, client_name_ansi, build, screen, keyboard, channels) on MCSConnectInitial; client_info (domain, username, password, client time zone tz_bias/tz_standard_name/tz_daylight_name) on ClientInfo. Most probes never do NLA: the Confirm prefers TLS, so the password arrives in client_info. An unknown CredSSP password still gets STATUS_LOGON_FAILURE.
  • sip: from, to, call_id, username, called_number (normalized dialed number; a REFER's transfer target), tool (fingerprinted scanner/dialer or fleet tell), the digest-auth fields realm/nonce/digest_uri/digest_response/algorithm/qop/nc/cnonce (the honeypot issued the nonce, so these crack the guessed password offline, hashcat -m 11400), variant/visit on writes, and dropped on UDP writes the reply budget kept from leaving. UDP calls end with one command rtp frame per caller media stream: dtmf digits, packets/lost, duration_ms, and payload_hash of the WAV recording.
  • ssh, telnet, jabber, minecraft, smtp, opcua, mqtt, dicom, pop3, ftp, http, rtsp, socks, hiflying, sadp, mssql, firebird, postgres, smb, rsync: username (or user) and password as the client sent them. Passwords are guessing attempts, not confidential data, so they are recorded in plaintext. smb also sets lm_response/ntlm_response (hex) for basic Session Setup challenge responses. rsync sets response (the base64 digest of the password and the AUTHREQD challenge) instead of password.
  • hl7: security (MSH-8) as the client sent it; some senders put a password or token there, so it is recorded in plaintext like other credentials.
  • dtls, jabber: server_name.
  • smb, tcp: shellcode on a read, listing shellcode/PE heuristics matched in the captured payload; smb also sets xor_key and decoded_hash when a DoublePulsar payload was de-obfuscated, and variant/visit on emulated DoublePulsar replies.

Example shape:

{
  "timestamp": "2026-05-15T12:00:00Z",
  "transport": "tcp",
  "srcHost": "203.0.113.10",
  "srcPort": "54321",
  "dstHost": "192.0.2.10",
  "dstPort": 80,
  "sensorID": "00000000-0000-0000-0000-000000000000",
  "sensorVersion": "v0.0.0",
  "rule": "Rule: tcp",
  "frameCount": 1,
  "endReason": "client_close",
  "handler": "http",
  "payload": "R0VUIC8gSFRUUC8xLjENCg0K",
  "decoded": [
    {
      "direction": "read",
      "command": "GET",
      "path": "/",
      "session_id": "11111111-1111-1111-1111-111111111111",
      "payload": "R0VUIC8gSFRUUC8xLjENCg0K"
    }
  ]
}

decoded is handler-specific:

Handler Transport Details
http TCP protocols/http.md
tcp TCP protocols/tcp.md
ipopts TCP (AF_PACKET) protocols/ipopts.md
udp UDP protocols/udp.md
proxy_tcp, proxy_udp TCP/UDP protocols/proxy.md
sip TCP/UDP/TLS protocols/sip.md
openvpn UDP protocols/openvpn.md
mdns UDP protocols/mdns.md
l2tp UDP protocols/l2tp.md
raknet UDP protocols/raknet.md
kerberos UDP protocols/kerberos.md
ike UDP protocols/ike.md
nbns UDP protocols/nbns.md
coap UDP protocols/coap.md
a2s UDP protocols/a2s.md
steam UDP protocols/steam.md
ddp UDP protocols/ddp.md
rtps UDP protocols/rtps.md
wsdiscovery UDP protocols/wsdiscovery.md
knx UDP protocols/knx.md
bacnet UDP protocols/bacnet.md
itp UDP protocols/itp.md
rip UDP protocols/rip.md
tftp UDP protocols/tftp.md
dht UDP protocols/dht.md
ntp UDP protocols/ntp.md
stun UDP protocols/stun.md
snmp UDP protocols/snmp.md
hiflying UDP protocols/hiflying.md
realtek UDP protocols/realtek.md
pcanywhere UDP protocols/pcanywhere.md
mssql_browser UDP protocols/mssql-browser.md
sadp UDP protocols/sadp.md
dtls UDP protocols/dtls.md
mqtt TCP protocols/mqtt.md
memcache TCP protocols/memcache.md
modbus TCP protocols/modbus.md
dnp3 TCP protocols/dnp3.md
enip TCP, UDP protocols/enip.md
slp UDP, TCP protocols/slp.md
radius UDP protocols/radius.md
opcua TCP protocols/opcua.md
gearman TCP protocols/gearman.md
rocketmq TCP protocols/rocketmq.md
nettcp TCP protocols/nettcp.md
mctp TCP protocols/mctp.md
dahua TCP protocols/dahua.md
tuya TCP protocols/tuya.md
sofia TCP protocols/sofia.md
bgp TCP protocols/bgp.md
cassandra TCP protocols/cassandra.md
cql TCP protocols/cql.md
rtsp TCP protocols/rtsp.md
icap TCP protocols/icap.md
ipp TCP protocols/ipp.md
dicom TCP protocols/dicom.md
hl7 TCP protocols/hl7.md
mongodb TCP protocols/mongodb.md
mssql TCP protocols/mssql.md
firebird TCP protocols/firebird.md
postgres TCP protocols/postgres.md
redis TCP protocols/redis.md
minecraft TCP protocols/minecraft.md
rcon TCP protocols/rcon.md
socks TCP protocols/socks.md
mcp TCP protocols/mcp.md
telnet TCP protocols/telnet.md
smtp TCP protocols/smtp.md
ssh TCP protocols/ssh.md
ftp TCP protocols/ftp.md
rfb TCP protocols/rfb.md
spice TCP protocols/spice.md
iscsi TCP protocols/iscsi.md
bittorrent TCP protocols/bittorrent.md
jabber TCP protocols/jabber.md
pop3 TCP protocols/pop3.md
imap TCP protocols/imap.md
whois TCP protocols/whois.md
ident TCP protocols/ident.md
dcerpc TCP protocols/dcerpc.md
rsync TCP protocols/rsync.md
dns TCP, UDP protocols/dns.md
acpp TCP protocols/acpp.md
rpcbind TCP/UDP protocols/rpcbind.md
nfs TCP protocols/nfs.md
wdb UDP protocols/wdb.md
adb TCP protocols/adb.md
nvms9000 TCP protocols/nvms9000.md
pcom TCP protocols/pcom.md
smb TCP protocols/smb.md
rdp TCP protocols/rdp.md

HTTP producer

When producers.http.enabled is true, Glutton marshals each event as JSON and POSTs it to producers.http.remote with Content-Type: application/json. From source:

  • HTTP client timeout: 10s; TLS handshake timeout: 5s.
  • Events from private source IPs are skipped.
  • Userinfo in the remote URL is used as HTTP Basic Auth.
  • Query strings in the configured URL are preserved.

hpfeeds producer

When producers.hpfeeds.enabled is true, Glutton connects to the broker at startup and publishes gob-encoded producer.Event values to producers.hpfeeds.channel.

producers:
  hpfeeds:
    enabled: false
    host: 172.26.0.2
    port: 20000
    ident: ident
    auth: auth
    channel: test

Treat all logged payloads as attacker-controlled data in downstream pipelines.