Bagaimana cara menyembunyikan kata sandi yang diteruskan sebagai argumen baris perintah?

43

Saya menjalankan daemon perangkat lunak yang memerlukan tindakan tertentu untuk memasukkan frasa sandi untuk membuka kunci beberapa fitur yang terlihat seperti itu:

$ darkcoind masternode start <mypassphrase>

Sekarang saya punya beberapa masalah keamanan pada server debian tanpa kepala saya.

Setiap kali saya mencari riwayat bash saya misalnya dengan Ctrl+Rsaya dapat melihat kata sandi yang sangat kuat ini. Sekarang saya membayangkan server saya terganggu dan beberapa penyusup memiliki akses shell dan dapat dengan mudah Ctrl+Rmenemukan frasa sandi saya dalam sejarah.

Apakah ada cara untuk memasukkan frasa sandi tanpa harus ditampilkan dalam bash history ps,, /procatau di mana saja?


Pembaruan 1 : Melewati kata sandi tanpa ke daemon melempar kesalahan. Ini bukan pilihan.


Pembaruan 2 : Jangan bilang saya untuk menghapus perangkat lunak atau petunjuk bermanfaat lainnya seperti menggantung pengembang. Saya tahu ini bukan contoh praktik terbaik tetapi perangkat lunak ini didasarkan pada bitcoin dan semua klien berbasis bitcoin adalah semacam server json rpc yang mendengarkan perintah ini dan masalah keamanannya yang diketahui masih dibahas ( a , b , c ) .


Pembaruan 3 : Daemon sudah mulai dan berjalan dengan perintah

$ darkcoind -daemon

Melakukan pshanya menunjukkan perintah startup.

$ 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

Jadi melewati perintah dengan frasa sandi tidak muncul psatau tidak ada /procsama sekali.

$ 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

Ini meninggalkan pertanyaan di mana sejarah muncul? Hanya di .bash_history?

Waqar Lim
sumber
1
Pertanyaan pertama adalah: apa yang terjadi jika Anda memulai daemon tanpa argumen frasa sandi. Apakah itu hanya meminta untuk itu?
MadHatter mendukung Monica
31
Saya tidak berpikir ada jawaban yang akan berhasil. Ketidakmampuan untuk meminta frasa sandi adalah kelemahan utama dalam daemon. Jika ini perangkat lunak gratis, dapatkan programmer dan perbaiki; jangan lupa untuk mempublikasikan perubahan Anda. Jika itu perangkat lunak berpemilik, hubungi vendor dan berteriak pada mereka (itu tidak akan memperbaiki apa pun, tetapi itu akan membuat Anda merasa lebih baik).
MadHatter mendukung Monica
4
Periksa dokumentasi Anda, mungkin mendukung membaca kata sandi itu dari variabel lingkungan sistem.
Elliott Frisch
3
Bahkan jika kata sandi tidak diberikan pada baris perintah ke daemon, masih bermasalah untuk memberikannya pada baris perintah dari perintah lain mana pun. Ini hanya terlihat dalam output ps untuk waktu yang sangat singkat, tetapi proses yang berjalan di latar belakang masih bisa mengambilnya. Tetapi tentu saja masih bermanfaat sehingga sulit untuk mengambil kata sandi.
kasperd
2
Lihatlah jawaban untuk pertanyaan ini , mereka menangani masalah ini dengan tepat.
dotancohen

Jawaban:

68

Sungguh, ini harus diperbaiki dalam aplikasi itu sendiri. Dan aplikasi semacam itu harus open source, sehingga memperbaiki masalah di aplikasi itu sendiri harus menjadi pilihan. Aplikasi terkait keamanan yang membuat kesalahan semacam ini mungkin membuat kesalahan lain juga, jadi saya tidak akan mempercayainya.

Interposer sederhana

Tapi Anda meminta cara yang berbeda, jadi ini dia:

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

Kompilasi ini dengan

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

kemudian jalankan proses Anda dengan

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

Pustaka interposer akan menjalankan kode ini sebelum mainfungsi dari aplikasi Anda dieksekusi. Ini akan menggantikan argumen baris perintah terakhir dengan kata sandi yang sebenarnya dalam panggilan ke utama. Baris perintah seperti yang dicetak /proc/*/cmdline(dan karena itu terlihat oleh alat seperti ps) masih akan berisi argumen palsu. Tentunya Anda harus membuat kode sumber dan pustaka yang Anda kompilasi darinya hanya dapat dibaca oleh Anda sendiri, jadi sebaiknya operasikan di chmod 0700direktori. Dan karena kata sandi tersebut bukan bagian dari permintaan perintah, bash history Anda juga aman.

Interposer yang lebih maju

Jika Anda ingin melakukan sesuatu yang lebih rumit, Anda harus ingat bahwa __libc_start_maindieksekusi sebelum pustaka runtime telah diinisialisasi dengan benar. Jadi saya sarankan menghindari panggilan fungsi kecuali mereka benar-benar penting. Jika Anda ingin dapat memanggil fungsi ke isi hati Anda, pastikan Anda melakukannya tepat sebelum mainitu sendiri dipanggil, setelah semua inisialisasi dilakukan. Untuk contoh berikut ini saya harus berterima kasih kepada Grubermensch yang menunjukkan cara menyembunyikan kata sandi yang dilewatkan sebagai argumen baris perintah yang getpassmenarik perhatian saya.

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

Ini meminta kata sandi, sehingga Anda tidak lagi harus menjaga perpustakaan interposer rahasia. Argumen placeholder digunakan kembali sebagai kata sandi, jadi aktifkan seperti ini

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

Alternatif lain akan membaca kata sandi dari deskriptor file (seperti mis. gpg --passphrase-fdLakukan), atau dari x11-ssh-askpass, atau apa pun.

MvG
sumber
4
Meskipun saya tidak mengerti dan tidak dapat menguji kode, saya mendapatkan intinya, dan ini sepertinya jawaban yang sebenarnya dan harus menjadi jawaban teratas.
Mark Henderson
Ini memang luar biasa.
Waqar Lim
Luar biasa. Sejauh yang saya tahu ini harus bekerja. Tentu saja Anda memerlukan akses ke sumbernya dan dapat melakukan kompilasi ulang. Kata sandi dapat dibaca di sumber dan file yang dikompilasi jika Anda menggunakan "string" atau sesuatu yang serupa jadi lebih baik pastikan tidak ada orang lain yang bisa membacanya.
Tonny
1
Seharusnya dimungkinkan untuk mengambil kata sandi pada STDIN dan masih memiliki pekerjaan ini, yang menghilangkan stringskerentanan. Lihat SO: Sembunyikan input kata sandi pada terminal .
Grubermensch
1
@ mulg0r: Standar eksternal "C" harus melakukan trik menekan nama mangling untuk fungsi yang relevan, yaitu __libc_start_main.
MvG
28

Bukan hanya sejarah. Ini akan muncul dalam output ps juga.

Siapa pun yang menulis perangkat lunak itu harus digantung, ditarik, dan dipotong-potong. Ini mutlak TIDAK untuk memasok kata sandi pada command-line terlepas dari perangkat lunak apa pun itu.
Untuk proses daemon bahkan lebih LEBIH tak termaafkan ...

Selain rm -f pada perangkat lunak itu sendiri saya tidak tahu solusi apa pun untuk ini. Jujur: Temukan perangkat lunak lain untuk menyelesaikan pekerjaan. Jangan gunakan sampah seperti itu.

Tonny
sumber
9
Terima kasih karena tidak membantu sama sekali. Ini adalah masalah keamanan yang lama dibahas , masih belum terpecahkan dan saya perlu solusi yang lebih baik daripada rm -fsekarang.
Waqar Lim
17
Sebenarnya, dia sangat membantu. Jika Anda melewatkan frasa sandi sebagai argumen, itu AKAN muncul di ps. Jadi sampai dev dapat memperbaikinya, dia menyarankan menggunakan sesuatu yang lain.
Safado
3
Maka Anda lebih baik mulai menulis Sistem Operasi lain. Tidak ada solusi lain yang tersedia saat ini yang saya sadari. Demi Tuhan aku berharap ada satu. Anda bukan satu-satunya dengan masalah ini.
Tonny
8
vertoe, jangan bersikap angkuh. Anda dapat meminta cara untuk melewatinya di selembar kertas kecil, tetapi itu tidak berarti cara seperti itu ada secara otomatis. read_x baik-baik saja, tetapi masih mengekspos frasa sandi melalui mis. ps, jadi itu tidak lebih baik daripada rmsolusinya.
MadHatter mendukung Monica
7
Sebelum Anda pergi dan melempar +1 lain pada jawaban yang tidak benar-benar ini dan mengeluh bahwa ini tidak mungkin, saya sarankan Anda meninjau jawaban MvG di bawah ini
Mark Henderson
19

Ini akan menghapus psoutput.

MENJADI SANGAT Sadar : Ini bisa merusak aplikasi. Anda sepatutnya diperingatkan bahwa di sini ada naga.

  • Proses asing tidak boleh mengotak-atik memori proses.
  • Jika proses ini bergantung pada wilayah ini untuk kata sandi, Anda dapat merusak aplikasi Anda.
  • Melakukan hal ini dapat merusak data kerja apa pun yang Anda miliki dalam proses itu.
  • Ini adalah hack gila.

Sekarang Anda telah diberi tahu tentang peringatan yang mengerikan ini. Ini akan menghapus output yang ditampilkan di ps. Itu tidak akan menghapus riwayat Anda, juga tidak akan menghapus riwayat pekerjaan bash (seperti menjalankan proses seperti myprocess myargs &). Tetapi pstidak akan lagi menunjukkan argumen.

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

Meminta program dengan menyimpannya, chmod +xitu. Kemudian melakukan ./whatever <pidoftarget> Jika ini berhasil, itu tidak akan menghasilkan output. Jika gagal, itu akan mengeluh tentang sesuatu dan berhenti.

Matthew Ife
sumber
18
. . . ini kreatif dan menakutkan.
voretaq7
EEK! Sekarang saya takut.
Janne Pikkarainen
Yikkes, itu bisa berhasil ... Saya tidak yakin sesuatu seperti AppArmor akan menangkap ini? Juga virusscanner berpotensi menangkap ini dan menyebabkan kekacauan dengan memblokir akun yang menyinggung yang akan menjadi 'root'. Memang ada naga ....
Tonny
@Tonny Untuk domain yang dilindungi, SELinux akan mencegahnya. Izin Unix dasar (DAC) Anda tidak memiliki rincian subjek yang cukup untuk menawarkan perlindungan apa pun dari perilaku ini (memungkinkan modifikasi proses memori dalam UID yang sama). Bagaimanapun, ini bukan bug - ini adalah fitur. Saya percaya ini adalah bagaimana gdbdapat memodifikasi memori dari proses yang sedang berjalan (dengan presisi bedah jauh lebih banyak daripada yang mungkin saya tambahkan).
Matthew Ife
11

Bisakah Anda meneruskan argumen dari file, hanya dapat diakses oleh root atau pengguna yang diperlukan?

Ini adalah TIDAK-tidak-besar untuk mengetikkan kata sandi di konsol, tapi jalan terakhir ... mulai baris Anda dengan spasi sehingga tidak muncul dalam sejarah.

vn.
sumber
Ada opsi shell yang mengaktifkannya, tapi saya pikir itu tidak diaktifkan secara default.
heinrich5991
export HISTCONTROL=ignorebothmengabaikan duplikat dan baris dengan ruang terkemuka untuk masuk ke dalam sejarah. Tambahkan ke .bashrc atau .bash_profile Anda.
Andreas
7

Mungkin ini berhasil (?):

darkcoind masternode start `cat password.txt`
Daniele Testa
sumber
3
Atau bahkan darkcoind masternode start `head -1`, jika Anda ingin memasukkan kata sandi secara manual.
kasperd
14
Frasa sandi masih tersedia melalui psdan utilitas serupa.
voretaq7
1
Pindah dari kata sandi plaintext .bash_historyke kata sandi plaintext memberi password.txtAnda apa, tepatnya?
MikeyB
1
@ MikeyB: Ada kemenangan kecil: Anda tidak akan secara tidak sengaja mengeksposnya saat mencari melalui sejarah Anda saat seseorang melihat dari balik bahu Anda.
MvG
1
@ MikeyB, Anda dapat membuat dan menghapus file itu setiap kali.
RiaD
4

Sayangnya, jika darkcoindperintah Anda mengharapkan kata sandi sebagai argumen baris perintah, maka itu akan diekspos melalui utilitas seperti ps. Satu-satunya solusi nyata adalah mendidik pengembang .

Meskipun pspaparan mungkin tidak dapat dihindari, Anda setidaknya bisa menjaga agar kata sandi tidak ditulis dalam file histori shell.

$ xargs darkcoind masternode start

password

CtrlD

File riwayat hanya boleh merekam xargs darkcoind masternode start, bukan kata sandi.

200_sukses
sumber
2
Atau jika Anda menggunakan bash, dimasukkan ignorespacedalam $HISTCONTROL, dan kemudian Anda dapat mencegah setiap perintah dari pergi ke dalam sejarah shell dengan awalan perintah dengan spasi.
derobert
3

Seperti yang telah dinyatakan orang lain, lihat ke kontrol riwayat shell Anda untuk menyembunyikan informasi dari history.

Tapi satu hal yang sepertinya belum disarankan adalah untuk me /proc- mount dengan hidepidparameter. Coba ubah /procbaris Anda /etc/fstabuntuk disertakan hidepid, seperti ini:

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

Anda dapat menyimpan kata sandi dari riwayat shell Anda dengan mengeksekusi perintah dari proses shell baru, yang kemudian segera Anda akhiri. Sebagai contoh:

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

Pastikan shdikonfigurasi untuk tidak menyimpan riwayatnya dalam file.

Tentu saja ini tidak mengatasi masalah lain, seperti kata sandi yang terlihat ps. Saya percaya, ada cara-cara bagi darkcoindprogram itu sendiri untuk menyembunyikan informasi ps, tetapi itu hanya memperpendek jendela kerentanan.

Keith Thompson
sumber
1
frasa sandi masih tersedia melalui psdan utilitas serupa.
voretaq7
3
@ voretaq7: Ya, seperti yang saya jelaskan di paragraf terakhir dari jawaban saya.
Keith Thompson
3
Memang - Anda adalah korban dari copypasta nakal di pihak saya :)
voretaq7
2

Untuk Bitcoin, jawaban pengembang resmi adalah dengan menggunakan pembungkus python yang disediakan di contrib/bitrpc/bitrpc.py( github ):

Ia meminta kata sandi dengan cara yang aman jika Anda menggunakan perintah walletpassphrase, misalnya. Tidak ada rencana untuk menambahkan fungsionalitas interaktif bitcoin-cli.

dan:

bitcoin-cli akan tetap apa adanya dan tidak mendapatkan fungsionalitas interaktif.

Sumber: # 2318

Buka dompet:

$ python bitrpc.py walletpassphrase

Ubah frasa sandi:

$ python bitrpc.py walletpassphrasechange

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

Untuk darkcoin berfungsi sebagai anlog:

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

Waqar Lim
sumber