Security

執行 perl -ne ‘…’ 的安全隱患 *

  • November 25, 2017

顯然,執行:

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是個問題。但在較小程度上< foofoo帶有前導或尾隨 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 )的副作用是$ARGVinyour code here顯示了尾隨的 NUL 字元。

2015-06-03 更新

perlv5.21.5 及更高版本有一個新的操作符,除了它不會進行特殊處理外,它<<>>的行為類似。參數將僅被視為文件名。因此,使用這些版本,您現在可以編寫:<>

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

(不要忘記--or 使用./*)而不用擔心它會覆蓋文件或執行意外的命令。

-n/-p仍然使用危險的<>形式。並且要注意符號連結仍然被遵循,所以這並不一定意味著在不受信任的目錄中使用是安全的。

引用自:https://unix.stackexchange.com/questions/170013