Hay un comentario interesante en el artículo de Seguridad de proceso y Derechos de acceso de MSDN:
... si el proceso A tiene un controlador para procesar B con acceso PROCESS_DUP_HANDLE , puede duplicar el pseudo controlador para el proceso B. Esto crea un controlador que tiene acceso máximo al proceso B.
Si traduce esto libremente al ruso, aquí dice que tener un descriptor para un proceso con el derecho de acceso PROCESS_DUP_HANDLE , podemos, utilizando la función DuplicateHandle (...) , obtener un descriptor con el máximo de máscaras de acceso permitidas para este proceso.

Demostración
El código fuente que explota esta característica es bastante simple:
#include <Windows.h> int wmain(int argc, PWSTR argv[]) { HANDLE ProcessAllAccessHandle; HANDLE ProcessDuplicateHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, _wtoi(argv[1])); if (ProcessDuplicateHandle) { if (DuplicateHandle(ProcessDuplicateHandle, GetCurrentProcess(), GetCurrentProcess(), &ProcessAllAccessHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) { CloseHandle(ProcessAllAccessHandle); } CloseHandle(ProcessDuplicateHandle); } return 0; }
Como resultado de la compilación y la vinculación, obtenemos una utilidad de prueba que toma el identificador de proceso de destino (PID) como argumento. Luego, la utilidad abre el proceso especificado con el PROCESS_DUP_HANDLE a la derecha. Por lo tanto, simulamos la condición necesaria para la disponibilidad de un descriptor para un proceso con el PROCESS_DUP_HANDLE correcto (== 0x40).
Como demostración, rastrearé la utilidad ensamblada en WinDbg:
0:000> lsa @$ip 0,3 > 13: if (ProcessDuplicateHandle) 14: { 15: if (DuplicateHandle(ProcessDuplicateHandle, 0:000> !handle @@C++(ProcessDuplicateHandle) 3 Handle 80 Type Process Attributes 0 GrantedAccess 0x40: None DupHandle HandleCount 9 PointerCount 260518
Y luego movimiento de la muñeca Al llamar a DuplicateHandle (...) obtenemos el segundo descriptor para el mismo proceso, pero con los permisos más amplios:
0:000> lsa @$ip 0,3 > 23: CloseHandle(ProcessAllAccessHandle); 24: } 25: CloseHandle(ProcessDuplicateHandle); 0:000> !handle @@C++(ProcessAllAccessHandle) 3 Handle 84 Type Process Attributes 0 GrantedAccess 0x1fffff: Delete,ReadControl,WriteDac,WriteOwner,Synch Terminate,CreateThread,,VMOp,VMRead,VMWrite,DupHandle,CreateProcess,SetQuota,SetInfo,QueryInfo,SetPort HandleCount 10 PointerCount 292877
El punto clave es el valor GrantedAccess, que para el nuevo descriptor es 0x1fffff, que corresponde a PROCESS_ALL_ACCESS . Desafortunadamente, WinDbg no muestra el PID del proceso de destino. Pero para asegurarse de que se reciba el descriptor para el proceso deseado, puede ver los descriptores mediante Process Explorer (después de especificar el PID especificado en los argumentos de la línea de comandos en el depurador):
0:000> dx argv[1] argv[1] : 0x1b7c2e2412c : "21652" [Type: wchar_t *]

En la captura de pantalla, la utilidad abre los descriptores para ejecutar notepad.exe.
¿Por qué está pasando esto?
En primer lugar, porque al duplicar el descriptor, si la máscara de acceso para el objeto no se expande (y el indicador de operación DUPLICATE_SAME_ACCESS está especialmente especificado para nosotros), no hay verificación de que el proceso (en el que se creará el descriptor duplicado) tenga acceso a este objeto. Solo se verifica que los descriptores de proceso pasados a la función DuplicateHandle (...) tengan la máscara de acceso permitida PROCESS_DUP_HANDLE . Y luego se copia el descriptor entre los procesos sin verificar los derechos de acceso (repito: si el nuevo descriptor tiene la máscara de derechos permitidos no más amplia que el descriptor duplicado original).
Y luego debe tenerse en cuenta que la llamada a GetCurrentProcess () devuelve una constante, ese mismo pseudodescriptor (pseudocontrolador) mencionado al comienzo de esta publicación. Hay dos pseudodescriptores documentados con valores constantes que faltan físicamente en la tabla de descriptores de proceso. Pero estos descriptores son procesados por todas las funciones del núcleo (junto con los descriptores habituales de la tabla de descriptores de proceso):
Es el valor de NtCurrentProcess (== -1) que devuelve la llamada GetCurrentProcess () .
Este pseudodescriptor en el marco de un proceso específico significa un objeto de este proceso con los derechos PROCESS_ALL_ACCESS (de hecho, hay matices, pero el artículo no trata sobre ellos). Resulta un enlace a sí mismo, pero a través del descriptor:

Es decir, nuestra llamada a DuplicateHandle (ProcessDuplicateHandle, GetCurrentProcess (), ...) se interpretará de la siguiente manera: desde el proceso abierto (destino), duplique el identificador con el valor -1. Y para el proceso de destino (aquel para el que tenemos el descriptor almacenado en la variable ProcessDuplicateHandle), el valor -1 se referirá a este mismo proceso de destino con los derechos PROCESS_ALL_ACCESS . Por lo tanto, como resultado, obtenemos un descriptor para el proceso de destino con los máximos derechos.
En lugar de un epílogo
Repito el pensamiento escrito al principio: si alguien recibe un descriptor para el proceso con el derecho PROCESS_DUP_HANDLE , entonces, bajo el modelo de seguridad de Windows, podrá obtener otro descriptor para el mismo proceso, pero con los derechos PROCESS_ALL_ACCESS (y hacer lo que quiera con el proceso )
Gracias a todos los que leyeron la publicación hasta el final. Invito a todos a realizar la encuesta para descubrir cómo tales publicaciones pueden ser interesantes / útiles para la audiencia.