Bagaimana argumen baris perintah ssh jauh bisa diuraikan


11

Saya telah melihat pertanyaan dan jawaban tentang perlunya meloloskan diri dari argumen ke perintah ssh jarak jauh. Pertanyaan saya adalah: Di mana dan kapan parsing kedua dilakukan?

Jika saya menjalankan yang berikut ini:

$ ssh otherhost pstree -a -p

Saya melihat yang berikut di output:

  |-sshd,3736
  |   `-sshd,1102
  |       `-sshd,1109
  |           `-pstree,1112 -a -p

Proses induk untuk perintah jarak jauh ( pstree) adalah sshd, tampaknya tidak ada shell di sana yang akan mem-parsing argumen baris perintah ke perintah jarak jauh, sehingga sepertinya tidak perlu kutip ganda atau melarikan diri ( tapi itu pasti). Jika sebaliknya saya ssh di sana terlebih dahulu dan mendapatkan shell login, lalu jalankan pstree -a -psaya melihat yang berikut di output:

  ├─sshd,3736
     └─sshd,3733
         └─sshd,3735
             └─bash,3737
                 └─pstree,4130 -a -p

Jadi jelas ada bashshell di sana yang akan melakukan parsing baris perintah dalam kasus itu. Tetapi kasus di mana saya menggunakan perintah jarak jauh secara langsung, sepertinya tidak ada shell, jadi mengapa pengutipan ganda diperlukan?

Jawaban:


22

Selalu ada shell jarak jauh. Dalam protokol SSH, klien mengirim string ke server untuk dieksekusi. Klien baris perintah SSH mengambil argumen baris perintah dan menyatukannya dengan spasi di antara argumen. Server mengambil string itu, menjalankan shell login pengguna dan meneruskannya string itu.

Tidak mungkin untuk memotong shell jarak jauh. Protokol tidak memiliki apa pun seperti mengirim array string yang dapat diuraikan sebagai array argv di server. Dan server SSH tidak akan mem-bypass shell jauh karena itu bisa menjadi pembatasan keamanan: menggunakan program terbatas karena shell pengguna adalah cara untuk menyediakan akun terbatas yang hanya diizinkan untuk menjalankan perintah tertentu (mis. Akun rsync-only atau akun khusus git).

Anda mungkin tidak melihat shellnya pstreekarena mungkin sudah hilang. Banyak shell memiliki optimasi di mana jika mereka mendeteksi bahwa mereka akan melakukan "jalankan perintah eksternal ini, tunggu sampai selesai, dan keluar dengan status perintah", maka shell menjalankan " execveperintah eksternal ini" sebagai gantinya. Inilah yang terjadi pada contoh pertama Anda. Bandingkan tiga perintah berikut:

ssh otherhost pstree -a -p
ssh otherhost 'pstree -a -p'
ssh otherhost 'pstree -a -p; true'

Dua yang pertama identik: klien mengirimkan data yang persis sama ke server. Yang ketiga mengirimkan perintah shell yang mengalahkan optimasi exec shell.


2
Ha! tidak percaya Anda mengalahkan saya untuk menjawab pertanyaan saya sendiri. Saya menemukan jawabannya setengah jalan melalui posting pertanyaan dan saya pikir saya harus menyelesaikannya dengan bertanya dan menjawabnya sendiri.
onlynone

10

Saya pikir saya sudah menemukan jawabannya:

$ ssh otherhost pstree -a -p -s '$$'
init,1         
  `-sshd,3736
      `-sshd,11998
          `-sshd,12000
              `-pstree,12001 -a -p -s 12001

Argumen untuk pstreeadalah: menunjukkan argumen baris perintah, menunjukkan pids, dan menunjukkan hanya proses induk dari pid yang diberikan. Ini '$$'adalah variabel shell khusus yang akan diganti bash dengan pidnya sendiri ketika bash mengevaluasi argumen baris perintah. Ini dikutip sekali untuk menghentikannya agar tidak ditafsirkan oleh shell lokal saya. Tapi itu tidak dikutip atau melarikan diri ganda untuk memungkinkannya untuk ditafsirkan oleh shell jarak jauh.

Seperti yang bisa kita lihat, itu diganti dengan 12001begitu itu pid dari shell. Kita juga dapat melihat dari output: pstree,12001bahwa proses dengan pid 12001 adalah pstree itu sendiri. Jadi, pstreeapakah cangkangnya?

Apa yang saya kumpulkan sedang terjadi di sana adalah yang bashsedang dipanggil dan itu menguraikan argumen baris perintah, tetapi kemudian dipanggil execuntuk mengganti sendiri dengan perintah yang sedang dijalankan.

Tampaknya hanya melakukan ini dalam kasus perintah jarak jauh tunggal:

$ ssh otherhost pstree -a -p -s '$$' \; echo hi
init,1         
  `-sshd,3736
      `-sshd,17687
          `-sshd,17690
              `-bash,17691 -c pstree -a -p -s $$ ; echo hi
                  `-pstree,17692 -a -p -s 17691
hi

Dalam hal ini, saya meminta dua perintah dijalankan: pstreediikuti oleh echo. Dan kita dapat melihat di sini bahwa bashsebenarnya muncul di pohon proses sebagai induk pstree.


Ya ! + 1. Ini mencontohkan apa yang Gilles tempatkan lebih dulu secara formal dan yang dicontohkan kedua. Mungkin memberinya pujian karena jawaban awalnya sudah beres?
Cbhihe

0

Mendukung apa yang dikatakan jawaban lain, saya mencari kode yang memanggil perintah pada remote, https://github.com/openssh/openssh-portable/blob/4f29309c4cb19bcb1774931db84cacc414f17d29/session.c#L1660 ...

1660    /*
1661     * Execute the command using the user's shell.  This uses the -c
1662     * option to execute the command.
1663     */
1664    argv[0] = (char *) shell0;
1665    argv[1] = "-c";
1666    argv[2] = (char *) command;
1667    argv[3] = NULL;
1668    execve(shell, argv, env);
1669    perror(shell);
1670    exit(1);

... yang, seperti yang Anda lihat, secara tanpa syarat dipanggil shelldengan argumen pertama -cdan argumen kedua command. Sebelumnya, shellvariabel diatur ke shell login pengguna sebagaimana dicatat dalam /etc/passwd. commandadalah argumen untuk fungsi ini, dan pada akhirnya diatur ke string membaca kata demi kata dari kawat (lihat session_exec_reqdi file yang sama ). Jadi, server tidak menafsirkan perintah sama sekali, tetapi sebuah shell selalu dipanggil pada remote.

Namun, bagian yang relevan dari spesifikasi protokol SSH tidak tidak muncul untuk meminta perilaku ini; hanya dikatakan

 byte      SSH_MSG_CHANNEL_REQUEST
 uint32    recipient channel
 string    "exec"
 boolean   want reply
 string    command

Pesan ini akan meminta server memulai eksekusi dari perintah yang diberikan. String 'perintah' dapat berisi path. Tindakan pencegahan normal HARUS dilakukan untuk mencegah pelaksanaan perintah yang tidak sah.

Ini mungkin karena tidak semua sistem operasi memiliki konsep shell command-line. Sebagai contoh, itu tidak akan gila untuk server ssh Classic MacOS untuk memberi makan string perintah "exec" ke juru bahasa AppleScript sebagai gantinya.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.