Si recordáis una inyección de procesos incluía una serie de pasos típicos:
1/ controlar el proceso de destino:
- OpenProcess
2/ asignar memoria ejecutable en el proceso remoto
- VirtualAllocEx
- NtMapViewOfSection
- Code Caves
3/ copiar el shellcode en el proceso asignado
- WriteProcessMemory
- Copia directa después de NtMapViewOfSection
- Atom Bombing
- Ghost Writing
4/ ejecutar el shellcode como parte del proceso de destino
- CreateRemoteThread
- NtQueueApcThread
- Thread manipulation
- SuspendThread
- SetThreadContext
- ResumeTherad
- Stack Bombing ROP Chains
- Callback overwriting
Solo vimos los básicos en uno de nuestros posts, pero más adelante cubriremos otros de los listados… En cualquier caso, cada uno de esos métodos en conjunción pueden generar una alerta porque los EDRs están monitorizando si ocurre alguna sucesión de estos eventos, además de que muchos se apoyan también en EtwTI.
Hoy vamos a ver una técnica que puede inyectar y ejecutar código en un proceso remoto sin algunos de estos indicadores comunes. Se trata de Threadless Process Injection de Ceri Coburn (@_EthicalChaos_), co-mantenedor de Rubeus y autor de BOF.NET, SweetPotato, SharpBlock entre otros. Los pasos con esta técnica dentro de un layout típico de memoria de un proceso serían los siguientes:
- Asignar nuestro hook code
- Ejecutar el shellcode dentro de un memory hole de nuestra DLL exportada
- Parchear la función exportada para el CALL del hook
- Esperar la actividad del proceso legítimo para llamar a la API hookeada
Código del hook
La primera instrucción de exportación de la DLL hookeada llama (CALL) a nuestro código de hook
- La dirección de retorno se extrae de la pila y se resta 5 para darnos nuestra dirección de exportación original que se hookeó
- Guardamos los registros volátiles en la pila, ya que deberán restaurarse antes de llamar a la función original.
- Restauramos el código de la función de exportación original para que el hook ya no esté presente
- Se realiza una llamada de función relativa al shellcode inyectado (inmediatamente después del código del hook)
- Una vez que se ejecuta el shellcode inyectado, restauramos todos los registros volátiles
- Finalmente restauramos la dirección de exportación original y el jmp, manteniendo el comportamiento previsto del programa inyectado.
Y para las pruebas yo usé una implementación en Python (tenéis también la opción original en C#). Echad un vistazo al repo:
https://github.com/rkbennett/pyThreadlessInject
Ayuda
python .\threadlessinject.py -h
Ejecución básica (usa el shellcode calc.exe por defecto)
python .\threadlessinject.py -d ntdll.dll -e NtTerminateProcess -p 10184
Ejecución de un shellcode encodeado en base64
python .\threadlessinject.py -d ntdll.dll -e NtTerminateProcess -p 10184 -r U1ZXVVRYZoPk8FBqYFpoY2FsY1RZSCnUZUiLMkiLdhhIi3YQSK1IizBIi34wA1c8i1wXKIt0HyBIAf6LVB8kD7csF41SAq2BPAdXaW5Fde+LdB8cSAH+izSuSAH3mf/XSIPEaFxdX15bww==
Ejecución de un shellcode desde un archivo
python .\threadlessinject.py -d ntdll.dll -e NtTerminateProcess -p 10184 -f c:\Users\IEUser\Downloads\shellcode.bin
PoC (cerrando notepad.exe)
Fuentes:
Serie MalDev práctico: