dimanche 13 octobre 2013

ht editor

Puisqu'il est possible d'ajouter du trailing à un fichier ELF sans qu'il ne se passe rien, je me demande comment faire pour mettre du code dans ce trailing et pointer dessus en modifiant l'entrypoint. Ca devrait être simple, mais je trouve pas vraiment de manière de faire.

Ce dont j'ai besoin:
  • savoir comment le fichier du disque se retrouve en mémoire
  • savoir comment ajouter le trailing du fichier dans la mémoire
  • modifier l'entry point (ça c'est le + simple) ou modifier n'importe quel appel de fonction pour pointer sur mon code ajouté
Pour manipuler le fichier ELF, j'utilise ht editor qui m'évite à devoir calculer tous les offsets à la main.
Si je regarde les program headers par exemple (F6 puis program headers):

L'entry0 est le physical header. Il débute à 0x08048034, offset 0x34 soit 52. Cela rejoint donc ce qui dit la commande readelf:
$ readelf -e hello | grep program
  Start of program headers:          52 (bytes into file)

et nous pouvons trouver les suivants, et le mapping entre le fichier sur disque et le fichier en mémoire, il s'agit apparemment d'une conversion simple offset --> 0x08048000+offset. 

Vérifions à l'aide d'objdump -d:
080482d0 <_start>:
 80482d0:       31 ed                   xor    %ebp,%ebp
 80482d2:       5e                      pop    %esi
 80482d3:       89 e1                   mov    %esp,%ecx
 80482d5:       83 e4 f0                and    $0xfffffff0,%esp
 80482d8:       50                      push   %eax
 80482d9:       54                      push   %esp
 80482da:       52                      push   %edx
 80482db:       68 30 84 04 08          push   $0x8048430
 80482e0:       68 d0 83 04 08          push   $0x80483d0
 80482e5:       51                      push   %ecx
 80482e6:       56                      push   %esi
 80482e7:       68 84 83 04 08          push   $0x8048384
 80482ec:       e8 b3 ff ff ff          call   80482a4 <__libc_start_main@plt>
 80482f1:       f4                      hlt   
 80482f2:       90                      nop

Et dans le binaire, si je regarde à x02d0 jusqu'à 0x2f2
$ dd if=hello of=_start bs=1 count=35 skip=720
35+0 enregistrements lus
35+0 enregistrements écrits
35 octets (35 B) copiés, 0,00425817 s, 8,2 kB/s
$ hexdump -C _start  

00000000  31 ed 5e 89 e1 83 e4 f0  50 54 52 68 30 84 04 08  |1í^.á.äðPTRh0...|
00000010  68 d0 83 04 08 51 56 68  84 83 04 08 e8 b3 ff ff  |hÐ...QVh....è³ÿÿ|
00000020  ff f4 90                                          |ÿô.|
On retrouve le code.

Prochaines étapes:
  • vérifier ou se retrouve le trailer dans la mémoire
  • trouver dans les headers les tailles des différents objets, headers, programmes, etc..
Mitsurugi

lundi 7 octobre 2013

ajout de code à un binaire elf?

Si je reprends mon hello world:
 ./hello
Hello World

Je peux ajouter plein de garbage à la fin:
$ hexdump -C nops
00000000  90 90 90 90 90 90 90 90  90 90 90 90 90 90 90 90  |................|
*
000000fb
$ cat hello nops > big

$ chmod +x big
$ ./big
Hello World
$


La question que je me pose, c'est comment charger tous ces nops en mémoire, puis de faire pointer l'EntryPoint dessus.
La fin du binaire n'est pas une zone de code, cela voudrait dire que je devrais tweaker les sections pour en ajouter une? N'y a t'il pas plus simple?

Mitsurugi

samedi 5 octobre 2013

Changement de l'entry point

Un peu de code:
 $ cat hello.c
#include <stdio.h>

int fonction(void)
{
    printf("Je suis dans la fonction\n");
    return 0;
}
int main(void)
{
    printf("Je suis dans le main\n");
    return 0;
}

Un binaire ELF démarre par son entry point:
$ readelf -h hello | grep Entry
  Entry point address:               0x80482d0

Si je change l'entry point par l'adresse de fonction, j'affiche "Je suis dans la fonction".
$ objdump -d hello | grep fonction
08048384 <fonction>:
$ hexdump -C hello | head -4
00000000  7f 45 4c 46 01 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 03 00 01 00 00 00  84 83 04 08 34 00 00 00  |............4...|
00000020  fc 09 00 00 00 00 00 00  34 00 20 00 07 00 28 00  |ü.......4. ...(.|
00000030  23 00 20 00 06 00 00 00  34 00 00 00 34 80 04 08  |#. .....4...4...|

Et donc:
$ ./hello
Je suis dans la fonction
Erreur de segmentation

Ca marche, mais ça plante.
En fait, l'entry point ne démarre pas dans main, mais dans une fonction _start qui doit faire des trucs. D'ailleurs, avec objdump, on voit qu'il se passe des trucs:
080482d0 <_start>:
 80482d0:    31 ed                    xor    %ebp,%ebp
 80482d2:    5e                       pop    %esi
 80482d3:    89 e1                    mov    %esp,%ecx
 80482d5:    83 e4 f0                 and    $0xfffffff0,%esp
 80482d8:    50                       push   %eax
 80482d9:    54                       push   %esp
 80482da:    52                       push   %edx
 80482db:    68 30 84 04 08           push   $0x8048430
 80482e0:    68 d0 83 04 08           push   $0x80483d0
 80482e5:    51                       push   %ecx
 80482e6:    56                       push   %esi
 80482e7:    68 a1 83 04 08           push   $0x80483a1
 80482ec:    e8 b3 ff ff ff           call   80482a4 <__libc_start_main@plt>


L'adresse de main est à 0x80483a1. Je pense que si je modifie le binaire à cet endroit pour y mettre l'adresse de fonction, tout se passera mieux:
000002E0   68 D0 83 04  08 51 56 68  84 83 04 08  E8 B3 FF FF  h....QVh........Et:
$ ./hello
Je suis dans la fonction
$


Mitsurugi

vendredi 4 octobre 2013

En tête ELF

Les fichiers exécutables sous linux sont  format ELF. Le format est décrit sur wikipedia (ELF). Par exemple, le 5e octet définit l'architecture du binaire. Si cet octet vaut 1, c'est du 32 bits, s'il vaut 2, c'est du 64 bits. L'archi est défini au 19e octet. Jouons un peu avec un hello world:
$ cat hello.c
#include <stdio.h>

int main(void) {
    printf("Hello World\n");
    return 0;
}
$ make hello
cc     hello.c   -o hello
$ hexdump -C hello | head -2

00000000  7f 45 4c 46 01 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 03 00 01 00 00 00  20 83 04 08 34 00 00 00  |........ ...4...|
$ file hello
hello: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, BuildID[sha1]=0x0b170e8da236317529906c11be894759a87a8cc3, not stripped
$


On peut "transformer" ce binaire en 64bits avec un coup de hexedit:
$ hexdump -C hello | head -2
00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 3e 00 01 00 00 00  20 83 04 08 34 00 00 00  |..>..... ...4...|

$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), corrupted program header size, corrupted section header size


Et voilà, un beau binaire 64 bits :)

File râle un peu car le 64 bit va chercher ailleurs les infos de taille de programme et de header. On peut les calculer d'ailleurs.

En vert le 32 bit pour le start of program headers et section headers. En souligné pour le 64 bit. (Il faut décaler de 4 octets car il y a l'entry point sur 64 bits avant)
$ hexdump -C hello | head -4
00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 3e 00 01 00 00 00  20 83 04 08 34 00 00 00  |..>..... ...4...|
00000020  a8 07 00 00 00 00 00 00  34 00 20 00 08 00 28 00  |........4. ...(.|
00000030  1f 00 1c 00 06 00 00 00  34 00 00 00 34 80 04 08  |........4...4...|

Ce qui représente une taille de section headers de 0x0028000800200034 soit 11259033430261812 octets (!!). Ca se confirme avec un coup de readelf:
$ readelf -h hello
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x3408048320
  Start of program headers:          1960 (bytes into file)
  Start of section headers:          11259033430261812 (bytes into file)
  Flags:                             0x1c001f
  Size of this header:               6 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         52
  Size of section headers:           0 (bytes)
  Number of section headers:         32820
  Section header string table index: 2052
$


Mitsurugi

jeudi 3 octobre 2013

lavabit et sa clé SSL

On lit dans l'article http://www.wired.com/threatlevel/2013/10/lavabit_unsealed/ que lavabit a été fermé car la NSA voulait les clés privées utilisées par le service.

On se doutait bien que la NSA était derrière, avec les mails (et tous leurs metadata associés) de snowden en ligne de mire. Mais je lis au milieu de l'article:
With the SSL keys, and a wiretap, the FBI could have decrypted all web sessions between Lavabit users and the site, (...).
Et là, je dis WTF?! Lavabit n'utilisait le perfect forward secrecy? Ils s'appelaient service de sécu?
Mitsurugi

mercredi 2 octobre 2013

un crackme

Certains crackmes simples peuvent se résoudre très rapidement.

J'en prends un sur crackmes.de (je change juste le nom). On constate:
$ ls -l crackme
-rw-r--r-- 1 mitsurugi mitsurugi 612 oct.   2 17:05 crackme


$ strings crackme
Password :
Great you did it !:)
         
QTBXCTU

$ ./crackme

Password : QTBXCTU
$


Ok, donc le mot de passe est obfusqué. Sans se poser de questions, on se dit que le mot de passe est peut-être simplement X-oré vu la taille du programme:
$ objdump -d crackme | grep xor
 80480b3:    31 db                    xor    %ebx,%ebx
 80480b7:    34 21                    xor    $0x21,%al

OK, 0x21 semble le bon candidat; une console ipython donne le résultat immédiatement:
$ ipython
In [1]: v = ''

In [2]: for p in 'QTBXCTU':
   ...:     v += chr((ord(p)^0x21))
   ...:    

In [3]: print v
pucybut
$ ./crackme



Password : pucybut
Great you did it !:)

Inutile d'essayer de reverser le reste.

Mitsurugi