Ý nghĩa bảo mật của việc chạy perl -ne '' '


27

Rõ ràng, đang chạy:

perl -n -e 'some perl code' *

Hoặc là

find . ... -exec perl -n -e '...' {} +

(giống với -pthay vì -n)

Hoặc là

perl -e 'some code using <>' *

thường được tìm thấy trong một lớp lót được đăng trên trang web này, có ý nghĩa bảo mật. Thỏa thuận là gì? Làm thế nào để tránh nó?

Câu trả lời:


33

Có vấn đề gì vậy

Đầu tiên, giống như nhiều tiện ích, bạn sẽ gặp vấn đề với tên tệp bắt đầu bằng -. Trong khi ở:

sh -c 'inline sh script here' other args

Các đối số khác được truyền cho inline sh script; với perltương đương,

perl -e 'inline perl script here' other args

Các đối số khác được quét để có thêm tùy chọn cho perl trước, không phải cho tập lệnh nội tuyến. Vì vậy, ví dụ, nếu có một tệp được gọi -eBEGIN{do something evil}trong thư mục hiện tại,

perl -ne 'inline perl script here;' *

(có hoặc không có -n) sẽ làm điều gì đó xấu xa.

Giống như các tiện ích khác, công việc xung quanh đó là sử dụng điểm đánh dấu kết thúc tùy chọn ( --):

perl -ne 'inline perl script here;' -- *

Nhưng ngay cả sau đó, nó vẫn nguy hiểm và điều đó thuộc về <>nhà điều hành được sử dụng bởi -n/ -p.

Vấn đề được giải thích trong perldoc perloptài liệu.

Toán tử đặc biệt đó được sử dụng để đọc một dòng (một bản ghi, bản ghi là các dòng theo mặc định) của đầu vào, trong đó đầu vào đó đến từ mỗi đối số lần lượt được đưa vào @ARGV.

Trong:

perl -pe '' a b

-pngụ ý một while (<>)vòng lặp xung quanh mã (ở đây trống).

<>đầu tiên sẽ mở a, đọc các bản ghi một dòng tại một thời điểm cho đến khi tệp hết và sau đó mở b...

Vấn đề là, để mở tệp, nó sử dụng hình thức đầu tiên, không an toàn open:

open ARGV, "the file as provided"

Với hình thức đó, nếu đối số là

  • "> afile", nó mở afiletrong chế độ viết,
  • "cmd|", nó chạy cmdvà đọc đầu ra của nó.
  • "|cmd", bạn có một luồng mở để ghi vào đầu vào của cmd.

Ví dụ:

perl -pe '' 'uname|'

Không xuất nội dung của tệp được gọi uname|(tên tệp btw hoàn toàn hợp lệ), nhưng đầu ra của unamelệnh.

Nếu bạn đang chạy:

perl -ne 'something' -- *

Và ai đó đã tạo một tệp có tên rm -rf "$HOME"|(lại là tên tệp hoàn toàn hợp lệ) trong thư mục hiện tại (ví dụ vì thư mục đó đã từng được người khác ghi lại hoặc bạn đã trích xuất một tệp lưu trữ tinh ranh hoặc bạn đã chạy một số lệnh tinh ranh, hoặc một lỗ hổng khác trong một số phần mềm khác đã bị khai thác), sau đó bạn gặp rắc rối lớn. Các lĩnh vực quan trọng cần biết về vấn đề đó là các công cụ xử lý tệp tự động trong các khu vực công cộng như /tmp(hoặc các công cụ có thể được gọi bằng các công cụ đó).

File gọi > foo, foo|, |foolà một vấn đề. Nhưng ở mức độ thấp hơn < foofoovới các ký tự khoảng cách ASCII hàng đầu hoặc dấu (bao gồm khoảng trắng, tab, dòng mới, cr ...) cũng như điều đó có nghĩa là các tệp đó sẽ không được xử lý hoặc sai.

Cũng lưu ý rằng một số ký tự trong một số bộ ký tự nhiều byte (như ǖtrong BIG5-HKSCS) kết thúc bằng byte 0x7c, mã hóa của |.

$ printf ǖ | iconv -t BIG5-HKSCS | od -tx1 -tc
0000000  88  7c
        210   |
0000002

Vì vậy, trong các địa phương sử dụng bộ ký tự đó,

 perl -pe '' ./nǖ

Sẽ cố chạy ./n\x88lệnh vì perlsẽ không cố diễn giải tên tệp đó trong miền địa phương của người dùng!

Cách khắc phục / khắc phục

AFAIK, bạn không thể làm gì để thay đổi hành vi mặc định không an toàn đó perlmột lần và cho tất cả các hệ thống.

Đầu tiên, sự cố chỉ xảy ra với các ký tự ở đầu và cuối tên tệp. Vì vậy, trong khi perl -ne '' *hoặc perl -ne '' *.txtlà một vấn đề,

perl -ne 'some code' ./*.txt

không bởi vì tất cả các đối số bây giờ bắt đầu với ./và kết thúc ở .txt(vì vậy không -, <, >, |, không gian ...). Tổng quát hơn, đó là một ý tưởng tốt để tiền tố globs với ./. Điều đó cũng tránh các vấn đề với các tệp được gọi -hoặc bắt đầu với -nhiều tiện ích khác (và ở đây, điều đó có nghĩa là bạn không cần --đánh dấu kết thúc tùy chọn ( ) nữa).

Sử dụng -Tđể bật taintchế độ giúp một phần nào đó. Nó sẽ hủy bỏ lệnh nếu gặp phải tệp độc hại đó (chỉ dành cho các trường hợp >|trường hợp, chứ không phải <hoặc khoảng trắng).

Điều đó hữu ích khi sử dụng các lệnh như vậy một cách tương tác để cảnh báo bạn rằng có điều gì đó tinh ranh đang diễn ra. Điều đó có thể không được mong muốn khi thực hiện một số xử lý tự động, vì điều đó có nghĩa là ai đó có thể khiến việc xử lý đó thất bại chỉ bằng cách tạo một tệp.

Nếu bạn muốn để xử lý tất cả các tập tin, bất kể tên của họ, bạn có thể sử dụng các ARGV::readonly perlmô-đun trên CPAN (không may thường không được cài đặt theo mặc định). Đó là một mô-đun rất ngắn mà:

sub import{
   # Tom Christiansen in Message-ID: <24692.1217339882@chthon>
   # reccomends essentially the following:
   for (@ARGV){
       s/^(\s+)/.\/$1/;   # leading whitespace preserved
       s/^/< /;       # force open for input
       $_.=qq/\0/;    # trailing whitespace preserved & pipes forbidden
   };
};

Về cơ bản, nó vệ sinh @ARGV bằng cách biến " foo|"ví dụ thành "< ./ foo|\0".

Bạn có thể làm tương tự trong một BEGINtuyên bố trong perl -n/-plệnh của bạn :

perl -pe 'BEGIN{$_.="\0" for @ARGV} your code here' ./*

Ở đây chúng tôi đơn giản hóa nó trên giả định ./đang được sử dụng.

Một tác dụng phụ của điều đó (và ARGV::readonly) mặc dù là $ARGVtrong your code heređó cho thấy nhân vật NUL kéo dài.

Cập nhật 2015-06-03

perlv5.21.5 trở lên có một <<>>toán tử mới hoạt động giống như <>ngoại trừ việc nó sẽ không thực hiện quá trình xử lý đặc biệt đó. Đối số sẽ chỉ được coi là tên tệp. Vì vậy, với các phiên bản đó, bây giờ bạn có thể viết:

perl -e 'while(<<>>){ ...;}' -- *

(đừng quên --hoặc sử dụng ./*mặc dù) mà không sợ nó ghi đè lên các tệp hoặc chạy các lệnh không mong muốn.

-n/ -pvẫn sử dụng <>hình thức nguy hiểm mặc dù. Và các liên kết cẩn thận vẫn đang được theo dõi, vì vậy điều đó không nhất thiết có nghĩa là nó an toàn để sử dụng trong các thư mục không đáng tin cậy.


2
bạn đã làm việc cả ngày, tôi sẽ đánh cuộc. làm tốt.
mikeerv

2
cập nhật tốt cho perl, nhưng thật kỳ lạ khi các nhà phát triển perl không thêm tùy chọn -P và -N để sử dụng nó (không thể thay đổi -p và -n vì một số tập lệnh có thể dựa vào hành vi không an toàn)
cas

9

Ngoài câu trả lời của @ Stéphane Chazelas , chúng tôi không phải lo lắng về vấn đề này nếu chúng tôi sử dụng -itùy chọn dòng lệnh:

$ perl -pe '' 'uname|'
Linux

$ perl -i -pe '' 'uname|'
Can't open uname|: No such file or directory.

Bởi vì khi sử dụng -itùy chọn, perlsử dụng stat để kiểm tra trạng thái tệp trước khi xử lý:

$ strace -fe trace=stat perl -pe '' 'uname|'
stat("/home/cuonglm/perl5/lib/perl5/5.20.1/x86_64-linux", 0x7fffd44dff90) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/5.20.1", 0x7fffd44dff90) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/x86_64-linux", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
Process 6106 attached
Linux
Process 6105 suspended
Process 6105 resumed
Process 6106 detached
--- SIGCHLD (Child exited) @ 0 (0) ---

$ strace -fe trace=stat perl -i -pe '' 'uname|'
stat("/home/cuonglm/perl5/lib/perl5/5.20.1/x86_64-linux", 0x7fffdbaf2e50) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/5.20.1", 0x7fffdbaf2e50) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/x86_64-linux", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("uname|", 0x785f40)                = -1 ENOENT (No such file or directory)
Can't open uname|: No such file or directory.

1
Không có một điều kiện cuộc đua có thể có giữa statkiểm tra và xử lý perl hiệu quả sẽ diễn ra ngay sau đó?
Totor

@Totor: Tôi nghĩ là không.
cuonglm

Nó không phải là về stat. Đó chỉ -ilà để chỉnh sửa các tệp tại chỗ, do đó, sẽ không hợp lý khi chấp nhận các đối số khác với đường dẫn tệp thực tế, do đó -i, việc xử lý đặc biệt không được thực hiện.
Stéphane Chazelas
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.