1 Introduction
1.1 Executive Summary
The purpose of the HardwareProtectedSsh project is to demonstrate a new approach for preventing the electronic theft of high-privilege network account credentials. High-privilege users include system administrators, DevOps, and some service accounts. This project demonstrates security hardening by way of the popular authentication protocol, Secure Shell (SSH), as well as a widely available hardware-based cryptographic engine, the Trusted Platform Module (TPM).
In summary, delivering cutting-edge enterprise security using a capability that most organizations have already paid for makes for a great return on investment.
1.2 TPM: An Underutilized Security Capability
The TPM standard offers several notable capabilities in a way that is impractical achieve with other mass-market technologies. This unique combination of capabilities includes:
- TPMs are built into the motherboard (or, alternately, System on a Chip, or SOC) itself. No additional hardware must be purchased, distributed, and managed.
- TPMs come from the manufacturer pre-configured with a tamper-resistant cryptographic identity, key storage, and crypto algorithm engine. Among other benefits, this allows a zero-touch bootstrapping of trust, especially desirable in Bring Your Own Device (BYOD) environments.
- TPM platform attestation is integrated into most PC firmware, as well as into many of the latest single-board computers. This combination of (UEFI based) Secure Boot and Measured Boot allows assertions to be made about whether a computer is running a trusted boot loader and operating system.
- TPM platform attestation is natively supported by Windows and Linux. This allows assertions to be made about whether a computer is running a trusted configuration of the operating system and kernel drivers/modules.
- TPMs can be remotely identified and queried using cryptographic challenge-response. This allows a trusted server to authenticate and interrogate user hardware (for example, over the internet) with much higher assurance than is afforded by existing configuration management tools, which ignore the threat of policy drift, and by existing Identity and Access Management solutions, which ignore the threat of computer credential theft.
Finally, the TPM cryptographic key storage engine and integrates naturally into common Public Key Infrastructure (PKI) use cases. However, there has remained a gap in middleware (i.e., the software that glues things together) support, especially on Linux. That gap has made the above high-security capabilities of TPM difficult to achieve in practice.
1.3 PKCS#11 Integration
This project aims to remove barriers to adoption of this important enterprise security defense capability. The primary software contribution of the HardwareProtectedSsh project is the implementation of a PKCS#11 (also known as the CryptoKi standard) cryptographic module that enforces TPM 2.0 remote platform attestation. While the initial use case for the module is SSH on Linux, the PKCS#11 API is widely adopted, and is supported by most PKI-aware applications.
1.4 SSH Integration
SSH testing is a sensible first verification step for the above PKCS#11 module, given (a) the prevalence of SSH-based remote administration for cloud computing and in-house datacenters and (b) the network tunneling capability of SSH. That is, even for use cases that don’t directly call for SSH (i.e., separate applications that would otherwise send traffic to different network ports), almost any protocol can be tunneled over SSH.
1.5 Tradeoffs between TPM 1.2 and TPM 2.0
A note about supporting TPM 2.0 devices instead of TPM 1.2: the latest SSH-2 protocol standard calls for use of SHA-256, or stronger, cryptographic hashing. Unfortunately, TPM 1.2 only supports SHA-1, a weaker algorithm. Thus, while TPM 1.2-capable devices remain prevalent, the legacy cryptographic limitations of TPM 1.2 inhibit its ability to keep up with the latest network security protocol standards (i.e., not just SSH).
As an aside, TPM 2.0 offers a number of features not present in TPM 1.2:
- Secure command execution. This capability, along with tighter integration into the trusted execution environment of the host hardware, mitigates computer bus man-in-the-middle attacks. This allows the remote trusted server to establish an authenticated and encrypted tunnel directly to the TPM itself.
- A rich policy language for protecting TPM objects such as keys, allowing them to be bound to clock time, for example. These policies also make it possible to reauthorize keys on hosts that have fallen out, and then back into, policy compliance.
- Symmetric algorithm support (AES, although this feature is not always present, due to export control fears)
- Elliptic-curve support (i.e., in addition to RSA)
2 Architecture and Data Flow
The following diagram shows the principal software components involved in completing platform attestation, TPM key sealing, and a successful SSH connection.
The following sequence of steps refers to the diagram above.
- For initial host registration, platform attestation, and key creation, the user runs the CliTst program. In a production environment, this procedure could be scripted to ensure that every qualified user and computer gets a trusted TPM key.
- For demonstration purposes, the ssh-keygen program is used to export the TPM user public key from libp11platformattestation-x64.so. The key must then be manually added to the user SSH known_hosts file on the server (in this case, since SSH is a peer-to-peer protocol, “server” means whatever computers are going to enforce the use of remote platform attestation and sealed keys).
-
a. In production, this could be automated for each user by instead pulling the public keys from the Attestation Service database.
b. Alternatively, SSHD could be extended to dynamically lookup public keys via a REST call to the AS in response to every connection attempt.
c. As a third option, certificates could be used, with a specific Certificate Authority (CA) and/or certificate policy OID designated for issuance to attested keys. - The user initiates connections using ssh (i.e., and the libp11platformattestation-x64.so PKCS#11 module). The previous steps ensure that the connection will only be successful if a known-good TPM key is being used on a host that is booted in a known-good configuration.
Details on performing the above steps in a lab environment are provided below.
3 Importance of Credential Theft Mitigation
Most security incidents entail credential theft. A common scenario is for a remote attacker gain initial entry into an enterprise network by one of two ways:
- Phishing
- Web app compromise
In either case, the net result is that the attacker is able to move laterally through the network using stolen credentials. Since the credentials are those of an otherwise legitimate user, and since the attacker is accessing a system that the user has been granted access to, it can take weeks or months for the organization to detect that a successful attack has taken place. That is, it’s difficult to distinguish the network activity of the attacker from the network activity of the users.
This problem is being tackled by the IT security industry on multiple fronts, including investment in machine learning-based monitoring and in broader support for multifactor authentication in the cloud. However, while monitoring is important, current state of the art is such that security event notification and incident response still take more time than is required by the typical attacker to achieve some sort of damaging data theft or reconnaissance.
Similarly, multifactor authentication is a must, but the devil is in the details. Many of the most common industry standard security protocols, such as OAuth and Kerberos, rely on session state artifacts (sometimes called tickets or tokens) that remain in computer memory after a user has been authenticated. Even if a multifactor credential was enforced for the initial authentication, the artifact can be used to gain access to network services for several hours thereafter. Attack tools look for those artifacts and use them to authenticate to other systems (each with its own cache of user authentication artifacts; this is why attacks can spread so quickly and easily).
Therefore, further investment is needed in authentication schemes that can enforce the use of a non-exportable credential for every connection attempt. SSH is a great candidate for this work because it:
- Is widely used by DevOps in cloud scenarios.
- Is flexible, in particular because of its ability to tunnel other protocols and application traffic. Security improvements to SSH offer great bang for the buck as compared to the investment of integration effort on other protocols.
- Via the SSH-2 standard, supports user authentication based on the RSA cryptosystem and SHA-2 cryptographic hash algorithm. Both algorithms are also supported by the TPM 2.0 standard.
- Enforces possession of a trusted user private key during each connection attempt.
- Allows extensibility in the form of PKCS#11 cryptographic modules for private key storage.
- Is multi-platform.
- Is open-source.
4 Rationale for TPM 2.0 Usage
The previous section explains the reasons for integrating with SSH. But why use TPM 2.0 for the key storage? The first consideration is the availability of TPMs. Most enterprise-class laptops have them, and an increasing number of server chassis either include them or offer them as a free add-on. In that sense, TPM is a significantly underutilized security capability that organizations have already paid for.
The second consideration is the ability to ensure that the user credential is non-exportable. TPM can create a general-purpose RSA keyset with a private key that cannot be exported and is never exposed to PC main memory.
Third, TPM integration with PC firmware, boot loaders, and operating systems allows for remote measurements to be taken of the system boot state. This procedure is called platform attestation. In the context of SSH integration, remote platform attestation allows SSHD to ensure that client devices are running a known-good combination of BIOS firmware, boot loader, loader, operating system kernel, and kernel-mode drivers.
The fourth consideration relates to differences between TPM 1.2 and 2.0. These were highlighted in the Introduction, above.
In summary, allowing the use of TPM 2.0 platform-attested private keys to be enforced in SSH provides a great mix of hard-core security, scenario coverage, and ease of integration with existing apps and business processes.
5 Install Prerequisite Packages
To get started with HardwareProtectedSsh, I recommend using Ubuntu 16.04, or later, on a supported, TPM 2.0-equipped PC. I used this command to dump a list of packages from my build computer:
( zcat $( ls -tr /var/log/apt/history.log*.gz ) ; cat /var/log/apt/history.log ) | egrep '^(Start-Date:|Commandline:)' | grep -v aptdaemon | egrep '^Commandline:'
And came up with the following starter list:
sudo apt-get install openssh-server autoconf autoconf-archive libtool pkg-config git build-essential libcurl4-gnutls-dev libssl-dev g++ make libboost-all-dev libssl-dev cmake libssl1.0.0-dbg
However, please do follow the instructions for each of the dependencies listed below.
6 Creating Enlistments
I start by creating a home subdirectory for all of my GitHub-based work. I also enable debug for gcc, and turn off the optimizer, when I’m building code for the purpose of integration testing.
cd ~
mkdir GitHub
export CFLAGS='-g -Og'
6.1 TPM2.0-TSS
The HardwareProtectedSsh project depends on the presence of a local (running) TPM resource manager. On Windows, the built-in TBS is used. On Linux, the open-source TPM resourcemgr contributed by Intel Corporation is required. For the latter, follow these instructions. Summary:
cd ~/GitHub
git clone https://github.com/01org/TPM2.0-TSS
cd TPM2.0-TSS
./bootstrap
CONFIG_SITE=$(pwd)/lib/default_config.site ./configure
make
sudo make install
sudo resourcemgr
Troubleshooting tip: if you encounter communications issues between any TPM command-line tool and the TPM chip itself, verify that exactly one instance of resourcemgr is running as root.
6.2 tpm2.0-tools
Use the Intel TPM 2.0 command-line tools for initialization. Follow these instructions. Summary:
cd ~/GitHub
git clone https://github.com/01org/tpm2.0-tools
cd tpm2.0-tools
./bootstrap
./configure
make
sudo make install
sudo tpm2_takeownership -o 1234 -e 1234 -l 1234
The last command, above, merits a couple of comments. First, regarding implementation, if you use different passwords for the various TPM hierarchies, you’ll need to modify the PKCS#11 code. Second, regarding threat model, the purpose of this project is to expose the full capabilities of the TPM to the local user. Requiring the user to elevate to root in order to perform network authentication would not be a smart risk tradeoff.
6.3 casablanca
The Casablanca REST library for C++ is used by the PKCS#11 module to communicate with the StrongNet Attestation Server. Follow these instructions. Summary:
cd ~/GitHub
git clone https://github.com/Microsoft/cpprestsdk casablanca
cd casablanca/Release
mkdir build.debug
cd build.debug
cmake .. -DCMAKE_BUILD_TYPE=Debug
make
sudo make install
6.4 HardwareProtectedSsh
cd ~/GitHub
git clone https://github.com/hdracer/HardwareProtectedSsh
cd HardwareProtectedSsh/TSS.MSR.CPP
make
cd ../AttestationLib
make
cd ../libp11platformattestation
make
sudo make install
cd ../CliTst
make
cd ../linux
./CliTst
In summary, the last line will attempt to open an existing keyset using the libp11platformattestation-x64.so PKCS#11 module. If no keyset exists, CliTst calls the P#11 API to create one. In response, the module attempts to contact the Attestation Server (via HTTP REST), perform remote platform attestation using TPM 2.0, generate a 2048-bit sealed RSA key, and store the resulting TPM blob in a local file under the user home directory.
6.5 openssl
cd ~/GitHub
git clone -b OpenSSL_1_0_2-stable https://github.com/openssl/openssl.git
cd openssl
./config -g -Og -fPIC
make
sudo make install
After installing a custom build of OpenSSL, it may be necessary to restore the system certificate trust configuration. See these notes as well as the following:
- Copy the contents of /etc/ssl/certs into /usr/local/ssl/certs and /usr/local/share/ca-certificates.
- The above files have to be: chmod 755
- In /usr/local/share/ca-certificates, run: sudo update-ca-certificates
6.6 openssh-portable
cd ~/GitHub
git clone https://github.com/openssh/openssh-portable
cd openssh-portable
./configure --with-ssl-dir=/usr/local/ssl
make
sudo make install
sudo ~/GitHub/openssh-portable/sshd -D -d -e
To switch the system over to the new SSHD binary, desirable in particular for debugging purposes:
- Edit sshd.service in /etc/systemd/system
- Change the path to /usr/local/sbin
- Reload systemd configuration: sudo systemctl daemon-reload
- sudo systemctl stop sshd
- sudo systemctl start sshd
7 Client Key Configuration
In PKCS#11 mode, the ssh-keygen tool doesn’t actually attempt to create a new key pair. Instead, ssh-keygen calls into the PKCS#11 module to enumerate existing (public) keys and then writes them to the console in base-64 encoded format.
ssh-keygen -E sha256 -D libp11platformattestation-x64.so
To allow that key to be used to log into an SSH peer as “you,” add it to the ~/.ssh/known_hosts file on each remote server.
8 Patching SSH
The three openssh-portable patches, below, implement the following changes.
8.1 Strict Usage of RSASSA-PKCS1-v1_5
The first patch is to modify SSHD to call the RSA_verify API in OpenSSL, rather than RSA decrypt API. The former ensures that the client signature is valid PKCS#1 v1.5 and that the 32-byte hash values match. This change replaces the custom signature scheme used by SSH-2 in which the (“plaintext”) hash is prepended with an OID.
While it is greatly inconvenient to break compatibility with the SSH standard, the standard (which is to say, the common implementation of SSH: a de facto standard adopted as a formal one) is flawed in two ways. The first flaw is that the hash OID added by the SSH application is redundant vis-à-vis the RSASSA-PKCS1-v1_5 signature standard. Quoting the RFC for the latter:
“…in the encoding method EMSA-PKCS1-v1_5, a hash function identifier is embedded in the encoding. Because of this feature, an adversary trying to find a message with the same signature as a previously signed message must find collisions of the particular hash function being used…”
The second flaw is that, by calling RSA encrypt/decrypt rather than sign/verify, SSH forces compatible cryptographic modules to expose more of the private key to leakage.
These two flaws specifically prevent interoperability with the TPM: in order to minimize the risk of chosen cipher text attacks against RSA private keys, the TPM 2.0 specification limits SHA-256 signing requests to exactly 32 bytes.
8.2 Stop Using SHA-1… Really!
Two related changes, one on the server and one in the client:
- In SSHD, restore server support for rsa-sha2-256 (and -512, although the current generation of Intel TPM 2.0 doesn’t support SHA512). Without this change, the client and server agree on SHA1, which is arguably a security defect in and of itself. But more to the point, libp11platformattestation-x64.so only supports SHA256.
- In SSH, default to SHA256 rather than SHA512 when the server supports both. This is related to the previous point, but it’s a separate change in the code (see below).
8.3 The Diffs
dan@UBA:~/GitHub/openssh-portable$ git diff
diff --git a/kex.c b/kex.c
index 6a94bc5..a44e456 100644
--- a/kex.c
+++ b/kex.c
@@ -405,7 +405,7 @@ kex_input_ext_info(int type, u_int32_t seq, void *ctxt)
}
found = match_list("rsa-sha2-512", val, NULL);
if (found) {
- kex->rsa_sha2 = 512;
+ //kex->rsa_sha2 = 512;
free(found);
}
}
diff --git a/ssh-rsa.c b/ssh-rsa.c
index cde05df..8ab685e 100644
--- a/ssh-rsa.c
+++ b/ssh-rsa.c
@@ -308,7 +308,7 @@ openssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen,
u_char *sigbuf, size_t siglen, RSA *rsa)
{
size_t rsasize = 0, oidlen = 0, hlen = 0;
- int ret, len, oidmatch, hashmatch;
+ int ret;
const u_char *oid = NULL;
u_char *decrypted = NULL;
@@ -330,21 +330,10 @@ openssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen,
ret = SSH_ERR_ALLOC_FAIL;
goto done;
}
- if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa,
- RSA_PKCS1_PADDING)) < 0) {
+ if (0 == RSA_verify(NID_sha256, hash, hashlen, sigbuf, siglen, rsa)) {
ret = SSH_ERR_LIBCRYPTO_ERROR;
goto done;
}
- if (len < 0 || (size_t)len != hlen + oidlen) {
- ret = SSH_ERR_INVALID_FORMAT;
- goto done;
- }
- oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0;
- hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0;
- if (!oidmatch || !hashmatch) {
- ret = SSH_ERR_SIGNATURE_INVALID;
- goto done;
- }
ret = 0;
done:
if (decrypted) {
diff --git a/sshkey.c b/sshkey.c
index c01da6c..73e2a6a 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -202,7 +202,7 @@ sshkey_alg_list(int certs_only, int plain_only, char sep)
const struct keytype *kt;
for (kt = keytypes; kt->type != -1; kt++) {
- if (kt->name == NULL || kt->sigonly)
+ if (kt->name == NULL)
continue;
if ((certs_only && !kt->cert) || (plain_only && kt->cert))
continue;
Then rebuild, deploy, and run SSHD as above.
9 Connect to the Server
While debugging, it can be useful to enable the following options:
- Verbose logging
- Explicit cryptographic algorithm selection (see also the patches described in the previous section)
- Client promiscuity vis-à-vis server authentication (never allow this in production)
The following ssh client command-line will attempt to use the first (and only) key enumerated by the libp11platformattestation-x64.so PKCS#11 module to connect to the server called UBA.
ssh -vvv -I libp11platformattestation-x64.so -o "HostKeyAlgorithms rsa-sha2-256" -o "StrictHostKeyChecking=no" dan@UBA