/greygoo

Grey Goo is a tiny, available and reliable remote command execution server and client designed purely for emergency situations.

Primary LanguageCApache License 2.0Apache-2.0

Grey Goo
========

Grey Goo is a small and simple server whose goal is to work when OpenSSH
doesn't.

- It supports public-key based client authentication and packet
integrity (but not encryption).
- It is extremely memory efficient (no explicit memory allocation
after initialization) and the server can rely on a tiny, custom crypto
library (the daemon is roughly 50kb in total).
- It relies on pre-initialized workers (process, not threads!) for
maximum reliability and availability.

More information
================

The daemon is a single file. Once it's running, it does not require file
system access anymore (unless the command the client requests requires
it of course).

The daemon is made of a main process (a factory) that will pre-fork
workers. Each worker can accept a new connection and will work even
if the factory is dead. Once a worker has accepted a new connection,
the factory will start a new worker to replace it. Even if the worker
un-expectedly dies for any reason, the factory will be notified and will
start a new worker.

Once a worker has accepted a connection, it'll authenticate the client,
send its own identity and accept any command.  There will be no explicit
memory allocation (outside of the use of a stack) in the critical
path between accepting a connection and running a command. The server
can use either OpenSSL or a custom, tiny, memory-efficient crypto
implementation.

Grey Goo does not support server authentication, but supports a strong
session with a client, client authentication via a RSA signature and
will bind the client authentication to the session. Since we have a
strong session, the server can send its identity, currently a string
with its hostname so that the client can detect blind redirections by an
attacker.

This makes Grey Goo a good match for restarting OpenSSH, saving a
machine from a fork bomb or rebooting it, but not suitable for changing
the root password or sending/reading other secrets.

Consistently with this, Grey Goo support packet integrity, but not
packet encryption.

Compilation
===========

You need the OpenSSL header files (for the client).
On Debian or Ubuntu: "apt-get install libssl-dev"

Commands
========

Grey Goo can accept a number of commands. Some of those commands are
purely internal, without any external dependencies.

Implemented:

- GG_PAYLOAD_CMD_FORK_EXECVE: execve a command. Input and output are redirected
  to the client in a secure (integrity) channel
- GG_PAYLOAD_CMD_DIRECT_EXECVE: support executing a command directly
 (without fork()), à la rexd. Only output is redirected to the client.
  No integrity. Should be avoided.
- GG_PAYLOAD_CMD_DMESG: return the last XXX bytes of the kernel ring buffer
- GG_PAYLOAD_CMD_WRITE_FILE: currently used to implement remote sysrq 
- GG_PAYLOAD_CMD_REBOOT: reboot

Memory allocation
=================

Grey Goo has been designed to pre-allocate memory as much as
possible and to have a tiny memory footprint.

To make the memory footprint even smaller, one can further reduce the size
of a packet, GG_PKT_MAX_SIZE.

Currently, we still use stack variables for small variables,
but we declare any stack variable that needs substantial space as
"static" so that it ends up in the .bss section (and in a load-time
allocated PT_LOAD segment). It's not ideal, but we found it less ugly
than having a giant main pre-allocation with everything.

Because of the "pre-allocation" constraint, a lot of structures that
you would expect to contain pointers will instead directly contain
storage. This is because we avoid having proper "constructors" that need
to allocate memory.

A consequence of this is that, we don't use many
pointers to incomplete types, which is also why we have to make each C
file depend on every headers (because they need to know how types are
actually implemented and be recompiled if this changes).


Crypto library
==============

gg-crypto.h has two implementations: a full OpenSSL
implementation (gg-crypto-openssl.c) and a partial ucryptolib one
(gg-crypto-ucryptolib.c).

ucryptolib is a tiny implementation written by Marius Schilder that is
fully embedded in Grey Goo.

The server can use either implementation, but the client must use
OpenSSL.

Protocol
========

The protocol is more detailed in the "PROTOCOL" file. In a nutshell:

  - We negociate a key with Diffie-Hellman between the server and the
client that is then used as a HMAC key to authenticate the packets.
  - We authenticate the client by requesting that he RSA-signs the first two
packets.
  - Then the server will send its identity (a string) to the client.

The rationale is the following: we want to guarantee the integrity of
the servers. We do not guarantee any confidentiality.

  Step A: the client is strongly authenticated

  A MITM attacker could do two main things:
  1. Set-up a rogue server and make us connect to it. Since we only care
    about the integrity of *our* servers, this attack is outside of
    the scope.  (But remember that Grey Goo should not be used to send any
    secret).

  2. Blindly redirect a client to another server (and make us, for
    instance reboot or change the configuration of another server).

  Step B: To prevent this attack, the server will send its hostname as an
  identifier. The client can then choose to abort the connection.

  With Step A and Step B, we are able to guarantee the integrity of our
  servers.

Diffie-Hellman: 1024 bits group, 2 as a generator of a subgroup.
RSA: 2048 bits

Users and keys:
===============

Grey Goo doesn't support a notion of users. The commands will be run with
whatever privileges Grey Goo was started with. However, Grey Goo supports two
different keys:

- a "root" key: the key that is used by default
- a "test" key: the key that is used when started with a special command line
  switch.

The former is generated with "make genrsa", the later with "make genrsatest".

It's important to understand that the "root" and "test" key are not
fundamentally different and give the exact same amount of privileges. The test
key was created so that one can debug the exact same binary that is used in
"production" without knowledge of the production key.

Both keys are "baked" in the client (ggc). They are encrypted with a key
derived from the supplied password via PBKDF2 (GG_HASH_ITERS iterations).

You may want to change the PBKDF2 salt in gg-password.c:password_to_key()
depending on your threat model and usage.

Misc features:
==============

- The main Grey Goo process (factory) tries to bullet-proof itself by
  raising its priority, becoming immune to the OOM killer and committing
  all virtual memory (mlockall()).
- Non blocking, best effort logging to a running syslogd.
- Lack of encryption allows further logging and auditing directly on the
  wire.

Killing Grey Goo:
=================

- No single process kill will completely kill Grey Goo
- To kill everything you can kill the process group
  (e.g. with kill -9 -group).

DoS:
====

Because of the way Grey Goo works, and as a trade-off, there is a big
dissymmetry between the CPU and memory cost of a new connection between
the server and a malicious client.

With a simple TCP connection, an attacker can make Grey Goo fork and
initialize a new worker.

To mitigate this, packet filtering could be done at a certain
perimeter, at the host level or alternatively in Grey Goo itself
(ggd.c:is_acceptable_address()).

As a first-level mitigation, there is some basic throttling and Grey Goo
will never create more than 10 workers / s.

TODO:
=====

Medium:
- Rewrite gg_packet_get properly
- Support for pseudo terminals
- Different HMAC keys for C->S and S->C
- Support baking an arbitrary number of keys in the client to ease key
  rotation
- Support sending multiples commands in one go (e.g. -s "t" -k)

Small:
- Support timeouts, especially for pre-auth stuff
- Change crypto initialization to other names
- Pre-allocate some stack space
- Support multiple target hosts in the client command line
- Automatically abort / warn in the client if the identity string sent by the
  server doesn't match something expected.