Set-UID, ambiente e descritores

Seguir a autoridade de um processo, a interpretação pela shell e os recursos herdados.

TLDR

Perguntar sobre esta página

ChatGPTClaudePerplexityGrokGeminiCopiar e abrirDeepSeekCopiar e abrir

Um programa de consulta pode abrir um ficheiro com autoridade que o utilizador não tem. A segurança depende de como escolhe o programa externo, interpreta os argumentos e entrega os recursos que já abriu. A mudança de UID é apenas uma parte desse percurso.

Ambiente em fork e execve

As variáveis de ambiente são pares de nomes e valores disponíveis ao processo. Por exemplo, PATH influencia a procura de executáveis em chamadas que fazem essa procura. Não é uma lista de permissões: escolher onde procurar e poder executar são decisões diferentes.

Considera um processo com PATH=/usr/bin:/bin e uma variável própria MODO=consulta:

OperaçãoAmbiente resultante
fork()O filho recebe uma cópia do ambiente do pai.
O filho muda MODOA sua cópia muda; não altera automaticamente o ambiente do pai.
execve(caminho, argv, envp)O novo programa recebe os pares indicados por envp.

execve substitui o programa no mesmo processo. Não cria um filho e não escolhe automaticamente o ambiente do pai. Passar environ transmite o ambiente corrente; passar uma lista terminada em ponteiro nulo permite selecionar os pares. A variante com envp=NULL usada pelo guião é aceite em Linux, mas não é uma interface portátil para um ambiente vazio.

UID real e UID efetivo

O UID real identifica normalmente o utilizador que iniciou o processo. O UID efetivo participa nas decisões de acesso. Ao executar um binário Set-UID, o sistema pode mudar o UID efetivo para o dono do executável, conservando o UID real. Por exemplo, um binário de root iniciado por um utilizador com UID 1000 pode ter UID real 1000 e efetivo 0.

Isto depende das condições de execução. Montagens nosuid, no_new_privs e outras restrições podem impedir o efeito Set-UID. Também não se deve extrapolar de um binário para um script interpretado: Linux ignora Set-UID no script.

O ambiente continua a exigir análise. O carregador dinâmico filtra variáveis como LD_PRELOAD em execução segura; isso não significa que todas as variáveis externas sejam removidas. O programa privilegiado precisa de controlar os valores que usa para decidir operações.

UID guardado e redução de privilégios

O UID guardado, ou saved set-user-ID, permite certas mudanças posteriores do UID efetivo. Em Linux com glibc atual, seteuid(u) muda apenas o efetivo, preservando o real e o guardado. Já setuid(u), quando o processo tem a autoridade CAP_SETUID, muda os três. Sem essa autoridade, setuid não tem o mesmo efeito geral: as mudanças permitidas dependem dos UIDs atuais.

Segue duas alternativas independentes, ambas a partir de um binário Set-UID de root iniciado pelo utilizador 1000. As chamadas indicadas têm sucesso, e não há outra autoridade que permita mudar UIDs:

EstadoRealEfetivoGuardado
Inicial100000
Após seteuid(1000)100010000
Após setuid(1000) a partir do inicial100010001000

Na primeira alternativa, seteuid(0) pode recuperar o efetivo 0 porque este continua guardado. Na segunda, essa chamada já não é permitida apenas pelos UIDs: o 0 deixou de ser real, efetivo ou guardado. Uma redução temporária não cumpre um pedido de abandono permanente de privilégios.

Verifica sempre o resultado das chamadas, mesmo a partir de root. Analisa separadamente grupos, Linux capabilities e recursos já abertos. Mudar os três UIDs não fecha descritores, como veremos a seguir.

PATH e interpretação pela shell

system("ls") pede a uma shell que interprete ls. A procura pode usar PATH: se uma pasta controlada pelo requerente vier primeiro, o nome pode selecionar outro executável. Fixar o formato do comando C não fixa o programa finalmente escolhido.

Agora o serviço usa um caminho absoluto, mas concatena um nome de ficheiro:

/* Esquema de uma chamada, não um programa completo. */
system("/bin/cat relatorio; printf MARCA");

A shell interpreta o ponto e vírgula como separador. Executa a consulta e depois outra operação. A segunda operação deste exemplo só imprime uma marca; demonstra por que tratar o argumento como parte de um comando aumenta a autoridade do requerente.

Com uma chamada direta a execve, o caminho do executável e os elementos de argv ficam separados. Um argumento relatorio; printf MARCA passa como um único nome, sem a shell o transformar em duas operações. Ainda é necessário autorizar o ficheiro, controlar opções aceites pelo executável e escolher um ambiente adequado. Retirar a shell não resolve qualquer uso perigoso de argumentos.

O efeito privilegiado de system depende também da shell. No ambiente descrito pelo guião, dash baixa privilégios quando deteta a diferença de UID; o laboratório compara com outra configuração. Não se conclui que toda chamada system num processo Set-UID executa a operação final como root.

Descritores depois de retirar privilégios

O serviço abre relatorio para escrita enquanto tem autoridade. O sistema concede um descritor, aqui 3. O serviço baixa depois os seus UIDs e executa outro programa. Se o descritor não foi fechado nem marcado close-on-exec, o novo programa pode herdá-lo.

A nova abertura verifica as permissões atuais do processo. Uma escrita pelo descritor já aberto usa a referência obtida antes; baixar o UID não repete automaticamente a decisão da abertura. No guião, este caso chama-se capability leaking. O recurso concreto é o descritor aberto, não necessariamente uma Linux capability.

Se o programa seguinte não deve receber essa autoridade, fecha o descritor antes da transferência e define close-on-exec para impedir herança por execve. Close-on-exec não fecha o descritor por baixar o UID nem impede o programa atual de o usar. Quando possível, retira privilégios antes de abrir o recurso, verifica erros e mantém apenas a autoridade necessária.

Evidência no LOGBOOK

O Moodle da semana 4 pede as Tasks 1–6, a comparação com system da Task 8 e a fuga de recurso da Task 9. Usa o ambiente isolado indicado pelo docente. Os exemplos do navegador de programação defensiva não reproduzem UID, processos ou permissões POSIX.

Para cada observação, regista o estado anterior, a operação, o resultado e a condição que o explica. Em PATH, identifica o executável escolhido e os UIDs relevantes. Em injeção de comandos, distingue o argumento recebido da operação interpretada pela shell. Na fuga de descritor, identifica quando foi aberto e por que permanece utilizável. Uma captura sem esta ligação não explica o mecanismo.

Segue o guião para a preparação e restauração do ambiente. A comparação que altera a shell do laboratório exige repor a configuração original no fim. Não transfiras essas alterações para o sistema de trabalho.

Exercícios

Executável fixo, argumento interpretado

Um serviço usa system("cat relatorio"). O requerente controla a primeira pasta de PATH. A equipa muda para system("/bin/cat " + nome), onde nome é texto externo. Explica que risco a alteração retira e qual permanece. Compara o argumento relatorio; printf MARCA nas duas chamadas seguintes: o comando concatenado para system e uma chamada direta a /bin/cat por execve com esse texto num único elemento de argv. Não assumes que a shell conserva privilégios.

Pista

Quem escolhe o executável e quem interpreta os caracteres do argumento?

Solução

O caminho absoluto retira a procura de cat por PATH; a shell continua a interpretar o comando. Em system, o ponto e vírgula separa a consulta e printf MARCA. Em execve, o texto passa como um único argumento: sem shell, o ponto e vírgula não cria outra operação. Ainda são necessários autorização do objeto, controlo das opções e ambiente adequado. O UID da operação final depende também da shell e das condições de execução; não basta saber que o serviço é Set-UID.

Confere a tua resposta:

  • Distingui procura por PATH de interpretação pela shell.
  • Expliquei o efeito do ponto e vírgula em system e em argv.
  • Não tratei execve como autorização de qualquer ficheiro.
Voltar à explicação
Duas formas de chegar ao mesmo ficheiro

Um serviço com UID efetivo 0 abre um ficheiro 0644 de root para escrita e recebe o descritor 3. Baixa todos os seus UIDs para 1000, com sucesso. Uma nova abertura para escrita é negada, mas o descritor continua válido. Assume permissões tradicionais, sem outras políticas ou restrições. Explica por que a escrita pelo descritor 3 ainda pode ocorrer. Se o serviço marcar o descritor close-on-exec, fica fechado ao baixar o UID? E após um execve bem-sucedido?

Pista

Identifica o momento em que foi concedida a referência de escrita.

Solução

A abertura verificou autoridade quando o UID efetivo era 0. Baixar os UIDs não fecha o descritor nem reaplica a decisão de abertura a cada escrita. A nova abertura usa a autoridade atual e é negada, enquanto o descritor 3 conserva a referência de escrita. Close-on-exec fecha-o num execve bem-sucedido, não na mudança de UID. O próprio serviço ainda o pode usar antes de execve; se já não precisa dele, deve fechá-lo.

Confere a tua resposta:

  • Identifiquei a abertura privilegiada como origem do descritor.
  • Distingui nova abertura de escrita pela referência existente.
  • Expliquei quando close-on-exec fecha e quando não fecha.

Erros frequentes

O recurso deste caso é um descritor. Retirar Linux capabilities não o fecha automaticamente.

Voltar à explicação
Cópia do ambiente e substituição do programa

O pai tem MODO=consulta e cria um filho com fork. O filho muda a sua variável para MODO=editar e faz execve com uma lista envp que contém apenas MODO=restrito, terminada por ponteiro nulo. As operações têm sucesso. Que valor fica no pai? Que valor recebe o novo programa? A chamada a execve criou mais um processo?

Solução

O pai mantém MODO=consulta: o filho alterou a sua cópia. O novo programa recebe MODO=restrito, escolhido em envp, e não o valor anterior do filho. fork criou o filho; execve substituiu o seu programa no mesmo processo. Distinguir estes momentos permite controlar o ambiente transmitido a uma operação sensível.

Confere a tua resposta:

  • Mantive o ambiente do pai independente da alteração do filho.
  • Usei envp como ambiente do novo programa.
  • Distingui criar um filho de substituir o programa no mesmo processo.
Voltar à explicação
Redução temporária ou permanente

Em Linux com glibc atual, duas alternativas partem de UIDs real 1000, efetivo 0 e guardado 0. A faz seteuid(1000); B, com CAP_SETUID, faz setuid(1000). Estas chamadas têm sucesso. Depois não há Linux capabilities nem outra autoridade que permita recuperar root. Indica os três UIDs resultantes e decide se seteuid(0) pode ter sucesso em cada alternativa. Um descritor aberto para escrita antes da redução fica automaticamente fechado?

Pista

A regra de seteuid permite ao processo sem privilégio selecionar o UID real, efetivo ou guardado.

Solução

A fica com real 1000, efetivo 1000 e guardado 0. Pode voltar a efetivo 0 por seteuid(0), usando o guardado. B fica com os três a 1000: nas hipóteses dadas, 0 já não é uma escolha autorizada e a recuperação falha. Nenhuma das mudanças fecha automaticamente o descritor. A capacidade de mudar UIDs e a referência a um ficheiro já aberto são autoridades distintas; para retirar a segunda, fecha o recurso ou controla a sua transferência.

Confere a tua resposta:

  • Segui os três UIDs em cada alternativa independente.
  • Justifiquei a recuperação apenas na alternativa com UID 0 guardado.
  • Separei perda de autoridade de mudança de UID e fecho de descritores.
Voltar à explicação

À tua maneira

Aparência

Modo de cor
Tema
Cor de destaque

Leitura

Fonte de leitura

Álgebra, lógica e uma ideia de cada vez.

Código

Fonte do código

Navegação

Cursor de leitura, sem edição. h, j, k, l movem; / pesquisa nesta página. v seleciona caracteres; V seleciona linhas; y copia. Tab permite destacar ou comentar. Espaço ou Enter abre blocos recolhíveis. Nos cartões, h, j, k, l mudam o foco.

CSS personalizado

Ativa uma sugestão ou guarda o teu CSS. Só muda este navegador.

Usa --page, --text e --accent para acompanhar o tema.

Seletores e recuperação

.site-header, .prose, .course-card, [data-reading-history], [data-playground] e .page-actions.

Abre a aparência com a tecla vírgula ou no rodapé. Se o CSS esconder os controlos, abre esta página sem CSS personalizado.

Pesquisar

Escreve para pesquisar em todo o site.

para escolher · Enter para abrir · Esc para fechar

Copiar fórmula

A cópia automática não está disponível. Copia o LaTeX abaixo.

Pesquisar nesta página

Enter procura · n / N repetem · Esc cancela. Ctrl / Cmd K pesquisa todo o site.

Atalhos de teclado

Clica numa tecla para a mudar. Esc cancela. Backspace desativa.

PesquisarCtrl / Cmd K

Os atalhos não interferem enquanto escreves. Tab e Enter funcionam sempre.