ผลกระทบด้านความปลอดภัยของการเรียกใช้ Perl - '' …


27

เห็นได้ชัดว่ากำลังทำงาน:

perl -n -e 'some perl code' *

หรือ

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

(เช่นเดียวกับ-pแทน-n)

หรือ

perl -e 'some code using <>' *

มักจะพบในหนึ่ง liners โพสต์บนเว็บไซต์นี้มีผลกระทบด้านความปลอดภัย ตกลงคืออะไร? จะหลีกเลี่ยงได้อย่างไร

คำตอบ:


33

มีปัญหาอะไร

-ก่อนเช่นค่าสาธารณูปโภคจำนวนมากที่คุณจะมีปัญหากับชื่อไฟล์ที่เริ่มต้นด้วย ขณะที่อยู่ใน:

sh -c 'inline sh script here' other args

args อื่น ๆ ถูกส่งผ่านไปยังinline sh script; ด้วยความperlเสมอภาค

perl -e 'inline perl script here' other args

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|(ชื่อไฟล์ที่ถูกต้องสมบูรณ์ btw) แต่เป็นเอาต์พุตของ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

ไม่ได้เพราะข้อโต้แย้งทั้งหมดนี้เริ่มต้นด้วย./และสิ้นสุดใน.txt(เพื่อไม่-, <, >, |พื้นที่ ... ) โดยทั่วไปมันเป็นความคิดที่ดีที่จะคำนำหน้าglobs./กับ นอกจากนี้ยังช่วยหลีกเลี่ยงปัญหาเกี่ยวกับไฟล์ที่เรียก-หรือเริ่มต้นด้วย-ยูทิลิตี้อื่น ๆ (และที่นี่หมายความว่าคุณไม่จำเป็นต้องทำเครื่องหมายสิ้นสุดตัวเลือก ( --) อีกต่อไป)

การใช้-Tเพื่อเปิดtaintโหมดช่วยในระดับหนึ่ง มันจะยกเลิกคำสั่งหากพบไฟล์ที่เป็นอันตรายดังกล่าว (สำหรับ>และใน|บางกรณีเท่านั้นไม่ใช่<หรือเว้นวรรค)

มีประโยชน์เมื่อใช้คำสั่งแบบโต้ตอบตามที่แจ้งเตือนคุณว่ามีบางอย่างที่เกิดขึ้น ซึ่งอาจไม่เป็นที่น่าพอใจเมื่อทำการประมวลผลอัตโนมัติเนื่องจากนั่นหมายความว่าบางคนสามารถทำให้การประมวลผลนั้นล้มเหลวเพียงแค่สร้างไฟล์

หากคุณไม่ต้องการที่จะดำเนินการทุกไฟล์โดยไม่คำนึงถึงชื่อของพวกเขาคุณสามารถใช้โมดูลใน 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
   };
};

โดยทั่วไปจะ sanitises @ ARGV โดยการเปิดตัวอย่างลงใน" foo|""< ./ foo|\0"

คุณสามารถทำเช่นเดียวกันในBEGINคำสั่งในperl -n/-pคำสั่งของคุณ:

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

ที่นี่เราลดความซับซ้อนของมันบนสมมติฐานที่./ใช้

ผลข้างเคียงของ (และARGV::readonly) ถึงแม้ว่า$ARGVในyour code hereนั้นแสดงให้เห็นว่าตัวละคร NUL ต่อท้าย

อัพเดท 2015-06-03

perlv5.21.5 ขึ้นไปมี<<>>โอเปอเรเตอร์ใหม่ที่ทำงานคล้ายกัน<>ยกเว้นว่ามันจะไม่ทำการประมวลผลพิเศษนั้น อาร์กิวเมนต์จะถูกพิจารณาว่าเป็นชื่อไฟล์เท่านั้น ดังนั้นด้วยเวอร์ชันเหล่านี้คุณสามารถเขียน:

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

(อย่าลืม--หรือใช้งาน./*) โดยไม่ต้องกลัวว่าจะเขียนทับไฟล์หรือรันคำสั่งที่ไม่คาดคิด

-n/ -pยังคงใช้<>รูปแบบอันตรายแม้ว่า และระวัง symlink ยังคงมีอยู่ดังนั้นจึงไม่ได้หมายความว่าปลอดภัยที่จะใช้ในไดเรกทอรีที่ไม่น่าเชื่อถือ


2
คุณทำงานมาตลอดทั้งวันฉันจะพนัน ทำได้ดี.
mikeserv

2
Nice update to perl แต่ก็แปลกที่ perl devs ไม่ได้เพิ่มตัวเลือก -P และ -N เพื่อใช้ประโยชน์จากมัน (ไม่สามารถเปลี่ยน -p และ -n ที่มีอยู่ได้เนื่องจากสคริปต์บางตัวอาจพึ่งพาพฤติกรรมที่ไม่ปลอดภัย)
cas

9

นอกจากคำตอบของ @ Stéphane Chazelasเราไม่ต้องกังวลเกี่ยวกับปัญหานี้หากเราใช้-iตัวเลือกบรรทัดคำสั่ง:

$ perl -pe '' 'uname|'
Linux

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

เพราะเมื่อใช้-iตัวเลือกให้perlใช้statเพื่อตรวจสอบสถานะไฟล์ก่อนดำเนินการ:

$ 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
ไม่มีเงื่อนไขการแข่งขันที่เป็นไปได้ระหว่างการstatตรวจสอบและการประมวลผล perl ที่มีประสิทธิภาพที่เกิดขึ้นหลังจาก?
Totor

@Totor: ฉันคิดว่าไม่
cuonglm

statมันไม่เกี่ยวกับ เป็นเพียง-iการแก้ไขไฟล์ในสถานที่ดังนั้นจึงไม่มีเหตุผลที่จะยอมรับอาร์กิวเมนต์นอกเหนือจากพา ธ ไฟล์จริงดังนั้นด้วย-iการประมวลผลพิเศษที่ไม่ได้ดำเนินการ
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.