Rodger

Aller au contenu | Aller au menu | Aller à la recherche

jeudi, octobre 17 2013

Requête récursive en postgresql

Supposons les deux tables suivantes. La première, "object" contient juste une information "name", tandis que la seconde permet de créer des liens orientés entre un objet "parent" et un objet "enfant". Voir le script de création des tables en annexe.

Et un contenu pour chacune de ces tables.

Pour un objet donné (via sa clé primaire), la fonction suivante renvoie la liste des objets parents.

CREATE OR REPLACE FUNCTION get_parent_list_hierarchy(IN _object_id integer)
  RETURNS TABLE(fk_id integer, fk_child_id integer, name character varying) AS
$BODY$
    WITH RECURSIVE hierarchy(fk_child_id, fk_parent_id) as (
        select fk_child_id, fk_parent_id
        from object_r_object_hierarchy h
        where fk_child_id = _object_id

        union all

        select r.fk_child_id, r.fk_parent_id
        from hierarchy p
            inner join object_r_object_hierarchy r
                ON r.fk_child_id = p.fk_parent_id
    ) select h.fk_parent_id, h.fk_child_id, object.name from hierarchy as h
         inner join object on h.fk_parent_id = object.pk_object_id
         limit 100
    ;
$BODY$
  LANGUAGE sql STABLE
  COST 100
  ROWS 1000;

La requête est stockée dans une fonction qui renvoie un table contenant l'id et le nom de l'objet ainsi que l'id de son parent. Il n'y a aucune contrainte sur l'unicité des parents : un objet peut avoir plusieurs parents au même titre qu'il peut avoir plusieurs enfants. On est dans ici dans la cas d'un graphe orienté et pas simplement d'une arborescence. Le cas d'une arborescence aurait été géré en injectant le fk_parent_id (nullable) dans la table object et en éliminant alors la table de relation object_r_object_hierarchy.

Le code de la fonction get_parent_list_hierarchy est en 2 grandes parties :
  1. la requête récursive (jusqu'à la parenthèse fermante)
  2. la sélection (après cette parenthèse)


La seconde partie récupère la liste d'id (objet et parent) et y joint les noms des objets.


La première partie est plus complexe et peut également se découper en 2 parties, avant et après le "union all" :
  1. avant l'union, on construit la liste correspondant à l'id passé en paramètre, on récupère ainsi tous les parents de l'objet concerné
  2. la partie après l'union fait les appels récursifs :
    • "from la liste construite initialement" (nommons-là p), on récupère tous les enregistrements de la table object_r_object_hierarchy (nommons-la r) dont le child_id est égal à un parent_id de p
    • les enregistrements obtenus sont ajoutés à p par le "union all"

Le limit 100 à la fin est là pour éviter qu'une requête ne boucle indéfiniment dans le cas où on aurait mal renseigné la table object_r_object_hierarchy en créant un cycle dans la hiérarchie.

lundi, juin 14 2010

Installation de Debian sur le N510 Premium de Samsung

J'ai acheté récemment un netbook : le N510 Premium de Samsung. Pourquoi ce modèle ? Principalement pour l'écran 11.6' en résolution HD (1366x768) et sa carte graphique nVidia (en l'occurrence c'est du ION LE).

Premier démarrage sous windows, premières impressions.

L'installation se passe toute seule, et à la fin, un reboot automatique (et obligatoire) lance le soft backup qui permet de faire une image du système sur le disque ou sur clé.

L'écran est très agréable, le clavier est assez grand, le touchpad est bien, sauf les boutons que je trouve un peu bas.

Première partie de l'install avec une clé usb (netinst)

Tout se passe bien jusqu'à la fin, au final :
  • noyau 2.6.26-2-686
  • touchpad OK, scroll sur le bord du touchpad OK, le "dual-touch" n'est vraisemblablement pas supporté
  • gestion du volume via les touches OK
  • gestion luminosité via les touches pas OK
  • réseau filaire OK
  • pas de wifi (driver atheros non inclus)
  • résolution native de l'écran (1366x768) semble respectée (mais System/Preferences/Screen Resolution incapable d'afficher la résol)
  • son OK
  • webcam OK (testée avec cheese)
  • le système voit bien 2 cpu (gnome-system-monitor)
  • bluetooth pas testé, mais gnome affiche l'icône dans le tray
Pour ceux que ça intéresse :

$ lspci
00:00.0 Host bridge: nVidia Corporation Device 0a82 (rev b1)
00:00.1 RAM memory: nVidia Corporation Device 0a88 (rev b1)
00:03.0 ISA bridge: nVidia Corporation Device 0aae (rev b3)
00:03.1 RAM memory: nVidia Corporation Device 0aa4 (rev b1)
00:03.2 SMBus: nVidia Corporation Device 0aa2 (rev b1)
00:03.3 RAM memory: nVidia Corporation Device 0a89 (rev b1)
00:03.5 Co-processor: nVidia Corporation Device 0aa3 (rev b1)
00:04.0 USB Controller: nVidia Corporation Device 0aa5 (rev b1)
00:04.1 USB Controller: nVidia Corporation Device 0aa6 (rev b1)
00:06.0 USB Controller: nVidia Corporation Device 0aa7 (rev b1)
00:06.1 USB Controller: nVidia Corporation Device 0aa9 (rev b1)
00:08.0 Audio device: nVidia Corporation Device 0ac0 (rev b1)
00:09.0 PCI bridge: nVidia Corporation Device 0aab (rev b1)
00:0b.0 IDE interface: nVidia Corporation Device 0ab5 (rev b1)
00:10.0 PCI bridge: nVidia Corporation Device 0aa0 (rev b1)
00:15.0 PCI bridge: nVidia Corporation Device 0ac6 (rev b1)
00:16.0 PCI bridge: nVidia Corporation Device 0ac7 (rev b1)
00:17.0 PCI bridge: nVidia Corporation Device 0ac7 (rev b1)
02:00.0 VGA compatible controller: nVidia Corporation Device 0876 (rev b1)
03:00.0 Network controller: Atheros Communications Inc. Device 002b (rev 01)
04:00.0 Ethernet controller: Marvell Technology Group Ltd. 88E8040 PCI-E Fast Ethernet Controller (rev 13)

Passage en squeeze

aptitude update, aptitude safe-upgrade, et BING ! tout est pété, il y a un package moisi : libapt-pkg-libc6.9-6-4.8 et pas moyen de trouver quoi que ce soit qui m'aide à résoudre le problème (dépendances impossibles à satisfaire, etc).

On tente le coup en sid et ça marche impec : les drivers atheros (wifi) contenus dans le noyau 2.6.35

L'installation des drivers nVidia (195.36.31) se passe parfaitement (comme d'habitude oserai-je dire) et un test de glxgears -fullscreen me donne une moyenne de plus de 280 images par seconde, ça va...

vendredi, avril 23 2010

Un Buffer overflow de A à Z (sous Linux) - Première partie

Après une introduction sur l'utilité et le fonctionnement de la pile, introduction dans laquelle nous avons joué à modifier le déroulement normal d'un programme C, nous allons voir d'un peu plus près le problème du buffer overflow et ce que l'on entend par un exploit de buffer overflow.

Au programme dans ce post : le principe général et l'étude du programme "vulnérable" qui sera la cible de notre attaque dans les prochains posts.

Principe

Le principe d'un buffer overflow est le suivant :
  • un programme copie des données dans un buffer local sans s'assurer que la place allouée est suffisante pour les accueillir (notre programme d'exemple fera un simple strcpy au lieu d'un strncpy, mais il aurait pu faire un fscanf(in,"%s",buf); ou autre)
  • dans la plupart des cas, déborder de la zone mémoire va provoquer une erreur de segmentation, dans d'autres cas cela va corrompre des données
  • nous verrons par la suite qu'en fournissant les données adéquates au programme, on peut réussir à exécuter le code machine que l'on veut, et ainsi lancer un programme quelconque (un shell par exemple) par l'intermédiaire du programme défaillant (au lieu de bêtement le faire planter)

Le gros problème de sécurité se pose lorsque le programme vulnérable possède le droit "setuid root" et qu'un utilisateur de base exploite le buffer overflow pour obtenir un shell root. Une autre exploitation de cette vulnérabilité consiste à faire exécuter non pas un shell mais du code qui va ouvrir une connexion sur un port de la machine et ensuite exécuter des commandes envoyées depuis une machine distante ("remote shell").

Notre but dans cette série de billets est de réussir à ouvrir un shell root en passant des données corrompues à un programme vulnérable qui s'exécute en mode setuid root.

Le programme vulnérable

Pour fixer les idées et se donner un exemple le plus simple possible, nous allons utiliser le programme suivant qui fait l'erreur de ne pas vérifier que la chaîne passée en 1er argument du programme ne fait pas plus que 7 caractères (+ l'octet nul de fin de chaîne).

#include <stdio.h>
#include <string.h>

int main(int argc, char** argv) {
    char name[8];
    if (argc == 2) {
        strcpy(name,argv[1]);
        printf("hello %s !\n",name);
    }
    puts("bye bye");
    return 0;
}

Faire planter le programme

Compilons ce programme avec les informations de débug (qui serviront plus bas) :
        $ gcc -g vulnerable.c -o vulnerable

Lorsque l'on lance ce programme avec en premier argument une chaîne de caractères trop longue, on obtient une erreur de segmentation:

        $ ./vulnerable Rodger
        hello Rodger !
        bye bye
        $ ./vulnerable ABCDEFGHIJKLMNOPQRSTUVWX
        hello ABCDEFGHIJKLMNOPQRSTUVWX !
        Segmentation fault
Le programme fait cette erreur car le strcpy a copié plus de caractères que le tableau name ne pouvait en accueillir et les caractères en trop sont allés écraser l'adresse de retour de la fonction main(). Le main s'exécute donc parfaitement, en faisant correctement le printf, mais lorsque l'on doit revenir à l'appelant (la libc) l'adresse à laquelle on aurait dû retourner a été corrompue et l'on saute à une adresse invalide. Un exploit de buffer overflow consiste à passer au programme vulnérable une chaîne de caractères spécialement écrite pour que l'on ne saute pas n'importe où dans la mémoire mais au contraire à l'adresse où se trouve un peu de code machine qui, par exemple, lancera un shell.

Comprendre ce plantage

Lancer "ddd" (d'où le flag "-g" de la compilation précédente).
        $ ddd ./vulnerable
Dans le menu "Source", cocher "Display Machine Code". On obtient ainsi la correspondance entre le code C et le code assembleur. Dans cette zone contenant le code assembleur, poser un point d'arrêt sur l'instruction "ret". Exécuter le programme avec en lui passant la chaîne de caractères "ABCDE...WX" en argument : dans le menu "Program" cliquer sur "Run..." et saisir la chaîne complète.
Le programme s'exécute jusqu'au point d'arrêt. Dans la sous-fenêtre gdb, taper la commande suivante :
        (gdb) x $esp
On obtient l'affichage suivant :
        0xbffff44c:        0x58575655
qui nous indique que la pile (le registre esp) pointe à l'adresse 0xbffff44c, et que l'on trouve à cette adresse les 4 octets 0x58, 0x57, 0x56 et 0x55 qui sont respectivement les codes ASCII des caractères 'X', 'W', 'V' et 'U' (man ascii pour le vérifier).
Exécuter l'instruction "ret" en cliquant sur "NEXTI" (ou en tapant "nexti" dans la sous-fenêtre gdb), l'erreur de segmentation se produit car on a essayé de sauter à l'adresse 0x58575655 qui n'est pas accessible. En tapant la commande suivante (dans la sous-fenêtre gdb) :
        (gdb) x $eip
on en a la confirmation : eip est le registre qui contient l'adresse de l'instruction à exécuter (EIP = Extended Instruction Pointer).

En passant la bonne chaîne de caractères en 1er argument du programme vulnérable, il est donc possible de faire sauter l'exécution à (presque) n'importe quelle adresse, si notre code "malicieux" se trouve à cette adresse c'est gagné... C'est ce que nous verrons dans les prochains posts...

jeudi, avril 8 2010

Gérer entièrement son thème sous dotclear hébergé chez blog.free.fr

Il est facile de reprendre un thème parmi ceux proposés dans l'installation de dotclear proposée par Free. Il est facile de retravailler la CSS en partant de celle d'un thème qu'on aime bien. Personnellement j'ai repris le thème Alto (merci à son auteur) et je l'ai étendu un peu en largeur (de 880 à 1000 pixels).

J'ai appliqué le thème personnalisé et ai collé la CSS modifiée par mes soins dans le formulaire de "configuration du thème".

Pour les images c'est moins simple.

Les images sont situées normalement dans un sous-répertoire du thème et il n'y a pas moyen d'accéder en ftp à ces répertoires, du moins à ma connaissance.

Pour pouvoir utiliser ses propres images dans un thème, j'entends par là : utiliser ses propres pictos, images de fond, etc, bref les images qui sont utilisées par la CSS, il faut utiliser le "gestionnaire de médias" :
  1. créer un répertoire "theme"
  2. aller dans ce répertoire
  3. uploader les images que l'on veut utiliser pour le thème
  4. modifier la CSS pour qu'elle utilise ces images
Pour ce dernier point, on se rend compte en cliquant sur le lien "ouvrir" d'une image (rss.png par exemple) que l'on vient de mettre dans "theme" que son url est "http://rodger.blog.free.fr/theme/rss.png", il suffit donc de mettre dans la CSS :
  background-image: url(/theme/rss.png);


mercredi, avril 7 2010

Jouer avec la pile (en C/ASM sous linux)

En guise d'introduction aux prochains billets sur le buffer-overflow, et pour bien comprendre comment fonctionne la pile, voici ce premier billet qui devrait clarifier les choses.

La pile

Il s'agit d'une zone de mémoire utilisée pour mémoriser des données temporairement:

  • lorsque l'on appelle un sous-programme, l'adresse à laquelle il faudra revenir pour continuer le déroulement du programme
  • les variables locales à ce sous-programme
Ces stockages sont temporaires car lorsque l'on sort du sous-programme ces données sont dépilées et donc perdues définitivement. C'est la raison pour laquelle il ne faut jamais retourner l'adresse d'une variable locale: elle est inutilisable hors du sous-programme où elle est définie.

Sur les architectures Intel c'est le registre ESP qui sert à accéder au sommet de la pile. Plus on a de variables locales et/ou d'appels imbriqués à des fonctions (par exemple pour de la récursivité), plus la pile grossit. Son sommet progresse vers les adresses décroissantes (l'adresse pointée par le registre ESP est de plus en plus basse).

Initialement la pile contient dans l'ordre (en commençant par les adresses hautes):

  • 4 octets nulls
  • le nom du programme
  • les variables d'environnement
  • les arguments du main
  • d'autres données
  • l'adresse de retour lorsque l'on sort du main (et qu'on retourne à la libc)
  • les variables locales au main
  • ...
En assembleur, les instructions pushl et popl permettent respectivement d'empiler et dépiler une donnée de 4 octets. Lorsqu'on empile, le registre esp est décrémenté de 4 et la donnée est écrite à cette nouvelle adresse. Lorsqu'on dépile c'est l'inverse qui se produit. L'instruction call sert à appeler un sous-programme : l'adresse de l'instruction qui succède au call est empilée (pour qu'on puisse y revenir plus tard) et le programme saute à l'adresse passée en paramètre au call. Inversement, l'instruction ret saute à l'adresse qu'elle dépile.

Comment connaître l'adresse à laquelle se trouve la pile ?

Première chose à savoir : lorsqu'une fonction retourne une valeur qui tient dans 32 bits (un int par exemple) gcc utilise le registre eax pour transmettre cette valeur à l'appelant. Par exemple, dans la fonction suivante (et sans optimisation particulière du compilateur, la valeur 1234 va être placée dans le registre eax, qui sera utilisée (ou pas) dans le code appelant:
int f() {
  return 1234;
}
Donc une solution pour connaître l'adresse du sommet de la pile peut s'écrire:
unsigned long* getesp() {
  __asm__("movl %esp, %eax");
}
Dans ce code, on appelle de l'assembleur en ligne (pour beaucoup plus d'infos voir http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html). Le code met juste la valeur actuelle du registre esp (le sommet de la pile) dans le registre eax (la valeur de retour de la fonction getesp). Donc on peut utiliser la fonction ainsi:
  printf("Ma pile est à l'adresse %x\n", getesp());

Jouons avec la pile

Que faut-il écrire comme code dans la fonction "function()" pour qu'à l'exécution le programme affiche que x vaut 12 (au lieu de 1) ?
#include <stdio.h>
unsigned long* getesp() {
  __asm__("movl %esp, %eax");
}

void function() {
  unsigned long* stack = getesp();
  // QUE MANQUE-T-IL ICI ???
}

void main() {
  int x;
  x = 12;
  function();
  x = 1;
  printf("maintenant x == %d\n",x);
}

Créons l'exécutable:
$ gcc stack_trick.c -o stack_trick

Si on l'exécute, l'affichage indiquera que x vaut 1...

Regardons le code machine produit par GCC

Pour cela utilisons la commande objdump ( -D pour désassembler, et le grep pour ne lister que les 7 lignes qui suivent celle contenant "<function>:" ) :
$ objdump -d stack_trick | grep \<function\>\: -A 7
08048237 <function>:
 8048237:    55                       push   %ebp
 8048238:    89 e5                    mov    %esp,%ebp
 804823a:    83 ec 10                 sub    $0x10,%esp
 804823d:    e8 ee ff ff ff           call   8048230 <getesp>
 8048242:    89 45 fc                 mov    %eax,-0x4(%ebp)
 8048245:    c9                       leave 
 8048246:    c3                       ret

Sur chaque ligne, les informations présentées par objdump sont ici en 3 colonnes :
  • l'adresse de chaque instruction. Cette adresse sera ensuite modifiée (on dit "relogée") lors du chargement du programme (et ne sera donc jamais celle affichée ici)
  • une ou plusieurs valeurs hexadécimales, ce sont les opcodes de l'instruction
  • l'instruction assembleur avec éventuellement une sorte de commentaire (pour le call, on a l'information que l'adresse à laquelle on saute est en fait la fonction getesp)
Le code que l'on va ajouter, va venir s'intercaler entre le mov et le leave.

Concernant les instructions qui modifient la pile, on repère:
  • 1 push (4 octets)
  • 1 sub de 16 octets (0x10)
  • 1 call (qui aura pour effet de descendre la pile de 4 octets supplémentaires)
  • à cela il faut ajouter encore 4 octets à cause du call vers function() depuis le main
Ce qui nous fait un total de 28 octets (ou 7 longs) entre le sommet de la pile renvoyé par getesp() et le sommet de la pile dans le main juste avant d'appeler function().

On sait donc maintenant que l'adresse à laquelle le programme ira quand on sortira de function() est stockée en "*(stack+7)". Nous devons donc modifier cette adresse pour qu'elle indique l'instruction C après le "x=1;".

Pour savoir de combien d'octets il faut augmenter l'adresse de retour de function(), regardons le code machine du main:
$ objdump -d stack_trick | grep \<main\>\: -A 16
080483db <main>:
 80483db:    55                       push   %ebp
 80483dc:    89 e5                    mov    %esp,%ebp
 80483de:    83 e4 f0                 and    $0xfffffff0,%esp
 80483e1:    83 ec 20                 sub    $0x20,%esp
 80483e4:    c7 44 24 1c 0c 00 00     movl   $0xc,0x1c(%esp)
 80483eb:    00
 80483ec:    e8 da ff ff ff           call   80483cb <function>
 80483f1:    c7 44 24 1c 01 00 00     movl   $0x1,0x1c(%esp)
 80483f8:    00
 80483f9:    b8 d0 84 04 08           mov    $0x80484d0,%eax
 80483fe:    8b 54 24 1c              mov    0x1c(%esp),%edx
 8048402:    89 54 24 04              mov    %edx,0x4(%esp)
 8048406:    89 04 24                 mov    %eax,(%esp)
 8048409:    e8 ee fe ff ff           call   80482fc <printf@plt>
 804840e:    c9                       leave 
 804840f:    c3                       ret

On y voit le call function() suivi immédiatement par un movl qui met la valeur 1 dans une variable locale qui se situe en 0x1c(%esp), il s'agit de la variable x. Cette instruction est traduite par les opcodes suivants:
c7 44 24 1c 01 00 00 00
qui occupent un total de 8 octets (attention objdump les affiche sur deux lignes, il y en a donc bien 8 et pas seulement 7).

Pour éviter d'exécuter cette instruction lorsque l'on reviendra du call, il faut donc que l'adresse de retour ne pointe pas sur la ligne du mov mais 8 octets plus loin. On peut donc maintenant compléter function():
void function() {
  unsigned long* stack = getesp();
  *(stack+7) += 8;
}

Et voilà !