(kreative) Einsatz­möglichkeiten kryptografischer Token

oder:

wie kriege ich das Passwort aus dem Token raus und ins Programm rein?

(mit möglichst viel bash)

kryptografische Token?

  • verschiedene Schnittstellen
    • Smartcard-Formfaktor
    • USB
    • NFC
  • verschiedene Standards
    • OpenPGP
    • PKCS11
    • PKCS15
    • HMAC-SHA

Beispiel-Implementierungen

  • OpenPGP-Smartcard
  • Sigilance
  • Nitrokey
  • Yubikey
  • Fidesmo?

Grundlagen

OpenPGP


echo asd > test
gpg -r B76C94DD -e -a test
rm test
cat test.asc
#-----BEGIN PGP MESSAGE-----
#Version: GnuPG v2
#
#hQEMA0lF8LJU85UOAQf+NyIekK8CIw7U12Dlq8Xt7xTPJCamBTN6aYOtXYsbqJ1F
# [...]
#=iwpb
#-----END PGP MESSAGE-----
gpg -q -o test -d test.asc
cat test
#asd
                           

Grundlagen

OpenPGP


echo test | gpg -r B76C94DD -e -a
#-----BEGIN PGP MESSAGE-----
#Version: GnuPG v2
#
#hQEMA0lF8LJU85UOAQf+NyIekK8CIw7U12Dlq8Xt7xTPJCamBTN6aYOtXYsbqJ1F
# [...]
#=iwpb
#-----END PGP MESSAGE-----
cat <<EOF | gpg -q -d
 -----BEGIN PGP MESSAGE-----
 [...]
 -----END PGP MESSAGE-----
EOF
#test
                           

Grundlagen

OpenSSL


# zum schnellen testen: Schlüssel erzeugen
openssl genpkey -algorithm RSA -out private.key -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in private.key -out public.pem
# verschlüsseln
echo asd > test
openssl pkeyutl -encrypt -in test -pubin -inkey public.pem -out test.ssl
rm test
# entschlüsseln
openssl pkeyutl -decrypt -in test.ssl -inkey private.key -out test
                           

Grundlagen

OpenSSL - mit Smartcard... und OpenSC


# bei einer Smartcard liegt vermutlich ein Zertifikat vor
# wir brauchen einen public key
openssl x509 -pubkey -noout -in cert.crt > public.pem
# verschlüsseln
echo asd > test
openssl pkeyutl -encrypt -in test -pubin -inkey public.pem -out test.ssl
rm test
# entschlüsseln - OpenSC to the rescue!
pkcs15-crypt --decipher --pkcs1 --input test.ssl --output test


                           

Grundlagen

was hatte eigentlich HMAC-SHA in der Liste zu suchen?

  • wird z.B. vom Yubikey unterstützt
  • wird z.B. von Passwort-Managern genutzt, Whitepaper von Yubico schlägt die Technik für Festplattenverschlüsselung vor
  • "kreatives" Verfahren:
    • Passwortmanager generiert Wegwerf-Public-Key
    • PWM generiert per HMAC-SHA mit dem Token einen privaten Schlüssel
    • AES-"Master-Schlüssel" des PWM wird mit diesem privaten Schlüssel symmetrisch verschlüsselt, gemeinsam mit obigem Wegwerf-Public-Key gespeichert
    • zum Entschlüsseln kann jederzeit das Private Key mit dem Token gebildet werden
    • beim nächsten Re-Encrypten des PWM nimmt man einen neuen Wegwerf-Public-Key

let's get started

Man nehme Software, die keine physikalischen Token unterstützt und fummelt sich was zurecht, damit es trotzdem klappt.

Ansätze:

  • Software erwartet Passwort per STDIN (trivial)
  • Software erwartet Schlüsseldatei
  • Software erwartet Passwort per Parameter (trivial, aber gefährlich. Wenn möglich: Finger weg!)
  • Software erwartet interaktive Passworteingabe
    • Konsole
    • GUI

der Einfachkeit halber...

...definieren wir uns eine Funktion, die das Passwort entschlüsselt und auf STDOUT ausspuckt


#!/bin/bash
function getkey(){
    gpg -q -d test.asc
}
                

irgendwie sowas in der Art.

zum Testen empfehle ich aber


#!/bin/bash
function getkey(){
    echo asd
}
                

Software erwartet Schlüsseldatei

Herausforderung:

  • Datei soll nicht auf der Festplatte landen (/dev/shm ?)
  • Datei soll möglichst nicht von anderen Nutzern gelesen werden (Unterordner mit beschränkten Rechten, nur zur Sicherheit?)
  • Datei soll möglichst nicht von anderer Software gelesen werden (?)
  • Datei soll möglichst nur einmal ausgelesen werden können (fifo?)
  • Datei soll auf jeden Fall wieder verschwinden (trap exit?)

Lösungsvorschlag

process substitution


#!/bin/bash
some-program --key-file=<(getkey)
                
  • Datei landet in /dev/fd/X
    • sollte auf /proc/<procid von some-program>/fd/X abbilden
    • dürfte hoffentlich nur im Speicher landen (swapping?)
  • ist eine fifo
  • zumindest auf meinem Testsystem sind die Rechte gut gesetzt
  • sollte allgemein ohne root-Rechte nicht zu klauen sein (?)
  • ist simpel, vermeidet also Fehler wenn man es statt dessen selber macht

Software erwartet Passwort per Parameter

  • enorm böse, da man per ps & co das Passwort einfach auslesen kann
  • Software umschreiben
  • geht nicht? Finger weg!
  • na gut... irgendwie kommt man schon drum rum - aber ich hab euch gewarnt...

...diese Lösung hab ich auf Stackoverflow gefunden...

Man schreibt sich eine library, die man in das Programm injected, die einen auf STDIN nach dem Passwort fragt (oder die irgendwie anders an das Passwort kommt) und dann intern die originale main-Methode mit dem richtigen Passwort aufruft. Gestartet wird auf der Kommandozeile mit einem Fake-Passwort.

aufgerufen wird es dann auf der Kommandozeile so:

LD_PRELOAD=$PWD/injectpassword.so some-program fake-password

...diese Lösung hab ich auf Stackoverflow gefunden...

das wäre der Beispielcode:


#define _GNU_SOURCE
#include <dlfcn.h>
#include <unistd.h>

static int (*real_main) (int, char * *, char * *);

static int my_main(int argc, char * * argv, char * * env) {
  char *pass = getpass(argv[argc - 1]);
  if (pass == NULL) return 1;
  argv[argc - 1] = pass;
  return real_main(argc, argv, env);
}

int __libc_start_main(
    int (*main) (int, char * *, char * *),
    int argc, char * * ubp_av,
    void (*init) (void),
    void (*fini) (void),
    void (*rtld_fini) (void),
    void (* stack_end)
  )
{
  int (*next)(
    int (*main) (int, char * *, char * *),
    int argc, char * * ubp_av,
    void (*init) (void),
    void (*fini) (void),
    void (*rtld_fini) (void),
    void (* stack_end)
  ) = dlsym(RTLD_NEXT, "__libc_start_main");
  real_main = main;
  return next(my_main, argc, ubp_av, init, fini, rtld_fini, stack_end);
}

aber ernsthaft: simply don't do it.

Software erwartet interaktive Passworteingabe auf der Konsole

expect to the rescue!


#!/usr/bin/expect
eval spawn some-program
expect "please enter password"
send "Frikandelle\r"
interact                    

und mit einem Passwort, dass wir per command injection bekommen:


#!/usr/bin/expect
set passwordfile [lindex $argv 0]
set fp [open $passwordfile r]
set password [read $fp]
close $fp

eval spawn ./some-program
expect "please enter password"
send $password
interact                  

Software erwartet interaktive Passworteingabe auf der GUI

  • unter Linux: xdotool

(
    getpass; 
    echo #für ein Newline
) | xdotool type \
    --window $(xdotool search --name "Passwortabfrage" --sync | head -n 1) \
    - #von SDTIN
                                    
  • unter Windows: Autohotkey

Und jetzt haben wir Zeit, uns auszutauschen und kreativ zu werden.

let's hack!