stealth(1)

πŸ“¦
version 4.04.00 Β· 3 versions
, in debian trixie, ubuntu questing, ubuntu resolute, ubuntu stonking

πŸŽ€ stealth(1) β€” Security Enhancement πŸŽ€

okay bestie, this is stealth from section 1 of the manual. Buckle up. πŸ’–


✨ NAME ✨

stealth β€” Stealthy File Integrity Scanner πŸ•΅οΈβ€β™€οΈ


✨ SYNOPSIS ✨

First, tiny note: `<uds>’ represents the location of the used Unix Domain Socket.

stealth  --daemon <uds> --dry-run --log <file-spec> --logmail
--max-size <size>[BKMG] --no-mail --parse-policy-file
--random-interval <seconds> --repeat <seconds>
--skip-files <file-spec> --syslog
--syslog-facility <fac> --syslog-priority <pri> --syslog-tag <tag>
--verbosity <value> policy

stealth  --dry-run --log <file-spec> --logmail
--max-size <size>[BKMG] --no-mail --parse-policy-file
--random-interval <seconds> --repeat <seconds>
--run-command <nr> --skip-files <file-spec> --stdout --syslog
--syslog-facility <fac> --syslog-priority <pri> --syslog-tag <tag>
--verbosity <value> policy

stealth {--ping,--reload,--rerun,--resume,--suspend,--terminate} <uds>

stealth --help --version

✨ DESCRIPTION ✨

okay so the name stealth? It's literally an acronym, bestie. It stands for:

SSH-based Trust Enforcement Acquired through a Locally Trusted
Host.

Stealth is based on an idea by Hans Gankema and Kees Visser, both at the Center for Information Technology of the University of Groningen. Hopko Meijering provided valuable suggestions for improvement. πŸ‘‘

Stealth’s main task is to perform file integrity tests. However, the testing itself will leave no sediments on the tested computer. Therefore, stealth has stealthy characteristics. This is considered an important feature, improving the security (integrity) of the software of computers monitored by stealth. ✨

Please realize that stealth intends to be just another security tool: other security measures like firewalls, portscanners, intrusion detection systems, dropping unencrypted protocols, etc. are usually required to improve the security of a group of computers that are connected to the Internet. Stealth is a file integrity scanner, and file integrity scanners offer no substitute for those tools (and vv.). πŸ’…

Stealth uses a policy file to determine the actions to perform. Each policy file is uniquely associated with a host being monitored. This host (called the client below) trusts the computer on which stealth runs, called the monitor (hence: a Locally Trusted Host). The monitor performs tasks (normally file integrity tests) that Enforce the Trust we have in the client computer. Since almost all integrity tests can be run on the client, one monitor can control many clients, even if the monitor itself uses aged hard- and software components. 🫢

As the monitor and the client are (i.e., should be) different computers, the monitor must communicate with the client in a secure fashion. This is realized through SSH. So, there’s another element of `local trust’ involved here: the client should permit the monitor to set up a secure SSH connection allowing the monitor to access sensitive elements in the client’s file system. πŸ”

It is important to ensure that public access to the monitor is prevented. No incoming services should be allowed. The only access to the monitor should be via its console and the monitor should be placed in a physically secure location. Sensitive information of clients are stored in the monitor’s file system. To access the clients stealth in daemon mode can use a passphrase-protected ssh-key, allowing stealth to perform its tasks thereafter. This, too, makes it important to prevent the monitor from being accessed by unauthorized persons. 🚫

If, instead of running stealth in daemon mode it is preferred to let stealth perform single, but automated integrity scans, then new ssh(1) connections may be difficult to establish if the used ssh-key is passphrase-protected. To implement this scenario (i.e., automated integrity scans using passphrase protected ssh-keys) the program ssh-cron(1) can profitably be used. ⏰

Stealth’s current way of connecting to clients uses a single ssh(1) connection, which results in only a single sshd(1) entry in the client’s logfiles, which lasts for the duration of stealth’s run. When using stealth in daemon mode this too minimizes the `footprint’ stealth has on the client hosts. πŸ‘£

The monitor itself normally only requires two types of outgoing services: SSH to reach its clients, and some mail transport agent (e.g., sendmail(1)) to forward its outgoing mail to some mail-hub. πŸ“§

Here is what happens when stealth running using the first synopsis:

  • First, the policy file is read. For each client a policy file is defined, specifying the actions to be performed, and specifying the values of several variables used by stealth. πŸ“–
  • If the command-line option --daemon <uds> is specified, stealth runs as a daemon process, using the Unix Domain Socket (<uds>) for communication with stealth processes running in IPC mode.

If access to the Unix Domain Socket defined by Stealth running in daemon mode should be restricted, it can be defined in a directory with is only accessible to the user running Stealth (this will often be the root-user).

When running in daemon mode, --repeat <seconds> may be specified to rerun the integrity scan every <seconds> seconds. If an integrity scan is being performed when, according to the repeat interval the next integrity scan is due, then the current scan is first completed. Once completed, the next integrity scan will be performed after seconds seconds. πŸ”

  • Next, the monitor opens a command shell on the client using ssh(1), and a command shell on the monitor computer itself using sh(1). πŸ’»
  • Once the command shells are available, commands defined in the policy file are executed in their order of appearance. Examples are given below. Normally, return values of the programs are tested. When return values are to be tested stealth terminates when a non-zero return value is sensed. If this happens, a message stating the reason why stealth terminated is written to the report file (and into the mail sent by stealth). In some cases (e.g., when the report file could not be written), the message is written to the standard error stream. 🚨
  • Very often integrity tests can be controlled using find(1), calling programs like ls(1), sha256sum(1) or its own -printf method to produce file-integrity related statistics. Most of these programs write file names at the end of generated lines. This characteristic is used by one of stealth’s internal routines to detect changes in the generated output. Such changes could indicate some harmful intent, like an installed root-kit. 😱
  • When changes are detected, they are logged in a report file, to which information is always appended. Stealth never reduces the report file’s size or rewrites its contents. When information is added to the report file (beyond a plain time stamp) the newly added information is e-mailed to a configurable e-mail address for further (human) processing. Usually the e-mail is sent to the systems manager of the tested client. Stealth follows the `dark cockpit’ approach in the sense that no mail is sent when no changes were detected. πŸ“
  • Report and other log-files may safely be rotated between a pair of --suppress and --resume commands (see below at the section `REPORT FILE ROTATION’). πŸ”„

If stealth should not be run as a daemon process the second synopsis can be used. In this case stealth performs one or more integrity scans (the latter when the --repreat option was specified). When a single integrity scan is requested stealth terminates after the scan. When --repeat is specified stealth shows a prompt (i.e., `? ’) and terminates after pressing the Enter-key. ⏯️

The third synopsis is used for communication with a stealth daemon. In this case the the Unix Domain Socket defined by the stealth daemon process must be specified after the option specifying the requested command. πŸ”Œ

✨ OPTIONS ✨

okay bestie, quick orientation before we dive in πŸ’– The short options are listed in parentheses, right after their long-option besties.

If an option description has a (C) next to it, that means it's command-line only β€” if you try to sneak it into the second section of the policy file, it just gets ignored, no cap.

In this whole overview, <uds> means the name of the Unix Domain Socket you're using, and <file-spec> means a (relative or absolute) specification of a file location.

With the first and second synopses, relative locations (of the Unix Domain Socket and of other file-specifications) get interpreted relative to the current working directory.

Also super important: command-line options overrule options defined in the policy-file. periodt. πŸ‘‘


  • --daemon (-d) <uds> β€” (C) run as a background (daemon) process. When the Stealth daemon process gets started, the Unix Domain Socket (<uds>) may not already exist. πŸ™ˆ
  • --dry-run β€” (C) no integrity scans or reloads are performed, but they're assumed OK. The remaining tasks are normally performed.
  • --help (-h) β€” (C) display help information and exit.
  • --log (-L) <file-spec> β€” log messages are appended to <file-spec>. If <file-spec> doesn't exist yet, it gets created first. ✨
  • --logmail β€” mail sent by stealth is logged (requires --log or --syslog).
  • --max-size <size>[BKMG] β€” files retrieved by GET commands may at most have <size> bytes (B), KBytes (K), MBytes (M), GBytes (G). The default size is 10M, the default unit is B.
  • --no-mail β€” mail is not sent. By default mail is sent as configured in the policy-file (--logmail can be specified independently from --no-mail).
  • --parse-policy-file (-p) β€” (C) parse the policy file, after which stealth ends. Specify it once to see the numbered commands; twice to see the policy file parsing steps as well. Results are written to the std. output.
  • --ping <uds> β€” (C) performs no actions, but it's used to verify that a stealth daemon can be reached via its Unix Domain Socket (<uds>). The daemon will respond even if it's currently performing an integrity scan. It's used by the /usr/bin/stealthcron script to verify that a stealth daemon is alive. 🫢
  • --random-interval (-i) <interval>[m]> β€” start the scan a random interval of <interval> seconds (or minutes if an m is appended (no blanks) to <interval>) following the delay specified at --repeat (see below). This option requires specification of the --repeat and --daemon options.
  • --reload <uds> β€” (C) reloads the configuration and skip-files and restarts the scan of the stealth daemon process. Options defined in the policy file are also reloaded. However, command-line options always take priority over options defined in the policy file, so when command-line options were used when starting stealth in daemon mode, they cannot be modified by reloading the policy file. πŸ”₯
  • --repeat <seconds> β€” wake up and perform an integrity scan at interrupts or after <seconds> seconds (or minutes if an m is appended (no blanks) to <seconds>) after completing the previous integrity scan. The option --random-interval can be used to add a random delay to <seconds> until the next integrity scan is performed. This option requires specification of the and --daemon option.
  • --rerun <uds> β€” (C) start executing the integrity scan commands that are specified in the stealth daemon process's policy file.
  • --resume <uds> β€” (C) resume a suspended stealth process, implies --rerun.
  • --run-command (-r) <nr> β€” (C) only execute command number <nr> (natural number). Command numbers are shown by stealth ---parse-policy-file. This option can only be specified using the second synopsis.
  • --skip-files (-s) <file-spec> β€” all entries in <file-spec> are skipped. Their integrity is not monitored. If an entry is already present in a log file then stealth once generates an IGNORING message in the mail sent to the address specified at EMAIL in the policy file. Each entry mentioned in <file-spec> must be on a line of its own and must be specified using absolute file paths. Entries ending in a slash are assumed to be directories whose full contents must be skipped. Other entries are interpreted as the names of files to skip. Initial and trailing blanks, empty lines and lines having a # as their 1st non blank character are ignored. Here are some examples:
        # skip all files in user's Mail directory
    /home/user/Mail/
        # skip user's .history file
    /home/user/.history
  • --stdout (-o) β€” messages are (also) written to the std. output stream (only available with the second synopsis).
  • --suspend <uds> β€” (C) suspends a currently active stealth process. Following --suspend use --resume to re-activate an stealth daemon or --terminate to end an stealth daemon.
  • --syslog β€” write syslog messages.
  • --syslog-facility <facility> β€” syslog facility to use. By default facility DAEMON is used.
  • --syslog-priority <priority> β€” syslog priority to use. By default priority NOTICE is used.
  • --syslog-tag <tag> β€” <tag> specifies the identifier that is prefixed to syslog messages. By default the tag STEALTH is used, see also the next section.
  • --terminate <uds> β€” (C) terminate a currently active stealth process.
  • --time-stamp (-t) <type> β€” the time-stamps to use. By default UTC. To use the local time specify --time-stamp LT. The --time-stamp option does not apply to time-stamps generated by syslog (see also the next section).
  • --usage β€” (C) display help information and exit.
  • --verbosity <value> β€” determines the amount of logged information. Requires options --log or --syslog. Possible values are:
    • 0: nothing is logged
    • 1: (default) mode reports and policy commands
    • 2: also: ipc commands and actions
    • 3: also: integrity scan informative messages
  • --version (-v) β€” (C) display stealth's version information and terminate.
  • policy β€” file specification of the policy file. If a relative location is specified then this location is interpreted relative to the current working directory. Stealth converts this relative specification to an absolute file location, and an option like --reload will reload the policy file from the thus determined absolute file path. 🌈

Only one of the options --daemon, --reload, --resume, --suspend or --terminate can be specified. The options --reload, --rerun, --resume, --suspend, and --terminate ignore any other options. πŸ’…

The following options are still recognized for backward compatibility with stealth pre-3.00 versions and will be removed in a future stealth version. They generate error messages suggesting alternatives:

  • --echo-commands (-e) β€” echo commands to std error when they are processed; use --log instead.
  • --keep-alive β€” run as a daemon; use --daemon instead.
  • --only-stdout β€” scan report is written to stdout; use --stdout instead.
  • --quiet (-q) β€” suppresses progress messages written to stderr; use --verbosity 0 instead.
  • --suppress <uds> β€” suppresses a currently active stealth process; use --suspend instead.

The following options were discontinued starting since stealth version 3.00.00:

  • --debug (option --verbosity or --dry-run could be used instead);
  • --no-child-processes;
  • --parse-config-file.

When specifying long options in policy files initial hyphens should be omitted. Here are some examples:

%%
log /tmp/stealth.log
verbosity 3

🚦 EXIT STATUS 🚦

When requesting an IPC command or when starting stealth as a daemon, 0 is returned if the command was successfully completed. Otherwise a non-0 value is returned. simple as that bestie πŸ’€


Once stealth has started as a foreground or daemon process performing file integrity scans, ssh(1) is used to connect to the client(s) monitored by stealth. While stealth runs, only one ssh(1) connection is opened to each client. This connection remains active during stealth's lifetime to minimize the number of sshd entries in the client's log files. iconic efficiency, honestly 🧚


πŸ“œ THE POLICY FILE πŸ“œ

The policy file consists of two sections, the second section is optional, and starts at a line merely containing %%.

The policy file's first section consists of two sets of data: use directives (starting with the keyword USE) and commands. Blank lines and information beyond hash-marks (#) are ignored, while lines following lines terminating in backslashes (\) are concatenated (en passant removing these trailing backslashes). Leading white space on lines of the policy file is ignored.

The (optional) second section starts at a line merely containing %%. Following this separating line several long option specifications can be entered (see below at section OPTIONS). Options specified on the command-line take priority over options specified in the policy file. Although the --reload option reloads the policy file, it will not change option values originally specified as command-line options. This section may contain specifications of the skip-files and log options. Relative file locations specified for these options are interpreted relative to the location of the policy file. E.g., if the policy file argument is specified as /root/client/policy then the specification log: client.log results in stealth writing its logs into the file /root/client/client.log. ✨


πŸŽ€ DEFINE DIRECTIVES πŸŽ€

DEFINE directives are used to associate longer strings of text with certain symbols. E.g., after DEFINE FINDARGS -xdev -type f -exec /usr/bin/sha256sum {} \; the specification ${FINDARGS} may be used in USE DIRECTIVES and commands (see below) to use the text associated with the FINDARGS symbol.

Note that DEFINE symbols may also be used in the definition of other DEFINE symbols as well. Direct or indirect circular definitions should be avoided, as they are either not or incompletely expanded. ⚠️ don't do that to yourself bestie

USE DIRECTIVES πŸŽ€

okay bestie, these are the knobs you turn in the first half of the policy file. They're written in CAPS and you gotta keep the casing exactly like it's written below β€” no lowercase shenanigans, the parser is not playing. Anything in angle brackets (like <this>) is a spot where you fill in your own value ✨

  • USE BASE <base-directory> β€” this sets the directory stealth operates out of. Every relative path after it (yes, including the relative paths in the second part of the policy) gets read as relative to BASE. By default BASE is just wherever you started stealth from.

BASE and any other paths that don't exist yet? stealth literally just makes them for you. Iconic.

Example:

USE BASE /root/client
  • USE DD <dd> β€” points at the dd(1) program, and it's used both on the server and on the client. The DD program is the one copying files between the client and the monitor over the ssh connection you already have going. stealth only reaches for this when it's running PUT and GET commands (more on those below). Default is /bin/dd.

Example showing the default:

USE DD /bin/dd
  • USE DIFF <diff> β€” where does diff(1) live on the monitor? That's all this is. diff is what compares the logfile from an earlier integrity check against a freshly made one. Default is /usr/bin/diff.

Example showing the default:

USE DIFF /usr/bin/diff
  • USE DIFFPREFIX <prefix> β€” this one is about the size of the prefix that DIFF slaps onto lines produced by commands run through stealth.

The default /usr/bin/diff prefixes lines with either > or < β€” that's two characters β€” so the default <prefix> value is 2.

Example showing the default:

USE DIFFPREFIX 2
  • USE EMAIL <address> β€” the email address that gets the report from the client's integrity scan. And here's the dark cockpit philosophy in action: mail only goes out when a modification is actually detected. No news is good news, no spam, we love that for you πŸ¦‹

Example showing the default (which is apparently an email address on the monitor):

USE EMAIL root
  • USE MAILER <mailer> β€” the program that sends mail to the EMAIL address. Now pay attention because this one's different: unlike DIFF and DD and (see below) SH and SSH, MAILER is run as a /bin/sh command β€” specifically so shell scripts can process the mail too. By default MAILER is /usr/bin/mail.

MAILER gets called with these arguments:

----------------------------------------------------------
MAILARGS, see below;
EMAIL, the addressee of the mail.
----------------------------------------------------------

Example showing the default:

USE MAILER /usr/bin/mail

As an alternative there's a script called stealthmail. It's a convenient filter that sorts stealth's output and keeps only the lines containing the text ADDED, MODIFIED, REMOVED or STEALTH. Usually those are the lines sysadmins actually care about. And no worries β€” the report and log files are always there if you need to figure out the actual nature of the changes πŸ’…

  • USE MAILARGS <args> β€” the arguments passed to MAILER, followed by the EMAIL spec.

Example showing the default:

USE MAILARGS -s "STEALTH scan report"

Heads up: blanks are allowed in the subject, you just gotta use double or single quotes around elements that contain blanks. Use \" if you need a double quote inside a string that's itself wrapped in double quotes, and use \' for a single quote inside a single-quoted string. Quotes within quotes, we contain multitudes 🫢

  • USE REPORT <file-spec> β€” names the reportfile. Info is always appended to it, never overwritten. Every single stealth integrity scan writes a time marker line into the report file. And then: only when additional info gets appended on top of that marker line does stealth mail the added contents off to the address in the USE EMAIL spec. If you give a relative file spec it's read relative to the USE BASE spec.

Example showing the default:

USE REPORT report
  • USE SH <sh> β€” the command shell the monitor uses to run commands on itself. Default is /bin/sh. This one must be an absolute path, no shortcuts.

Example showing the default:

USE SH /bin/sh
  • USE SSH <user> β€” no default. You have to specify it. And it must be an absolute path. Periodt.

So, assuming the client trusts the monitor (which, like, is the whole entire point of this program, so it shouldn't be a very strong assumption), the move is to put the monitor's public ssh key into the client's root .ssh/authorized_keys file and grant the monitor root access to the client. You normally need root access to reach all the directories and files on the client's file system, bestie.

In practice, connecting to an account that uses the sh(1) shell is preferred. If that account is already running some other shell, make sure that shell doesn't define its own redirections for standard input and standard output. One way to handle that is to force the execution of /bin/sh right there in the USE SSH spec. Examples:

# root's shell is /bin/sh:
    USE SSH root@client -T -q
# root uses another shell, but the use of /bin/bash is forced:
    USE SSH root@client -T -q exec /bin/bash
# an alternative:
    USE SSH root@client -T -q exec /bin/bash --noprofile

In some installations stealth gets used to inspect the monitor itself, even though that is not recommended β€” it breaks one of the main reasons stealth exists in the first place 😭 But in those situations (so, where stealth is used to monitor the integrity of the localhost), you could specify /bin/bash at the USE SSH directive. For example:

# For stealth inspecting localhost:
    USE SSH /bin/bash --noprofile

COMMANDS πŸ‘‘

Following the USE specifications, you can start specifying commands. They get executed in the order they appear in the policy file β€” first come, first run. Processing keeps going until the last command has been processed, or until a tested command (see below) returns a non-zero return value. So one bad return value and the whole run just stops, no cap πŸ’€


LABEL COMMANDS 🏷️

These are the LABEL commands you've got available:

  • LABEL <text> β€” defines a text-label that gets written to the REPORT file, sitting right in front of the output generated by the next CHECK-command. And here's the thing: if that next CHECK-command generates no output, the text-label doesn't get written to the REPORT file at all. Once a LABEL has been defined it stays in use until it's redefined by the next LABEL. And if you want to suppress label printing entirely? Use an empty LABEL spec.

The text may contain \n characters (that's two characters, not a newline) which get transformed into a newline character.

Example:

LABEL Inspecting files in /etc\nIncluding subdirectories
LABEL

(In this example the former LABEL specification is erased by the latter LABEL command.) ✨

πŸ’– LOCAL COMMANDS πŸ’–

okay bestie, LOCAL commands are the ones that run right on the monitor itself. no ssh side quests needed (unless you make them, but we'll get to that). let's go.

  • LOCAL <command> β€” runs <command> on the monitor using the SH command shell. It must succeed, aka it has to return a zero exit value. no exceptions, periodt.

Example:

LOCAL scp rootsh@client:/usr/bin/sha256sum /tmp

this copies the client's sha256sum(1) program to the monitor. slay.

  • LOCAL NOTEST <command> β€” runs <command> on the monitor using the SH command shell. This one may or may not succeed, it's giving "we move" energy.

Example:

LOCAL NOTEST mkdir /tmp/subdir

this creates /tmp/subdir on the monitor. The command fails if the directory cannot be created, but this does not terminate stealth. iconic.

  • LOCAL CHECK [LOG =] <file-spec> [pathOffset] <command> β€” runs <command> on the monitor using the SH command shell. It must succeed. Then the output of this command is compared to the output of this command generated during the previous integrity check run by stealth. it's a whole vibe check for your files.

The phrase LOG = is optional. When a relative file location is specified at <file-spec>, it's interpreted relatively to the USE BASE path specification. got it?

pathOffset is also optional. If specified, it defines the (0-based) offset where path-names of inspected files start in lines produced by <command>. By default stealth assumes that the first occurrence of a forward slash defines the first character of the path-names of inspected files. lowkey clever.

For example, if diff-output looks like this:

01234567890123456789012345678901234567890 (column offsets)
33c33
< 90d8b506d249634c4ff80b9018644567  filename-specification
---
> b88d0b77db74cc4a742d7bc26cdd2a1e  filename-specification

then the specification LOCAL CHECK logfile 36 command-to-be-executed informs stealth where to find the filename specifications in the diff-output. Using the standard /usr/bin/diff command, this offset equals 2 + the offset of the filename-specification found in command-to-be-executed. math is mathing.

Any differences between the previous and current output are written to REPORT. If differences were found, the existing logfile name is renamed to logfile.YYMMDD-HHMMSS, with YYMMDD-HHMMSS the (UTC) datetime-stamp at the time stealth was run. time stamping, we love to see it.

Note that eventually many logfile.YYMMDD-HHMMSS files could be created: It is up to the monitor's systems manager to decide what to do with old datetime-stamped logfiles. bestie, clean up your room.

The logfile specifications may use relative and absolute paths. When relative paths are used, these paths are relative to BASE. When the directories implied by the logfile specifications do not yet exist, they are created first. it's giving organized.

Example:

LOCAL CHECK LOG = local/sha256sum sha256sum /tmp/sha256sum

this command checks the SHA256 sum of the /tmp/sha256sum program. The resulting output is saved at BASE/local/sha256sum. The program must succeed (i.e., sha256sum must return a zero exit-value). no cap.

  • LOCAL NOTEST CHECK <logfile> [pathOffset] <command> β€” runs <command> on the monitor using the SH command shell. The command may or may not succeed. Otherwise, it performs exactly like the LOCAL CHECK ... command discussed above. basically the chill version.

Example:

LOCAL NOTEST CHECK LOG=local/sha256sum sha256sum /tmp/sha256sum

this command checks the SHA256 sum of the /tmp/sha256sum program. The resulting output is saved at BASE/local/sha256sum. The program must succeed (i.e., sha256sum must return a zero exit-value). (yes, even the NOTEST one says "must succeed" here, we don't make the rules.)

Note that the scp(1) command can be used to copy files between the client and the monitor, using a local command. This, however, is discouraged, as a separate ssh(1)-connection is required for each separate scp(1) command. This subtlety was brought to the author's attention by Hopko Meijerink (h.meijering@rug.nl). shoutout to Hopko, real one.

To copy files between the client and the monitor, the GET and PUT commands (described below) should be used instead, as these commands use the existing ssh(1) connection. In general, LOCAL commands should not be used to establish additional ssh(1) connections to a client. periodt.

πŸ”₯ REMOTE COMMANDS πŸ”₯

Okay bestie, so remote commands are literally commands you run on the client through the SSH shell. They go through the standard PATH that's set for that SSH shell. BUT β€” and this is important β€” the manpage says it's advised to spell out the full pathname of the programs you want to run. Why? Because otherwise you get what it calls β€œtrojan approaches”: a trojan horse sitting in an earlier directory of the PATH-specification than the program you actually meant. 😭 So yeah, full paths. Always. No cap.

Two special remote commands exist, and they're the icons of the show: GET and PUT. They copy files between the client and the monitor. ✨

Internally, GET and PUT use the DD specification. And here's the tea: if you use a non-default specification, you gotta make sure that alternate program accepts dd(1)'s if=, of=, bs= and count= options. With GET, the options bs=, count= and of= are used. With PUT, the options bs=, count= and if= are used. Normally though? There should be no need to alter the default DD specification. πŸŽ€


GET, bestie πŸ’–

The GET command may be used as follows:

  • GET <client-path> <local-path> β€” Copy the file indicated by client-path at the client to local-path at the monitor. Here, client-path must be the full path of an existing file on the client. local-path may either be a local directory β€” in which case the client's file name is used β€” or another file name may be specified, in which case the client's file is copied to the specified local filename. If the local file already exists, it is overwritten by the copy-procedure.

Example:

GET /usr/bin/sha256sum /tmp

The program /usr/bin/sha256sum, available at the client, is copied to the monitor's /tmp directory. If, for whatever reason, copying fails, then stealth terminates. πŸ’€

  • GET NOTEST <client-path> <local-path> β€” Copy the file indicated by client-path at the client to local-path at the monitor. Again, client-path must be the full path of an existing file on the client. local-path may either be a local directory β€” in which case the client's file name is used β€” or another file name may be specified, in which case the client's file is copied to the specified local filename. If the local file already exists, it is overwritten by the copy-procedure.

Example:

GET NOTEST /usr/bin/sha256sum /tmp

The program /usr/bin/sha256sum, available at the client, is copied to the monitor's /tmp directory. Remaining commands in the policy file are executed, even if the copying process wasn't successful. πŸ™ˆ


PUT, also bestie πŸ’…

The PUT command may be used as follows:

  • PUT <local-path> <remote-path> β€” Copy the file indicated by local-path at the monitor to remote-path at the client. The argument local-path must be the full path of an existing file on the monitor. The argument remote-path must be the full path to a file on the client. If the remote file already exists, it is overwritten by PUT.

Example:

PUT /tmp/sha256sum /usr/bin/sha256sum

The program /tmp/sha256sum, available at the monitor, is copied to the client as usr/bin/sha256sum. If the copying fails, stealth terminates. 😭

  • PUT NOTEST <local-path> <remote-path> β€” Copy the file indicated by local-path at the monitor to remote-path at the client. The argument local-path must be the full path of an existing file on the monitor. The argument remote-path must be the full path to a file on the client. If the remote file already exists, it is overwritten by PUT.

Example:

PUT NOTEST /tmp/sha256sum /usr/bin/sha256sum

Copy the file indicated by local-path at the monitor to remote-path at the client. The argument local-path must be the full path of an existing file on the monitor. The argument remote-path must be the full path to a file on the client. If the remote file already exists, it is overwritten by PUT. Remaining commands in the policy file are executed, even if the copying process wasn't successful. πŸ¦‹


Plain commands can be executed on the client computer by merely specifying them. Of course, this implies that programs on the client which are named, e.g., LABEL, LOCAL or USE, cannot be executed, since these names are interpreted otherwise by stealth. It's unlikely that this restriction presents much of a problem.... (the manpage's own ellipsis, we're just passing it along ✨)

The following commands are available for execution on the client:

  • <command-path> β€” Execute command-path on the client using the SSH command shell (it is strongly advised to specify a full path to the command to execute). The command must succeed (i.e., must return a zero exit value). However, any output generated by the the command is ignored. (yes, "the the" β€” we don't make the rules, we just deliver them πŸ’€)

Example:

/usr/bin/find /tmp -type f -exec /bin/rm {} \;

This command removes all ordinary files in and below the client's /tmp directory.

  • NOTEST <command-path> β€” Execute command-path on the client, using the SSH command shell. The command may or may not succeed.

Example:

NOTEST /usr/bin/find /tmp -type f -exec /bin/rm {} \;

Same as the previous command, but this time the exit value of /usr/bin/find is not interpreted.

  • CHECK [LOG =] <file-spec> [pathOffset] <command-path> β€” Execute command-path on the client, using the SSH command shell.

The phrase LOG = is optional. When a relative file location is specified at <file-spec> it is interpreted relatively to the USE BASE path specification.

PathOffset is also optional, and has the same meaning as for the LOCAL CHECK command, described above. The command must succeed. The output of this command is compared to the output of this command generated during the previous run of stealth. Any differences are written to REPORT. If differences were found, the existing logfile name is renamed to logfile.YYMMDD-HHMMSS, with YYMMDD-HHMMSS the datetime-stamp at the time stealth was run. πŸ‘‘

Note that the command is executed on the client, but the logfile is kept on the monitor. This command represents the core of the method implemented by stealth: there will be no residues of the actions performed by stealth on the client computers. 🧚

Several examples (note the use of the backslash as line continuation characters):

CHECK LOG = remote/ls.root \
/usr/bin/find / \
-xdev -perm /6111 -type f -exec /bin/ls -l {} \;

All suid/gid/executable files on the same device as the root-directory (/) on the client computer are listed with their permissions, owner and size information. The resulting listing is written on the file BASE/remote/ls.root.

CHECK remote/sha256.root \
/usr/bin/find / \
-xdev -perm /6111 -type f -exec /usr/bin/sha256sum {} \;

The SHA256 checksums of all suid/gid/executable files on the same device as the root-directory (/) on the client computer are determined. The resulting listing is written on the file BASE/remote/sha256.root.

  • NOTEST CHECK [LOG =] <file-spec> [pathOffset] <command-path> β€” Execute command-path on the client, using the SSH command shell.

The phrase LOG = is optional. When a relative file location is specified at <file-spec> it is interpreted relatively to the USE BASE path specification.

PathOffset is also optional, and has the same meaning as for the LOCAL CHECK command, described above. The command may or may not succeed. Otherwise, the program acts identically as the CHECK ... command, described above.

Example:

NOTEST CHECK LOG = remote/sha256.root \
/usr/bin/find / \
-xdev -perm /6111 -type f -exec /usr/bin/sha256sum {} \;

The SHA256 checksums of all suid/gid/executable files on the same device as the root-directory (/) on the client computer are determined. The resulting listing is written on the file BASE/remote/sha256.root. stealth does not terminate if the /usr/bin/find program returns a non-zero exit value. 🌈


The maximum download size (using GET or CHECK) can be specified using the --max-size option, see below. By default this size is set at 10M. 🌸


πŸŽ€ REPORT FILE ROTATION πŸŽ€

So here's the situation, bestie: stealth only appends information to the report file. Which means that report file's size may eventually become prohibitively large, and log-rotation may be desirable. πŸ’€

It is of course possible to issue a --terminate command, rotate the logfiles, and restart stealth β€” but stealth also offers a facility to temporarily suspend integrity scans performed by a stealth daemon process:

  • Calling stealth with the option --suspend <uds> suspends the daemon's integrity scans. If stealth is actually performing a series of integrity scans when --suspend is issued, the currently executing command is first completed after which the --suspend command completes. Once the stealth daemon has been suspended, automatic or explicit integrity scan requests are denied, and the daemon can only be instructed to resume its scanning tasks (stealth --resume <uds>) or to terminate (stealth --terminate <uds>).
  • Once stealth --suspend <uds> has returned, the report file may safely be rotated (using, e.g., logrotate(1)), and a new (empty) report file may optionally be created by the logrotation process. ✨
  • Once the log-rotation has been completed, the log-rotation process should issue the command stealth --resume <uds>. This resumes the activities of a suspended stealth daemon process, immediately performing the next integrity scan. Following this the stealth daemon is back to its original integrity scanning mode. πŸ¦‹

Here is an example of logrotate(1) specification rotating stealth log-files:

/root/stealth/clienthost/small/report /var/log/stealth/clienthost-small.log {
    daily
    rotate 4
    compress
    missingok
    copytruncate
    sharedscripts
    prerotate
        /usr/bin/stealth --suspend /root/stealth/client/small.uds
    endscript
    postrotate
        /usr/bin/stealth --resume /root/stealth/client/small.uds
    endscript
}

rotate, suspend, resume, slay πŸ’–

πŸ”„ RELOAD, RERUN AND TERMINATE πŸ”„

okay bestie so when you run stealth with that third synopsis, here's the whole tea:

  • When you start it as stealth --reload <uds>, the stealth daemon process re-reads its policy file and β€” if you gave one β€” the --skip-files specification file. Then, right after that, the stealth daemon process runs a file integrity scan using all the info from the re-read policy and skip-files files. And here's the thing: stealth can reload the modified contents of the policy and skip-files names you originally specified. But if you need to use a different policy and/or skip-files file? Nope, you gotta start another stealth process and hand it those new filenames. That's just how it is πŸ’…
  • When you start it as stealth --rerun <uds>, the stealth daemon does another scan. Unless it's been suspended with stealth --suspend <uds>, in which case… no scan for you, bestie.
  • When you start it as stealth --terminate <uds>, the stealth daemon gets terminated. bye πŸ‘‹

πŸ“œ RSYSLOG FILTERING πŸ“œ

when you're running rsyslogd(1), you can use property based filters to filter syslog messages and write them to whatever file you want. like, say you wanna grab messages that start with the syslog message tag (think: STEALTH), you'd do:

:syslogtag, isequal, "STEALTH:"   /var/log/stealth.log
:syslogtag, isequal, "STEALTH:"   stop
heads up bestie: the colon is part of the tag, but it does not get specified with the syslog-tag option. sneaky little colon πŸ’€

so what this does is: every message with the STEALTH: tag gets written to /var/log/stealth.log, and then it gets discarded. and if you want to go more elaborate with your filtering, that's totally supported too β€” go peek at http://www.rsyslog.com/doc/rsyslog_conf_filter.html and http://www.rsyslog.com/doc/property_replacer.html.

now, the time stamps that rsyslogd writes? stealth's --time-stamp option has zero control over those. nope. those are controlled by, like, a TZ specification in /etc/default/rsyslog. so if you want UTC stamps, just add this line:

export TZ=UTC

to /etc/default/rsyslog, then restart rsyslogd. boom, now rsyslogd generates time stamps using UTC. slay ✨


πŸš€ DEPLOYMENT SUMMARY πŸš€

here's the lil checklist of advised steps for installing stealth, all of which get elaborated on in stealth's User Guide (chapter Running stealth):

  • Install stealth (e.g., use dpkg(1) to install the .deb file);
  • Construct one or more policy files;
  • Automate (re)starting stealth using cron(1) or ssh-cron(1) (possibly calling stealthcron);
  • Set up automated log-file rotation, using, e.g., stealthcleanup and logrotate(1), defining one or more /etc/logrotate.d/stealth... configuration files.

like, that's the whole vibe check. do these and you're golden 🫢


πŸ“ FILES πŸ“

  • /usr/share/doc/stealth/;
  • the policy file;
  • files under the BASE directory as defined in the policy file;
  • the report file as defined by the policy's USE REPORT directive.

πŸ‘€ SEE ALSO πŸ‘€

cron(1), dd(1), diff(1), dpkg(1), find(1), logrotate(1), ls(1), mail(1), sha256sum(1), passwd(5), rsyslog(1), sendmail(1), sh(1), ssh(1), ssh-cron(1)


🩺 DIAGNOSTICS 🩺

by default, executed commands get echoed to stderr. if that's giving too much chatter for your taste, hit it with -q to suppress the echoing. easy ✨


πŸ› BUGS πŸ›

None reported

(iconic honestly, we love a bug-free moment πŸ’–)


This is free software, distributed under the terms of the GNU General Public License. Copyright remains with the author. Stealth is found at https://fbb-git.gitlab.io/stealth/.


πŸ›οΈ ORGANIZATION πŸ›οΈ

University of Groningen.


πŸ‘‘ AUTHOR πŸ‘‘

Frank B. Brokken (f.b.brokken@rug.nl).

and that's the whole page, bestie!! this gorgeous thing is stealth version stealth_4.04.00, dated 2005-2025, and it's the stealth(1) man page. go forth and scan your filesystems with main character energy πŸŒΈπŸ¦‹βœ¨

πŸ“¦ where this page comes from