Por que o bit setuid funciona inconsistentemente?

8

Eu escrevi o código:

// a.c
#include <stdlib.h>
int main () {
  system("/bin/sh");
  return 0;
}

compilado com o comando:

gcc a.c -o a.out

adicionado bit setuid:

sudo chown root.root a.out
sudo chmod 4755 a.out

No Ubuntu 14.04, quando executo como usuário geral, tenho privilégios de root.

mas no Ubuntu 16.04, eu ainda tenho o shell do usuário atual.

Porque é diferente?

user10883182
fonte

Respostas:

9

O que mudou é que ele /bin/shse tornou bashou permaneceu, dashrecebendo uma bandeira adicional -pimitando o comportamento do bash.

O Bash exige que o -psinalizador não elimine o privilégio setuid, conforme explicado em sua página de manual :

Se o shell for iniciado com o ID do usuário (grupo) efetivo diferente do ID do usuário (grupo) real e a opção -p não for fornecida, nenhum arquivo de inicialização será lido, as funções do shell não serão herdadas do ambiente, os SHELLOPTS As variáveis ​​BASHOPTS, CDPATH e GLOBIGNORE, se aparecerem no ambiente, são ignoradas e o ID do usuário efetivo é definido como o ID do usuário real . Se a opção -p for fornecida na chamada, o comportamento de inicialização será o mesmo, mas o ID do usuário efetivo não será redefinido.

Antes, dashnão se importava com isso e permitia a execução de setuid (sem fazer nada para evitá-lo). Mas a página dashde manual do Ubuntu 16.04 tem uma opção adicional descrita, semelhante a bash:

-p priv
Não tente redefinir o uid efetivo se ele não corresponder ao uid. Isso não está definido por padrão para ajudar a evitar o uso incorreto de programas raiz setuid via system (3) ou popen (3).

Esta opção não existia no upstream (que pode não ter sido reativo a um patch proposto * ) nem no Debian 9, mas está presente no Debian buster que obteve o patch desde 2018.

OBSERVAÇÃO: conforme explicado por Stéphane Chazelas, é tarde demais para invocar "/bin/sh -p", system()porque system()executa qualquer coisa fornecida /bin/she, portanto, o setuid já foi descartado. A resposta de derobert explica como lidar com isso, no código anterior system().

* mais detalhes sobre a história aqui e ali .

AB
fonte
system("bash -p")é executado sh -c "bash -p"para que os privilégios já tenham sido eliminados quando bashé executado.
Stéphane Chazelas
oh bem ok Vou remover a parte "solução" melhor tratada pela resposta de derobert de qualquer maneira.
AB
Veja também bugs.debian.org/cgi-bin/bugreport.cgi?bug=734869
Stéphane Chazelas
Parece que o Ubuntu consertou o traço no ardiloso (15.10). bugs.launchpad.net/ubuntu/+source/dash/+bug/1215660
Marque Plotnick
11
Note que o Debian costumava fazer o contrário. Ele costumava corrigir o bash para não eliminar privilégios. Se é uma melhoria é discutível. Agora, as pessoas que querem fazer um system()com privilégios elevados (o que, concedido, eles não deveriam em primeiro lugar) acabam fazendo um setresuid()antes, o que é muito pior, pois o shell não sabe que é sensível, então não ative o modo em que não confia no ambiente.
Stéphane Chazelas
8

Provavelmente, o shell está alterando seu ID do usuário efetivo de volta para o ID do usuário real como parte de sua inicialização por algum motivo ou outro. Você pode verificar isso adicionando:

/* needs _GNU_SOURCE; non-Linux users see setregid/setreuid instead */
uid_t euid = geteuid(), egid = getegid();
setresgid(egid, egid, egid);
setresuid(euid, euid, euid);

antes do seu system(). (Na verdade, mesmo no Linux, você provavelmente só precisa definir os reais; os salvos devem ser bons para deixar em paz. Isso é apenas uma força bruta para depurar. Dependendo do motivo pelo qual você está definido, você pode precisar para salvar os IDs reais em algum lugar também.)

[Além disso, se este não é apenas um exercício para aprender como o setid funciona, há muitos problemas de segurança com que se preocupar, especialmente ao chamar um shell. Existem muitas variáveis ​​de ambiente, por exemplo, que afetam o comportamento do shell. Prefira uma abordagem já existente, como sudose possível.]

derobert
fonte
Eu me concentrei no motivo da mudança, enquanto você fornecia como evitar o problema. +1
AB
Obviamente, se você realmente precisa de um local para salvar os IDs reais, o ID salvo é um local muito conveniente para isso. Especialmente se os IDs efetivos não forem root: root.
21719 Kevin