Tại sao nguồn của Bash không cần bit thực thi?


57

Với Bash, sourcecó thể thực thi một tập lệnh mà không cần tập bit thực thi. Đây là hành vi được ghi lại và dự kiến, nhưng điều này không chống lại việc sử dụng bit thực thi?

Tôi biết, điều sourceđó không tạo ra một subshell.


2
Thực tế chmodcó thể cho phép bạn đặt các quyền (bao gồm cả `x) với số bát phân cho một số manh mối về thời đại mà nó xuất phát. Tôi sẽ không ngạc nhiên nếu nó bắt đầu như một chỉ báo nhanh chóng và bẩn thỉu "đây là tệp nhị phân bạn có thể thực thi", từ những ngày trước khi cô ấy được phát minh, nhưng tôi không có bằng chứng nào về điều đó
đưa vào

9
Sau đó, một lần nữa, khi SUID đi vào hoạt động, các mức độ bảo vệ khác nhau cho các loại người dùng khác nhau trở nên quan trọng hơn. Hãy tiếp tục và đọc chương trình SUID của tôi nếu bạn muốn. Nhưng nếu bạn thực thi nó chỉ bằng cách đọc nó, các quyền hạn SUID sẽ không xuất hiện cùng với nó
đưa vào

@infixed Trình tải chương trình Linux thậm chí sẽ không nhìn vào shebang trừ khi bit thực thi được đặt. (Để tự mình lấy sừng của mình một chút: xem tại đây .)
Kyle Strand

Bạn cũng có thể có + xr, nhưng điều đó hơi lạ vì thường có thể đọc nhị phân bằng cách tiêm mã vào quy trình đang chạy
Niklas B.

@KyleStrand bằng cách "nếu bạn thực thi nó chỉ bằng cách đọc nó, các quyền hạn SUID sẽ không đi cùng với nó" Tôi đã hình dung ra một cái gì đó dọc theo dòngcp /sbin/suidexecutable /tmp/mycopy; /tmp/mycopy
đưa vào

Câu trả lời:


36

Bash là một thông dịch viên; nó chấp nhận đầu vào và làm bất cứ điều gì nó muốn. Nó không cần phải chú ý đến bit thực thi. Trong thực tế, Bash là di động và có thể chạy trên các hệ điều hành và hệ thống tệp không có bất kỳ khái niệm nào về một bit thực thi.

Điều quan tâm về bit thực thi là kernel hệ điều hành. execVí dụ, khi nhân Linux thực hiện một hệ thống tệp, nó sẽ kiểm tra xem hệ thống tệp không được gắn với một noexectùy chọn, nó sẽ kiểm tra bit thực thi của tệp chương trình và thực thi bất kỳ yêu cầu nào được áp đặt bởi các mô-đun bảo mật (như SELinux hoặc AppArmor).

Lưu ý rằng bit thực thi là một loại điều khiển khá tùy ý. Ví dụ, trên hệ thống Linux x86-64, bạn có thể bỏ qua xác minh của kernel đối với bit thực thi bằng cách gọi rõ ràng /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2là trình thông dịch :

cp /bin/ls /tmp/
chmod -x /tmp/ls
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 /tmp/ls

Điều này hơi giống với việc tìm nguồn cung cấp mã nguồn Bash trong Bash, ngoại trừ đó ld.solà trình thông dịch và mã mà nó thực thi là mã máy ở định dạng ELF.


3
Sử dụng trình tải như một "trình thông dịch" của "mã byte", tình cờ là một nhị phân thực thi thực sự ..... tuyệt vời. Cảm ơn vì điều đó.
Kyle Strand

@KyleStrand có thể có một số mức độ gián tiếp (sự khác biệt giữa chương trình được lưu trữ và quá trình được tải và cách bộ nhớ được ánh xạ, các trình giám sát khác nhau, VM, v.v.) nhưng về nguyên tắc , mã máy có thể được thực thi trực tiếp bởi CPU (không có vi mã, v.v.) và do đó mã máy không được diễn giải: phần cứng hiểu nó như là một ngôn ngữ bản địa, không có phiên dịch tiếng Anh.
jfs

2
@JFSebastian Vâng, chúng tôi biết rằng đó là mã gốc, do đó "trình thông dịch" nằm trong dấu ngoặc kép. Công việc của ld.solà làm liên kết động.
200_success

@JFSebastian Những gì 200_success nói. Ngoài ra, đó là ý nghĩa của "nhị phân thực thi", và tôi cũng đặt "mã byte" trong dấu ngoặc kép (vì AFAIK "mã byte" thường không được sử dụng để chỉ mã nhị phân thực thi được biên dịch thực tế). Ngoài ra, tên người dùng tốt đẹp.
Kyle Strand

@JFSebastian Tôi tin rằng các CPU giống như x86 hiện đại thực sự là RISC bên trong, với tập lệnh CISC kiểu cũ về cơ bản điều khiển việc thực thi vi mã của các lệnh RISC tương ứng, trong đó một lệnh CISC có thể khiến nhiều lệnh RISC được thực thi. Vì vậy, theo một nghĩa nào đó, việc gọi CPU làm gì với mã máy RISC là "diễn giải", nếu không hoàn toàn chính xác theo quan điểm kỹ thuật, thì ít nhất là một mô hình tinh thần hợp lệ. AES-NI có lẽ là một trong những ví dụ cực đoan hơn: một hướng dẫn CISC cho mỗi vòng AES!
một CVn

57

sourcehoặc dấu chấm. tương đương nhưng tiêu chuẩn không thực thi tập lệnh, nhưng đọc các lệnh từ tệp tập lệnh, sau đó thực thi chúng, từng dòng một, trong môi trường shell hiện tại.

Không có gì chống lại việc sử dụng chút thực là, bởi vì vỏ chỉ cần đọc phép đọc nội dung của tập tin.

Bit thực thi chỉ được yêu cầu khi bạn chạy tập lệnh. Ở đây, shell sẽ fork()xử lý mới sau đó sử dụng execve()hàm để tạo hình ảnh tiến trình mới từ tập lệnh, được yêu cầu là tệp thường xuyên, có thể thực thi được.


5
@alexis: Tôi không chắc là tôi thực sự làm theo. Nếu bạn làm như vậy bash < script, về cơ bản bạn sẽ nhận được kết quả tương tự như source script. Kiểm tra bit thực hiện cung cấp bảo vệ gì?
Trình biên dịch dễ thấy

27
@alexis, đây không phải là công việc của phiên dịch viên để kiểm tra quyền của các tập lệnh mà nó diễn giải. Không làm điều này - không phải Python, không phải Ruby, không phải máy ảo Java, không phải các shell ngẫu nhiên khác. Bit thực thi kiểm soát xem execvhọ của các tòa nhà chọc trời có thể được sử dụng với một tệp thực thi hay không, liệu trình thông dịch sẽ chạy nó hay không. Tại sao mọi người nhầm lẫn (và phá vỡ khả năng đánh giá mã được truyền phát từ các nguồn không phải tệp) bằng cách phá vỡ các quy ước?
Charles Duffy

14
@alexis, ... hơn nữa, có một ý nghĩa có giá trị là có một thư viện shell không thể thực thi được: Nó nói "Tôi là một thư viện, không phải là một lệnh - nguồn cho tôi, đừng thực thi tôi". Nếu không, bạn cần phải viết mã để phát hiện và khắc phục việc lạm dụng mã dự định chỉ có nguồn gốc, nhưng bằng cách không có quyền + x, bạn có thể đảm bảo rằng người dùng không thể (không) sử dụng sai thư viện theo cách như vậy.
Charles Duffy

6
@alexis "đó là một quyết định của các tác giả" Chỉ theo nghĩa là mọi mã có thể khác không được đưa vào là một quyết định. Shell mở tệp để đọc, để đọc các lệnh từ nó. Tôi không mong đợi nó kiểm tra quyền đọc, nó chỉ cố mở tệp và thất bại nếu không thể. Tương tự như vậy, nó không kiểm tra quyền ghi hoặc thực thi, vì những kiểm tra đó không liên quan để đọc tệp.
Randy Orrison

2
@alexis Mặc dù vậy, điều này được sử dụng trong thực tế, ví dụ với virtualenv của python, tập lệnh để kích hoạt nó có nghĩa là có nguồn gốc, và không được thực thi, do đó bin/activatekhông có bit thực thi. Các kịch bản hoàn toàn có thể là thư viện hoặc những thứ khác như thế. Tôi đoán có .sh cũng có thể là một tín hiệu, nhưng có tối thiểu súng chân là tốt: thật tuyệt khi không thể chạy một ./bin/activatecách tình cờ thay vì. bin/activate
daboross

20

Bit thực thi (không giống phần còn lại) trên các tệp nonsetuid và nonsetguid không phải là một cơ chế bảo mật. Bất cứ điều gì bạn có thể đọc, bạn đều có thể chạy gián tiếp và Linux sẽ cho phép bạn đọc gián tiếp bất cứ thứ gì bạn có thể chạy nhưng không đọc trực tiếp (điều đó đủ để tạo ra một lỗ hổng trong khái niệm về x-bit không được đặt (g) uid x-bit biện pháp an ninh).

Đó là một điều tiện lợi: Hãy để hệ thống chạy trực tiếp cho tôi nếu bit được đặt, nếu không tôi cần phải thực hiện gián tiếp ( bash the_script;hoặc một số hack để có được hình ảnh bộ nhớ của tệp thực thi không có quyền đọc ).

Bạn có thể đặt nó để thuận tiện nếu bạn có ý định cả trong nguồn và thực thi không thể chịu được.

Tuy nhiên, rõ ràng, nhiều người triển khai thư viện chia sẻ chia sẻ suy nghĩ của bạn và do đó, nhiều hệ thống yêu cầu các thư viện dùng chung, về cơ bản là tương đương với các phần mềm bảo vệ vỏ, có thể được đánh dấu để có thể sử dụng được. Xem tại sao các thư viện chia sẻ thực thi? .


3
@ Motte001 Họ có thể thực thi các tệp đính kèm nếu họ thực sự muốn. Thật tiện lợi.
PSkocik

2
Bit thực thi không phải để bảo mật: Nếu tôi sở hữu một tệp, tôi có thể tự do với chmodnó và làm cho nó có thể thực thi được. Nó là để sắp xếp dữ liệu từ các chương trình, vì vậy câu hỏi của OP là một câu hỏi hợp lý.
alexis

1
@ Motte001 Đó là một tính năng bảo mật của ứng dụng thư / trình duyệt tệp GUI - nó có thể dễ dàng quyết định rằng việc nhấp đúp vào bất kỳ tệp nào có tên kết thúc ".sh" được thực thi trong một thiết bị đầu cuối (kiểu Windows) hoặc ngược lại là gấp đôi nhấp vào bất kỳ tệp nào trong thư mục "tệp đính kèm đã tải xuống" sẽ gọi ứng dụng xem trước hộp cát tích hợp. Các xbit là chỉ là một nơi thêm nó có thể đọc / viết một gợi ý về những gì để làm.
IMSoP

3
Một lý do chúng ta cần bit thực thi là để chạy các chương trình setuid, đặc biệt là các chương trình được sở hữu bởi root. Mặc dù bạn chắc chắn có thể tự thực hiện chúng, bạn cần HĐH để chạy chúng để chúng có các quyền cần thiết. Trong một số trường hợp khác, nó thực sự chỉ là một tiện lợi.
Waleed Khan

2
"Linux sẽ cho phép bạn đọc bất cứ thứ gì bạn có thể chạy qua / Proc" - Chà, hạt nhân Debian của tôi không: /tmp$ cp /bin/cat ./cat ; chmod a-rw ./cat ; ./cat & cp /proc/$!/exe /tmp/cat2->cp: cannot stat ‘/proc/16260/exe’: Permission denied
ilkkachu

9

Đó là một câu hỏi hay! Unix sử dụng bit thực thi để phân biệt giữa các chương trình và dữ liệu. HĐH không yêu cầu bit thực thi, vì tập lệnh có nguồn gốc không được chuyển cho HĐH để thực thi như một quy trình mới. Nhưng shell xử lý một tập lệnh có nguồn gốc như một chương trình và sẽ tìm kiếm $PATHtệp bạn muốn lấy. Vì vậy, bản thân shell có thể đã yêu cầu cấp phép thực thi cho các tệp có nguồn gốc; Nhưng nó đã không.

Câu hỏi phải được đưa ra từ lâu. Thiết kế của vỏ Bourne là kết quả của "một chuỗi dài sửa đổi, đối thoại, thảo luận" giữa những người từ chối của Bell Labs và nhiều quyết định thiết kế đã được thảo luận bởi SR Bourne và những người khác trong nhiều năm qua. Thật không may, cái nhìn nhanh của tôi không tìm thấy bất kỳ cuộc thảo luận nào về tính năng nguồn (để bảo vệ tôi, thật khó để google cho nó). Những gì tôi đã tìm thấy là "." Lệnh này không xuất hiện trong phần giới thiệu đầu tiên về shell này bởi chính Bourne, nhưng nó có mặt trong phiên bản 7 hoàn thiện hơn .

Cơ quan vắng mặt, đây là cách giải thích của riêng tôi:

  1. Các .lệnh, aka source, là trong bao gồm văn bản có hiệu lực (như #includetrong tiền xử lý C) vào mã nguồn của kịch bản thực hiện hoặc phiên tương tác. Như vậy, tập tin đi kèm được cho là không được "thực thi".

  2. Triết lý Unix luôn là cung cấp cho các lập trình viên đủ dây để tự treo. Quá nhiều hạn chế cầm tay và tùy tiện chỉ cản trở. Chỉ gần đây, một số bản phân phối đã rm -r /từ chối làm những gì bạn yêu cầu. (Lệnh này nói rmđể xóa tất cả mọi thứ trên máy tính của bạn. Đừng cố gắng nó như là người chủ! Hoặc tốt hơn, chứ không phải ở tất cả.) Vì vậy, có lẽ Bourne tại al. chỉ quyết định rằng khi bạn cố gắng tìm nguồn tệp, bạn được cho là biết bạn đang làm gì. Điều đó cũng tránh được công việc không cần thiết, và sau đó chu kỳ rất quan trọng.


Nhưng người ta chắc chắn không nên làm những gì @cat nói.
TUYỆT VỜI

Tôi ngạc nhiên khi nó không được gắn cờ và xóa @TOOGAM nhưng tôi đã đặt vào / s!
mèo

Ở đó, tôi không biết những gì @cat đã viết nhưng tôi đã chứng minh câu trả lời nhiều hơn. (Nhưng thôi nào, nó đã đủ rõ ràng từ bối cảnh rồi.)
alexis

4

Theo như HĐH, một tập tin chứa shell script chỉ là dữ liệu. Nếu bạn chuyển tên của tệp dữ liệu đó cho sourcelệnh hoặc chuyển nó trên dòng lệnh cho lệnh gọi bash shell, tất cả các hệ điều hành nhìn thấy là một chuỗi xảy ra trùng với tên của tệp chứa dữ liệu.

Làm thế nào bit thực thi sẽ có liên quan trong trường hợp đó?


3

Sự khác biệt rất quan trọng vì bạn có thể có một tệp lệnh shell không hữu ích như một tệp thực thi, nhưng chỉ hữu ích khi có nguồn gốc. Đối với tệp này, bạn có thể tắt bit thực thi và sau đó nó sẽ không bao giờ được truy cập trừ khi rõ ràng trong lệnh nguồn. Lý do cho một điều như vậy là để có tác dụng phụ trên vỏ nó được chạy từ. Đối với một ví dụ cụ thể, tôi có một tập lệnh gọi là fix_path để xem và sửa đổi đường dẫn.


1

Chỉ trong trường hợp bất kỳ ai quan tâm đến các nghiên cứu tiếp theo và / hoặc làm rõ: Trong lớp vỏ gần như POSIX đã triển khai cách đây một thời gian, các hoạt động bên trong của các hàm 'exec_program ()' và các hàm 'buildin_source ()' rất mẫu mực. Trong các chức năng đó, bạn thấy chính xác sự khác biệt giữa chúng là gì:

https://github.com/rsenn/shish/blob/master/src/builtin/builtin_source.c

https://github.com/rsenn/shish/blob/master/src/exec/exec_program.c

về cơ bản nguồn có thể được xem như là shell tạm thời chuyển hướng mô tả tệp bên trong của nó, nơi nó phân tích cú pháp shell từ (thiết bị đầu cuối trong chế độ tương tác). vì thế nó rất giống với chuyển hướng khác như <input_file.txt>>append_to_something.listvà những chỉ cần đóng và mở file.

vì vậy việc thực thi được xử lý bởi lệnh execve()gọi hệ thống mà bit thực thi là bắt buộc.

Tôi nhớ rằng đã thấy một số hệ thống cho phép thực thi các nhị phân ELF / a.out, nhưng thông qua việc thực thi "/lib/ld-dynamic-linker.so" và với chương trình nhị phân (không có bit exec) làm đối số đầu tiên. Tôi tin rằng đó là trên một số máy DEC Alpha hoặc VAX (Có thể là SCO Unix?)


-2

Một góc nhìn khác:

Kịch bản có nguồn gốc về cơ bản bao gồm các nội dung shell và các lệnh gọi chương trình. Shell dựng (với sourcemột trong số chúng) là các bộ phận của vỏ và vỏ phải được thực thi ngay từ đầu. Mỗi chương trình được gọi (đó là ELF, một tập lệnh khác với shebang, bất cứ điều gì) phải được thiết lập bit thực thi, nếu không nó sẽ không chạy.

Vì vậy, nó không chống lại việc sử dụng bit thực thi, bởi vì không có gì nếu không có bit thực thi sẽ chạy. Việc xác nhận không xảy ra đối với toàn bộ tập lệnh có nguồn gốc; nó được thực hiện cho từng phần riêng biệt, nhưng nó là.


Các bản dựng Shell không có nguồn gốc từ tập lệnh shell. Thông thường chúng là một phần của shell thực thi. Trong ksh93, zsh và một vài shell khác, chúng có thể là phần mở rộng shell.
fpmurphy

@ fpmurphy1 Đây không phải là câu của tôi "shell dựng (...) là một phần của vỏ"?
Kamil Maciorowski
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.