it-swarm-vi.com

Cho phép setuid trên tập lệnh Shell

Bit quyền setuid bảo Linux chạy chương trình với id người dùng hiệu quả của chủ sở hữu thay vì người thực thi:

> cat setuid-test.c

#include <stdio.h>
#include <unistd.h>

int main(int argc, char** argv) {
    printf("%d", geteuid());
    return 0;
}

> gcc -o setuid-test setuid-test.c
> ./setuid-test

1000

> Sudo chown nobody ./setuid-test; Sudo chmod +s ./setuid-test
> ./setuid-test

65534

Tuy nhiên, điều này chỉ áp dụng cho thực thi; Các tập lệnh Shell bỏ qua bit setuid:

> cat setuid-test2

#!/bin/bash
id -u

> ./setuid-test2

1000

> Sudo chown nobody ./setuid-test2; Sudo chmod +s ./setuid-test2
> ./setuid-test2

1000

Wikipedia nói :

Do khả năng xảy ra lỗi bảo mật tăng lên, nhiều hệ điều hành bỏ qua thuộc tính setuid khi áp dụng cho các tập lệnh Shell thực thi.

Giả sử tôi sẵn sàng chấp nhận những rủi ro đó, có cách nào để bảo Linux xử lý bit setuid giống như trên các tập lệnh Shell giống như trên các tệp thực thi không?

Nếu không, có một cách giải quyết chung cho vấn đề này? Giải pháp hiện tại của tôi là thêm mục sudoers để cho phép ALL chạy tập lệnh đã cho như người dùng tôi muốn nó chạy, với NOPASSWD để tránh mật khẩu Nhắc. Nhược điểm chính của điều đó là nhu cầu nhập sudoers mỗi khi tôi muốn làm điều này và nhu cầu của người gọi đến Sudo some-script Thay vì chỉ some-script

195
Michael Mrozek

Linux bỏ qua bit setuid¹ trên tất cả các tệp thực thi được giải thích (nghĩa là các tệp thực thi bắt đầu bằng dòng #!). comp.unix.questions FAQ giải thích các vấn đề bảo mật với tập lệnh Shell setuid. Những vấn đề này có hai loại: liên quan đến Shebang và liên quan đến Shell; Tôi đi vào chi tiết hơn dưới đây.

Nếu bạn không quan tâm đến bảo mật và muốn cho phép các tập lệnh setuid, trong Linux, bạn sẽ cần vá kernel. Kể từ hạt nhân 3.x, tôi nghĩ bạn cần thêm một cuộc gọi vào install_exec_creds trong hàm load_script , trước khi gọi đến open_exec, Nhưng tôi chưa thử nghiệm.


Setuid Shebang

Có một điều kiện chủng tộc vốn có theo cách Shebang (#!) Thường được triển khai:

  1. Hạt nhân mở tệp thực thi và thấy rằng nó bắt đầu bằng #!.
  2. Hạt nhân đóng thực thi và thay vào đó mở trình thông dịch.
  3. Nhân chèn đường dẫn đến tập lệnh vào danh sách đối số (dưới dạng argv[1]) Và thực thi trình thông dịch.

Nếu tập lệnh setuid được cho phép với triển khai này, kẻ tấn công có thể gọi một tập lệnh tùy ý bằng cách tạo một liên kết tượng trưng đến tập lệnh setuid hiện có, thực thi nó và sắp xếp để thay đổi liên kết sau khi kernel đã thực hiện bước 1 và trước khi trình thông dịch chuyển sang mở đối số đầu tiên của nó. Vì lý do này, hầu hết các đơn vị đều bỏ qua bit setuid khi họ phát hiện ra một Shebang.

Một cách để bảo mật việc triển khai này là cho kernel khóa tệp script cho đến khi trình thông dịch đã mở nó (lưu ý rằng điều này phải ngăn chặn không chỉ hủy liên kết hoặc ghi đè tệp, mà còn đổi tên bất kỳ thư mục nào trong đường dẫn). Nhưng các hệ thống unix có xu hướng né tránh các khóa bắt buộc và các liên kết tượng trưng sẽ làm cho một tính năng khóa chính xác đặc biệt khó khăn và xâm lấn. Tôi không nghĩ có ai làm theo cách này.

Một vài hệ thống unix (chủ yếu là OpenBSD, NetBSD và Mac OS X, tất cả đều yêu cầu cài đặt kernel để bật) thực hiện setuid an toàn Shebang bằng cách sử dụng bổ sung Tính năng: đường dẫn /dev/fd/N đề cập đến tệp đã được mở trên mô tả tệp [~ # ~] n [~ # ~] (vì vậy, mở /dev/fd/N tương đương với dup(N)). Nhiều hệ thống unix (bao gồm cả Linux) có /dev/fd Nhưng không có tập lệnh setuid.

  1. Hạt nhân mở tệp thực thi và thấy rằng nó bắt đầu bằng #!. Giả sử mô tả tệp cho tệp thực thi là 3.
  2. Nhân mở trình thông dịch.
  3. Hạt nhân chèn /dev/fd/3 Danh sách đối số (dưới dạng argv[1]) Và thực thi trình thông dịch.

Trang Shebang của Sven Mascheck có nhiều thông tin về Shebang trên các đơn vị, bao gồm hỗ trợ setuid .


Thông dịch viên Setuid

Giả sử bạn đã quản lý để làm cho chương trình của bạn chạy bằng root, vì hệ điều hành của bạn hỗ trợ setuid Shebang hoặc vì bạn đã sử dụng trình bao bọc nhị phân gốc (chẳng hạn như Sudo). Bạn đã mở một lỗ hổng bảo mật? Có thể . Vấn đề ở đây là không về diễn giải so với các chương trình được biên dịch. Vấn đề là hệ thống thời gian chạy của bạn có hoạt động an toàn hay không nếu được thực thi với các đặc quyền.

  • Bất kỳ tệp thực thi nhị phân gốc được liên kết động nào đều được giải thích bởi trình tải động (ví dụ /lib/ld.so), Tải các thư viện động theo yêu cầu chương trình. Trên nhiều thông báo, bạn có thể định cấu hình đường dẫn tìm kiếm cho các thư viện động thông qua môi trường (LD_LIBRARY_PATH Là tên chung cho biến môi trường) và thậm chí tải các thư viện bổ sung vào tất cả các nhị phân đã thực hiện (LD_PRELOAD) . Kẻ gây ra chương trình có thể thực thi mã tùy ý trong ngữ cảnh của chương trình đó bằng cách đặt libc.so Được chế tạo đặc biệt trong $LD_LIBRARY_PATH (Trong số các chiến thuật khác). Tất cả các hệ thống lành mạnh đều bỏ qua các biến LD_* Trong các tệp thực thi setuid.

  • Trong shell như sh, csh và các dẫn xuất, các biến môi trường tự động trở thành tham số Shell. Thông qua các tham số như PATH, IFS, và nhiều hơn nữa, kẻ xâm lược tập lệnh có nhiều cơ hội để thực thi mã tùy ý trong ngữ cảnh của tập lệnh Shell. Một số shell đặt các biến này thành mặc định lành mạnh nếu chúng phát hiện ra rằng tập lệnh đã được gọi với các đặc quyền, nhưng tôi không biết rằng có bất kỳ triển khai cụ thể nào mà tôi sẽ tin tưởng.

  • Hầu hết các môi trường thời gian chạy (dù là mã gốc, mã byte hoặc được giải thích) đều có các tính năng tương tự. Rất ít người thực hiện các biện pháp phòng ngừa đặc biệt trong các tệp thực thi setuid, mặc dù các mã chạy mã gốc thường không làm gì lạ hơn liên kết động (điều này có các biện pháp phòng ngừa).

  • Perl là một ngoại lệ đáng chú ý. Nó hỗ trợ rõ ràng các tập lệnh setuid một cách an toàn. Trong thực tế, tập lệnh của bạn có thể chạy setuid ngay cả khi hệ điều hành của bạn bỏ qua bit setuid trên tập lệnh. Điều này là do Perl vận chuyển với trình trợ giúp gốc setuid thực hiện các kiểm tra cần thiết và khôi phục trình thông dịch trên các tập lệnh mong muốn với các đặc quyền mong muốn. Điều này được giải thích trong hướng dẫn perlsec . Trước đây, các tập lệnh Perl setuid cần #!/usr/bin/suidperl -wT Thay vì #!/usr/bin/Perl -wT, Nhưng trên hầu hết các hệ thống hiện đại, #!/usr/bin/Perl -wT Là đủ.

Lưu ý rằng việc sử dụng trình bao bọc nhị phân riêng không có gì để ngăn chặn các vấn đề này . Trong thực tế, nó có thể làm cho tình huống tệ hơn, vì nó có thể ngăn môi trường thời gian chạy của bạn phát hiện ra rằng nó được gọi với các đặc quyền và bỏ qua cấu hình thời gian chạy của nó.

Trình bao bọc nhị phân riêng có thể làm cho tập lệnh Shell an toàn nếu trình bao bọc vệ sinh môi trường . Kịch bản phải lưu ý không đưa ra quá nhiều giả định (ví dụ: về thư mục hiện tại) nhưng điều này sẽ đi. Bạn có thể sử dụng Sudo cho điều này với điều kiện là nó được thiết lập để vệ sinh môi trường. Các biến trong danh sách đen dễ bị lỗi, vì vậy luôn có danh sách trắng. Với Sudo, hãy đảm bảo rằng tùy chọn env_reset Đã được bật, rằng setenv đã tắt và env_fileenv_keep Chỉ chứa các biến vô hại.


TL, DR:

  • Setuid Shebang không an toàn nhưng thường bị bỏ qua.
  • Nếu bạn chạy một chương trình với các đặc quyền (thông qua Sudo hoặc setuid), hãy viết mã gốc hoặc Perl hoặc khởi động chương trình bằng một trình bao bọc vệ sinh môi trường (chẳng hạn như Sudo với tùy chọn env_reset).

¹ Cuộc thảo luận này được áp dụng như nhau nếu bạn thay thế set set set cả hai đều bị bỏ qua bởi nhân Linux trên các tập lệnh

213

Một cách để giải quyết vấn đề này là gọi tập lệnh Shell từ một chương trình có thể sử dụng bit setuid.
[.__.] Nó giống như Sudo. Ví dụ, đây là cách bạn sẽ thực hiện điều này trong chương trình C:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    setuid( 0 );   // you can set it at run time also
    system( "/home/pubuntu/setuid-test2.sh" );
    return 0;
 }

Lưu nó dưới dạng setuid-test2.c.
[.__.] biên dịch
[.__.] Bây giờ hãy thực hiện setuid trên chương trình nhị phân này:

su - nobody   
[enter password]  
chown nobody:nobody a.out  
chmod 4755 a.out  

Bây giờ, bạn sẽ có thể chạy nó và bạn sẽ thấy tập lệnh của mình được thực thi mà không có quyền.
[.___.

57
Hemant

Tôi tiền tố một vài tập lệnh trong thuyền này, do đó:

#!/bin/sh
[ "root" != "$USER" ] && exec Sudo $0 "[email protected]"

Lưu ý rằng điều này không sử dụng setuid mà chỉ thực hiện tệp hiện tại với Sudo.

24
rcrowley

Nếu bạn muốn tránh gọi Sudo some_script bạn chỉ có thể làm:

  #!/ust/bin/env sh

  Sudo /usr/local/scripts/your_script

Các chương trình SETUID cần được thiết kế hết sức cẩn thận khi chúng chạy với quyền root và người dùng có quyền kiểm soát lớn đối với chúng. Họ cần phải tỉnh táo - kiểm tra mọi thứ. Bạn không thể làm điều đó với các tập lệnh vì:

  • Shell là những phần mềm lớn tương tác nhiều với người dùng. Gần như không thể kiểm tra tất cả mọi thứ - đặc biệt là vì hầu hết các mã không có ý định chạy trong chế độ như vậy.
  • Các kịch bản là một giải pháp chủ yếu nhanh chóng và thường không được chuẩn bị cẩn thận đến mức chúng sẽ cho phép setuid. Chúng có nhiều tính năng nguy hiểm tiềm tàng.
  • Họ phụ thuộc rất nhiều vào các chương trình khác. Nó không đủ để Shell được kiểm tra. sed, awk, v.v. cũng cần phải được kiểm tra

Xin lưu ý rằng Sudo cung cấp một số kiểm tra độ tỉnh táo nhưng không đủ - kiểm tra từng dòng trong mã của riêng bạn.

Như một lưu ý cuối cùng: xem xét sử dụng khả năng. Chúng cho phép bạn cung cấp một quy trình đang chạy như một đặc quyền đặc biệt của người dùng thường yêu cầu quyền root. Tuy nhiên, ví dụ, trong khi ping cần thao tác mạng, thì không cần phải có quyền truy cập vào tệp. Tôi không chắc chắn tuy nhiên nếu chúng được thừa kế.

12
Maciej Piechotka

lệnh super [-r reqpath] [args]

Super cho phép người dùng được chỉ định thực thi các tập lệnh (hoặc các lệnh khác) như thể chúng là root; hoặc nó có thể đặt các nhóm uid, gid và/hoặc bổ sung trên cơ sở mỗi lệnh trước khi thực hiện lệnh. Nó được dự định là một sự thay thế an toàn để tạo tập lệnh gốc setuid. Super cũng cho phép người dùng thông thường cung cấp các lệnh để thực thi bởi người khác; chúng thực thi với uid, gid và các nhóm người dùng cung cấp lệnh.

Super giới thiệu một tệp `` super.tab '' để xem liệu người dùng có được phép thực thi lệnh được yêu cầu hay không. Nếu được phép, super sẽ thực thi pgm [args], trong đó pgm là chương trình được liên kết với lệnh này. (Root được phép thực thi theo mặc định, nhưng vẫn có thể bị từ chối nếu quy tắc loại trừ root. Người dùng thông thường không được phép thực thi theo mặc định.)

Nếu lệnh là một liên kết tượng trưng (hoặc liên kết cứng) với siêu chương trình, thì việc gõ% lệnh args tương đương với việc nhập% super lệnh args (Lệnh không phải là siêu hoặc siêu sẽ không nhận ra rằng nó được gọi thông qua một liên kết.)

http://www.ucolick.org/~will/RUE/super/README

http://manpages.ubfox.com/manpages/utopic/en/man1/super.1.html

5
Nizam Mohamed

Bạn có thể tạo bí danh cho Sudo + tên của tập lệnh. Tất nhiên, việc đó còn tốn nhiều công sức hơn để thiết lập, vì sau đó bạn cũng phải thiết lập bí danh, nhưng nó giúp bạn không phải gõ Sudo.

Nhưng nếu bạn không bận tâm đến những rủi ro bảo mật khủng khiếp, hãy sử dụng Shell setuid làm trình thông dịch cho tập lệnh Shell. Không biết điều đó sẽ làm việc cho bạn, nhưng tôi đoán nó có thể.

Tuy nhiên, hãy để tôi nói rằng tôi khuyên bạn không nên thực sự làm điều này. Tôi chỉ đề cập đến nó cho mục đích giáo dục ;-)

4
wzzrd

Nếu vì lý do nào đó Sudo không có sẵn, bạn có thể viết một tập lệnh bao bọc mỏng trong C:

#include <unistd.h>
int main() {
    setuid(0);
    execle("/bin/bash","bash","/full/path/to/script",(char*) NULL,(char*) NULL);
}

Và một khi bạn biên dịch nó, đặt nó là setuid với chmod 4511 wrapper_script.

Điều này tương tự với một câu trả lời được đăng khác, nhưng chạy tập lệnh với môi trường sạch sẽ và sử dụng rõ ràng /bin/bash Thay vì Shell được gọi bởi system(), và do đó sẽ đóng một số lỗ hổng bảo mật tiềm năng.

Lưu ý rằng điều này loại bỏ hoàn toàn môi trường. Nếu bạn muốn sử dụng một số biến môi trường mà không mở các lỗ hổng, bạn thực sự chỉ cần sử dụng Sudo.

Rõ ràng, bạn muốn chắc chắn rằng tập lệnh chỉ có thể ghi được bằng root.

2
Chris