Selasa, 18 Juli 2017

Shellcode



Shellcode
Yang populer ransomware dan worm adalah malware berbahaya dan menyebar lewat jaringan. bagamana meraka bisa menyebar lewat jaringan? meraka mengunakan celah keamanan yang ada di komputer sasaranya, celah tersebut memungkinkan mengeksekusi code a.k.a bahasa komputer dengan Code eksekusi jarak jauh (Remote Code Execution). Untuk itu malware harus mengexplore kelemahan tersebut di komputer sasarannya jika celah tersebut ada maka kirim code kemudian code tereksekusi. Code Tersebut dinamakan ShellCode.
Shellcode di program dengan bahasa assmbly dan dalam perogramanya harus mengerti bagaimana kerja dan setruktur dari sistem operasi tertentu, Yang paling penting adalah shellcode tidak error saat di eksekusi. Setiap sistem operasi memiliki karakteristik masing-masing dan bagaimana shellcode bisa dijalankan pun berbeda, Seperti :
1.       Windows
Shellcode pada windows harus mencari BaseAddress dari library dasar (kernel32.dll, ntdll.dll, etc) atau library tertentu. Untuk itu harus menumukan address PEB (Process Envirnoment Block) dari sekmen FS pada sys 32bit dan GS pada sys 64bit, kemudian Mencari LDR dari PEB, Mencari BaseAddress Library di LDR Module.
2.       Linux
Int 0x80 adalah kunci dari shellcode linux, subfungsi dari int 0x80 atau syscall jika arch-x86_64 anda bisa cari di google. Subfungsi ini adalah fungsi dasar dari kernel linux.
Dari Penjelasan diatas masing-masing os punya struktur kerja yang berbeda, jadi shellcode yang di buat kusus untuk sistem operasi tertentu dan juga asitruktur tertentu, lihat diatas sys32bit dan sys64bit untuk mendapatkan aksess PEB, Sekmen yg di aksess berbeda. Tentunya dengan berbeda system membutuhkan jalan yang berbeda. 
Pada sistem operasi windows, shellcode pd dasarnya perlu beberapa tahapan yaitu :
1.       Cari BaseAddress dari library tertentu.
2.       Gunakan BaseAddress Untuk Mencari Procedure/Fungsi Pada library modul.
3.       Eksekusi Procedure/Fungsi.
Dengan ini shellcode beroprasi mengandalkan library dari windows. Namun NX/DEP mencegah shellcode mudah untuk di eksekusi, namun masih ada cara untuk bisa di lewati.
Untuk memahami lebih lanjut perlu mempraktekannya. Seperti yang di ketahui shellcode diprogram dengan bahasa assmbly untuk membuatnya perlu memperlajari assmbly dan beberapa dasar tentang pointer, panjang register, dan bagaimana applikasi berkerja, jika anda ingin membuat sebuah Shellcode hal-hal berikut menjadi sangat penting. Untuk memahami bagai mana shellcode berkerja tak begitu perlu memahami assmbly, namun bisa di jelaskan ke bahasa pemrograman yang lebih humanfriendly seperti C++. Dengan ini bisa lebih di pahami oleh programer bagai mana shellcode berkerja. Namun secara kusus sistem operasi windows yang akan di jabarkan pada artikel ini.
Yak, Dengan C++ bisa lebih mudah di pahami. Dengan tahap :
a.       Cari BaseAddress dari library tertentu.
1.    Untuk Mendapatkan Baseaddress pertama harus mencari PEB address terlebih dahulu. Dengan memangil Fungsi “unsigned long  __readfsdword(unsigned long offset);”, fungsi ini sama dengan “mov eax, [fs:{offset}]” pada bahasa assmbly yang bertujuan meng-copy data dari sekment FS.  Jadi sekarang tinggal mengetahui di mana PEB berada, lihat di parameter __readfsdword ada variable offset dan untuk offset PEB adalah 0x30/48. Setelah itu PEB didapat.
{gambar getpeb}
2.    Setelah Mendapatak PEB kemudian mencari LDR di PEB. LDR berada pada offset 0x0c/12 dari PEB, karena itu hanya pointer LDR maka harus pergi ke LDR di mana beradia. Kita pergi ke LDR dengan “void *LDR = *(void**)((unsigned char*)PEB+0x0c);”, tenang masih bisa dicabarkan lagi dengan cara ini :
void *LDR; unsigned int offset_;
offset = (unsigned int)PEB; // cast void* ke unsigned int
offset += 0x0c;                      // add offset_ dengan 0x0c atau 12
LDR = *(void**)(offset);      // pergi ke LDR
pemahaman type-casting dibutuhkan.
Sebenarnya kedua cara tersebut sama, namun dari cara2 berikut yg efisien adalah “(unsigned char*)PEB+0x0c” lebih sedikit menulisnya dan saat di-compile pun akan lebih sedikit intruksinya. Catatan : “untuk (unsigned char*)var jika di tambah 2 maka ditambah dengan 2 (unsigned char) dan (unsigned int*)var jika di tambah 2 maka ditambah dengan 2 (unsigned int).
3.    Setelah LDR Didapatkan selanjutnya mencari baseaddress dari library tertentu misalnya “kernel32.dll”, untuk menemukanya perlu mengetahui struktur dari LDR yaitu :
typedef struct _LDR_DATA {
unsigned long Length; // 0x00 atau 0
int initialized; // 0x04 atau 4
void *SsHandle; // 0x08 atau 8
LIST_ENTRY InLoadOrderModuleList; // 0x0c atau 12
LIST_ENTRY InMemoryOrderModuleList; // 0x14 atau 20
LIST_ENTRY InInitializationOrderModuleList; // 0x1c atau 28
} LDR_DATA, *pLDR_DATA; // 0x24 atau 36
Dan struktur LIST_ENTRY yaitu :
typedef struct _LIST_ENTRY {
struct _LIST_ENTRY *Flink;       // 0x00 atau 0
struct _LIST_ENTRY *Blink;       // 0x04 atau 4
} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY; // 0x08 atau 8
Struktur-Struktur berikut sangat penting untuk mengetahui di mana library berada. Pertama struktur _LIST_ENTRY memiliki struktur pointer *Flink dan *Blink masing-masing panjang data 32bit atau dword atau 4bytes, jadi ukuran atau size dari setruktur _LIST_ENTRY adalah 8bytes. Kemudian _LDR_DATA ukuran 36bytes terdiri dari u_int, int, dan void* yg masing-masing pajang data 4bytes dan 3 x LIST_ENTRY yang masing-masing 8bytes. Hal berikut penting untuk menentukan alamat pada variable pada struktur dengan menentukan panjang data di setiap variable.
Setelah mengerti pada bagian penting pada struktur maka berikutnya menentukan veriable mana saja yang bisa di pilih untuk mentukan baseaddress pada struktur _LDR_DATA adalah InLoadOrderModuleList, InMemoryOrderModuleList, dan InInitializationOrderModuleList. ketiga variable tersebut adalah struktur _LIST_ENTRY yang memiliki 2 variable pointer flink dan blink, lalu gunakan pointer flink untuk mencari _LDR_MODULE.
pLDR_MODULE _module = *(pLDR_MODULE*)((unsigned char*)LDR + 0x0c); 
Untuk pergi ke _LDR_MODULE.
Setruktur _LDR_MODULE :
typedef struct _ldr_module{
                LIST_ENTRY InLoadOrderModuleList_; // 0x00
                LIST_ENTRY InMemoryOrderModuleList_;
                LIST_ENTRY InIntializationOrderModuleList_;
                LPVOID ModuleBaseAddress; // <-- Disini Module BaseAddress
                LPVOID EntryPoint;
                LPVOID Reserved;
                union{
                                _size_hl _size_;
                                int _size_int;
                }u_size_fdllname;
                WCHAR *FullDllName;
                union{
                                _size_hl _size_;
                                int _size_int;
                }u_size_dllname;
                WCHAR *BaseDllName; // <-- Disini Module DllName atau nama library
}LDR_MODULE, *pLDR_MODULE;
Struktur _size_hl :
typedef struct size_{
                WORD Length;
                WORD MaxLength;
} _size_hl;

Untuk mencari module berikutnya.
pLDR_MODULE _module = *(pLDR_MODULE*)(_module);
_module adalah pointer LDR_MODULE yg menujukan nilai dari _module, maka yang akan digunakan variable paling pertama dari struktur untuk ke _LDR_MODULE Berikutnya atau flink Berikutnya.

b.      Dengan BaseAddress Untuk mencari fungsi dalam library modul.
1.    Taukah anda? Data yg di copy kememori akan diletakan seperti tumpukan dus dan setiap dus memiliki ukuran masing-masing dan tempatnya masing. saat mengeksekusi applikasi maka applikasi akan meload data kememory dan mencari library yg ada di memory atau moload library jika library belum di load ke memory. Setiap data yang di copy ke memory memiliki alamat. Baseaddress adalah alamat awal diri data tertentu ditempatkan di tumpukan tertentu dan memiliki ukuran data tertentu. Jadi tujuanya mencari baseaddress adalah mencari dimana alamat awal dari library dll tertentu yang di copy ke memory dan akan mencari fungsi-fungsi di library dll tersebut. Untuk Mencari fungsi2 di library maka harus mengerti seperti apa format PE (Portable Executable) pada applikasi windows, itu penting untuk mengetahui di mana EAT (Export Address Table) berada.
Portable Executable adalah format untuk ekstensi Executable (EXE) dan Dynamic-Link Library (DLL). Format Portable Executable memiliki struktur data. struktur tersebut bisa dijelaskan berdasarkan ilustrasi berikut.



EAT bisa ditemukan di Data Directory dari struktur IMAGE_OPTIONAL_HEADER. Data Directory adalah IMAGE_DATA_DIRECTORY array yg memiliki panjang data 16 IMAGE_DATA_DIRECTORY atau 8byte * 16.
typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
Pada IMAGE_DATA_DIRECTORY terdapat entry “VirtualAddress” yg menujukan pointer tertentu dan entry “Size” menujukan panjang data. Alamat EAT di index 0. Selain itu kita bisa mengetahui index-index lainya.
#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor


2.    Dari Penjelasan sebelumnya EAT bisa ditemukan di Data Directory array ke-0  atau IMAGE_DIRECTORY_ENTRY_EXPORT data index, DIRECTORY_EXPORT data memiliki Struktur header yaitu IMAGE_EXPORT_DIRECTORY.
typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;
    DWORD   Base;
    DWORD   NumberOfFunctions;
    DWORD   NumberOfNames;
    DWORD   AddressOfFunctions;     // RVA from base of image
    DWORD   AddressOfNames;         // RVA from base of image
    DWORD   AddressOfNameOrdinals;  // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
Entry AddressOfFunctions, AddressOfNames, dan AddressOfNameOrdinals adalah tabel address, dan Entry NumberOfFunctions -> banyaknya fungsi dan NumberOfNames -> Banyaknya nama fungsi. Di tabel address berikut Nama fungsi, Nama Ordinal, dan fungsi bisa ditemukan.

3.    Untuk Mencari Fungsi tertentu dari tabel address harus mengetahui Nama Fungsi terlebih dahulu, kemudian tentukan index ke-n dari array Nama Fungsi, setelah itu temukan Nama Ordinal fungsi dari index ke-n, Nilai Nama Ordinal fungsi digunakan untuk menentukan address fungsi dan Nilai Nama Ordinal adalah index ke-n dari address fungsi array.
Penjelasan :


Ingat address fungsi pada tabel adalah RVA (Relative Virtual Address), jika data di aksess dari memory, BaseAddress menjadi penentu VA (Virtual Address) pada memory.

c.       Mengeksekusi procedur / fungsi
Untuk Bisa mengekesuki prosedur / fungsi di pemrograman C++ harus mendefinisikan fungsi berdasarkan parameter fungsi tertentu. Misal fungsi “Beep” yang memiliki 2 parameter yaitu (DWORD dwFreq, DWORD dwDuration), mendifinisikanya dengan Syntax typedef, Untuk referensi tentang typedef anda bisa cari di MSDN atau situs lainnya.
Untuk Fungsi “Beep” bentuk definisinya :
typedef BOOL (FAR __stdcall *Beep_) (DWORD dwFreq, DWORD dwDuration);
Pada fungsi lain anda harus memdifinisikanya sesuai dengan paramater fungsinya.
Untuk bisa memanggil fungsi Beep harus temukan VA dari fungsi Beep, gunakan typedef Beep_ untuk memanggil fungsi.
Syntax Penggunaan typedef Beep_ :
Beep_ func;
func = (Beep_)va_fungsi;
func(440, 1000);
Atau
((Beep_)va_fungsi)(440, 1000);

Berikut adalah Penjelasan bagaimana shellcode pada sistem operasi windows untuk mencari fungsi dan memanfaatkan fungsi tersebut untuk beroprasi di komputer sasaranya. Walau terekeskusi di komputer sasarn Shellcode akan terhapus saat reboot atau pun shutdown, itu karena shellcode hanya tersimpan pada memory sementara. 

Shellcode dengan ukuran data sedikit dan tidak se-compleks applikasi, mampu memanipulasi komputer korban dengan berkerja secara sembunyi. seperti RNA virus yang di injek kedalam sel yang bisa menggangu metabolisme sel dan merubah susunan DNA. Walau pada kenyataanya kebanyakan RNA virus di tujukan untuk menggandakan diri.

Soruce Code (findingapi.zip)


Tidak ada komentar:

My Blog List