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:
Posting Komentar