Skip to content
teamfudPublic

About

Just a funny little logging guy.

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

61 Commits

Folders and files

Repository files navigation

elmer

Simple tool for detecting and alerting suspicious activity on Linux, Windows, and FreeBSD/pfSense.

Built-in detections cover reverse shells (bash/dev/tcp, nc -e, socat, python/perl/ruby/php one-liners, mkfifo), tunnels (chisel, frp, ngrok, ssh -D/-L/-R, DNS tunnelers), credential access (shadow reads, mimikatz, LSASS dumps, SAM saves), LOLBins (certutil, bitsadmin, mshta, rundll32, regsvr32), persistence actions (useradd, chmod u+s, setcap, schtasks, net user /add), anti-forensics (history -c, log shredding), and recon tools. Rules are regex-based and extensible via config.

Quick start

Install once per box; after that elmer runs as a service (started at boot, restarted whenever it dies) and the only interaction is reading its logs.

# Linux (root): systemd service
sudo ./elmer-linux-amd64 install -c elmer.yaml
tail -f /var/log/elmer/elmer.log

# Windows (elevated): LocalSystem service, also enables 4688 auditing
.\elmer-windows-amd64.exe install -c elmer.yaml
Get-Content -Wait -Tail 50 C:\ProgramData\elmer\logs\elmer.log

# pfSense / FreeBSD (root shell, menu option 8): rc.d service under daemon(8)
./elmer-freebsd-amd64 install -c elmer.yaml
tail -f /var/log/elmer/elmer.log

Running elmer during a competition? See the competition-day runbook: installing on each box, reading alerts, and what to do about them. The full documentation covers configuration, every monitor and rule, the log format, alert channels and the security model.

Without -c, install keeps an existing config or writes the commented default (elmer init-config writes the same template for editing first; elmer test-alerts -c elmer.yaml checks delivery). Re-run install to upgrade the binary or push a new config; elmer uninstall removes the service but keeps config, state and logs.

Logs live in /var/log/elmer (Linux, also journald) or C:\ProgramData\elmer\logs (Windows): elmer.log has one line per event, events.jsonl every field for jq. They rotate by size, carry a heartbeat every 5 minutes (a gap means elmer was down), and keep recording when red team cuts internet to the alert channels. elmer start still runs in the foreground for testing.

Channels: stdout (always, colored or --json NDJSON for jq), Discord webhook (batched embeds, rate-limit aware), ntfy push (severity → priority), generic webhook (full event JSON, optional HMAC-SHA256 X-Elmer-Signature). Each channel filters by min_severity and has its own queue — a hung webhook never blocks the others. A Wazuh agent can also ship events.jsonl to a manager, using the rules in contrib/wazuh/ (see the runbook).

Startup hunt

Every other monitor reports changes, so anything planted before elmer first runs would look normal. elmer therefore sweeps the box at startup and again every hunt.interval (30m), reporting on re-sweeps only what was not there last time, so re-planted implants show up again. elmer hunt does the same sweep by hand, printing findings most-severe first without alerting:

  • Linux: running processes (deleted/memfd/tmp/hidden executables, [kworker] masquerades, LD_PRELOAD, pids hidden from /proc), listeners and raw/packet sockets, uid-0 and passwordless accounts, sudo rules, SSH keys and sshd backdoor settings, shell profiles, cron, systemd units, ld.so.preload, PAM, tainted/hidden kernel modules, SUID shells and root-granting file capabilities, webshells, and dpkg --verify / rpm -Va for trojaned binaries and PAM modules.
  • Windows: processes (system names from the wrong path, user-writable locations), listeners and external connections, services and service DLLs, Run keys, startup folders, scheduled tasks, WMI subscriptions, IFEO/SilentProcessExit, sticky-keys swaps, Winlogon, AppInit_DLLs, LSA packages, Defender exclusions/disables, local admins, webshells.
  • FreeBSD / pfSense: the Linux account/SSH/cron/shell/PAM/SUID checks plus rc.d boot scripts, libmap.conf redirects, kernel modules outside /boot, pkg check -s for modified packaged files, code in the pfSense GUI that no package owns, PHP/nginx settings that inject code into the GUI, and pfSense's config.xml: users with SSH keys, boot commands (shellcmd/earlyshellcmd), cron jobs, port forwards, WAN allow rules, packages, logins handed to an external auth server.

Commands found in cron lines, units, Run keys and tasks also go through the regular detection rules. Disable with monitors: {hunt: false}.

Scored-service watchdog

Uptime is a third of the score, and the scoreboard says a check fails, not why. The watchdog alerts HIGH when a service stops working and MEDIUM when it is back, after two failed checks in a row so a deliberate restart does not page. It never restarts anything.

With no configuration it watches every reachable (non-loopback) TCP listener seen in the first two minutes after start, and says why one went away:

HIGH service down: tcp/3306 (mysqld): nothing listens on port 3306 any more;
     /usr/sbin/mysqld (pid 812) has exited. The log lines just before this one show what changed.
HIGH service down: tcp/22 (sshd): port 22 now listens on loopback only: unreachable to the scoring engine

Configured services also get a local protocol check (tcp, http, https, ssh, ftp, smtp, mysql, dns):

watchdog:
  services:
    - {name: web, port: 80, check: http, path: /}
    - {name: mysql, port: 3306, check: mysql}

It sees the box, not the network: a pfSense rule, routing, or a changed password the scoring engine logs in with can still fail a check while everything looks fine locally (other monitors report those changes).

FreeBSD / pfSense

There is no tracing tier: elmer polls the process table (every poll_interval), stats fim.paths and runs sockstat every 2s. Like Linux's /proc fallback, a process that starts and exits between polls can be missed. The persistence monitor diffs the live pfctl ruleset and, on pfSense, the security-relevant parts of /conf/config.xml within seconds of every save (new users or SSH keys, boot commands, cron jobs, port forwards, pass rules, auth servers), along with the GUI's PHP and nginx configs. Logins, including webConfigurator logins and failures, come from /var/log/auth.log. Keep pfSense's "Use RAM Disks" off so logs survive reboots.

Linux telemetry tiers

elmer picks the best telemetry source available and degrades loudly:

  1. bpftrace (root + bpftrace on PATH, kernel with tracepoints): successful execve/execveat with full argv, connect/bind with peer address and owning pid. The .bt script is embedded; elmer spawns the host bpftrace binary. elmer-bpftracecheck (built from tools/bpftracecheck, shipped with the Linux release) verifies it works on a box. If bpftrace dies mid-run, elmer raises a HIGH alert and the tiers below take over.
  2. Netlink proc connector (root): exec events (when bpftrace is not running) and setuid-to-root escalations from binaries that aren't setuid-root (cap_setuid abuse, exploits), enriched from /proc. Runs alongside bpftrace for the setuid events.
  3. /proc polling (any user): exec diffs every poll_interval — misses processes that exec and die between scans.

Commands

Command Purpose
elmer install [-c cfg] Install/upgrade as a self-restarting service and start it
elmer uninstall Remove the service (keeps config, state, logs)
elmer start [-c cfg] [--json] Run all monitors in the foreground until interrupted
elmer hunt [--min-severity low] [--json] One-shot sweep for implants already on the box
elmer audit [--write-baseline] One-shot persistence surface report
elmer test-alerts Synthetic alert through every channel
elmer init-config [path] Write a commented default config
elmer harden Platform steps to maximize telemetry (Windows: runs them)
elmer version Print version

Config discovery: -c → $ELMER_CONFIG → ./elmer.yaml → /etc/elmer/elmer.yaml (/usr/local/etc/elmer/elmer.yaml on FreeBSD, %ProgramData%\elmer\elmer.yaml on Windows). With no config file elmer runs on built-in defaults.

Building

make test           # unit tests (host platform)
make build          # dist/elmer-{linux,windows,freebsd}-{amd64,arm64}[.exe]

Releases: make checksums VERSION=v1.0.0 PUBKEY=<public key> builds every target with the tag stamped into elmer version and the signing key baked in, plus SHA256SUMS. elmer isn't published on GitHub: the team hosts the binaries on its own server (see docs/runbook.md).

Static binaries, CGO_ENABLED=0. Dependencies: cobra, x/sys, yaml.v3. Linux top-tier telemetry requires the host bpftrace package (apt-get install bpftrace).

About

Just a funny little logging guy.

Resources

Stars

1 star

Watchers

0 watching

Forks

Used by

Contributors

Languages