Como PROCESS_DUP_HANDLE se transforma em PROCESS_ALL_ACCESS

Há uma observação interessante no artigo Process Security and Access Rights do MSDN:


... se o processo A tiver um identificador para processar B com acesso PROCESS_DUP_HANDLE , ele poderá duplicar o pseudo identificador para o processo B. Isso cria um identificador que tem acesso máximo ao processo B.

Se você traduzir isso livremente para o russo, aqui diz que, tendo um descritor para um processo com o acesso PROCESS_DUP_HANDLE certo, podemos, usando a função DuplicateHandle (...) , obter um descritor com as máscaras de acesso máximas permitidas para esse processo.



Demonstração


O código-fonte que explora esse recurso é bastante simples:


#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 da compilação e vinculação, obtemos um utilitário de teste que usa o identificador de processo de destino (PID) como argumento. Em seguida, o utilitário abre o processo especificado com o PROCESS_DUP_HANDLE à direita. Assim, simulamos a condição necessária para a disponibilidade de um descritor para um processo com o PROCESS_DUP_HANDLE correto (== 0x40).


Como demonstração, rastreio o utilitário montado no 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 

E então movimento do pulso Ao chamar DuplicateHandle (...), obtemos o segundo descritor para o mesmo processo, mas com as permissões mais amplas:


 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 

O ponto principal é o valor GrantedAccess, que para o novo descritor é 0x1fffff, que corresponde a PROCESS_ALL_ACCESS . Infelizmente, o WinDbg não exibe o PID do processo de destino. Mas, para garantir que o descritor seja recebido pelo processo desejado, é possível examinar os descritores pelo Process Explorer (depois de especificar o PID especificado nos argumentos da linha de comando no depurador):


 0:000> dx argv[1] argv[1] : 0x1b7c2e2412c : "21652" [Type: wchar_t *] 


Na captura de tela, o utilitário abre os descritores para executar o notepad.exe.


Por que isso está acontecendo?


Primeiro, porque ao duplicar o descritor, se a máscara de acesso para o objeto não for expandida (e o sinalizador de operação DUPLICATE_SAME_ACCESS for especialmente especificado para nós), não há verificação de que o processo (no qual o descritor duplicado será criado) tenha acesso a esse objeto. Só é verificado se os descritores de processo passados ​​para a função DuplicateHandle (...) possuem a máscara de acesso permitida PROCESS_DUP_HANDLE . E a cópia do descritor entre os processos ocorre sem verificar os direitos de acesso (repito: se o novo descritor tiver a máscara de direitos permitidos não maior que o descritor duplicado original).


E, em seguida, deve-se observar que a chamada para GetCurrentProcess () retorna uma constante, o mesmo pseudo-descritor (pseudo-identificador) mencionado no início desta publicação. Existem dois pseudo-descritores documentados com valores constantes que estão fisicamente ausentes na tabela de descritores de processos. Mas esses descritores são processados ​​por todas as funções do kernel (junto com os descritores usuais da tabela de descritores de processos):


MacroValorDescrição do produto
ZwCurrentProcess / NtCurrentProcess(PUNHO) -1Descritor de Processo
ZwCurrentThread / NtCurrentThread(PUNHO) -2Descritor de Tópicos

É o valor de NtCurrentProcess (== -1) que retorna a chamada GetCurrentProcess () .


Esse pseudo-descritor na estrutura de um processo específico significa um objeto desse processo com os direitos PROCESS_ALL_ACCESS (na verdade, existem nuances, mas o artigo não é sobre elas). Acontece que esse vínculo consigo mesmo, mas através do descritor:


Ou seja, nossa chamada para DuplicateHandle (ProcessDuplicateHandle, GetCurrentProcess (), ...) será interpretada da seguinte maneira: no processo aberto (destino), duplique o identificador com o valor -1. E para o processo de destino (aquele no qual temos o descritor armazenado na variável ProcessDuplicateHandle), o valor -1 se referirá a esse mesmo processo de destino com os direitos PROCESS_ALL_ACCESS . Portanto, como resultado, obtemos um descritor para o processo de destino com direitos máximos.


Em vez de um epílogo


Repito o pensamento escrito no início: se alguém recebe um descritor para o processo com o PROCESS_DUP_HANDLE certo, então, no modelo de segurança do Windows, ele poderá obter outro descritor para o mesmo processo, mas com os direitos PROCESS_ALL_ACCESS (e fazer o que bem entender com o processo )


Obrigado a todos que leram a publicação até o fim. Convido todos a participarem da pesquisa para descobrir como essas publicações podem ser interessantes / úteis para o público.

Source: https://habr.com/ru/post/pt448472/


All Articles