Soluzione 1:
Davvero, questo dovrebbe essere risolto nell'applicazione stessa. E tali applicazioni dovrebbero essere open source, quindi la risoluzione del problema nell'app stessa dovrebbe essere un'opzione. Un'applicazione correlata alla sicurezza che commette questo tipo di errore potrebbe commettere anche altri errori, quindi non mi fiderei.
Semplice interposer
Ma stavi chiedendo un modo diverso, quindi eccone uno:
#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);
}
Compilalo con
gcc -O2 -fPIC -shared -o injectpassword.so injectpassword.c -ldl
quindi esegui il tuo processo con
LD_PRELOAD=$PWD/injectpassword.so darkcoind masternode start fakepasshrase
La libreria interposer eseguirà questo codice prima del main
funzione dall'applicazione viene eseguita. Sostituirà l'ultimo argomento della riga di comando con la password effettiva nella chiamata a main. La riga di comando come stampata in /proc/*/cmdline
(e quindi visto da strumenti come ps
) conterrà comunque l'argomento falso. Ovviamente dovresti rendere il codice sorgente e la libreria che compili da esso leggibili solo a te stesso, quindi è meglio operare in un chmod 0700
directory. E poiché la password non fa parte dell'invocazione del comando, anche la tua cronologia bash è al sicuro.
Interposer più avanzato
Se vuoi fare qualcosa di più elaborato, tieni presente che __libc_start_main
viene eseguito prima che la libreria di runtime sia stata correttamente inizializzata. Quindi suggerirei di evitare qualsiasi chiamata di funzione a meno che non sia assolutamente essenziale. Se vuoi essere in grado di chiamare le funzioni a tuo piacimento, assicurati di farlo appena prima del main
stesso viene richiamato, dopo che tutta l'inizializzazione è stata eseguita. Per il seguente esempio devo ringraziare Grubermensch che mi ha fatto notare come nascondere una password passata come argomento della riga di comando che riportava getpass
alla mia attenzione.
#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);
}
Ciò richiede la password, quindi non è più necessario mantenere segreta la libreria dell'interposer. L'argomento segnaposto viene riutilizzato come richiesta di password, quindi invocalo come
LD_PRELOAD=$PWD/injectpassword.so darkcoind masternode start "Password: "
Un'altra alternativa potrebbe leggere la password da un descrittore di file (come ad esempio gpg --passphrase-fd
fa), o da x11-ssh-askpass
, o qualsiasi altra cosa.
Soluzione 2:
Non è solo la storia. Verrà visualizzato in ps anche l'output.
Chiunque abbia scritto quel software dovrebbe essere impiccato, disegnato e squartato. È un NO assoluto dover fornire una password sulla riga di comando indipendentemente dal software.
Per un processo demone è ancora PIÙ imperdonabile...
Oltre a rm -f sul software stesso non conosco alcuna soluzione per questo. Onestamente:trova altri software per portare a termine il lavoro. Non usare questa spazzatura.
Soluzione 3:
Questo cancellerà il ps
uscita.
SIATE MOLTO CONSAPEVOLI :Questo potrebbe interrompere l'applicazione. Sei debitamente avvertito che qui ci sono draghi.
- I processi estranei non dovrebbero armeggiare nella memoria dei processi.
- Se il processo si basa su questa regione per la password, potresti interrompere la tua applicazione.
- In questo modo potresti corrompere tutti i dati di lavoro che hai in quel processo.
- Questo è un trucco folle.
Ora sei debitamente informato di questi terribili avvertimenti. Questo cancellerà l'output visualizzato in ps
. Non cancellerà la tua cronologia, né cancellerà la cronologia dei processi bash (come l'esecuzione del processo come myprocess myargs &
). Ma ps
non mostrerà più gli argomenti.
#!/usr/bin/python
import os, sys
import re
PAGESIZE=4096
if __name__ == "__main__":
if len(sys.argv) < 2:
sys.stderr.write("Must provide a pid\n")
sys.exit(1)
pid = sys.argv[1]
try:
cmdline = open("/proc/{0}/cmdline".format(pid)).read(8192)
## On linux, at least, argv is located in the stack. This is likely o/s
## independent.
## Open the maps file and obtain the stack address.
maps = open("/proc/{0}/maps".format(pid)).read(65536)
m = re.search('([0-9a-f]+)-([0-9a-f]+)\s+rw.+\[stack\]\n', maps)
if not m:
sys.stderr.write("Could not find stack in process\n");
sys.exit(1)
start = int("0x"+m.group(1), 0)
end = int("0x"+m.group(2), 0)
## Open the mem file
mem = open('/proc/{0}/mem'.format(pid), 'r+')
## As the stack grows downwards, start at the end. It is expected
## that the value we are looking for will be at the top of the stack
## somewhere
## Seek to the end of the stack minus a couple of pages.
mem.seek(end-(2*PAGESIZE))
## Read this buffer to the end of the stack
stackportion = mem.read(8192)
## look for a string matching cmdline. This is pretty dangerous.
## HERE BE DRAGONS
m = re.search(cmdline, stackportion)
if not m:
## cause this is an example dont try to search exhaustively, just give up
sys.stderr.write("Could not find command line in the stack. Giving up.")
sys.exit(1)
## Else, we got a hit. Rewind our file descriptor, plus where we found the first argument.
mem.seek(end-(2*PAGESIZE)+m.start())
## Additionally, we'll keep arg0, as thats the program name.
arg0len = len(cmdline.split("\x00")[0]) + 1
mem.seek(arg0len, 1)
## lastly overwrite the remaining region with nulls.
writeover = "\x00" * (len(cmdline)-arg0len)
mem.write(writeover)
## cleanup
mem.close()
except OSError, IOError:
sys.stderr.write("Cannot find pid\n")
sys.exit(1)
Invoca il programma salvandolo, chmod +x
esso. Poi facendo ./whatever <pidoftarget>
Se funziona, non produrrà alcun output. Se fallisce, si lamenterà di qualcosa e si chiuderà.
Soluzione 4:
Puoi passare l'argomento da un file, accessibile solo da root o dall'utente richiesto?
È un ENORME no-no digitare le password nella console, ma ultima risorsa... inizia la tua riga con uno spazio in modo che non appaia nella cronologia.
Soluzione 5:
Forse funziona (?):
darkcoind masternode start `cat password.txt`