執行 perl -ne ‘…’ 的安全隱患 *
顯然,執行:
perl -n -e 'some perl code' *
或者
find . ... -exec perl -n -e '...' {} +
(與
-p
代替相同-n
)或者
perl -e 'some code using <>' *
經常在此站點上發布的單行程式碼中發現,具有安全隱患。這是怎麼回事?如何避免?
有什麼問題
首先,與許多實用程序一樣,您會遇到以 . 開頭的文件名的問題
-
。而在:sh -c 'inline sh script here' other args
其他參數傳遞給
inline sh script
; 與perl
等價物,perl -e 'inline perl script here' other args
首先掃描其他參數以獲取perl的更多選項,而不是內聯腳本。因此,例如,如果
-eBEGIN{do something evil}
在目前目錄中有一個文件被呼叫,perl -ne 'inline perl script here;' *
(有或沒有
-n
)會做一些壞事。與其他實用程序一樣,解決方法是使用選項結束標記 (
--
):perl -ne 'inline perl script here;' -- *
但即便如此,它仍然很危險,這取決於/
<>
使用的運算符。-n``-p
該問題在
perldoc perlop
文件中進行了解釋。該特殊運算符用於讀取輸入的一行(一條記錄,記錄預設為行),其中該輸入來自依次傳入的每個參數
@ARGV
。在:
perl -pe '' a b
-p
意味著while (<>)
圍繞程式碼的循環(此處為空)。
<>
將首先打開a
,一次讀取一行記錄,直到文件用完,然後打開b
…問題是,要打開文件,它使用第一種不安全的形式
open
:open ARGV, "the file as provided"
使用這種形式,如果論點是
"> afile"
, 它以書寫模式打開afile
,"cmd|"
,它執行cmd
並讀取它的輸出。"|cmd"
,您打開了一個流以寫入cmd
.例如:
perl -pe '' 'uname|'
不輸出呼叫文件的內容
uname|
(順便說一句,一個完全有效的文件名),而是輸出uname
命令。如果您正在執行:
perl -ne 'something' -- *
並且有人在目前目錄中創建了一個名為
rm -rf "$HOME"|
(同樣是一個完全有效的文件名)的文件(例如,因為該目錄曾經可以被其他人寫入,或者您提取了一個不可靠的存檔,或者您執行了一些不可靠的命令,或者其他一些軟體的另一個漏洞被利用了),那麼你就有大麻煩了。需要注意該問題的重要領域是在公共區域/tmp
(或此類工具可能呼叫的工具)中自動處理文件的工具。名為
> foo
,foo|
的文件|foo
是個問題。但在較小程度上< foo
,foo
帶有前導或尾隨 ASCII 空格字元(包括空格、製表符、換行符、cr…),這意味著這些文件將不會被處理或錯誤的文件將被處理。另請注意,某些多字節字元集中的某些字元(例如
ǖ
在 BIG5-HKSCS 中)以字節 0x7c 結尾,即|
.$ printf ǖ | iconv -t BIG5-HKSCS | od -tx1 -tc 0000000 88 7c 210 | 0000002
所以在使用該字元集的語言環境中,
perl -pe '' ./nǖ
將嘗試執行該
./n\x88
命令,因為不會perl
嘗試在使用者的語言環境中解釋該文件名!如何修復/解決
AFAIK,您無法更改
perl
系統範圍內一勞永逸的不安全預設行為。首先,問題僅出現在文件名開頭和結尾的字元上。所以,雖然
perl -ne '' *
或perl -ne '' *.txt
是一個問題,perl -ne 'some code' ./*.txt
不是因為所有的論點現在都以(所以不是, , , , space…)開始
./
和結束。更一般地說,在glob前加上. 前綴是個好主意。這也避免了與許多其他實用程序呼叫或開始的文件的問題(在這裡,這意味著您不再需要選項結束 ( ) 標記)。.txt``-``<``>``|``./``-``-``--
使用
-T
開啟taint
模式在一定程度上有所幫助。如果遇到此類惡意文件,它將中止命令(僅適用於>
and|
情況,而不是<
或空格)。這在以互動方式使用此類命令時很有用,因為它會提醒您有一些可疑的事情正在發生。但是,在進行一些自動處理時,這可能是不可取的,因為這意味著有人可以通過創建文件來使處理失敗。
如果您確實想處理每個文件,無論它們的名稱如何,您都可以使用CPAN 上的
ARGV::readonly
perl
模組(不幸的是,預設情況下通常不安裝)。這是一個非常短的模組,它可以: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 }; };
基本上,它通過將 @ARGV 轉換
" foo|"
為"< ./ foo|\0"
.您可以在命令中的
BEGIN
語句中執行相同操作perl -n/-p
:perl -pe 'BEGIN{$_.="\0" for @ARGV} your code here' ./*
./
在這裡,我們根據正在使用的假設對其進行簡化。
ARGV::readonly
那個 (and )的副作用是$ARGV
inyour code here
顯示了尾隨的 NUL 字元。2015-06-03 更新
perl
v5.21.5 及更高版本有一個新的操作符,除了它不會進行特殊處理外,它<<>>
的行為類似。參數將僅被視為文件名。因此,使用這些版本,您現在可以編寫:<>
perl -e 'while(<<>>){ ...;}' -- *
(不要忘記
--
or 使用./*
)而不用擔心它會覆蓋文件或執行意外的命令。
-n
/-p
仍然使用危險的<>
形式。並且要注意符號連結仍然被遵循,所以這並不一定意味著在不受信任的目錄中使用是安全的。