Do “man” do Linux, lemos que “para o propósito de verificação de permissões, tradicionalmente a implementação do UNIX distinguiu duas categorias de processos: privilegiado ( da qual o usuário ID efetivo é 0, referenciado como superusuário ou root), e não-privilegiados (da qual o UID efetivo é diferente de zero). Processos privilegiados contornam todas as verificações do Kernel, enquanto processos não-privilegiados estão sujeitos a verificação total de permissão baseada na credencial do processo (usualmente: UID efetivo, GID efetivo e lista suplementar de grupos)”.
O “man” continua: “Iniciando com o Kernel 2.2, o Linux divide processos tradicionalmente associados com superusuário em unidades distintas, conhecidas como ‘capacidades’ (capabilities), que podem ser independentemente ativadas ou desativadas”.
Em outras palavras: antes, para um usuário rodar algum comando ou processo que necessitasse de alguma função reservada ao superusuário, era necessário:
- Dar “bit SUID” ao executável. Com isto, o usuário poderia rodar um programa com as permissões do dono do arquivo
- Ou dar permissão de “sudo” para o usuário. Assim o usuário pode rodar alguns comandos determinados como superusuário
O problema é que de acordo com o “princípio do menor privilégio”, que diz para apenas dar o privilégio necessário para realizar alguma ação, as duas formas acima expostas pecam, já que dão muito mais permissão que o necessário. Assim, o Linux, desde o Kernel 2.2, criou as “capacidades” que um executável pode ter de forma independente.
Capacidades possíveis
As capacidades que podem ser dadas para um executável no Linux são:
CAP_AUDIT_CONTROL – Ativar e desativar auditoria de Kernel; mudar regras de filtro de auditoria; ler startus e regras de filtro e de auditoria
CAP_AUDIT_READ – Permite ler um log de auditoria via multicast netlink socket
CAP_AUDIT_WRITE – Escrever registros no log de auditoria do Kernel
CAP_BLOCK_SUSPEND – Permite cancelamento de uma “suspensão” do sistema
CAP_CHOWN – Permite mudança de UIDs e GIDs em arquivos
CAP_DAC_OVERRIDE – Contorna verificações de permissão de leitura, escrita e execução
CAP_DAC_READ_SEARCH – Contorna verificação de leitura de um arquivo e diretórios e também verificação de execução
CAP_FOWNER – Contorna verificações que o Filesystem UID precisa bater com o UID do arquivo (por exemplo chmod e utime); permite definir flags inode em arquivos; permite definir “ACLs” em arquivos;
CAP_FSETID – permite definir um “set-group-ID” para qualquer arquivo que o GID não bata com o “filesystem”
CAP_IPC_LOCK – Permite dar um “lock” na memória (mlock, mlockall, mmap, etc)
CAP_IPC_OWNER – Contorna verificações de permissão para operações nos objetos System V IPC
CAP_KILL – Contorna verificações de permissões em enviar sinais. Por exemplo com kill e a operação KDSIGACCEPT no ioctl.
CAP_LEASE – Permite concessões em arquivos (fcntl)
CAP_LINUX_IMMUTABLE – permite definir FS_APPEND_FL e FS_IMMUTABLE_FL em flags inode (ioctl_iflags)
CAP_MAC_ADMIN – Permite configuração de MAC ou mudança de estados
CAP_MAC_OVERRIDE – Permite contornar Controles Mandatórios de Acesso (MAC)
CAP_MKNOD – Permite criar arquivos especiais com o “mknod”
CAP_NET_ADMIN – Permite configurar interfaces de rede; administrar firewall; alterar tabelas de roteamento; definir modo promíscuo e outras atividades relacionadas a administração de redes
CAP_NET_BIND_SERVICE – Permite dar um “bind” em portas privilegiadas (abaixo de 1024)
CAP_NET_RAW – Usar pacotes e sockets RAW
CAP_SETGID – Permite realizar manipulações de GID em processos e forjar o GID quando passar alguma credencial por UNIX domain socket
CAP_SETFCAP – Permite definir capacidades de um arquivo
CAP_SETPCAP – Permite adicionar ou remover capacidades de uma herança de chamado de um processo qualquer
CAP_SETUID – Permite manipulações de UIDs de processos
CAP_SYS_ADMIN – Permite uma série de operações restritas a administração de sistema (quotactl, mount, umount, sethostname, etc)
CAP_SYS_BOOT – Permite usar reboot e kexec_load
CAP_SYS_CHROOT – Permite usar chroot
CAP_SYS_MODULE – Permite carregar e descarregar módulos de Kernel
CAP_SYS_NICE – Permite redefinir a “importância” (ou prioridade) de processos (nice, setpriority)
CAP_SYS_PACCT – Permite usar acct
CAP_SYS_PTRACE – Permite realizar um “trace” em um processo usando ptrace e transferir dados de e para a memória de um processo usando process_vm_readv e process_vm_writev
CAP_SYS_RAWIO – Permite realizar operações de I/O reservadas
CAP_SYS_RESOURCE – Permite operações como usar espaços reservados em ext2, realizar ioctls em ext3, etc
CAP_SYS_TIME – Permite alterar o relógio do sistema
CAP_SYS_TTY_CONFIG Permite usar vhangup e outras operações em terminais virtuais
CAP_SYSLOG – Permite operações privilegiadas de syslog
CAP_WAKE_ALARM – Permite criar condições que “despertarão” o sistema
Como isso funciona na prática?
Como expliquei, com “capabilities” é possível dividir funções específicas que eram reservadas ao superusuário sem necessitar entregar permissão completa de superusuário. Imagine, por exemplo, que seja necessário que um processo qualquer consiga manipular o relógio do sistema, mas que não consiga manipular “UIDs” ou fazer chamadas “IOCTLs” privilegiadas? Então o administrador poderia dar apenas essa permissão com CAP_SYS_TIME e setcap desta forma:
setcap “cap_sys_time+ep” arquivo
Este comando define que o “arquivo” terá acesso para alterar o relógio (cap_sys_time) de forma “efetiva” e “permitida”, mas não poderá realizar nenhuma outra função privilegiada.
Mas espera. Efetiva e Permitida? Isso!
Capacidade Efetiva, Herdada e Permitida
Capacidades Efetivas (e – effective) são aquelas que o processo pode realmente realizar! Por exemplo, um processo não pode criar um “raw socket” se não tiver efetivamente uma capacidade de “cap_net_raw”.
Capacidades Permitidas (p – permitted) são limitadores para capacidades efetivas. O “man” do Linux diz que se uma capacidade não for “permitida”, ainda que a tenha efetivamente, “pode nunca readquiri-la”.
Capacidades Herdadas (i – inheritable) são capacidades preservadas através de um “execve”.
Além disso é possível usar flags “=”,”+” e “-“.
O operador “=” significa que todas a capacidade é resetada em todos os modos e então definido apenas na especificada. Por exemplo “cap_chown=e” tira a capacidade de mudar UIDs e GUIs de arquivos de forma efetiva, permitida e herdada e ativa novamente apenas de forma efetiva.
Os operadores “+” e “-“, diferente do operador “=”, apenas adicionam um retiram especificamente as capacidades especificadas sem alterar os outros modos que possam ter.
Capacidades podem “abaixar” um privilégio
Para demonstrar isso, localizei onde estava o “tar” e copiei para um usuário limitado, sem permissão de sudo.
Se eu tentar compactar um arquivo que eu não tenha permissão, por exemplo o “shadow”, eu não conseguirei porque meu tar não tem permissão nem capacidade para isso.
Porém se eu mudar para um usuário com permissão de rodar comandos como superusuário (sudo), alterar o “dono” deste tar para root e dar bit suid e permissão 755, meu usuário restrito conseguirá arquivar arquivos restritos e até rodar um shell privilegiado (escalação de privilégios por SUID File clássico).
E se eu retirar as “capacidades”, mesmo com o bit SUID, o tar não conseguirá arquivar! Para isso vou rodar o comando:
setcap “all-eip” ./tar
Veja que o meu “tar” continua pertencendo ao “root” e continua com o bit suid. Mas mesmo assim, não é mais possível abrir o “/etc/shadow” porque meu tar perdeu essa capacidade de forma “efetiva”, “permitida” e por “herança”.
Capacidades podem elevar privilégios (privilege escalation)
Assim como é possível limitar privilégios com “capabilities”, também é possível elevar privilégios de arquivos sem privilégios! Ainda no “tar”, vou alterar o dono para o usuário restrito porém darei capacidade efetiva e permitida para contornar qualquer verificação de leitura, escrita e execução (CAP_DAC_OVERRIDE).
Veja que o tar está limitado, porém ao dar capacidade de ler, editar e executar qualquer arquivo, é possível compactar arquivos restritos! O arquivo gerado “shadow.tar” terá a permissão do usuário atual. Então eu posso descompactar este arquivo, os arquivos filhos herdarão a permissão e, assim, eu tenho acesso ao arquivo “shadow” que eu não teria acesso normalmente!
Conseguindo shell privilegiado com “python” e “cap_setuid”
Neste momento copiei o interpretador Python para a pasta home do usuário restrito. As permissões estão definidas como meu usuário restrito. Ao tentar elevar meu “UID” (ID de usuário) para zero (ou seja, para root) e rodar um shell, eu tenho a permissão negada porque meu Python não tem esta capacidade.
Agora vou dar capacidade de setuid (CAP_SETUID) para meu interpretador Python e mesmo sem bit SUID, conseguirei um shell privilegiado:
Procurando arquivos com “capacidades” em todo o sistema
Agora que vimos que “capacidades” podem ser um importante meio de elevação de privilégios, precisamos descobrir como procurar todos os arquivos com “capacidades” em um sistema. Ainda bem que isso é bem fácil fazer. Basta digitar o comando:
/usr/sbin/getcap -r / 2>/dev/null
Esse comando irá procurar recursivamente em todo o sistema todos os arquivos que tenham alguma capacidade e mostrará. Os erros de permissão que eventualmente se tenha, serão enviados para “/dev/null” (na prática equivale dizer: não me mostre nenhum erro).
Observe capacidades: “cap_setuid” (mudar UID), “cap_dar_override” (ler, editar e executar qualquer coisa), “cap_chown” (mudar dono e grupos de arquivos) e outras que julgar interessante da lista acima.
Abraços e até a próxima
Pingback: Gestão de Acesso Privilegiado – Neotel Segurança Digital