2009-02-17 7 views
24

Tôi cần chạy tập lệnh bash dưới dạng root (mật khẩu sudo hoặc su không khả thi) và vì bạn không thể thiết lập tập lệnh trong Linux, tôi đã nghĩ đến việc gọi nó từ một thực thi và làm setuid:Gọi một tập lệnh từ chương trình C gốc setuid - tập lệnh không chạy dưới dạng root

$ cat wrapper.c 
int main(void) 
{ 
     system("/bin/bash ./should_run_as_root.sh"); 
} 
$ gcc -o wrapper wrapper.c 
$ sudo chown root wrapper 
$ sudo chmod ug+s wrapper 
$ ll wrapper 
-rwsr-sr-x 1 root users 6667 2009-02-17 11:11 wrapper 
$ 

này hoạt động - như trong kịch bản chạy một cách chính xác - nhưng kịch bản chạy như những người dùng thực hiện "./wrapper".

Tại sao? Và làm thế nào để thực hiện chính xác điều này?

Cảm ơn!

+2

Vì lý do đằng sau các câu trả lời bên dưới, hãy xem 'hệ thống người dùng' và http://stackoverflow.com/questions/1051370/why-do-i-need-setuid0-within-a-setuid-root-c- program-that-calls-an-administrati –

Trả lời

38

Kể từ khi các bit suid về thực thi chỉ thay đổi UID hiệu quả (euid) thực thi sẽ chạy như, và không phải là UID thực (RUID) mà getuid() lợi nhuận, và thêm vào những hạn chế trên suid giải thích kịch bản (bất kỳ đầu thực thi với "#!"), một số vỏ như bash như một biện pháp an toàn bổ sung sẽ đặt EUID trở lại RUID trong trường hợp này, bạn sẽ cần sử dụng cuộc gọi setuid(0) trong mã C trước khi thực thi tập lệnh.

Xem man trang của setuid, seteuid, getuid, và geteuid để học ngữ nghĩa chính xác của UID thực và hiệu quả.

(CẢNH BÁO) Dĩ nhiên, đây là một điểm thích hợp để đề cập đến hạn chế về suid script trong nhiều hệ thống Unix, vỏ và phiên dịch, ở đó vì một lý do, đó là nếu kịch bản không phải là rất cẩn thận về việc khử trùng đầu vào của nó và trạng thái của môi trường khi nó được thực hiện, chúng nguy hiểm và có thể được khai thác để leo thang bảo mật. Vì vậy, hãy rất cẩn thận khi làm điều này. Đặt quyền truy cập vào tập lệnh và trình bao bọc của bạn một cách nghiêm ngặt nhất có thể, chỉ cho phép tập lệnh rất cụ thể này mà bạn dự định thực hiện và xóa môi trường trong chương trình C của bạn trước khi bắt đầu tập lệnh, thiết lập các biến môi trường chẳng hạn như PATH là cần thiết theo thứ tự đúng và không có thư mục nào có thể ghi được cho người khác.

+0

Bạn có an toàn hơn để lưu UID gốc và đặt lại vào cuối chương trình không? – skinp

+0

Nếu chương trình của bạn làm những việc khác sau khi tập lệnh kết thúc, điều đó không cần điều này, thì có. Nếu tất cả những gì bạn làm là quay trở lại, thì nó không thực sự quan trọng. Trong trường hợp điển hình, một trình bao bọc kịch bản lệnh sẽ sử dụng exec và không phải hệ thống (...), do đó, trình bao bọc sẽ không chạy sau khi kịch bản kết thúc nữa. –

+0

@TomAlsberg Bạn có biết mã nguồn/tài liệu để xác nhận hành vi của Bash cho 'auto reset uid' này không? – steveyang

6

Một điều cần lưu ý ở đây là giới hạn ở đây là từ bash chứ không phải chính hệ thống * nix. Bash thực sự thực hiện xác minh trên các kịch bản SUID để chỉ thực hiện chúng với gốc EUID. Nếu bạn có vỏ cũ hơn, bạn thường sẽ có được những gì bạn muốn ra khỏi hộp. Ví dụ, sh không làm cho loại này xác minh:

$ cat wrapper.c 
int main(void) 
{ 
      system("/bin/sh -c whoami"); 
} 

$ ls -l wrapper 
-rwsr-sr-x 1 root users 8887 Feb 17 14:15 wrapper 
$ ./wrapper 
root 

Với bash:

$ cat wrapper.c 
int main(void) 
{ 
      system("/bin/bash -c whoami"); 
} 

$ ls -l wrapper 
-rwsr-sr-x 1 root users 8887 Feb 17 14:18 wrapper 
$ ./wrapper 
skinp 

Tuy nhiên, câu trả lời của Tom nói chung là con đường để đi để thực hiện một wrapper cho các chương trình SUID root

+0

Bạn đúng, tất nhiên. Linux xử lý hạn chế các script suid bằng cách kiểm tra chế độ thực thi trong cuộc gọi exec, và hạn chế này là một biện pháp an toàn bổ sung của một số trình thông dịch. Không chắc tại sao tôi lại bị nhầm lẫn khi tôi viết cái này. Tôi đã sửa lại câu trả lời của mình. Cảm ơn! –

+0

Và dĩ nhiên tôi đã bỏ lỡ điều đó trong mã của OP, anh ấy không thực sự thực hiện một kịch bản với #! thông số kỹ thuật của trình thông dịch, nhưng gọi trực tiếp bash, do đó, hạn chế của hạt nhân sẽ không áp dụng trong trường hợp này - nhưng hạt nhân không có hạn chế này cho #! kịch bản với chế độ tập tin suid. –

+0

Không hoạt động (nữa). Một mặt, 'hệ thống người đàn ông' nói' Không sử dụng hệ thống() từ một chương trình có các đặc quyền set-user-ID hoặc set-group-ID, bởi vì ... '. Mặt khác, điều này hoạt động trong Ubuntu 14.04 nhưng không có trong Ubuntu 16.04. – anumi

3

Thêm setuid (0) vào tập lệnh và tuân thủ. Nó shoudl làm việc sau này.

$ cat wrapper.c 
int main(void) 
{ 
     setuid(0); 
     system("/bin/bash ./should_run_as_root.sh"); 
} 
$ gcc -o wrapper wrapper.c 
$ sudo chown root wrapper 
$ sudo chmod ug+s wrapper 
$ ll wrapper 
-rwsr-sr-x 1 root users 6667 2009-02-17 11:11 wrapper 
$ 
+2

Không sử dụng hệ thống() từ một chương trình có đặc quyền set-user-ID hoặc set-group-ID, vì các giá trị lạ cho một số biến môi trường có thể được sử dụng để phá hoại tính toàn vẹn của hệ thống. Sử dụng hàm exec (3) của các hàm thay vào đó, nhưng không phải execlp (3) hoặc execvp (3). system() sẽ không, trên thực tế, hoạt động tốt từ các chương trình có ID người dùng thiết lập hoặc đặt- đặc quyền nhóm ID trên các hệ thống mà/bin/sh là bash phiên bản 2, vì bash 2 giảm đặc quyền khi khởi động. (Debian sử dụng bash đã sửa đổi không thực hiện điều này khi được gọi là sh.) – cateof

0

Tại sao sudo không thể thực hiện được?Nó tránh hoành lỗ hổng bảo mật như:

bash-3.2$ cat test 
#!/bin/bash 
echo ima shell script durp durp 
bash-3.2$ chmod +x test 
bash-3.2$ ./test 
heh heh 
bash-3.2$ 

Do môi trường không được vệ sinh đúng cách, ví dụ như trong trường hợp này:

export echo='() { builtin echo heh heh; }' 

sudo sanitizes trường hợp này, và các trường hợp cạnh có lẽ khác và gotchas rằng cũng sẽ không viết vào một wrapper suid tùy chỉnh.

+0

man sudo: “Chạy tập lệnh shell qua sudo có thể hiển thị cùng một lỗi hạt nhân làm cho tập lệnh shell setuid không an toàn trên một số hệ điều hành ...” wrapper để làm kịch bản suid có lẽ là tốt nhất, nhưng bạn có biết một? Sudo là không an toàn ở đây, bởi vì nó dựa vào hạt nhân để truyền kịch bản qua/dev/fd/...! –

1

Các ví dụ là không an toàn khủng khiếp và cho phép bất kỳ ai có hai bit kiến ​​thức để chạy bất kỳ chương trình nào họ muốn làm người dùng setuid.

Không bao giờ đi qua vỏ trừ khi bạn khử trùng môi trường trước, hầu hết các ví dụ được hiển thị ở đây đều dễ bị IFS và PATH đặt trước khi chạy.