Làm cách nào để sử dụng lệnh tích hợp trong bash một cách rõ ràng và an toàn


19

Có một câu hỏi tương tự liên quan đến kịch bản 'gói', nơi bạn muốn thay thế cdbằng một lệnh gọi hàm dựng sẵn cd.

Tuy nhiên, dưới ánh sáng của shellshock et al và biết rằng bash nhập các hàm từ môi trường, tôi đã thực hiện một vài thử nghiệm và tôi không thể tìm cách gọi nội dung cdtừ tập lệnh của mình một cách an toàn .

Xem xét điều này

cd() { echo "muahaha"; }
export -f cd

Bất kỳ tập lệnh nào được gọi trong môi trường này bằng cách sử dụng cd sẽ bị hỏng (xem xét ảnh hưởng của một cái gì đó như cd dir && rm -rf .).

Có các lệnh để kiểm tra loại lệnh (được gọi một cách thuận tiện type) và các lệnh để thực thi phiên bản dựng sẵn thay vì một chức năng ( builtincommand ). Nhưng, lo và kìa, chúng cũng có thể được ghi đè bằng các hàm

builtin() { "$@"; }
command() { "$@"; }
type() { echo "$1 is a shell builtin"; }

Sẽ mang lại những điều sau đây:

$ type cd
cd is a shell builtin
$ cd x
muahaha
$ builtin cd x
muahaha
$ command cd x
muahaha

Có cách nào để buộc bash sử dụng lệnh dựng sẵn một cách an toàn không, hoặc ít nhất phát hiện ra rằng một lệnh không phải là nội dung, mà không xóa toàn bộ môi trường?

Tôi nhận ra nếu ai đó kiểm soát môi trường của bạn, dù sao bạn cũng có thể bị lừa, nhưng ít nhất là đối với các bí danh, bạn có tùy chọn không gọi bí danh bằng cách chèn một \trước đó.


bạn có thể chạy tập lệnh của mình bằng cách sử dụng envlệnh trước đây, như thế này:env -i <SCRIPT.sh>
Arkadiusz Drabchot

Chỉ khi envkhông được xác định lại là một chức năng. Điều này thật đáng sợ. Trước tiên tôi nghĩ rằng các ký tự đặc biệt sẽ giúp - gọi với đường dẫn đầy đủ bao gồm /, sử dụng .để nguồn, v.v. Nhưng chúng cũng có thể được sử dụng cho tên hàm! Bạn có thể xác định lại bất kỳ chức năng nào bạn muốn, nhưng thật khó để quay lại gọi lệnh ban đầu.
orion

1
Đúng, nhưng nếu bạn định chạy bất kỳ tập lệnh nào trên máy bị xâm nhập thì dù sao bạn cũng bị lừa. Hoặc, viết tập lệnh của bạn #/bin/shnếu đây không phải là vỏ tương tác mặc định.
Arkadiusz Drabchot

Câu trả lời:


16

Olivier D gần như đúng, nhưng bạn phải đặt POSIXLY_CORRECT=1trước khi chạy unset. POSIX có một khái niệm về Tích hợp đặc biệtbash hỗ trợ điều này . unsetlà một trong những nội dung như vậy. Tìm kiếm SPECIAL_BUILTINtrong builtins/*.ctrong nguồn bash cho một danh sách, nó bao gồm set, unset, export, evalsource.

$ unset() { echo muahaha-unset; }
$ unset unset
muahaha-unset
$ POSIXLY_CORRECT=1
$ unset unset

Rogue unsethiện đã được loại bỏ khỏi môi trường, nếu bạn bỏ đặt command, type, builtinsau đó bạn sẽ có thể tiếp tục, nhưng unset POSIXLY_CORRECTnếu bạn đang dựa vào phi POSIX-hành vi hoặc các tính năng bash tiên tiến.

Điều này không giải quyết các bí danh, vì vậy bạn phải sử dụng \unsetđể chắc chắn rằng nó hoạt động trong vỏ tương tác (hoặc luôn luôn, trong trường hợp expand_aliasescó hiệu lực).

Đối với người hoang tưởng, điều này sẽ sửa chữa mọi thứ, tôi nghĩ:

POSIXLY_CORRECT=1
\unset -f help read unset
\unset POSIXLY_CORRECT
re='^([a-z:.\[]+):' # =~ is troublesome to escape
while \read cmd; do 
    [[ "$cmd" =~ $re ]] && \unset -f ${BASH_REMATCH[1]}; 
done < <( \help -s "*" )

( while, do, done[[được dành riêng từ và không cần biện pháp phòng ngừa.) Lưu ý chúng tôi đang sử dụng unset -fđể chắc chắn chức năng unset, mặc dù các biến và chức năng chia sẻ không gian tên tương tự nó có thể cho cả hai để tồn tại cùng một lúc (nhờ Etan Reisner) trong trường hợp này unset-ing hai lần cũng sẽ làm được mẹo. Bạn có thể đánh dấu một chức năng chỉ đọc, bash không ngăn bạn bỏ đặt chức năng chỉ đọc lên đến và bao gồm cả bash-4.2, bash-4.3 không ngăn bạn nhưng nó vẫn tôn vinh các nội dung đặc biệt khi POSIXLY_CORRECTđược đặt.

Chỉ đọc POSIXLY_CORRECTkhông phải là vấn đề thực sự, đây không phải là boolean hoặc gắn cờ sự hiện diện của nó cho phép chế độ POSIX, vì vậy nếu nó tồn tại dưới dạng chỉ đọc, bạn có thể dựa vào các tính năng POSIX, ngay cả khi giá trị trống hoặc 0. Bạn chỉ cần bỏ đặt các hàm có vấn đề theo một cách khác so với ở trên, có lẽ với một số thao tác cắt và dán:

\help -s "*" | while IFS=": " read cmd junk; do echo \\unset -f $cmd; done

(và bỏ qua bất kỳ lỗi nào) hoặc tham gia vào một số kịch bản khác .


Ghi chú khác:

  • functionlà một từ dành riêng, nó có thể được đặt bí danh nhưng không bị ghi đè bằng một hàm. (Bí danh functionlà rắc rối nhẹ vì \function không phải là được chấp nhận như một cách bỏ qua nó)
  • [[, ]] là những từ dành riêng, chúng có thể được đặt bí danh (sẽ bị bỏ qua) nhưng không bị ghi đè bằng một hàm (mặc dù các hàm có thể được đặt tên như vậy)
  • (( không phải là tên hợp lệ cho hàm, cũng không phải bí danh

Thật thú vị, cảm ơn! Tôi có vẻ kỳ lạ với tôi rằng bash sẽ mặc định không bảo vệ unsetet al khỏi bị ghi đè.
falstro

Điều này cần -flập luận để unsetkhông? Mặt khác, nếu cả hai biến và hàm có tên giống như nội dung được xác định thì unsetsẽ chọn biến để bỏ đặt trước. Ngoài ra, điều này không thành công nếu bất kỳ chức năng che khuất nào được đặt readonlykhông? (Mặc dù điều đó có thể phát hiện được và có thể gây ra lỗi nghiêm trọng.) Ngoài ra, điều này [có vẻ như tích hợp sẵn.
Etan Reisner

1
Tôi không nghĩ \unset -f unsetcó ý nghĩa, cho rằng nó sẽ được thực hiện trong vòng lặp đọc. Nếu unsetlà một chức năng, \unsetthông thường bạn sẽ giải quyết nó thay vì dựng sẵn, nhưng bạn có thể sử dụng \unsetmột cách an toàn miễn là chế độ POSIX có hiệu lực. Một cách rõ ràng kêu gọi \unset -ftrên [, .:có thể là một ý tưởng tốt, mặc dù là không bao gồm regex của bạn cho họ. Tôi cũng sẽ thêm -fvào \unsettrong vòng lặp, và sẽ \unset POSIXLY_CORRECTsau khi vòng lặp, thay vì trước đây. \unalias -a(sau \unset -f unalias) cũng cho phép một người từ bỏ lối thoát một cách an toàn trên các lệnh tiếp theo.
Adrian Günter

1
@ AdrianGünter Thử : [[ ${POSIXLY_CORRECT?non-POSIX mode shell is untrusted}x ]], điều này sẽ khiến tập lệnh không tương tác thoát ra với lỗi được chỉ định nếu biến không được đặt hoặc tiếp tục nếu nó được đặt (trống hoặc bất kỳ giá trị nào).
mr.spuratic

1
"Bạn phải sử dụng \unset" ... Cần lưu ý rằng trích dẫn của bất kỳ (các) ký tự nào sẽ hoạt động để tránh việc mở rộng bí danh, không chỉ đầu tiên. un"se"tsẽ làm việc tốt như vậy, abeit ít đọc. "Từ đầu tiên của mỗi lệnh đơn giản, nếu không được trích dẫn, được kiểm tra xem nó có bí danh không." - Bash Ref
Robin A. Meade

6

Tôi nhận ra nếu ai đó kiểm soát môi trường của bạn, dù sao bạn cũng có thể bị lừa

Vâng, cái đó Nếu bạn chạy một tập lệnh trong một môi trường không xác định, mọi thứ có thể sai, bắt đầu bằng việc LD_PRELOADkhiến quy trình shell thực thi mã tùy ý trước khi nó đọc tập lệnh của bạn. Cố gắng bảo vệ chống lại một môi trường thù địch từ bên trong kịch bản là vô ích.

Sudo đã vệ sinh môi trường bằng cách loại bỏ bất cứ thứ gì trông giống như định nghĩa hàm bash trong hơn một thập kỷ. Kể từ Shellshock , các môi trường khác chạy tập lệnh shell trong môi trường không được tin cậy hoàn toàn đã theo sau.

Bạn không thể chạy tập lệnh một cách an toàn trong một môi trường được đặt bởi một thực thể không đáng tin cậy. Vì vậy, lo lắng về định nghĩa chức năng là không hiệu quả. Vệ sinh môi trường của bạn và làm như vậy các biến mà bash sẽ hiểu là các định nghĩa hàm.


1
Tôi không đồng ý với điều này hoàn toàn. Bảo mật không phải là một đề xuất nhị phân tập trung vào "vệ sinh" hoặc "không được khử trùng". Đó là về việc loại bỏ các cơ hội khai thác. Và thật không may, sự phức tạp của các hệ thống hiện đại có nghĩa là có nhiều cơ hội khai thác cần phải được khóa chặt. Nhiều cơ hội khai thác xoay quanh các cuộc tấn công ngược dòng dường như vô hại, chẳng hạn như thay đổi biến PATH, xác định lại các hàm tích hợp, v.v. Cung cấp cho một người hoặc lập trình cơ hội để thực hiện và toàn bộ hệ thống của bạn dễ bị tổn thương.
Dejay Clayton

@DejayClayton Tôi hoàn toàn đồng ý với nhận xét của bạn, ngoại trừ câu đầu tiên, vì tôi không thấy nó mâu thuẫn với câu trả lời của tôi như thế nào. Loại bỏ một cơ hội khai thác sẽ giúp, nhưng không loại bỏ chỉ một nửa trong số đó trong đó một tinh chỉnh tầm thường trong cuộc tấn công cho phép nó tiếp tục hoạt động.
Gilles 'SO- ngừng trở nên xấu xa'

Quan điểm của tôi là bạn không thể "vệ sinh môi trường của mình" và sau đó ngừng lo lắng về các vectơ tấn công khác nhau. Đôi khi nó trả tiền để "bảo vệ chống lại một môi trường thù địch từ bên trong kịch bản." Ngay cả khi bạn giải quyết vấn đề "định nghĩa hàm", bạn vẫn phải lo lắng về những thứ như có PATH khi ai đó có thể đặt một tập lệnh tùy chỉnh có tên "cat" hoặc "ls" vào một thư mục, và sau đó bất kỳ tập lệnh nào gọi "cat" "hoặc" ls "thay vì" / bin / cat "và" / bin / ls "đang thực hiện khai thác một cách hiệu quả.
Dejay Clayton

@DejayClayton Rõ ràng việc vệ sinh môi trường không giúp ích gì cho các vectơ tấn công không liên quan đến môi trường. (Ví dụ một kịch bản mà các cuộc gọi /bin/cattrong một chroot có thể gọi bất cứ điều gì.) Vệ sinh môi trường không cung cấp cho bạn một lành mạnh PATH(giả sử bạn đang làm đúng tất nhiên). Tôi vẫn không thấy quan điểm của bạn.
Gilles 'SO- đừng trở nên xấu xa'

Xem xét rằng PATH là một trong những cơ hội lớn nhất để khai thác và nhiều thực tiễn PATH đáng nghi được ghi lại dưới dạng lời khuyên được đề xuất ngay cả trong các blog bảo mật và cũng xem xét rằng đôi khi các ứng dụng được cài đặt sắp xếp lại PATH để đảm bảo các ứng dụng đó hoạt động, tôi không thể thấy mã hóa phòng thủ trong một kịch bản sẽ là một ý tưởng tồi. Trên thực tế, những gì bạn cho là một PATH lành mạnh có thể dễ bị tổn thương trước những khai thác mà bạn chưa gặp phải.
Dejay Clayton

1

Bạn có thể sử dụng unset -fđể loại bỏ các hàm dựng sẵn, lệnh và loại.


2
xin lỗi, unset() { true; }chăm sóc điều đó
falstro

1
Chỉ trích! Và liệt kê các chức năng được xác định cũng dựa vào nội dung. Tôi từ bỏ!
odc
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.