Làm thế nào để ssh đối số dòng lệnh từ xa được phân tích cú pháp


11

Tôi đã thấy các câu hỏi và câu trả lời về việc cần phải thoát hai đối số thành các lệnh ssh từ xa. Câu hỏi của tôi là: Chính xác thì việc phân tích cú pháp thứ hai được thực hiện ở đâu và khi nào?

Nếu tôi chạy như sau:

$ ssh otherhost pstree -a -p

Tôi thấy sau đây trong đầu ra:

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

Quá trình cha của lệnh từ xa ( pstree) là sshd, dường như không có bất kỳ shell nào ở đó sẽ phân tích cú pháp các đối số dòng lệnh cho lệnh từ xa, do đó dường như không cần phải trích dẫn hoặc thoát hai lần ( nhưng nó chắc chắn là). Nếu thay vào đó tôi ssh ở đó trước và nhận được một vỏ đăng nhập, và sau đó chạy pstree -a -ptôi thấy sau đây trong đầu ra:

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

Vì vậy, rõ ràng có một bashshell ở đó sẽ thực hiện phân tích cú pháp dòng lệnh trong trường hợp đó. Nhưng trường hợp tôi sử dụng lệnh từ xa trực tiếp, dường như không có vỏ, vậy tại sao trích dẫn kép lại cần thiết?

Câu trả lời:


22

Luôn có một vỏ từ xa. Trong giao thức SSH, máy khách sẽ gửi cho máy chủ một chuỗi để thực thi. Máy khách dòng lệnh SSH lấy các đối số dòng lệnh của nó và nối chúng với một khoảng trắng giữa các đối số. Máy chủ lấy chuỗi đó, chạy shell đăng nhập của người dùng và chuyển chuỗi đó.

Không thể bỏ qua vỏ từ xa. Giao thức không có bất cứ điều gì như gửi một chuỗi các chuỗi có thể được phân tích cú pháp dưới dạng một mảng argv trên máy chủ. Và máy chủ SSH sẽ không bỏ qua shell từ xa vì đó có thể là một hạn chế bảo mật: sử dụng chương trình bị hạn chế vì shell của người dùng là cách cung cấp tài khoản bị hạn chế chỉ được phép chạy một số lệnh nhất định (ví dụ: tài khoản chỉ có rsync hoặc một tài khoản chỉ git).

Bạn có thể không thấy vỏ trong pstreevì nó có thể đã biến mất. Nhiều shell có tối ưu hóa trong đó nếu chúng phát hiện ra rằng chúng sắp thực hiện lệnh chạy lệnh bên ngoài này, hãy đợi nó hoàn thành và thoát với trạng thái lệnh của lệnh, sau đó shell chạy ra execvelệnh của lệnh bên ngoài này. Đây là những gì xảy ra trong ví dụ đầu tiên của bạn. Tương phản ba lệnh sau:

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

Hai cái đầu giống hệt nhau: máy khách gửi chính xác cùng một dữ liệu đến máy chủ. Cái thứ ba gửi một lệnh shell để đánh bại tối ưu hóa exec của shell.


2
ha! không thể tin rằng bạn đánh bại tôi để trả lời câu hỏi của riêng tôi. Tôi đã tìm ra nó giữa chừng khi đăng câu hỏi và hình dung tôi chỉ nên tiếp tục với việc hỏi và tự trả lời nó.
chỉ

10

Tôi nghĩ rằng tôi đã tìm ra nó:

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

Các đối số pstreelà: hiển thị các đối số dòng lệnh, hiển thị các pids và chỉ hiển thị các tiến trình cha của pid đã cho. Đây '$$'là một biến shell đặc biệt mà bash sẽ thay thế bằng pid của chính nó khi bash đánh giá các đối số dòng lệnh. Nó được trích dẫn một lần để ngăn nó khỏi bị diễn giải bởi vỏ địa phương của tôi. Nhưng nó không nghi ngờ được trích dẫn hoặc thoát ra để cho phép nó được giải thích bởi shell từ xa.

Như chúng ta có thể thấy, nó được thay thế bằng 12001pid của vỏ. Chúng ta cũng có thể thấy từ đầu ra: pstree,12001rằng quá trình với pid 12001 là chính pstree. Vậy pstreelà vỏ?

Những gì tôi thu thập được đang diễn ra ở đó là nó bashđang được gọi và nó đang phân tích cú pháp các đối số dòng lệnh, nhưng sau đó nó gọi execđể thay thế chính nó bằng lệnh đang được chạy.

Có vẻ như nó chỉ thực hiện điều này trong trường hợp một lệnh từ xa duy nhất:

$ 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

Trong trường hợp này, tôi yêu cầu hai lệnh được chạy: pstreetheo sau echo. Và chúng ta có thể thấy ở đây bashthực tế xuất hiện trong cây quy trình với tư cách là cha mẹ của pstree.


Vâng + 1. Nó minh họa những gì Gilles đặt chính thức hơn trước và thứ hai được minh họa. Có lẽ cho anh ta tín dụng cho câu trả lời sớm của mình là theo thứ tự?
Cbhihe

0

Để hỗ trợ những gì các câu trả lời khác đã nói, tôi đã tra cứu mã gọi các lệnh trên điều khiển từ xa, 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);

... Mà, như bạn có thể thấy, gọi vô điều kiện shellvới đối số -cthứ nhất và đối số thứ hai command. Trước đó, shellbiến được đặt thành vỏ đăng nhập của người dùng như được ghi trong /etc/passwd. commandlà một đối số cho hàm này và cuối cùng được đặt thành một chuỗi đọc nguyên văn ngoài dây (xem session_exec_reqtrong cùng một tệp ). Vì vậy, máy chủ hoàn toàn không diễn giải lệnh, nhưng shell luôn được gọi trên điều khiển từ xa.

Tuy nhiên, một phần có liên quan của đặc tả giao thức SSH không không xuất hiện để yêu cầu hành vi này; nó chỉ nói

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

Thông báo này sẽ yêu cầu máy chủ bắt đầu thực hiện lệnh đã cho. Chuỗi 'lệnh' có thể chứa một đường dẫn. Các biện pháp phòng ngừa thông thường PHẢI được thực hiện để ngăn chặn việc thực hiện các lệnh trái phép.

Điều này có lẽ là do không phải tất cả các hệ điều hành đều có khái niệm về trình vỏ dòng lệnh. Chẳng hạn, máy chủ ssh MacOS cổ điển sẽ không cung cấp chuỗi lệnh "exec" cho trình thông dịch AppleScript .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.