[NULLCON-CTF] Exploit400 (fr)

Voici un petit write-up du challenge exploit400, du CTF Nullcon Hackim 2014.

Vous pouvez trouver le binaire ici : exploit400
Et la source ici : exploit400.c

Le fait que nous avions accés au code source du programme nous a bien facilité la tâche.

Le programme est on ne peut plus simple :
– Il ouvre et lit le flag de validation, et le stock dans le heap.
– Il demande ensuite une saisie, qui abouti facilement à un stack based overflow.

Il n’y a pas beaucoup de protection, et j’ai programmé mon exploit en prenant en compte l’ASLR probable :

Comment lire le flag stocké en mémoire ? J’ai choisi de faire du ROP afin de bypass l’ASLR et NX.

La première chose que j’ai fais, c’est mettre à zéro 4 bytes du segment DATA. Pour se faire, j’ai utilisé la fonction read, présente dans la @PLT, afin d’envoyer 0x00000000.

Ensuite, j’exécute un malloc, d’avoir une adresse du heap, et connaître ainsi l’emplacement du flag.

L’adresse du flag est alors à : EAX – OFFSET. Avec quelques gadgets, je stocke cette valeur en mémoire. (dans la zone initialisé grâce à la fonction read)

Je m’envois alors cette valeur via la fonction write

J’ai choisi d’utiliser une technique de fake-stack-frame + stack-pivot afin d’avoir un code « dynamique » (je calcule comme ça les offsets du heap dans l’exploit, et je peux créer la fake-stack-frame en conséquence). Je reçois ma stack via read(), que je stock dans le segment DATA.
J’exécute ensuite une instruction du type pop ebp; leave; ret; afin de changer de stack-frame.

Envois du payload

Mise à zéro d’une variable en mémoire (premier read)

Lecture de l’adresse du HEAP envoyée

Création + envoi de la fake-stack-frame. Il s’agit s’implement d’un write() envoyant le flag.

Réception du flag et validation du challenge !!!

Un challenge simple, mais plutôt marrant.

Voici l’exploit entier :

Publié dans CTF | Marqué avec , , , | Laisser un commentaire

[Hack.lu CTF] FluxArchiv (Part 1) – 400

Petit, write up.

Un chall ELF 64bits, et allez… on se retrouve avec une genre de pkzip home made et une archive avec mot de passe, l’idée étant de trouver ce mot de passe. on peut tester le mot de passe avec « -l ». Ils sont sympas, le chall indiquait que le mot de passe était composé de chiffre de majuscules et faisait 6 lettres. (Et bien sur mon bruteforceur magique ne sais pas faire maj + chiffres sniff…)

 

Donc, ouverture dans IDA, affichage des strings et on cherche « Given password is not correct ». on tape X pour avoir la liste ou cette string est utilisée.

2013-10-24 23_21_19-IDA - C__Documents and Settings_john_Desktop_archiv.i64 (archiv)

On ouvre le 1er, et La on tombe sur un bout de code qui fait peur au premier abort par sa taille.

2013-10-24 23_22_34-IDA - C__Documents and Settings_john_Desktop_archiv.i64 (archiv)

On lache pas, on fait comme un saumon depuis un des print de notre message d’erreur on remonte jusqu’au début on tombe sur les tests, on retrouve le test de l’option « -l ».

2013-10-24 23_30_08-IDA - C__Documents and Settings_john_Desktop_archiv.i64 (archiv)

A partir de là on repart dans l’autre sens, il y a une On se rend compte aussi que toutes les fonctions passent par les mêmes fonctions. la premiere fonction que j’ai nommé shamypass fait un SHA1 de notre mot de passe. On se rend compte aussi que toutes les fonctions passent par ces mêmes fonctions.

2013-10-24 23_31_26-IDA - C__Documents and Settings_john_Desktop_archiv.i64 (archiv)

Après l’appel qui hash notre pass, le prog ouvre le fichier, passe par une fonction EncryptDecrypt qui vérifie que c’est bien une archive en vérifiant un entête « FluXArchiV13 » puis lis quelques bytes. Puis la juteuse fonction verifyarchiv est appelée, c’est elle qui conditionne le message d’erreur.

2013-10-24 23_44_51-IDA - C__Documents and Settings_john_Desktop_archiv.i64 (archiv)

La fonctions est une fonctions de shuffle qui va randomiser tous les bytes de notre sha1 de password..j’ai commencé à la reverse…

2013-10-24 23_52_28-IDA - C__Documents and Settings_john_Desktop_archiv.i64 (archiv)

et puis bon.. mal à la tête,  il était tard, feignasse, tout cela;  Donc GDB et breakpoint sur 0x402AD2, là on a dans RAX la position de la source et dans EDX pour se rassurer la valeur.

On arrive donc à faire le tableau de conversion de notre sha1 en sha1shufflé.

[ 0,0×7,0xe,0x1,0X8,0xf,0x2,0x9,0x10,0x3,0xa,0x11,0x4,0xb,0x12,0x5,0xc,0x13,0x6,0xd]

En clair, Donc :

shadest[0]=shasrc[0]
shadest[1]=shasrc[0x7]
shadest[2]=shasrc[0xe]
etc…

Derriere cela, en 0x402AEC il y a un re-sha1 de notre sha1 shufflé. Et enfin le test de notre sha1(shuffle(sha1(password)) avec le sha1 de l’archive. En breakant avec Gdb sur la comparaison j’ai obtenu le sha1 de l’archive que l’on recherche: 372942DF2712824505D8171F4F0bcb14153D39BA

2013-10-25 00_04_18-IDA - C__Documents and Settings_john_Desktop_archiv.i64 (archiv)

Au final la procédure pour générer lehash en python est celle là

 

Bitk s’est occupé de mettre le bruteforceur autour, le mot de passe était PWF41L.

Publié dans CTF | Marqué avec , , , | Laisser un commentaire

[CSAW-CTF] Crackme300 write-up (fr)

Ce chall était un crackme accessible à distance qui quand on lui donne le bon password, va lire et affiche un fichier contenant le flag.

Il était possible de trouver rapidement cette fonction en reverse statique, le programme ne cachant pas grand choses. Sous IDA il suffit d’afficher les strings, de double cliquer sur le message “Invalide registration code” puis X et on y est. La string inputée passe dans une fonction de hashing et si EDX est égal au chiffre attendu (0xEF2E3558h) on gagne.

Capture d’écran 2013-09-21 à 17.20.17

Capture d’écran 2013-09-21 à 17.19.20

 

 

 

 

Dès lors il est apparu qu’a part brute forcer le mot de passe on n’arriverai pas à grand choses car le mot de passe passe dans une moulinette du diable qui shift et add les octets.

Capture d’écran 2013-09-21 à 17.00.58

En clair on commence avec un magic number 0x539h (1337), on le multiplie par 32 et on additionne le caractère de notre password. On rajoute au résultat le résultat de l’opération du dernier tour. et on recommence pour le caractère suivant. Le tout étant potentiellement écrété naturellement par les registre 32bits.

Avec ce genre de moulinette tôt ou tard on tombe sur une collision, la version ‘Perl’ de cet algo n’ayant pas fait mouche avec une juteuse wordlist, et n’ayant pas les compétences mathématique pour le faire élégamment (Si tenté que l’on puisse);

Version ‘perl’ du dit algorithme (c) YodZeb.

1
2
3
4
5
6
7
8
    $start=0x539;
    foreach $c (split //) {
        if ($c!~/\n/) {
            $old=$start;
            $start=( ($old << 5) + $old + ord($c) ) ;
            $start &= 0xFFFFFFFF;
        }
    }

Allons y pour un brute forçage plus académique. Avec un peu d’optimisation je suis arrivé à 19 millions de hash/sec sur un seul CPU de mon petit AMD Phenom II et je monte a 28 millions avec un peu de nettoyage de code C pour ce write-up.

Pour fairce cela la première étape est de ré-encoder la fonction en ASM. Pour que cela dépote un peut plus. L’idée est de virer le plus possible les accès mémoire dans la boucle interne et de garder toute la mamaille dans des registres. Voici ma version du hash, plus ‘concise’ :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
;File hash.asm
section .text
GLOBAL mybrute
mybrute:
  ; Normalement il faut un appel
  ;       qui sauve ebp et esp
  ; Mais ici on touchera pas a la pile
  ; on s’en passe et on libere un registre
  ;[esp] = retour addr
  ;[esp+4]  = mystring
    mov   esi, dword [esp+4]  ; mystring
    xor   eax,eax    ; pour nettoyer la partie haute d’eax
    lodsb            ; On increment ESI, on met le 1er char dans AL
    mov   ebx, 0x539 ; Ebx sera le checksum, on y met le magicnumber
.myloop
    xor   ecx,ecx    ; xor 32 suivis de mov 8…
    mov   cl,al      ; equivalent a movzx mais en moins de cycles
    mov   edx,ebx    ; [currentsum] est ebx
    shl   edx,5      ; Multiplication
    add   ecx,edx    ; Addition avec char
    add   ebx,ecx    ; Addition avec checksum
    lodsb            ; pointer+1, next char dans al
    test  al,al      ; est t’on a la fin de la stringZ ?
    jnz   .myloop
  ; Ici EAX est a O, en C, 1 c’est true
    cmp    ebx, 0xEF2E3558 ; TEST pour la string mystère
    je    .good
    ret
.good:
    inc    eax
    ret

Voici le code C autour qui s’occupe de générer les candidats ;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/*
brute.c
(c) Thanat0s.trollprod.org / 2013
WTF licencing
// jusque 6 char printable 742.912.017.120  742 Milliards de combinaisons…
*/
// ************ LIB ***************
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
extern mybrute () asm (« mybrute »);
// ********* Variables *************
int g_bf_len, g_char_max,pass_len, g_char_min;
// ************ CODE ***************
// Hexadecimal print
void hexprint(unsigned char buf[255],int x) {
  int i;
  for (i = 0; i < x; i++) {
      if (i > 0) printf(« : »);
          printf(« %02X », buf[i]);
  }
}
// BruteForce loop from AAA to ZZZ
void brute_force(int max_len,int char_max,int min_char) {
  unsigned char tmp_buff[255];
  int last_char, count, idx = 0;
  // remplis le buffer de AAAA
  for (idx = 0; idx <= max_len; idx++) {
    tmp_buff[idx] = min_char;
  }
  tmp_buff[0]–; // Fixup 1er Char
  // Big Loop.. tant que le dernier char est <…
  while (tmp_buff[max_len] <=  min_char    ) {
    tmp_buff[0]++;  // inc char
  // Scan et inc/dec char autour
  for (idx = 0; idx < max_len; idx++ )  {
    if (tmp_buff[idx] > char_max ) {  // inc char suivant
      tmp_buff[idx] = min_char;
      tmp_buff[idx+1]++;
    }
  }
  last_char = tmp_buff[max_len]; // Create Stringz
  tmp_buff[max_len]=0;
  // TEST print si ok
  if (mybrute(tmp_buff)) {
    printf(« \nGot a Winner —-> »);
    hexprint(tmp_buff,idx);
    printf (« <->%s<—- WIN\n »,tmp_buff);
    exit(0);  // Break on WIN
  }
  tmp_buff[max_len] = last_char;
}
}
// Main programm
int main() {
  for (pass_len = 1;  pass_len <= 25   ; ++pass_len) { // Bruteforcons de 1 a 25 Char (mec patient)
    g_char_max = 126; // Maximum tilda
    g_char_min =32; // min char commence au espace
    brute_force(pass_len,g_char_max,g_char_min);
  }
  printf(« Welcome in 2019, i didn’t find it\n »);
  return 0;
}

Et le makefile qui va bien;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
all: brute
force: clean brute
clean:
        rm brute *.o
brute: brute.c hash.o
        gcc -c brute.c
        gcc hash.o brute.o -o brute
        chmod +x brute
hash.o: hash.asm
        yasm -f elf -m x86 hash.asm -o hash.o

Avec ça, et la combinaison juteuse de tous les caractères printables de 0x20h ‘espace’ à 0x7Eh ’tilda’. Le mot de passe est apparu en pleine nuit en moins de 4h. La version ‘propre’ présentée ici le fait en un peu plus de 2h.

1
2
3
4
5
6
7
$ time ./brute
Got a Winner —->62:33:20:36:3E:36<->b3 6>6<—- WIN
real 124m36.048s
user 124m1.713s
sys 0m0.000s

Flag…

1
2
3
Enter registration code: b3 6>6
Thank you, valued customer!
Your key is: day 145: they still do not realize this software sucks
Publié dans CTF | Marqué avec , , , , | Laisser un commentaire

[CSAW-CTF] Exploit300 write-up (fr)

Voici un petit write-up pour l’exploit300 du CSAW CTF 2013.

Vous pouvez trouver le binaire ici :
fil_chal

On a un programme qui nous demande un nom d’utilisateur et un mot de passe. Après un petit coup de strings ou de GDB, on voit facilement que ce sont des chaines de caractères codées en dur dans le programme :

Pour continuer l’exécution, il faut donc envoyer csaw2013 pour l’username et S1mplePWD pour password.

On remarque ensuite qu’on nous redemande une chaine de caractère, qui est ensuite passée à atoi(). Il s’agit de la taille passée en paramètre du prochain recv(). Seul la borne supérieur est checké, en envoyant une valeur négative, on peut aboutir à un stack based overflow.

La suite n’est pas compliquée étant donnée que .bss est +RWX :
On jump sur recv@PLT, en passant en paramètre l’adresse de .bss, puis on envoit notre shellcode. On a plus qu’à jumper sur .bss pour exécuter notre payload !

Le shellcode est un simple cat(« ./key ») utilisant des syscalls, et réutilisant la socket (fd=4).

Voici l’exploit :

Il n’y avait pas tellement de difficulté dans ce challenge pour ceux qui ont l’habitude des failles applicatives…

Publié dans CTF | Marqué avec , , , | Laisser un commentaire

[CSAW-CTF] Exploit400 write-up (fr)

Voici un petit write-up pour le challenge exploit400 du CSAW-CTF 2013.

Vous pouvez trouver le binaire ici :
miteegashun

On a donc un ELF 32 bits statique et strippé.

L’avantage du binaire statique, c’est qu’on va avoir tout un tas de gadgets pour faire du ROP.

On remarque assez vite qu’en envoyant une longue chaine de caractère, on controle facilement EIP :

Pour faciliter le débuguage avec GDB, j’ai utilisé un fifo :

On remarque quelque chose d’étrange : déjà, l’adresse de la stack est en 0x080fXXXX. On imagine donc que le binaire a sa propre stack située dans le segment DATA.

Ensuite, après avoir écrasé EIP, on retournera dans notre buffer lorsqu’on essayera de faire du ROP.

Essayons quelque chose :

Cette fois, on controle toute la chaine d’exécution !

Notre payload ressemblera à ça après calcul des différents offsets :

Payload overview

Payload overview

Il y a une petite difficultée supplémentaire : le file descriptor doit être fermée pour que le payload soit exécuté. On ne peut donc pas réutiliser la socket !
Vu que nous disposons d’une grosse quantité de gadgets, je me suis amusé à faire une ROP chain basé sur l’instruction int 0x80.
Elle exécute à la suite : socket()/connect()/dup2()/execv

Voilà l’exploit :

Par contre, le fichier contenant la clef s’affichait seulement avec ls -l (why ?!).
Un challenge plutôt sympa.

Publié dans CTF | Marqué avec , , , | Laisser un commentaire