Como o curl protege uma senha de aparecer na saída ps?

68

Percebi há algum tempo que nomes de usuário e senhas dados curlcomo argumentos de linha de comando não aparecem na pssaída (embora, é claro, eles possam aparecer no seu histórico do bash).

Da mesma forma, eles não aparecem /proc/PID/cmdline.

(Porém, a duração do argumento combinado de nome de usuário / senha.)

Demonstração abaixo:

[root@localhost ~]# nc -l 80 &
[1] 3342
[root@localhost ~]# curl -u iamsam:samiam localhost &
[2] 3343
[root@localhost ~]# GET / HTTP/1.1
Authorization: Basic aWFtc2FtOnNhbWlhbQ==
User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.15.3 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
Host: localhost
Accept: */*



[1]+  Stopped                 nc -l 80
[root@localhost ~]# jobs
[1]+  Stopped                 nc -l 80
[2]-  Running                 curl -u iamsam:samiam localhost &
[root@localhost ~]# ps -ef | grep curl
root      3343  3258  0 22:37 pts/1    00:00:00 curl -u               localhost
root      3347  3258  0 22:38 pts/1    00:00:00 grep curl
[root@localhost ~]# od -xa /proc/3343/cmdline 
0000000    7563    6c72    2d00    0075    2020    2020    2020    2020
          c   u   r   l nul   -   u nul  sp  sp  sp  sp  sp  sp  sp  sp
0000020    2020    2020    0020    6f6c    6163    686c    736f    0074
         sp  sp  sp  sp  sp nul   l   o   c   a   l   h   o   s   t nul
0000040
[root@localhost ~]# 

Como esse efeito é alcançado? Está em algum lugar no código fonte de curl? (Presumo que seja um curlrecurso, não um psrecurso? Ou é um recurso do kernel de algum tipo?)


Além disso: isso pode ser alcançado fora do código-fonte de um executável binário? Por exemplo, usando comandos de shell, provavelmente combinados com permissões de root?

Em outras palavras, eu poderia, de alguma forma, mascarar um argumento que apareça na saída /procou na pssaída (mesma coisa, acho) que passei para algum comando arbitrário do shell? (Eu acho que a resposta para isso é "não", mas parece valer a pena incluir essa meia pergunta extra.)

Curinga
fonte
16
Não é uma resposta, mas observe que essa abordagem não é segura . Há uma janela de corrida entre o início do programa e a limpeza das sequências de argumentos durante as quais qualquer usuário pode ler a senha. Não aceite senhas confidenciais na linha de comandos.
R ..
11
Vagamente relacionado: a quem pertencem as variáveis ​​de ambiente? e Alguém na prática usa environdiretamente para acessar variáveis ​​de ambiente? - a linha inferior: a lista de argumentos, como a lista de variáveis ​​de ambiente, está na memória de processo de leitura / gravação do usuário e pode ser modificada pelo processo do usuário.
Scott
11
@ JPhi1618, basta transformar o primeiro caractere do seu greppadrão em uma classe de caracteres. Por exemplops -ef | grep '[c]url'
Wildcard
11
@py, não é muito complicado. Algumas expressões regulares combinam entre si e outras não. curlcorresponde, curlmas [c]urlnão corresponde [c]url. Se você precisar de mais detalhes, faça uma nova pergunta e ficarei feliz em responder.
Wildcard

Respostas:

78

Quando o kernel executa um processo, copia os argumentos da linha de comando para ler / gravar a memória pertencente ao processo (na pilha, pelo menos no Linux). O processo pode gravar nessa memória como qualquer outra memória. Quando psexibe o argumento, ele lê de volta o que estiver armazenado naquele endereço específico na memória do processo. A maioria dos programas mantém os argumentos originais, mas é possível alterá-los. A descrição POSIX dosps estados que

Não é especificado se a sequência representada é uma versão da lista de argumentos conforme foi passada ao comando quando iniciado ou se é uma versão dos argumentos, pois eles podem ter sido modificados pelo aplicativo. Os aplicativos não podem depender de poder modificar sua lista de argumentos e ter essa modificação refletida na saída do ps.

A razão pela qual isso é mencionado é que a maioria das variantes do unix reflete a mudança, mas as implementações do POSIX em outros tipos de sistemas operacionais podem não.

Esse recurso é de uso limitado porque o processo não pode fazer alterações arbitrárias. No mínimo, o comprimento total dos argumentos não pode ser aumentado, porque o programa não pode alterar o local onde psbuscará os argumentos e não pode estender a área além do tamanho original. O comprimento pode efetivamente ser diminuído colocando bytes nulos no final, porque os argumentos são seqüências terminadas em nulo no estilo C (isso é indistinguível de ter um monte de argumentos vazios no final).

Se você realmente deseja cavar, pode procurar a fonte de uma implementação de código aberto. No Linux, a fonte de psnão é interessante, tudo o que você verá lá é que ele lê os argumentos da linha de comando do sistema de arquivos proc , em . O código que gera o conteúdo desse arquivo está no kernel, em . A parte da memória do processo (acessada com ) vai do endereço para ; esses endereços são registrados no kernel quando o processo é iniciado e não podem ser alterados posteriormente./proc/PID/cmdlineproc_pid_cmdline_readfs/proc/base.caccess_remote_vmmm->arg_startmm->arg_end

Alguns daemons usam essa capacidade para refletir seu status, por exemplo, eles mudam argv[1]para uma string como startingou availableou exiting. Muitas variantes unix têm uma setproctitlefunção para fazer isso. Alguns programas usam essa capacidade para ocultar dados confidenciais. Observe que isso é de uso limitado, pois os argumentos da linha de comando são visíveis enquanto o processo é iniciado.

A maioria dos idiomas de alto nível copia os argumentos para objetos de sequência e não permite modificar o armazenamento original. Aqui está um programa em C que demonstra essa capacidade alterando argvelementos diretamente.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
    int i;
    system("ps -p $PPID -o args=");
    for (i = 0; i < argc; i++)
    {
        memset(argv[i], '0' + (i % 10), strlen(argv[i]));
    }
    system("ps -p $PPID -o args=");
    return 0;
}

Saída de amostra:

./a.out hello world
0000000 11111 22222

Você pode ver a argvmodificação no código fonte do curl. Curl define uma função cleanargnasrc/tool_paramhlp.c qual é usado para alterar um argumento para todos os espaços usando memset. Em src/tool_getparam.cesta função é utilizada algumas vezes, por exemplo, por redacting a senha do usuário . Como a função é chamada a partir da análise de parâmetro, isso acontece no início de uma chamada de curvatura, mas o despejo da linha de comando antes que isso aconteça ainda mostrará as senhas.

Como os argumentos são armazenados na própria memória do processo, eles não podem ser alterados do lado de fora, exceto usando um depurador.

Gilles 'SO- parar de ser mau'
fonte
Ótimo! Portanto, com relação ao snippet de especificações, entendo que seria compatível com POSIX fazer com que o kernel armazene os argumentos originais da linha de comando do processo fora da memória de leitura e gravação do processo ( além da cópia na memória de leitura e gravação) ? E, em seguida, há psargumentos de relatório dessa parte da memória do kernel, ignorando as alterações feitas na memória de leitura e gravação dos processos? Mas (se eu entendi direito?) A maioria das variações do UNIX nem sequer faz o primeiro, então você não pode fazer uma psimplementação com o último sem modificações no kernel, já que os dados originais não são mantidos em lugar algum?
Wildcard
11
@Wildcard Correct. Pode haver implementações do Unix que mantêm o original, mas acho que nenhum dos mais comuns. O idioma C permite que o conteúdo das argventradas seja alterado (você não pode definir argv[i], mas pode escrever argv[i][0]até argv[i][strlen(argv[i])]), portanto, deve haver uma cópia na memória do processo.
Gilles 'SO- stop be evil' -
2
Função relevante no código fonte do curl: github.com/curl/curl/blob/master/src/tool_paramhlp.c#L139
sebasth 11/17
4
@ Wildcard, o Solaris faz isso. A linha de comando vista por / usr / ucb / ps é a cópia (mutável) de propriedade do processo. A linha de comando vista por / usr / bin / ps é a cópia (imutável) de propriedade do kernel. O kernel mantém apenas os primeiros 80 caracteres. Qualquer outra coisa está truncada.
precisa saber é o seguinte
11
@Wildcard Na verdade, os valores nulos à direita são argumentos vazios. Na pssaída, muitos argumentos vazios parecem não ter nada lá, mas sim, faz diferença se você verificar quantos espaços existem e poderá observar mais diretamente /proc/PID/cmdline.
Gilles 'SO- stop be evil' (
14

As outras respostas respondem bem à pergunta de maneira geral. Para responder especificamente " Como esse efeito é alcançado? Ele está em algum lugar no código fonte do curl? ":

Na seção de análise de argumento do código-fonte de ondulação , a -uopção é tratada da seguinte maneira:

    case 'u':
      /* user:password  */
      GetStr(&config->userpwd, nextarg);
      cleanarg(nextarg);
      break;

E a cleanarg()função é definida da seguinte maneira:

void cleanarg(char *str)
{
#ifdef HAVE_WRITABLE_ARGV
  /* now that GetStr has copied the contents of nextarg, wipe the next
   * argument out so that the username:password isn't displayed in the
   * system process list */
  if(str) {
    size_t len = strlen(str);
    memset(str, ' ', len);
  }
#else
  (void)str;
#endif
}

Portanto, podemos ver explicitamente que o argumento nome de usuário: senha argvé substituído por espaços, conforme descrito pelas outras respostas.

Trauma Digital
fonte
Eu gosto que o comentário cleanargdeclare explicitamente que está fazendo o que a pergunta está fazendo!
Floris
3

Um processo pode não apenas ler seus parâmetros, mas também escrevê-los.

Não sou desenvolvedor, portanto não estou familiarizado com essas coisas, mas pode ser possível externamente com uma abordagem semelhante à alteração dos parâmetros do ambiente:

https://stackoverflow.com/questions/205064/is-there-a-way-to-change-another-processs-environment-variables

Hauke ​​Laging
fonte
Ok, mas a execução, por exemplo, bash -c 'awk 1 /proc/$$/cmdline; set -- something; awk 1 /proc/$$/cmdline'mostra que, pelo menos no shell, definir os parâmetros é diferente de modificar o que o kernel vê como parâmetros do processo.
Curinga
4
@Wildcard Os argumentos posicionais em um script de shell são inicialmente cópias de alguns dos argumentos de linha de comando do processo de shell. A maioria dos shells não permite que o script altere os argumentos originais.
Gilles 'SO- stop be evil' -
@ Gilles, sim, esse foi o ponto do meu comentário. :) A afirmação geral de que um processo pode fazer isso (primeira frase desta resposta) não responde se isso pode ser alcançado pelos recursos de shell existentes. A resposta para isso parece ser "não", que é o que eu adivinhei no final da minha pergunta.
Wildcard