Realmente, isso deve ser corrigido no próprio aplicativo. E esses aplicativos devem ser de código aberto, para que a correção do problema no próprio aplicativo seja uma opção. Um aplicativo relacionado à segurança que comete esse tipo de erro pode cometer outros erros também, por isso não confiaria nele.
Interposer simples
Mas você estava pedindo um jeito diferente, então aqui está um:
#define _GNU_SOURCE
#include <dlfcn.h>
int __libc_start_main(
int (*main) (int, char * *, char * *),
int argc, char * * ubp_av,
void (*init) (void),
void (*fini) (void),
void (*rtld_fini) (void),
void (* stack_end)
)
{
int (*next)(
int (*main) (int, char * *, char * *),
int argc, char * * ubp_av,
void (*init) (void),
void (*fini) (void),
void (*rtld_fini) (void),
void (* stack_end)
) = dlsym(RTLD_NEXT, "__libc_start_main");
ubp_av[argc - 1] = "secret password";
return next(main, argc, ubp_av, init, fini, rtld_fini, stack_end);
}
Compile isso com
gcc -O2 -fPIC -shared -o injectpassword.so injectpassword.c -ldl
execute seu processo com
LD_PRELOAD=$PWD/injectpassword.so darkcoind masternode start fakepasshrase
A biblioteca intermediária executará este código antes que a função main
do seu aplicativo seja executada. Ele substituirá o último argumento da linha de comando pela senha real na chamada principal. A linha de comando, conforme impressa em /proc/*/cmdline
(e, portanto, vista por ferramentas como ps
), ainda conterá o argumento falso. Obviamente, você teria que fazer com que o código-fonte e a biblioteca que você compila fossem legíveis apenas para você, portanto, opere melhor em um diretório chmod 0700
. E como a senha não faz parte da chamada de comando, seu histórico bash também é seguro.
Interposer mais avançado
Se você quiser fazer algo mais elaborado, deve ter em mente que __libc_start_main
é executado antes que a biblioteca de tempo de execução seja inicializada corretamente. Então, sugiro evitar qualquer chamada de função, a menos que seja absolutamente essencial. Se você quiser chamar funções para o conteúdo do seu coração, certifique-se de fazer isso antes que main
seja invocada, depois que toda a inicialização estiver concluída. Para o exemplo a seguir, tenho que agradecer a Grubermensch, que apontou como ocultar uma senha passada como argumento de linha de comando que trouxe getpass
para minha atenção.
#define _GNU_SOURCE
#include <dlfcn.h>
#include <unistd.h>
static int (*real_main) (int, char * *, char * *);
static int my_main(int argc, char * * argv, char * * env) {
char *pass = getpass(argv[argc - 1]);
if (pass == NULL) return 1;
argv[argc - 1] = pass;
return real_main(argc, argv, env);
}
int __libc_start_main(
int (*main) (int, char * *, char * *),
int argc, char * * ubp_av,
void (*init) (void),
void (*fini) (void),
void (*rtld_fini) (void),
void (* stack_end)
)
{
int (*next)(
int (*main) (int, char * *, char * *),
int argc, char * * ubp_av,
void (*init) (void),
void (*fini) (void),
void (*rtld_fini) (void),
void (* stack_end)
) = dlsym(RTLD_NEXT, "__libc_start_main");
real_main = main;
return next(my_main, argc, ubp_av, init, fini, rtld_fini, stack_end);
}
Isso solicita a senha, assim você não precisa mais manter a biblioteca do interposer em segredo. O argumento de espaço reservado é reutilizado como prompt de senha, portanto invoque isso como
LD_PRELOAD=$PWD/injectpassword.so darkcoind masternode start "Password: "
Outra alternativa seria ler a senha de um descritor de arquivo (como, por exemplo, gpg --passphrase-fd
), ou de x11-ssh-askpass
, ou qualquer outra coisa.