Le Buffer overflow

Sécurité bases de données pas de Commentaire »

Buffer overflow

Un buffer overflow (BOF) est littéralement un dépassement de la capacité d’un buffer de données. Rien de bien méchant, cela arrive fréquement lorsque l’on manipule des pointeurs et que l’on programme sans trop de précautions.
La conséquence est généralement un ‘crash’ du programme, qui tente un accès à des zones en dehors de son espace d’adressage (donne des segmentation fault ou protection fault).

fwd2La saturation ou le crash de serveur n’est généralement pas le but ultime des hackers. L’acquisition de privilèges ou l’ouverture de backdoor est plus souvent ciblée.

Dans le domaine de la sécurité cette technique du BOF est utilisée pour ‘dérouter’ la séquence normale des instructions d’un programme et le forcer à éxécuter une routine spécifique, permettant généralement l’obtention de droits ‘super utilisateur’. Cette routine ou ce programme exploitant la faille est appelé un « exploit » (en Anglais dans le texte)

fwd2 ‘droits super utilisateur’ ne veut pas dire seulement obtention de l’UID ‘root’ ou ‘Administrateur’. Beaucoup d’applications utilisent des variables ou des tables pour gérér en interne des niveaux de privilèges indépendemment de l’OS. Des flags du type ‘useradmin=1′ ou ‘loginok=true’ ou ‘privileges=ALL’ sont monnaie courante dans les programmes et beaucoup plus facilement exploitables.

De l’importance des attaques par buffer oveflow

Ce problème est important de par sa notoriété et également de par sa persistance. Les premières mises en évidence de la possibilité de ce type d’attaque datent de 1995 et sont toujours d’actualité.
Statistiquement si l’on consulte les sites de référérence que sont le site du Cert et le site d’infosys security par exemple, on constate qu’une part importante des pbs traités concerne le BOF. Voir plus particulièrement le rapport du dernier trimestre 2003 du CERT.

fwd2bien qu’abondamment documenté sur le web, ce type d’attaque nécessite une grande ténacité et de bonnes compétences en programmation de bas niveau (assembleur), en désassemblage (les sources sauf cas ideal de certains logiciels libres n’étant pas à la disposition des hackers) et en gestion de la mémoire et architecture système (structure et adressage de la pile, contenu des bibliothèques sytème notamment).

Des conditions d’occurence d’un buffer overflow

Un certain nombre de conditions sont indispensables à l’occurence d’un BOF :

  • code de programmation vulnérable
    • langage utilisé permissif (C),
    • fonctions utilisées laxistes (strcpy(), strcat(), sprintf(), vsprintf(), gets(), scanf())
    • appels à des fonctions privilégiées (system()),
    • utilisation de variable donnant des privilèges,
    • absence de test du code, etc.
  • programme attaqué s’exécutant avec un niveau de privilège fort (uid root par exemple)
  • lisibilité / accessibilité du code (accès au source ou désassemblage explicite, pas de free(), ni de user_check())
  • ordonnancement des données dans le programme (variable importante ‘écrasable’ , adresse de retour de subroutines ‘écrasable’

mais aussi

  • exposition / intérêt du site
  • ténacité du hacker

Principe détaillé du buffer overflow

Un exemple idéal (un heap overflow)

D’après Ghost Rider « Introduction to Buffer Overflow », www.governmentsecurity.org

Soit le petit programme C suivant


main(int argc, char **argv) {
char *somevar;
char *important;
somevar = (char *)malloc(sizeof(char)*4);
important = (char *)malloc(sizeof(char)*14);
strcpy(important, « command »); /*This one is the important variable*/
stcrpy(somevar, argv[1]);
….. Code here ….
}

Il présente qq caractéristiques remarquables :

  • Il utilise des appels de commande système via la variable ‘important’.
  • Il a (au moins) un paramètre d’entrée (argv[1]) stocké dans la variable ‘somevar’.
  • La variable ‘someva’r est allouée en mémoire AVANT ‘important’, on peut en déduire que son adresse sera probablement inférieure a celle de ‘important’.
  • Enfin, malgré cette précédence, ‘somevar’ prendra sa valeur a l’execution APRES ‘important’

Modifions ce programme pour lui faire imprimer les adresses et les contenus, et executons le en lui passant le parametre ‘TOTO’ :

$mon_programme TOTO
0×8049700: T(0x616c62)
0×8049701: O(0x616c)
0×8049702: T(0×61)
0×8049703: O(0×0)
0×8049704: (0×0)
0×8049705: (0×0)
0×8049706: (0×0)
0×8049707: (0×0)
0×8049708: (0×0)
0×8049709: (0×19000000)
0x804970a: (0×190000)
0x804970b: (0×1900)
0x804970c: (0×19)
0x804970d: (0×63000000)
0x804970e: (0x6f630000)
0x804970f: (0x6d6f6300)
0×8049710: c (0x6d6d6f63)
0×8049711: o (0x616d6d6f)
0×8049712: m (0x6e616d6d)
0×8049713: m (0x646e616d)
0×8049714: a (0x646e61)
0×8049715: n (0x646e)
0×8049716: d (0×64)
0×8049717: (0×0)

On connait désormais le décalage d’adresse entre ‘somevar’ et ‘important’.
Le C et strcpy() le permettant, on va se permettre un dépassement de buffer de la variable ‘somevar’ pour substituer NOTRE commande à la ‘command’ d’origine.

0×8049700: T(0x646e6573)
0×8049701: O(0x2d646e65)
0×8049702: T(0x2d2d646e)
0×8049703: O(0x2d2d2d64)
0×8049704: – (0x2d2d2d2d)
0×8049705: – (0x2d2d2d2d)
0×8049706: – (0x2d2d2d2d)
0×8049707: – (0x2d2d2d2d)
0×8049708: – (0x2d2d2d2d)
0×8049709: – (0x2d2d2d2d)
0x804970a: – (0x2d2d2d2d)
0x804970b: – (0x2d2d2d2d)
0x804970c: – (0x2d2d2d2d)
0x804970d: – (0x6e2d2d2d)
0x804970e: – (0x656e2d2d)
0x804970f: – (0x77656e2d)
0×8049710: n (0x6377656e) <— c’est la !
0×8049711: e (0x6f637765)
0×8049712: w (0x6d6f6377)
0×8049713: c (0x6d6d6f63)
0×8049714: o (0x616d6d6f)
0×8049715: m (0x6e616d6d)
0×8049716: m (0x646e616d)
0×8049717: a (0x646e61)
0×8049718: n (0x646e)
0×8049719: d (0×64)
0x804971a: (0×0)

C’est la le miracle : on peut modifier le déroulement de l’exécution d’un programme sans en modifier le code (heureusement il faudrait le recompiler et reinstaller l’executable sur la cible). Le paramètre d’entrée est le seul point…d’entrée du programme et on peut faire une subsitution de code, si tant est que certains appels se fassent à travers des variables et qu’elles soient correctement ‘rangées’ en mémoire.

Un exemple un peu moins ideal (un stack overflow)

D’après airWalk, « Introduction to buffer overflows » – for interScape, may 1999

Soit le programme C suivant :

void someFunction(char *str) {
char buffer[16];
strcpy(buffer, str);
}
void main()
{
char bigString[256];
int i;
for( i = 0; i < 255; i++)
bigString[i] = ‘A’;
someFunction(bigString);
}

A l’exécution il provoque une erreur du type : ‘Segmentation violation’ .
Pourquoi ? Parce que c’est au sein, et plus précisément juste avant le ‘return’ de la fonction qu’a lieu un dépassement de buffer.
Les 240 ‘A’ supplémentaires vont écraser les zones mémoires suivant la fin de la variable ‘buffer’ , et en particulier l’adresse de retour de la fonction, en y subsituant une valeur (pleine de ‘A’) invalide…d’ou l’erreur sus nommée.
On perçoit que dans ce cas, si l’on si prend bien il est possible de modifier l’adresse de retour d’une fonction et donc modifier le déroulement de l’exécution d’un programme.

Reste maintenant un détail, écrire une exploit capable de menacer le système. A la différence de l’exemple précédent qui faisait des appels à des commandes système, que l’on remplaçait, celui ci n’en fait pas…c’est son coté moins idéal.

fwd2rappelons que « l’exploit » du programme est souvent conditionnée par le niveau de privilège d’exécution du programme hacké. Un uid root (ou son équivallent Windows administrateur) permet + facilement d’executer des commandes fatales.

Les « exploit »s de buffer overflow

Nous savons comment on peut derouter un programme en écrivant l’exacte quantité de mémoire et en écrasant l’adresse de retour de la routine par une nouvelle adresse. Rest e à faire exécuter un nouveau code.
Ou peut se trouver le nouveau code agressif ?
ll est impossible de modifier physiquemnt le programme compilé de la cible. Le nouveau code ne peut qu’être passé qu’AU SEIN des données modifiées responsables du buffer overflow !
La taille du code ne correspondant pas forcément exactement à l’offset entre début de buffer et adresse de retour à écraser, précédera le code par des NOPs.
Que contient le code agressif ?
L’hypothèse étant que le programme vulnérable a un niveau de privilège intéressant (root), une des exploits les + classiques sera de lancer un shell permettant d’executer des commandes ‘intéressantes’ : rm -R /*, cat /etc/passwd, mail, etc.
Le code sera d’abord écrit en C, compilé , linké (gcc) et désassemblé (avec gdb). Les codes hexadécimaux du programme seront ensuite passés directement dans le buffer.

fwd2 Si certains octets du code sont à zero, certaines des fonctions vulnérables (comme strcpy()) risquent d’interpréter le 0×00 comme une fin de chaîne ;-((

Les moyens d’actions

  • Utiliser des langages ‘sécurisés’ : Cyclone ou Java Vs C, strncpy() Vs strcpy()
  • tester le code avec des outils spécialisés (Flawfinder, BFBtester, StackGuard)
  • Patcher !

Flashback et ORA-38760 au redémarrage sur RAC

Musée des erreurs pas de Commentaire »

Au reboot de la base vous récuperez une erreur ORA-38760.

PB : This database instance failed to turn on flashback database

Cause : Database flashback is on but this instance failed to start generating flashback data. Look in alert log for more specific errors.

C’est très souvent du au fait que vous avez créé des points de restauration garantis.

Arrêtez les deux instances et démarrez en une en mode mount.

C:\Documents and Settings\admin.avassallo>set TNS_ADMIN=E:\admin_oracle\network_admin
C:\Documents and Settings\admin.avassallo>set ORACLE_SID=QUALIF1
C:\Documents and Settings\admin.avassallo>sqlplus / as sysdba
SQL*Plus: Release 10.2.0.4.0 – Production on Mar. Juin 23 09:47:14 2009
Copyright (c) 1982, 2007, Oracle.  All Rights Reserved.
Connecté à une instance inactive.
SYS@QUALIF1> startup mount
Instance ORACLE lancée.
Total System Global Area  629145600 bytes
Fixed Size                  1298264 bytes
Variable Size             260047016 bytes
Database Buffers          360710144 bytes
Redo Buffers                7090176 bytes
Base de données montée.

On vérifie l’existence de point de restauration garantis que l’on va dropper allègrement.

SYS@QUALIF1> select name from v$restore_point where guarantee_flashback_database
= ‘YES’;
NAME
——————————————————————————–
PRC_SECOND
PRC_TROIS
SYS@QUALIF1> drop restore point PRC_TROIS;
Point de restauration supprimé.
SYS@QUALIF1> drop restore point PRC_SECOND;
Point de restauration supprimé.

Arrêt de la flashback!

SYS@QUALIF1> alter database flashback off;
Base de données modifiée.
SYS@QUALIF1> alter database open;
Base de données modifiée.
SYS@QUALIF1>
Vérification de la place dans la Flash back Recovery Area.
E:\admin_oracle\scripts>sqlplus -S / as sysdba  @E:\admin_oracle\scripts\asm_dis
kgroup.sql
NAME                             TOTAL_MB    FREE_MB USABLE_FILE_MB
—————————— ———- ———- ————–
DGDATA                             307098     104932          26874
DGFRA                              102348      52146          13279
DGHISTO                            245676     104586          31820

Arrêt à nouveau de l’instance 1

SYS@QUALIF1> shutdown immediate
Base de données fermée.
Base de données démontée.
Instance ORACLE arrétée.
SYS@QUALIF1> quit
Déconnecté de Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 – Produc
tion
With the Partitioning, Real Application Clusters, OLAP, Data Mining
and Real Application Testing options

Redémarrage de la Flashback

C:\Documents and Settings\admin.avassallo>sqlplus / as sysdba
SQL*Plus: Release 10.2.0.4.0 – Production on Mar. Juin 23 09:58:50 2009
Copyright (c) 1982, 2007, Oracle.  All Rights Reserved.
Connecté à une instance inactive.
SYS@QUALIF1> startup mount
Instance ORACLE lancée.
Total System Global Area  629145600 bytes
Fixed Size                  1298264 bytes
Variable Size             260047016 bytes
Database Buffers          360710144 bytes
Redo Buffers                7090176 bytes
Base de données montée.
SYS@QUALIF1> alter database flashback on;
Base de données modifiée.
SYS@QUALIF1> alter database open;
Base de données modifiée.

Redémarrage de la deuxième instance.
SYS@QUALIF2>
SYS@QUALIF2> startup
Instance ORACLE lancée.
Total System Global Area  629145600 bytes
Fixed Size                  1298264 bytes
Variable Size             226492584 bytes
Database Buffers          394264576 bytes
Redo Buffers                7090176 bytes
Base de données montée.
Base de données ouverte.
SYS@QUALIF2>

Et le tour est joué.

Astuce : Créér une DB avec ASM sur windows avec un fichier au lieu d’un disque. by albanlepunk

ASM 2 Commentaires »

Sous windows (ici avec une version 10.2.0.4).

Avec le DBCA lancer la création d’une base, au moment de choisir la destination des fichiers de la db , choisir ASM.

Il faut à ce moment là lui présenter un disque ou une volume non formaté.

Vous n’avez pas de partition libre et non formaté sous la main.

On peut avec asmtool -create Nom_Fichier taille en mégas

EX : D:\>asmtool -create D:\oracle\product\10.2.0\oradata\ASM\asmdb_setra.asm 5000

Après ce « disque » est vu comme éligible pour ASM.

Ensuite Créer une instance ASM avec oradim sans oublier de créer les répertoires qui vont bien.

mkdir %ORACLE_BASE%\admin\+ASM\bdump
mkdir %ORACLE_BASE%\admin\+ASM\cdump
mkdir %ORACLE_BASE%\admin\+ASM\hdump
mkdir %ORACLE_BASE%\admin\+ASM\pfile
mkdir %ORACLE_BASE%\admin\+ASM\udump
oradim -new -asmsid +ASM -syspwd change_on_install -pfile C:\oracle\product\10.1.0\admin\+ASM\pfile\initASM.ora -spfile
    -startmode auto -shutmode immediate

D:\>set ORACLE_SID=+ASM

D:\>sqlplus / as sysdba

SQL*Plus: Release 10.2.0.4.0 – Production on Mer. Juin 10 16:28:11 2009

Copyright (c) 1982, 2007, Oracle.  All Rights Reserved.

ConnectÚ Ó :
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 – Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> create pfile=’D:\oracle\product\10.2.0\db_1\database\initASM.ora’ from spfi
le;

Fichier créé.
SQL> shutdown immediate
ORA-15100: nom de groupe de disques non valide ou absent
Instance ASM arrétée

Modifier le initASM.ora créé précédemment.

_asm_allow_only_raw_disks=false
asm_diskgroups='DATADG'

SQL> startup pfile=D:\oracle\product\10.2.0\db_1\database\initASM.ora
Instance ASM démarrée

Total System Global Area   83886080 bytes
Fixed Size                  1295152 bytes
Variable Size              57425104 bytes
ASM Cache                  25165824 bytes
ORA-15110: aucun groupe de disques n’est monte

SQL> create diskgroup DATADG external redundancy disk ‘D:\oracle\product\10.2.0\
oradata\ASM\asmdb_setra.asm’;

Groupe de disques crÚÚ.

SQL>

Démarrer le dbca et lancer la création d’une base de données sur ASM, votre diskgroup sera vu dans dbca.

On peut procéder de même en créant un deuxième fichier puis diskgroup FRADG pour la Flash Recovery Area .

Et le tour est joué.

Vider ASM de ses fichiers avec Alter diskgroup drop file! by albanlepunk

ASM pas de Commentaire »

Il est possible de « rentrer » dans ASM et de naviguer dedans en utilisant l’exécutable asmcmd.

C:\oracle\product\10.2.0\db_1\BIN>asmcmd -p
ASMCMD [+] > ls
DATADG/
FRADG/
ASMCMD [+] > cd DATADG
ASMCMD [+DATADG] > ls
ASMCMD [+DATADG] > ls
ASMCMD [+DATADG] > cd ..
ASMCMD [+] > cd FRADG
ASMCMD [+FRADG] > ls
TEST/

la commande rm ayant cours, on peut donc supprimer des fichiers manuellement

Comment automatiser cette tache, par exemple avant de faire un Rman Duplicate sur un serveur de restauration?

Il est possible de supprimer des fichiers dans ASM en étant connecté sys  (cad ORACLE_SID=+ASM) avec l’instruction :  alter diskgroup « XXXXX » drop file ‘yyyyyyyy’;

Toute la difficulté consiste à récupérer les bons noms de fichiers tels qu’ils sont dans ASM.

Voilà un script qui permet de le faire.

set long 20000
set line 200
set pagesize 10000
set heading off
set verify off
set feedback off
set echo off

spool c:\temp\00_nettoie_file_asm.sql

select ‘alter diskgroup ‘||g.name||’ drop file ‘|| »’+'|| g.name||’.'||f.file_number||’.'||f.incarnation|| »’;’
FROM v$asm_diskgroup g,
v$asm_file f,
v$asm_alias a
where g.name in (‘DATADG’,'FRADG’)
and g.group_number = f.group_number
and g.group_number = a.group_number
and f.file_number = a.file_number
order by f.file_number;

spool off

@c:\temp\00_nettoie_file_asm.sql

set heading on
set verify on
set feedback on
set echo on
exit

Penser à renseigner le diskgroup in (‘   ‘,’   ‘).

Votre instance ASM est vide et prête à accueuillir de nouveaux fichiers.

Faire un duplicate avec RMAN sous windows! by albanlepunk

Administration Oracle, Rman pas de Commentaire »

# Créer le fichier de mot de passe pour la base clone

orapwd file=$ORACLE_HOMEdbsorapwCLONE.ora password=XXXXXXXXXX

# Créer le fichier d’init pour la base clone

# Copier le init.ora de la base cible

SQL> create pfile=’$ORACLE_HOMEdbsinitCLONE.ora’ from spfile;

File created.

#  Apporter les modifs nécéssaires au fichier init.

db_file_name_convert = (‘$ORADATACLONE’, ‘$ORADATACLONE’)
log_file_name_convert = (‘$ORADATACLONE’, ‘$ORADATACLONE’)
control_files = ‘$ORADATACLONEcontrol01.ctl’
, ‘$ORADATACLONEcontrol02.ctl’
, ‘$ORADATACLONEcontrol03.ctl’
db_name = ‘CLONE’
instance_name = ‘CLONE’
background_dump_dest = ‘$ADMINCLONEbdump’
core_dump_dest = ‘$ADMINCLONEcdump’
user_dump_dest = ‘$ADMINCLONEudump’
service_names = ‘SERVICE.CLONE’
log_archive_dest_1 = ‘location=$ORADATACLONEarchive MANDATORY’

# Créer un nouveau service windows pour la base CLONE à dupliquer.

oradim -new -sid CLONE -intpwd D:oracleproduct10.2.0db_1databaseorapwCLONE.ora.ora -startmode auto -pfile ‘D:oracleproduct10.2.0db_1databaseinitCLONE.ora’

# Créer les repertoires admin

mkdir $ORADATACLONE
mkdir $ORADATACLONEarchive
mkdir $ORADATACLONEbdump
mkdir $ORADATACLONEcdump
mkdir $ORADATACLONEcreate
mkdir $ORADATACLONEpfile
mkdir $ORADATACLONEscripts
mkdir $ORADATACLONEudump

# Démarrer l’instance CLONE

set ORACLE_SID=CLONE

sqlplus « / as sysdba »

SQL> startup nomount

# Modifier le listener et le tnsnames pour que l’on puisse se connecter à la base auxiliaire

sqlplus « sys/XXXXXXXX@CLONE as sysdba »

# Vérifier que la base cible soit montée ou ouverte

# Se connecter à la base cible et la base auxiliaire avec RMAN

rman target sys/XXXXXXXX@TARGET auxiliary sys/xxxxxxxx@CLONE

connected to target database: TARGET (DBID=3850478880)
connected to auxiliary database: CLONE (not mounted)

RMAN>

# Lancer la commande duplicate
# On peut mettre un critère de temps.
# ex : duplicate target database to CLONE until time ‘SYSDATE-1′;.
# ou dans un script run

duplicate target database to CLONE;

Ex de script jusqu’à la sequence X du Thread Y (because RAC)

run
{ sql ‘alter system archive log current’;
set until sequence=11212 THREAD=2;
allocate auxiliary channel ch1 type disk;
allocate auxiliary channel ch2 type disk;
allocate auxiliary channel ch3 type disk;
allocate auxiliary channel ch4 type disk;
duplicate target database to ‘DBRESTAU’ pfile=’D:oracleproduct10.2.0db_1databaseinitCLONE.ora’ nofilenamecheck; }
exit