Signature-du-Terroir

Construct a signature of the installed software state or check the integrity of the installation using a previously made signature.

Usage: signduterre.py [options] FILE1 FILE2 ...

Options:

  -h, --help            show this help message and exit
  -s HEX, --salt=HEX    Enter salt in cleartext. If not given, a hexadecimal
                        salt will be suggested. The SUGGESTED[=N] keyword will
                        cause the selection of the suggested string. N is the
                        number of salts generated (default N=1). If N>1, all
                        will be printed and a random one will be used to
                        generate the signature (selection printed to STDERR).
  -a, --all-salts-pattern
                        Use all salts in sequence, randomly replace salts with
                        incorrect ones in the output to create a pattern of
                        failing hashes indicated by a corresponding integer
                        number (eg, 7 = 1110 = [OK, OK, OK, Fail]). Depends on
                        '--salt SUGGESTED=N'. Implies --total-only.
  -p TEXT, --passphrase=TEXT
                        Enter passphrase in cleartext, the keyword
                        SUGGESTED[=N] will cause the suggested passphrase to
                        be used. If N>1, N passphrases will be printed to
                        STDERR and a random one will be used (selection
                        printed to STDERR). Entering the name of an existing
                        file (or '-' for STDIN) will cause it to be read and a
                        random passphrase found in the file will be used
                        (creating a signature), or they will all be used in
                        sequence (--check-file).
  -c FILE, --check-file=FILE
                        Check contents with the output of a previous run from
                        file or STDIN ('-'). Except when the --quiet option is
                        given, the previous output will contain all
                        information needed for the program, but not the
                        passphrase and the --execute option.
  -i FILE, --input-file=FILE
                        Use names from input-file or STDIN ('-'), use one
                        filename per line
  -u USER, --user=USER  Execute $(cmd) as USER, default 'nobody' (root/sudo
                        only)
  -S, --Status          For each file, add a line with unvarying file status
                        information: st_mode, st_ino, st_dev, st_uid, st_gid,
                        and st_size (like the '?' prefix, default False)
  --Status-values=MODE  Status values to print for --Status, default MODE is
                        'fmidlugs' (file, mode, inode, device, nlinks, uid,
                        gid, size). Also available a(time), m(time), and
                        c(time).
  -t, --total-only      Only print the total hash, unsets --detailed-view
                        (default True)
  -d, --detailed-view   Print hashes of individual files, is unset by --total-
                        only (default False)
  -e, --execute         Interpret ${ENV} and $(cmd) (default False)
  --execute-args=ARGS   Arguments for the $(cmd) commands ($1 ....)
  -n, --no-execute      Explicitely do NOT Interpret ${ENV} and $(cmd)
  -m, --manual          Print the manual and exit
  --manual-html         Print the manual in HTML format and exit
  -r, --release-notes   Print the release notes and exit
  -l, --license         Print license text and exit
  -v, --verbose         Print more information on output
  -q, --quiet           Print minimal information (hide filenames). If the
                        output is used with --check-file, the command line
                        options and arguments must be repeated.

FILE1 FILE2 ... Names and paths of one or more files to be checked. All file arguments in SdT accept '-' as the STDIN file (ie, piped data).

Any name starting with a '$', eg, $PATH, will be interpreted as an environmental variable or a command according to the bash conventions: '$ENV' and '${ENV}' as variables, '$(cmd;cmd...)' as system commands (bash --restricted -c 'cmd;cmd...' PID). Where PID the current Process ID is (available as positional parameter $0). Other parameters can be entered with the --execute-args option ($1 etc). Do not forget to enclose the arguments in single ''-quotes! The commands are scanned for unwanted characters and these are removed (eg, ' and \, however, escaping $ is allowed, eg, '\$1'). The use of '$(cmd;cmd...)' requires explicit use of the -e or --execute option.

If executed as root or sudo, $(cmd;cmd...) will be executed as 'sudo -H -u <user>' which defaults to --user nobody ('--user root' is at your own risk). This will obviously not work when invoked as non-root/sudo. --user root is necessary when you need to check privileged information, eg, you want to check the MBR with '$(dd if=/dev/hda bs=512 count=1 status=noxfer | od -X)' However, as you might use --check-file with files you did not create yourself, it is important to be warned if commands are to be executed.

Interpretation of $() ONLY works if the -e or --execute options are entered. signduterre.py can easily be adapted to automatically use the setting in the check-file. However, this is deemed insecure and commented out in the distribution version.

The -n or --no-execute option explicitely supress the interpretation of $(cmd) arguments.

Meta information from stat() on files is signed when the filename is preceded by a '?'. '?./signduterre.py' will extract (st_mode, st_ino, st_dev, st_nlinks, st_uid, st_gid, st_size) and hash a line of these data (visible with --verbose). The --Status option will automatically add such a line in front of every file. Note that '?' is implied for directories. Both '/' and '?/' produce a hash of, eg,: stat(/) = [st_mode=041775, st_ino=2, st_dev=234881026, st_uid=0, st_gid=80, st_size=1360] Note that nlinks of a directory include every file in the directory, so this will check whether files have been added to a directory.

Arguments enclosed in []-brackets will be hidden in the output. That is, '[/proc/self/exe]' will show up as '[1]' in the output (or '[n]' with n the number of the hidden argument), equivalent to the use of the --quiet option. This means the hidden arguments must be entered again when using the --check-file (-c) option.

Signature-du-Terroir

A very simple tool to generate a signature that can be used to test the integrity of files and "states" in a running installation. signduterre.py constructs a signature of the current system state and checks installation state with a previously made signature. The files are hashed with a passphrase to allow detection of compromised systems while running on the same system. The signature checking can be subverted, but the flexibillity of signduterre.py and the fact that the output of any command can be tested should hamper automated root-kit attacks.

signduterre.py writes a total SHA-256 hash to STDOUT of all the files and commands entered as arguments. It can also write a hash for each individual file (insecure). The output of a signature can be send to a file and later used to check with --check-file. Hashes are calculated with a hashed salt + passphrase sequence pre-pended to create unpredictable hashes. This procedure ensures that an attacker does not know whether or not the correct passphrase has been entered. An attacker can only know when to supply the requested hash values if she knows the passphrase or has copies available of all the tested files and output of commands to calculate the hashes on the fly.

The Problem

How to test whether your system has been compromised when you can only use the potentially compromised system. The solution is to store a password encrypted signature (or fingerprint) of your system when you are sure it is in a good state. Then you check whether the system can still distinguish between correct and incorrect passwords when it regenerates the signature. The trick is to use the right data (ie, questions) to generate the signature.

SECURITY

When run on a compromised system, signduterre.py can be subverted if the attacker keeps a copy of all the files and command outputs, and reroutes the open() and stat() functions, or simply delegating signduterre.py to a chroot jail with the original system. In principle, signduterre.py only checks whether the computer responds identically to when the signature file was made. There is no theoretic barrier against a compromised computer perfectly simulating the original system when tested, but behaving adversely at other times. Except for running from clean boot media (USB?), I know of no theoretical sound solution to this problem.

However, this scenario assumes the use of unlimited resources and time. Inside a limited, real computer system, the attacker must make compromises on what can and what cannot be simulated with the available time and hardware. The idea behind signduterre.py is to "ask difficult questions" that increase the cost of simulating the original system high enough to make detection of successful attacks likely.signduterre.py simply intends to raise the bar high enoug. One point is to store the times needed to create the original hashes. This timing can later be used to see whether the new timings are reasonable. If the same hardware takes considerably longer to perform the same calculations, or needs a much longer delay before it starts, the tester might want to see where this time is spent.

Signature-du-Terroir works on the assumption that any attacker in control of a compromised system cannot predict whether the passphrase entered is correct or not. An attacker can always intercept the in- and output of signduterre. When running with --check-file, this means the program can be made to print out OK irrespective of the tests. A safe use of signduterre.py is to start with a random number of incorrect passphrases and see whether they fail.

THE CORRECT USE OF signduterre.py IS TO ENTER A RANDOM NUMBER OF INCORRECT PASSPHRASES FOR EACH TEST AND SEE WHETHER IT FAILS EVERY TIME!

On a compromised system, signduterre.py's detailed file testing (--detailed-view) is easily subverted. With a matched file hash, the attacker will know that the correct passphrase has been entered and can print out the stored hashes or 'ok's for the rest of the checks. So if the attacker keeps any entry in the signature file uncompromised, she can intercept the output, test the password on the unchanged entry and substitute the requested hashes for the output if the hash of that entry matches.

When checking for root-kits and other malware, it is safest to compare the signature files from a different, clean, system. But then you would not need signduterre.py anyway. If you have to work on the system itself, only use the -t or --total-only options to create signatures with a total hash and without individual file hashes. Such a signature can be used to check whether the system is unchanged. Another signature file WITH A DIFFERENT PASSPHRASE can then be used to identify the individual files that have changed. If a detailed signature file has the same passphrase, an attacker could use that other file to read the individual file hashes to check whether the correct passphrase was entered.

Using the --check-file option in itself is UNsafe. An attacker simply has to print out 'OK' to defeat the check. This attack can be foiled by making it unpredictable when signduterre.py should return 'OK'. This can be done by using a list of salts or passphrases where only one of them (or none!) is correct. Any attacker will have to guess when to return 'OK'.

As generating and entering wrong passphrases and salts is tedious, users have to be supported in correct use of SdT. To assist users, the '--salt SUGGESTED=<N>' option will generate a number N of salts. When checking, each of these salts is tried in turn. An attacker that is unable to simulate the uncompromised system will have to guess which one of the salts is the correct one, and whether or not the passphrase is correct. This increases the chances of detecting compromised systems. If this is not enough guess work, the '-a', '--all-salts-pattern' option will use all salts in sequence to generate total hashes, but random salts will be changed in the output. This generates a pattern of failed salt tests. This pattern is translated into a bit pattern and printed as an integer ([Fail, Fail, OK, Fail, OK, OK, Fail, OK] = 00101101 (least significant first) = 10110100 (unsigned bin) = 180). On creation of a signature, this number is printed to STDERR, on checking (--check-file) it is printed to STDOUT (note that the number will never become 0 or all Fail). So for '--salt SUGGESTED=<N> --all-salts-pattern' the probability of guessing the correct output goes from 1/N to 1/(2^N - 1). Note that '--all-salts-pattern' will work, but is pointless, without '--salt SUGGESTED=<N>' with N>1.

The '--passphrase SUGGESTED=N' option will generate and print N passphrases. One of these is chosen at random for the signature. The number of the chosen passphrase is printed on STDERR with the passwords. When checking a file, the stored passphrases can be read in again, either by entering the passphrase file after the --passphrase option ('--passphrase <passphrase file>'), or directly from the --check-file. signduterre.py will print out the result for each of the passphrases.

Note, that storing passphrases in a file and feeding it to signduterre.py is MUCH less secure than just typing them in. Moreover, it might completely defeat the purpose of signduterre.py. If future experiences cast any more doubt on the security of this option, it will be removed.

For those who want to know more about what an "ideal attacker" can do, see:
Ken Thompson "Reflections on Trusting Trust"
http://cm.bell-labs.com/who/ken/trust.html
http://www.ece.cmu.edu/~ganger/712.fall02/papers/p761-thompson.pdf

David A Wheeler "Countering Trusting Trust through Diverse Double-Compiling"
http://www.acsa-admin.org/2005/abstracts/47.html

and the discussion of these at Bruce Schneier's 'Countering "Trusting Trust"'
http://www.schneier.com/blog/archives/2006/01/countering_trus.html

Manual

The intent of signduterre.py is to ensure that the signature cannot be subverted even if the system has been compromised by an attacker that has obtained root control over the computer and any existing signature files.

signduterre.py asks for a passphrase which is PRE-pended to every file before the hash is constructed (unless the passphrase is entered with an option). As long as the passphrase is not compromised, the hashes cannot be reconstructed. A randomly generated, unpadded base-64 encoded 16 Byte password (ie, ~22 characters) is suggested in interactive use. If '--passphrase SUGGESTED' is entered on the command line or no passphrase is enetered when asked, the suggested value will be used. This value is printed to STDERR (the screen or 2) for safe keeping. Please, make sure you store the printed passphrase. For instance:

$ python signduterre.py -p SUGGESTED -s SUGGESTED /boot/* /sbin/* /bin/* \
2> Signature_`date "+%Y%m%d_%H-%M-%S"`.pwd > Signature_`date "+%Y%m%d_%H-%M-%S"`.sdt
will store the passphrase (and all error messages) in a file like 'Signature_20090630_11-14-03.pwd' and the check-file in 'Signature_20090630_11-14-03.sdt'.

It is not secure to store files with the passphrase on the system you want to check. However, you could pipe STDERR to some safe site.

Good passphrases are difficult to remember, so their plaintext form should be protected. To protect the passphrase against rainbow and brute force attacks, the passphrase is concatenated to a salt phrase and hashed before use (SHA-256).

The salt phrase is requested when constructing a signature. In interactive use, an 8 byte hexadecimal (= 16 character) salt from /dev/urandom is suggested. If '--salt SUGGESTED' is entered on the command line as the salt, the suggested value will be used. The salt is printed in plaintext to the output. The salt will make it more difficult to determine whether the same passphrase has been used to create different signatures.

At the bottom, a 'TOTAL HASH' line will be printed that hashes all the lines printed for the files. This includes the file names as printed on the hash lines. It is not inconceivable that existing signature files could have been compromised in ways that might be missed when checking the signature. The total hash will point out such changes.

Examples:

$ python signduterre.py --execute --detail --salt 436a73e3 --passphrase liauwefa3251EWC signduterre.py \
 /sbin/* /bin/* /usr/bin/find /usr/bin/file /usr/bin/python* '${PATH}' \
 > Signature_`date "+%Y%m%d_%H-%M-%S"`.txt

Prints a signature to the file Signature_20090625_14-31-54.txt (with your date). The signature contains the SHA-256 hashes of the files, signduterre.py, /sbin/*, /bin/*, /usr/bin/find, /usr/bin/file, /usr/bin/python*, and a hash of the PATH environment variable.

$ python signduterre.py --execute --detail --salt SUGGESTED --passphrase SUGGESTED --Status --detailed-view \
  signduterre.py /sbin/* /bin/* /usr/bin/find /usr/bin/file /usr/bin/python* '${PATH}' \
  2> Signature_`date "+%Y%m%d_%H-%M-%S"`.pwd > Signature_`date "+%Y%m%d_%H-%M-%S"`.txt

Prints a signature to the system Signature_20090625_14-31-54.txt (with your date) and the automatically generated password to Signature_20090625_14-31-54.pwd (with your date). The salt will be automatically determined. The signature contains the SHA-256 hashes of the file status and file contents of signduterre.py, /sbin/*, /bin/*, /usr/bin/find, /usr/bin/file, /usr/bin/python* on separate lines, and a hash of the PATH environment variable.

$ python signduterre.py --execute --passphrase liauwefa3251EWC -c Signature_20090625_14-31-54.txt

Will check the files and PATH variable from the signature file Signature_20090625_14-31-54.txt.

$ python signduterre.py --passphrase liauwefa3251EWC -c Signature_20090625_14-31-54.txt
$ python signduterre.py --passphrase liauwefa3251EWC -c Signature_20090625_14-31-54.txt --no-execute

Will both fail if Signature_20090625_14-31-54.txt contains a $(cmd) entry. The --no-execute option is default and prevents the execute option (if reading the execute optionfrom the signature file has been activated).

$ python signduterre.py signduterre.py --salt SUGGESTED -passphrase SUGGESTED=20 signduterre.py \
2> Signature_`date "+%Y%m%d_%H-%M-%S"`.pwd > Signature_`date "+%Y%m%d_%H-%M-%S"`.sdt

Will generate and print 20 passphrases and print a signature using one randomly chosen passphrase from these 20. Everything is written to the files 'Signature_20090630_16-44-34.pwd' and 'Signature_20090630_16-44-34.sdt'.

$ python signduterre.py signduterre.py -c Signature_20090630_16-44-34.txt

Will check all 20 passphrases generated before from the Signature file and print the results.

$ sudo python signduterre.py  -u root -s SUGGESTED -p SUGGESTED --Status-values='i' -v -e -t \
'?/proc/self/root' '?/usr/bin/python' '$(dd if=/dev/hda bs=512 count=1 | od -X)' \
>Signature_`date "+%Y%m%d_%H-%M-%S"`.sdt

Will hash the inode numbers of the effective root directory (eg, chroot) and the executable (python) together with the contents of the MBR (Master Boot Record) in Hex. It uses suggested salt and passphrase. Accessing /dev/hda is only possible when root, so the command is entered with sudo and --user root.