Lösung 1:
Wirklich, das sollte in der Anwendung selbst behoben werden. Und solche Anwendungen sollten Open Source sein, sodass das Beheben des Problems in der App selbst eine Option sein sollte. Eine sicherheitsrelevante Anwendung, die diese Art von Fehlern macht, könnte auch andere Fehler machen, also würde ich ihr nicht vertrauen.
Einfacher Interposer
Aber Sie haben nach einem anderen Weg gefragt, also hier ist einer:
#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);
}
Kompilieren Sie dies mit
gcc -O2 -fPIC -shared -o injectpassword.so injectpassword.c -ldl
Führen Sie dann Ihren Prozess mit
ausLD_PRELOAD=$PWD/injectpassword.so darkcoind masternode start fakepasshrase
Die Interposer-Bibliothek führt diesen Code vor main
aus Funktion aus Ihrer Anwendung ausgeführt wird. Es ersetzt das letzte Kommandozeilenargument durch das tatsächliche Passwort im Aufruf von main. Die Befehlszeile wie in /proc/*/cmdline
gedruckt (und daher von Tools wie ps
gesehen ) enthält jedoch immer noch das falsche Argument. Offensichtlich müssten Sie den Quellcode und die Bibliothek, die Sie daraus kompilieren, nur für Sie selbst lesbar machen, arbeiten Sie also am besten in einem chmod 0700
Verzeichnis. Und da das Passwort nicht Teil des Befehlsaufrufs ist, ist auch Ihr Bash-Verlauf sicher.
Erweiterter Interposer
Wenn Sie etwas Ausführlicheres tun möchten, sollten Sie sich daran erinnern, dass __libc_start_main
wird ausgeführt, bevor die Laufzeitbibliothek ordnungsgemäß initialisiert wurde. Daher würde ich vorschlagen, Funktionsaufrufe zu vermeiden, es sei denn, sie sind absolut notwendig. Wenn Sie nach Herzenslust Funktionen aufrufen können, tun Sie dies unbedingt kurz vor main
selbst aufgerufen wird, nachdem die Initialisierung abgeschlossen ist. Für das folgende Beispiel muss ich mich bei Grubermensch bedanken, der darauf hinwies, wie man ein als Kommandozeilenargument übergebenes Passwort versteckt, das getpass
brachte zu meiner Aufmerksamkeit.
#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);
}
Dies fordert zur Eingabe des Passworts auf, sodass Sie die Interposer-Bibliothek nicht länger geheim halten müssen. Das Platzhalter-Argument wird als Passwort-Eingabeaufforderung wiederverwendet, rufen Sie es also wie folgt auf
LD_PRELOAD=$PWD/injectpassword.so darkcoind masternode start "Password: "
Eine andere Alternative würde das Passwort aus einem Dateideskriptor lesen (wie z.B. gpg --passphrase-fd
tut) oder ab x11-ssh-askpass
, oder was auch immer.
Lösung 2:
Es ist nicht nur die Geschichte. Es wird in ps angezeigt auch ausgegeben.
Wer auch immer dieses Stück Software geschrieben hat, sollte aufgehängt, gezeichnet und geviertelt werden. Es ist ein absolutes NEIN, ein Passwort auf der Befehlszeile eingeben zu müssen, unabhängig davon, um welche Software es sich handelt.
Für einen Daemon-Prozess ist es noch unverzeihlicher...
Neben rm -f Auf der Software selbst kenne ich keine Lösung dafür. Ehrlich gesagt:Finden Sie andere Software, um die Arbeit zu erledigen. Verwenden Sie keinen solchen Müll.
Lösung 3:
Dadurch wird ps
gelöscht Ausgabe.
SEIEN SIE SEHR BEWUSST :Dies könnte die Anwendung beschädigen. Ihr seid gebührend gewarnt, dass hier Drachen sind.
- Fremde Prozesse sollten nicht im Speicher eines Prozesses herumfummeln.
- Wenn der Prozess für das Passwort auf diese Region angewiesen ist, können Sie Ihre Anwendung beschädigen.
- Dadurch könnten alle Arbeitsdaten, die Sie in diesem Prozess haben, beschädigt werden.
- Das ist ein wahnsinniger Hack.
Jetzt werden Sie ordnungsgemäß über diese düsteren Warnungen informiert. Dadurch wird die in ps
angezeigte Ausgabe gelöscht . Es löscht weder Ihren Verlauf noch den Bash-Job-Verlauf (z. B. das Ausführen des Prozesses wie myprocess myargs &
). Aber ps
zeigt die Argumente nicht mehr an.
#!/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)
Rufen Sie das Programm auf, indem Sie es speichern, chmod +x
es. Dann mache ./whatever <pidoftarget>
Wenn dies funktioniert, wird keine Ausgabe erzeugt. Wenn es fehlschlägt, wird es sich über etwas beschweren und beenden.
Lösung 4:
Können Sie das Argument aus einer Datei übergeben, auf die nur root oder der erforderliche Benutzer zugreifen kann?
Es ist ein RIESIGES No-No, Passwörter in die Konsole einzugeben, aber letzte Möglichkeit ... beginnen Sie Ihre Zeile mit einem Leerzeichen, damit sie nicht im Verlauf erscheint.
Lösung 5:
Vielleicht funktioniert das (?):
darkcoind masternode start `cat password.txt`