CRyptographic Administration SHell -- crash
===========================================
Build
=====
Just type "make", on BSD type "make -f Makefile.bsd", for embedded systems please
see at the end of this document. crash was successfully tested on Linux (openSUSE 10.3, 11.1),
FreeBSD 7.2 and OpenSolaris. It probably runs on a lot of other Linux
and BSD's but I was not able to test it there yet.
The utmp/wtmp logging probably needs to be tuned; BSD is just messy there.
To generate server and auth keys, type "make keys" or see further
instructions.
Running
=======
crash does not need any config or option files to run. Its easy and
straight forward to use. Anything can be enabled/disabled/featured by
runtime switches:
stealth@linux ~> ./crashd -h
./crashd: invalid option -- h
Usage: ./crashd [-U] [-q] [-a] [-6] [-w] [-H host] [-p port] [-A authorized keys]
[-k server key-file] [-c server X509 certificate]
[-t trigger-file] [-m trigger message] [-e] [-g good IPs]
-a -- always login (if authenticated), even with a /bin/false default shell
-U -- run as user (e.g. turn off setuid() calls) if invoked as such
-e -- extract key and certfile from the binary itself (no -k/-c needed)
-q -- quiet mode, turns off logging and utmp entries
-6 -- use IPv6 rather than IPv4
-w -- wrap around PID's so crashd appears in system PID space
-H -- host/IP to connect to; if omitted it uses passive connect (default)
-p -- port to connect/listen to; default is 2222
-P -- local port used in active connects (default is no bind)
-g -- file containing list of good IP/IP6's in D/DoS case (default off)
-A -- authorized-key files for users if starts with '/'; subdir in users ~
containing authorized_keys file otherwise; 'self' means to use
blob-extraction (see -e); default is .crash
-k -- servers key file; default is ./serverkey.pem
-c -- X509 certificate-file that belongs to serverkey (-k); default is ./pubkey.x509
-t -- watch a triggerfile for certain message (-m) before connect/listen
-m -- wait with connect/listen until message in file (-t) is seen
I think most of it is pretty self-explaining. crashd can run as user. -U makes
it skipping apropriate setuid() calls, effectively being able to run as user.
It only accepts logins to that user then by checking login name's UID against
current EUID.
Both, crashc and crashd can use active and passive connect. Whenever
a host-argument (-H) is given, it uses active connect to this Host
and the belonging port (-p). If -H is given, it also accepts
-P which specifies the local port it has to bind to before
doing active connect.
If -w is used it forks itself as "[nfsd]" and tries to wrap around its
PID to be somewhere around the system daemons. This can take a while!
Key setup
=========
The easiest way is to "make keys". But you can also do it by hand:
To generate a X509 certificate containing the public server key, you
type
$ umask 066
$ openssl genrsa -out serverkey.pem 4096
$ openssl req -new -x509 -nodes -sha1 -key serverkey.pem -out pubkey.x509
The values you enter for Country-Name, Email, CN etc. do not matter
since crashc is not validating the X509. Rather it compares the public
key it obtained from the server with the key it has in its local
key-store belonging to that server (similar to SSH). That way,
crash cannot fall into the common X509 traps like a lot
of web browsers do.
The server key is not encrypted since crashd is usually started
via init scripts. Rather, the key file must have proper permissions
so only apropriate users can read it (mode 0600). You can, if you like,
also encrypt the server key but then you have to enter a passphrase
whenever crashd is started.
To generate public/private RSA keypair for your authentication, you
type
$ openssl genrsa -out privkey.pem -aes256 4096
$ openssl rsa -in privkey.pem -pubout -out pubkey.pem
copy pubkey.pem to ~/.crash/authorized_keys on the remote box, and
use privkey.pem for the "crashc -i" argument.
Auth-Key sizes larger than 4096 bit (as used above) must not be used;
the tokens do not fit into the auth handshake otherwise.
crashc is using the .crash/ subdir by default to check for
already seen server keys. If you connect to a host via -H $host -p $port
then a keyfile of form .crash/HK_$host:$port is looked up unless
you specify a path to a known keyfile.
CGI
===
crashd automatically detects whether it has been invoked as a CGI by
a webserver by checking $QUERY_STRING environment variable. It parses
and converts the query-string into arguments it understands. It
does not translate %2F etc characters! They should not be needed,
since spaces, '(' and other weird characters do not make sense when
calling crashd. Arguments that dont have a parameter such as -U
have to be given "=1" argument to enable it, such as in:
http://127.0.0.1/cgi-bin/crashd?-K=/path/to/serverkey.pem& \
-C=/path/to/pubkey.x509&-p=1234&-A=/tmp/.crash/authorized_keys&-U=1&-a=1
which invokes crashd on the host 127.0.0.1 as user (probably "wwwrun" or whatever
the webserver is running as).
For pentesting or embedded/emergency systems, crashd has the -e option.
If -e is used, it extracts the server key-file and the X509 certificate
from the ELF binary file, which have to be appended first:
$ cat serverkey.pem>>crashd
$ cat pubkey.x509>>crashd
If you give "-A self" instead of a valid authentication directory or file,
it also extracts the user-key used for authentication from its binary.
It has to be appended in the same manner as above. This is useful in pentests
where you cannot upload arbitrary amount of files or do not know the
exact pathname of the upload storage:
http://127.0.0.1/cgi-bin/crashd?-A=self&-U=1&-e=1&-a=1
-a is needed since most likely the wwwrun user has a /bin/false shell,
which -a ignores.
crashd is using mkstemp() to store the key files temporarily, with
mode 0444 (world readable!) since it needs to access authentication
files as user. So be warned that, if you have users, they may read
the private key used during SSL handshake. After all, its just an
emergency mode!!!Stripping the crashd binary is not possible after
appending the keys, or they will get lost.
Back-connect etc. also work in CGI mode as well.
If using that, client should use -K switch to tell client which key to use
to authenticate the server.
cross-build for speedport/fritzbox DSL routers
==============================================
In Germany, these devices are quite common and support open build
environment/SDK since the firmware is based on Linux and available
in source. Download/unpack the SDK/tarball and the MIPS toolchain. I assume you
are familar with building router images for these devices. The openssl
package for the speedport W-500V I tested has version 0.9.7 and I had
to build it by hand before, so the references to the libssl.a
inside crash Makefile are valid. On the system I tested (500V)
I had to make it static, since dynamic openssl library requires libdl
which was not on the device. Building a firmware image often
calls strip on the user binaries, so take care it doesnt
strip off the embedded keys if using -e!
Then, inside userapps/opensource/ directory, unpack the crash tarball,
edit the makefile to match "true" at the first ifeq() and then you are able
to just "make crashd" to build crashd for the DSL router. Nice eh?
You can now put it into the firmware image, make it loaded at boot and you
are able to admin your DSL box from remote via full protected crypto shell
and without stupid web interface.
Here's how I connect to a 500V DSL modem running crashd inside:
stealth@linux ~> ./crashc -l root -H 192.168.2.1 -K .crash/HK_127.0.0.1\:2222 -v \
-i privkey.pem -c '/bin/sh -i'
crash: starting crypted administration shell
crash: connecting to 192.168.2.1:2222 ...
Enter PEM pass phrase:
crash:{
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4PKEDfrj1v1RlK307j1o
JX9aUuadp3KjGqs2gpoxYTuYf8oBh+1qhwbsH5wC1oQ4HerlIAfLFfUL0+yjy1BO
jNW/4sCo3QSxByRC9DORmfADrxJYkWjyC+cgdXCRaudYBLsz4oIDbg6B8Gtzma8c
ifFPUuSfEoLMRlQOSBIr15WzjS9/wIJfU+D0maJAFRySvVhcRQEbQZ5uuhLnmf4R
zEEiVV+HU79AfhuaKjs1gnTCZwj/dUmsD2zNjHuU6bZpjiQlQdkXLKe2wTvvYFQL
pbmx/XEapoW+tes9pbGgkFIs0jQLz/xYB07luUy46teVAVyB70R7l/WDyFpocPwW
seLNINDUKTvN6vo4UJD9tg1xhClim+P8Jnkyh5X5MqSdXwYC4N/FQsGs223jtxY2
XvQNPBR06d8HJu66GCDSTy7jKKARQ3c3Odq87JDOquUntR1pg26Dj3lOHWVQpuof
7jZ6vLyivpnh4JcdBgGTkzC+Ua0VE1tlokl58+FjhkIbeijD6FK+xcUNaylRo9YM
TrHjBnCi9FIb85WfBo1pj+basKAM85+BkFX3sLC+HR62CL1qJKue7qDBMJbK4INx
ZvoDpdJyBRZXLx+lGisasT3zgNDjJAjSWYn5bY1FfTMBZbvhZyN2/5Uxe85VicUV
8NS0w2TCghjgA0B+hiln1mMCAwEAAQ==
-----END PUBLIC KEY-----
crash:}
Using fallback suid method
Using fallback suid method
BusyBox v1.00 (2008.06.03-09:17+0000) Built-in shell (msh)
Enter 'help' for a list of built-in commands.
# ps aux
[...]
206 root 1712 S httpd
210 root 232 S smdog
520 root 1160 S /bin/crashd -A /var/pubkey.pem
546 root 1464 S /bin/crashd -A /var/pubkey.pem
561 root 352 S /bin/sh -c /bin/sh -i
562 root 396 S /bin/sh -i
715 root 340 R ps aux
#
Keep in mind that on embedded systems, UNIX98 pty's are often not available and
there are only a limited number of BSD pty's (2 or so) so you cant login a hundred
times simultaneously.
DoS mitigation
==============
crashd includes some sort of D/DoS protection. Only one connection per second
is allowed per IP, except if the IP is listed (or the network it belongs to)
in a good-IP file given with -g at startup.
Per default no good IPs are assigned. Network-address-goodness only works with
IPv4 yet. A simple good-IP file may look like this:
# sample good-IP file
192.168.3.1
192.168.2.0
10.0.0.0
fe80:216::1234
# end of file
Together with the interval timer for hanging un-authenticated
connections this allows to have no more than 12 'hanging'
crashd's at the same time, still allowing you to login
if you are listed in good-IPs and your underlying TCP/IP stack
is not already trashed.
Author
======
Sebastian Krahmer