Preciso ocultar alguns argumentos confidenciais para um programa que estou executando, mas não tenho acesso ao código-fonte. Também estou executando isso em um servidor compartilhado, portanto não posso usar algo como, hidepid
porque não tenho privilégios de sudo.
Aqui estão algumas coisas que eu tentei:
export SECRET=[my arguments]
, seguido de uma chamada para./program $SECRET
, mas isso não parece ajudar../program `cat secret.txt`
ondesecret.txt
contém meus argumentos, mas o todops
- poderoso é capaz de farejar meus segredos.
Existe alguma outra maneira de ocultar meus argumentos que não envolva intervenção do administrador?
ps
não está fazendo nada de mágico para "descobrir seus segredos". De qualquer forma, os programas razoavelmente escritos devem oferecer uma opção de linha de comando para ler um segredo de um arquivo especificado ou do stdin, em vez de levá-lo diretamente como argumento.Respostas:
Conforme explicado aqui , o Linux coloca os argumentos de um programa no espaço de dados do programa e mantém um ponteiro para o início dessa área. É isso que é usado por
ps
e assim por diante para encontrar e mostrar os argumentos do programa.Como os dados estão no espaço do programa, eles podem ser manipulados. Fazer isso sem alterar o próprio programa envolve carregar um calço com uma
main()
função que será chamada antes do principal principal do programa. Esse calço pode copiar os argumentos reais para um novo espaço e, em seguida, substituir os argumentos originais, de forma queps
eles apareçam nulos.O código C a seguir faz isso.
Não é possível intervir
main()
, mas você pode intervir na função padrão da biblioteca C__libc_start_main
, que passa a chamar main. Compile esse arquivoshim_main.c
conforme observado no comentário no início e execute-o como mostrado. Deixei umprintf
no código para verificar se ele está sendo chamado. Por exemplo, executeentão faça ae
ps
você verá um comando em branco e argumentos sendo mostrados.Ainda há uma pequena quantidade de tempo que o comando args pode estar visível. Para evitar isso, você pode, por exemplo, alterar o calço para ler seu segredo de um arquivo e adicioná-lo aos argumentos passados para o programa.
fonte
/proc/pid/cmdline
o segredo será exibido (o mesmo que quandocurl
tenta ocultar a senha fornecida na linha de comando). Enquanto estiver usando LD_PRELOAD, você pode agrupar main para que o segredo seja copiado do ambiente para o argumento que o main recebe. Como chamadaLD_PRELOAD=x SECRET=y cmd
onde você chamarmain()
comargv[]
sendo[argv[0], getenv("SECRET")]
/proc/pid/environ
. Isso pode ser sobrescrito da mesma maneira que os argumentos, mas deixa a mesma janela./proc/pid/cmdline
é público,/proc/pid/environ
não é. Havia alguns sistemas em queps
(um executável setuid) expunha o ambiente de qualquer processo, mas acho que você não se deparará hoje em dia. O ambiente é geralmente considerado suficientemente seguro . Não é seguro desviar de processos com o mesmo euid, mas eles geralmente podem ler a memória dos processos pelo mesmo euid de qualquer maneira, portanto, não há muito o que fazer sobre isso.main
método do programa empacotado também remova a variável de ambiente para evitar vazamentos acidentais nos processos filhos. Como alternativa, o wrapper pode ler todos os argumentos da linha de comando de um arquivo.Leia a documentação da interface da linha de comandos do aplicativo em questão. Pode muito bem haver uma opção para fornecer o segredo a partir de um arquivo, e não como um argumento diretamente.
Se isso falhar, envie um relatório de bug contra o aplicativo, alegando que não há uma maneira segura de fornecer um segredo a ele.
Você sempre pode adaptar (!) Cuidadosamente a solução na resposta do meuh às suas necessidades específicas. Preste atenção especial ao comentário de Stéphane e seus acompanhamentos.
fonte
Se você precisar passar argumentos para o programa para fazê-lo funcionar, você ficará sem sorte, não importa o que faça, se não puder usar
hidepid
no procfs.Como você mencionou que este é um script bash, você já deve ter o código fonte disponível, pois o bash não é um idioma compilado.
Caso contrário, você poderá reescrever o cmdline do processo usando
gdb
ou similar e brincar comargc
/argv
uma vez que ele já foi iniciado, mas:Eu realmente recomendo obter o código fonte ou conversar com o fornecedor para modificar o código. O fornecimento de segredos na linha de comandos em um sistema operacional POSIX é incompatível com a operação segura.
fonte
Quando um processo executa um comando (por meio da
execve()
chamada do sistema), sua memória é limpa. Para passar algumas informações pela execução, asexecve()
chamadas do sistema usam dois argumentos para isso: theargv[]
eenvp[]
matrizes.Essas são duas matrizes de strings:
argv[]
contém os argumentosenvp[]
contém as definições de variáveis de ambiente como cadeias de caracteres novar=value
formato (por convenção).Quando você faz:
(aqui foram adicionadas as aspas ausentes ao redor da expansão do parâmetro).
Você está executando
cmd
com o secret (value
) passado emargv[]
eenvp[]
.argv[]
será["cmd", "value"]
eenvp[]
algo assim[..., "PATH=/bin:...", "HOME=...", ..., "SECRET=value", "TERM=xterm", ...]
. Comocmd
não está fazendo nadagetenv("SECRET")
ou equivalente para recuperar o valor do segredo dessaSECRET
variável de ambiente, colocá-lo no ambiente não é útil.argv[]
é conhecimento público. Mostra na saída deps
.envp[]
hoje em dia não é. No Linux, mostra em/proc/pid/environ
. Ele é mostrado na saída deps ewww
BSDs (e com procps-ng'sps
no Linux), mas apenas para processos em execução com o mesmo uid efetivo (e com mais restrições para executáveis setuid / setgid). Pode aparecer em alguns logs de auditoria, mas esses logs de auditoria devem ser acessíveis apenas pelos administradores.Em resumo, o ambiente que é passado para um executável deve ser privado ou pelo menos tão privado quanto a memória interna de um processo (que, em algumas circunstâncias, outro processo com os privilégios corretos também pode acessar com um depurador, por exemplo, e pode também ser despejado no disco).
Como
argv[]
é de conhecimento público, um comando que espera que os dados sigam em sua linha de comando é quebrado por design.Geralmente, os comandos que precisam receber um segredo fornecem uma outra interface para isso, como por meio de uma variável de ambiente. Por exemplo:
Ou através de um descritor de arquivo dedicado como stdin:
(
echo
sendo incorporado, ele não aparece na saída deps
)Ou um arquivo, como o
.netrc
forftp
e alguns outros comandos ouAlguns aplicativos como
curl
(e essa também é a abordagem adotada por @meuh aqui ) tentam ocultar a senha que eles receberamargv[]
de olhares indiscretos (em alguns sistemas, substituindo a parte da memória em que asargv[]
strings estavam armazenadas). Mas isso realmente não está ajudando e oferece uma falsa promessa de segurança. Isso deixa uma janela entre aexecve()
e a substituição, ondeps
ainda mostrará o segredo.Por exemplo, se um invasor sabe que você está executando um script
curl -u user:somesecret https://...
(por exemplo em um trabalho cron), tudo o que ele precisa fazer é remover do cache as (muitas) bibliotecas quecurl
usam (por exemplo, executando ash -c 'a=a;while :; do a=$a$a;done'
), para para desacelerar sua inicialização e até mesmo fazer uma ineficiênciauntil grep 'curl.*[-]u' /proc/*/cmdline; do :; done
é suficiente para capturar essa senha nos meus testes.Se os argumentos forem a única maneira de transmitir o segredo aos comandos, ainda pode haver algumas coisas que você pode tentar.
Em alguns sistemas, incluindo versões mais antigas do Linux, apenas os primeiros bytes (4096 no Linux 4.1 e anterior) das cadeias
argv[]
podem ser consultados.Lá, você pode fazer:
E o segredo seria oculto porque passou dos primeiros 4096 bytes. Agora, as pessoas que usaram esse método devem se arrepender agora, já que o Linux, desde o 4.2, não trunca mais a lista de argumentos
/proc/pid/cmdline
. Observe também que não é porqueps
não mostrará mais do que muitos bytes de uma linha de comando (como no FreeBSD, onde parece estar limitado a 2048) que não se pode usar o mesmo uso da APIps
para obter mais. Entretanto, essa abordagem é válida em sistemas ondeps
é a única maneira de um usuário comum recuperar essas informações (como quando a API é privilegiada eps
é setgid ou setuid para usá-la), mas ainda não é potencialmente disponível para o futuro.Outra abordagem seria não passar o segredo,
argv[]
mas injetar código no programa (usandogdb
ou$LD_PRELOAD
hackear) antes demain()
iniciar, que insere o segredo noargv[]
recebidoexecve()
.Com
LD_PRELOAD
, para executáveis dinamicamente não-setuid / setgid vinculados em um sistema GNU:Então:
Em nenhum momento teria
ps
mostrado ops -opid,args
lá (-opid,args
sendo o segredo neste exemplo). Observe que estamos substituindo elementos daargv[]
matriz de ponteiros , não substituindo as seqüências apontadas por esses ponteiros, razão pela qual nossas modificações não aparecem na saída deps
.With
gdb
, ainda para executáveis dinamicamente não-setuid / setgid vinculados e em sistemas GNU:Ainda assim
gdb
, uma abordagem não-GNU específica, que não depende de executáveis vinculados dinamicamente ou que possuam símbolos de depuração, deve funcionar para qualquer ELF executável no Linux, pelo menos, poderia ser:Testando com um executável vinculado estaticamente:
Quando o executável pode ser estático, não temos uma maneira confiável de alocar memória para armazenar o segredo; portanto, precisamos obtê-lo de outro lugar que já esteja na memória do processo. É por isso que o ambiente é a escolha óbvia aqui. Também ocultamos esse
SECRET
env var ao processo (alterando-o paraSECRE=
) para evitar vazamentos se o processo decidir despejar seu ambiente por algum motivo ou executar aplicativos não confiáveis.Isso também funciona no Solaris 11 (desde que os binutils gdb e GNU estejam instalados (pode ser necessário renomear
objdump
paragobjdump
).No FreeBSD (pelo menos x86_64, eu não sei o que aqueles primeiros 24 bytes (que se tornam 16 quando gdb (8.0.1) é interativo sugerindo que pode haver um erro no gdb lá) na pilha são), substitua o
argc
eargv
definições com:(você também pode precisar instalar o
gdb
pacote / porta, pois a versão que vem com o sistema é antiga).fonte
O que você pode fazer é
então, supondo que você esteja escrevendo seu
./program
em C (ou alguém o faça e possa alterá-lo ou melhorá-lo), use getenv (3) nesse programa, talvez comoe depois que
export
você acabou de executar./program
no mesmo shell. Ou o nome da variável de ambiente pode ser passado para ele (executando./program --secret-var=SECRET
etc ...)ps
não revelará seu segredo, mas o proc (5) ainda pode fornecer muitas informações (pelo menos para outros processos do mesmo usuário).Consulte também isso para ajudar a projetar uma maneira melhor de transmitir argumentos do programa.
Veja esta resposta para uma melhor explicação sobre globbing e o papel de uma concha.
Talvez você
program
tenha outras maneiras de obter dados (ou usar a comunicação entre processos com mais sabedoria) do que argumentos simples do programa (certamente deveria, se se pretende processar informações confidenciais). Leia sua documentação. Ou talvez você esteja abusando desse programa (que não se destina a processar dados secretos).Ocultar dados secretos é realmente difícil. Não passar por argumentos do programa não é suficiente.
fonte
./program
, de modo que o primeiro semestre de esta resposta não parece ser relevante.