Vraag Hoe verberg ik een wachtwoord dat is doorgegeven als argument in de commandoregel?


Ik voer een software-daemon uit die voor bepaalde acties een wachtwoordzin moet invoeren om een ​​aantal functies te ontgrendelen die er bijvoorbeeld als volgt uitziet:

$ darkcoind masternode start <mypassphrase>

Nu kreeg ik wat beveiligingsproblemen op mijn headless debian-server.

Telkens wanneer ik mijn bash-geschiedenis doorzoek met bijvoorbeeld Ctrl+R Ik kan dit super sterke wachtwoord zien. Nu stel ik me voor dat mijn server is gecompromitteerd en dat sommige indringers shell-toegang hebben en dat kan eenvoudig Ctrl+R om mijn wachtwoordzin in de geschiedenis te vinden.

Is er een manier om de wachtwoordzin in te voeren zonder dat deze in de bash-geschiedenis wordt weergegeven, ps, /proc of ergens anders?


Update 1: Als u geen wachtwoord doorgeeft aan de daemon, wordt er een fout gegenereerd. Dit is geen optie.


Update 2: Vertel me niet om de software of andere nuttige hints zoals het ophangen van de ontwikkelaars te verwijderen. Ik weet dat dit geen best-practice-voorbeeld is, maar deze software is gebaseerd op bitcoin en alle op bitcoin gebaseerde clients zijn een soort van json rpc-server die naar deze commando's luistert en het is een bekend beveiligingsprobleem dat nog steeds wordt besproken (een, b, c).


Update 3: De daemon is al gestart en draait met de opdracht

$ darkcoind -daemon

Aan het doen ps toont alleen de opstartopdracht.

$ ps aux | grep darkcoin
user     12337  0.0  0.0  10916  1084 pts/4    S+   09:19   0:00 grep darkcoin
user     21626  0.6  0.3 1849716 130292 ?      SLl  May02   6:48 darkcoind -daemon

Dus het doorgeven van de commando's met de wachtwoordzin verschijnt niet in ps of /proc helemaal niet.

$ darkcoind masternode start <mypassphrase>
$ ps aux | grep darkcoin
user     12929  0.0  0.0  10916  1088 pts/4    S+   09:23   0:00 grep darkcoin
user     21626  0.6  0.3 1849716 130292 ?      SLl  May02   6:49 darkcoind -daemon

Dit laat de vraag waar de geschiedenis verschijnt? Alleen in .bash_history?


42
2018-05-02 15:30


oorsprong


De eerste vraag moet zijn: wat gebeurt er als je de daemon start zonder het wachtwoordzin-argument. Roept het er gewoon om aan? - MadHatter
Ik denk niet dat er een antwoord is dat zal werken. Het onvermogen om om een ​​wachtwoordzin te vragen, is a groot tekortkoming in de daemon. Als het gratis software is, pak dan een programmeur en repareer het; vergeet niet om uw wijzigingen te publiceren. Als het bedrijfseigen software is, bel dan de verkoper op en roep hem (die niets zal repareren, maar je zult je er wel beter door voelen). - MadHatter
Controleer uw documentatie, het kan het lezen van dat wachtwoord vanuit een systeemomgevingsvariabele ondersteunen. - Elliott Frisch
Zelfs als het wachtwoord niet wordt gegeven op de opdrachtregel van de daemon, is het nog steeds problematisch om dit op de opdrachtregel van een ander commando te geven. Het is maar heel kort zichtbaar in de ps-uitvoer, maar een proces dat op de achtergrond draait, kan het nog steeds oppikken. Maar het is natuurlijk nog steeds de moeite waard het moeilijker te maken om het wachtwoord op te halen. - kasperd
Kijk naar de antwoorden op deze vraag, ze behandelen precies dit probleem. - dotancohen


antwoorden:


Echt, dit moeten worden opgelost in de applicatie zelf. En dergelijke toepassingen moeten open source zijn, zodat het oplossen van het probleem in de app zelf een optie zou moeten zijn. Een beveiligingsapplicatie die zo'n fout maakt, kan ook andere fouten maken, dus ik zou het niet vertrouwen.

Eenvoudige interposer

Maar je vroeg om een ​​andere manier, dus hier is er een:

#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);
}

Compileer dit met

gcc -O2 -fPIC -shared -o injectpassword.so injectpassword.c -ldl

voer dan je proces uit met

LD_PRELOAD=$PWD/injectpassword.so darkcoind masternode start fakepasshrase

De interposer-bibliotheek voert deze code vóór de main functie van uw toepassing wordt uitgevoerd. Het vervangt het laatste argument van de opdrachtregel door het daadwerkelijke wachtwoord in de aanroep naar de hoofdtaak. De opdrachtregel zoals afgedrukt /proc/*/cmdline (en daarom gezien door hulpmiddelen zoals ps) bevat echter nog steeds het nepargument. Uiteraard zou je de broncode en de bibliotheek die je compileert ervan moeten maken, alleen leesbaar voor jezelf, dus best in een chmod 0700 directory. En aangezien het wachtwoord geen deel uitmaakt van de opdrachtaanroep, is uw bash-geschiedenis ook veilig.

Meer geavanceerde interposer

Als je iets uitgebreider wilt doen, moet je dat onthouden __libc_start_main wordt uitgevoerd voordat de runtime-bibliotheek correct is geïnitialiseerd. Dus ik zou voorstellen om functieaanroepen te vermijden, tenzij deze absoluut noodzakelijk zijn. Als u naar hartenlust functies wilt kunnen bellen, moet u dit voor kort doen main zelf wordt aangeroepen, nadat alle initialisatie is voltooid. Voor het volgende voorbeeld moet ik Grubermensch bedanken die erop wees hoe een wachtwoord dat is doorgegeven als argument voor de commandoregel te verbergen wat bracht getpass onder mijn aandacht.

#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);
}

Dit vraagt ​​om het wachtwoord, zodat u de interposer-bibliotheek niet langer geheim hoeft te houden. Het tijdelijke aanduiding-argument wordt opnieuw gebruikt als wachtwoordprompt, dus roep zoiets aan

LD_PRELOAD=$PWD/injectpassword.so darkcoind masternode start "Password: "

Een ander alternatief zou het wachtwoord uit een bestandsbeschrijving lezen (zoals b.v. gpg --passphrase-fd doet), of van x11-ssh-askpass, of wat dan ook.


65
2018-05-03 19:49



Hoewel ik de code niet begrijp en niet kan testen, begrijp ik de kern ervan, en deze ziet eruit als een echt antwoord en zou het beste antwoord moeten zijn. - Mark Henderson♦
Dit is inderdaad geweldig. - Afri
Geweldig. Voor zover ik kan vertellen zou dit moeten werken. Natuurlijk hebt u toegang tot de bron nodig en kunt u opnieuw compileren. Het wachtwoord is leesbaar in de bron en het gecompileerde bestand (en) als u "strings" of iets dergelijks zo beter gebruikt, zorg ervoor dat niemand anders die kan lezen. - Tonny
Het moet mogelijk zijn om het wachtwoord op STDIN te nemen en dit werk nog steeds te hebben, waardoor de strings kwetsbaarheid. Zien SO: wachtwoordinvoer op terminal verbergen. - Grubermensch
@ Grubermensch Je hebt gelijk. - Tonny


Het is niet alleen de geschiedenis. Het zal verschijnen in ps uitvoer ook.

Degene die dat stukje software heeft geschreven, moet worden opgehangen, getekend en ingekwartierd. Het is een absoluut NEE om een ​​wachtwoord te moeten opgeven op de opdrachtregel, ongeacht welke software het ook is.
Voor een daemon-proces is het zelfs MEER onvergeeflijk ...

behalve rm -f op de software zelf weet ik hier geen oplossing voor. Eerlijk gezegd: zoek andere software om de klus te klaren. Gebruik dergelijke rommel niet.


28
2018-05-02 15:41



Bedankt dat je helemaal niet behulpzaam bent. Dit is lang besproken beveiligingsprobleem, nog steeds niet opgelost en ik heb een betere oplossing nodig dan rm -fnu. - Afri
Eigenlijk is hij erg behulpzaam. Als u de wachtwoordzin als argument doorgeeft, wordt deze weergegeven in ps. Dus totdat de ontwikkelaar dit kan oplossen, stelt hij voor om iets anders te gebruiken. - Safado
Dan kun je beter beginnen met het schrijven van een ander besturingssysteem. Er is momenteel GEEN andere oplossing beschikbaar waarvan ik op de hoogte ben. Bij God, ik wou dat er een was. Je bent niet de enige met dit probleem. - Tonny
vertoe, niet snippie krijgen. U kunt vragen om een ​​manier om het door te geven op kleine stukjes papier, maar dat betekent niet dat zoiets automatisch bestaat. read_x is prima, maar legt de passphrase toch bloot via bijvoorbeeld ps, dus het is niet beter dan de rm oplossing. - MadHatter
Voordat je gaat en nog eens een +1 gooit op dit niet-echt-een-antwoord en klaag dat dit onmogelijk is, raad ik je aan om te reviewen MvG's antwoord hieronder - Mark Henderson♦


Hiermee wordt de ps output.

WEES ZEER BEWUST: Dit kan de toepassing doorbreken. Je wordt gewaarschuwd dat hier draken zijn.

  • Buitenlandse processen zouden niet in een procesgeheugen moeten rondslingeren.
  • Als het proces afhankelijk is van deze regio voor het wachtwoord, kunt u uw toepassing verbreken.
  • Als u dit doet, kan dit eventuele werkgegevens die u in dat proces hebt, beschadigen.
  • Dit is een gestoorde hack.

Nu wordt u naar behoren op de hoogte gebracht van deze ernstige waarschuwingen. Hiermee wordt de uitvoer gewist die wordt weergegeven in ps. Het zal je geschiedenis niet wissen, noch zal het de bash jobgeschiedenis wissen (zoals het runnen van het proces zoals myprocess myargs &). Maar ps zal niet langer de argumenten tonen.

#!/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)

Roep het programma aan door het op te slaan, chmod +x het. Dan doen ./whatever <pidoftarget> Als dit werkt, produceert het geen uitvoer. Als het mislukt, zal het klagen over iets en stoppen.


17
2018-05-02 22:04



. . . dit is zowel creatief als angstaanjagend. - voretaq7
EEK! Nu ben ik bang. - Janne Pikkarainen
Yikkes, dat zou kunnen werken ... ik weet niet zeker of zoiets als AppArmor dit zou kunnen vangen? Ook kan de virusscanner mogelijk dit onderscheppen en schade aanrichten door het beledigende account te blokkeren dat 'root' zou zijn. Er zijn inderdaad draken ... - Tonny
@Tonny Voor beschermde domeinen zou SELinux dit voorkomen. Uw basis Unix-machtigingen (DAC) mist voldoende subjectgranulariteit om enige bescherming tegen dit gedrag aan te bieden (modificatie van processengeheugen binnen dezelfde UID mogelijk). Hoe dan ook, het is geen bug - het is een functie. Ik geloof dat dit is hoe gdb kan het geheugen van lopende processen wijzigen (met veel meer chirurgische precisie dan ik zou kunnen toevoegen). - Matthew Ife


Kun je het argument doorgeven vanuit een bestand, alleen toegankelijk voor root of de gewenste gebruiker?

Het is een ENORME nee-nee om wachtwoorden in de console te typen, maar het laatste verhaal ... begin je regel met een spatie zodat deze niet in de geschiedenis verschijnt.


10
2018-05-02 16:33



Er was een shell-optie die dit mogelijk maakt, maar ik denk dat deze standaard niet was ingeschakeld. - heinrich5991
export HISTCONTROL=ignoreboth negeert zowel duplicaten als regels met een leidende ruimte voor toegang tot de geschiedenis. Voeg het toe aan uw .bashrc of .bash_profile. - Andreas


Misschien werkt dit (?):

darkcoind masternode start `cat password.txt`

6
2018-05-02 19:31



Of zelfs darkcoind masternode start `head -1`, als u het wachtwoord handmatig wilt invoeren. - kasperd
De wachtwoordzin is nog steeds beschikbaar via ps en soortgelijke hulpprogramma's. - voretaq7
Verplaatsen van een leesbaar wachtwoord in .bash_history naar een leesbaar wachtwoord in password.txt krijgt u wat, precies? - MikeyB
@MikeyB: Er is een kleine overwinning: je zult het niet per ongeluk blootleggen tijdens het doorzoeken van je geschiedenis terwijl iemand over je schouder kijkt. - MvG
@MikeyB, u mag dat bestand elke keer maken en verwijderen. - RiaD


Helaas, als je darkcoind command verwacht dat het wachtwoord een opdrachtregelargument is, dan zal het worden getoond via hulpprogramma's zoals ps. De enige echte oplossing is om educatie van de ontwikkelaars.

Terwijl de ps blootstelling kan onvermijdelijk zijn, u kunt op zijn minst voorkomen dat het wachtwoord wordt uitgeschreven in het shell-geschiedenisbestand.

$ xargs darkcoind masternode start

peensswOrd

CtrlD

Het geschiedenisbestand mag alleen opnemen xargs darkcoind masternode start, niet het wachtwoord.


3
2018-05-04 08:13



Of als je bash gebruikt, zet ignorespace in $HISTCONTROLen dan kun je voorkomen ieder opdracht om naar de shell-geschiedenis te gaan door de opdracht met een spatie vooraf te zetten. - derobert


U kunt het wachtwoord uit de geschiedenis van uw shell houden door het commando uit een nieuw shell-proces uit te voeren, dat u dan onmiddellijk beëindigt. Bijvoorbeeld:

bash$ sh
sh$ darkcoind masternode start 'correct horse battery staple'
sh$ exit
bash$

Zorg ervoor dat sh is geconfigureerd niet om de geschiedenis in een bestand op te slaan.

Dit heeft uiteraard geen betrekking op de andere problemen, zoals het wachtwoord dat zichtbaar is in ps. Er zijn, geloof ik, wegen voor de darkcoind programmeer zelf om de informatie te verbergen ps, maar dat verkort alleen het venster van de kwetsbaarheid.


1
2018-05-02 20:38



de wachtwoordzin is nog steeds beschikbaar via ps en soortgelijke hulpprogramma's. - voretaq7
@ voretaq7: Ja, zoals ik expliciet heb bevestigd in de laatste alinea van mijn antwoord. - Keith Thompson
Inderdaad - je was het slachtoffer van mijn copieuze copypasta :) - voretaq7


Zoals anderen hebben gesteld, kijk in je shell history control voor het verbergen van de informatie uit de geschiedenis.

Maar één ding dat niemand lijkt te hebben voorgesteld, is te monteren /proc met de hidepid parameter. Probeer je te wijzigen /proc regel binnen /etc/fstab opnemen hidepid, zoals dit:

# <file system> <mount point>   <type>  <options>       <dump>  <pass>
proc            /proc           proc    defaults,hidepid=2        0       0

1
2018-05-17 12:19





Voor Bitcoin is het officiële antwoord van de ontwikkelaar om de meegeleverde Python-wrapper in te zetten contrib/bitrpc/bitrpc.py (github):

Het vraagt ​​om een ​​wachtwoord op een veilige manier als u de opdracht gebruikt walletpassphrase, bijvoorbeeld. Er zijn geen plannen om interactieve functionaliteit toe te voegen bitcoin-cli.

en:

bitcoin-cli blijft zoals het is en krijgt geen interactieve functionaliteit.

Bron: # 2318

Portemonnee ontgrendelen:

$ python bitrpc.py walletpassphrase

Wijzig wachtwoordzin:

$ python bitrpc.py walletpassphrasechange

https://github.com/bitcoin/bitcoin/tree/master/contrib/bitrpc

Voor darkcoin werkt het anlogue:

https://github.com/darkcoin/darkcoin/tree/master/contrib/bitrpc


1
2018-01-01 08:44