ผลกระทบด้านความปลอดภัยของการใช้ข้อมูลที่ไม่ได้รับอนุญาตในการประเมินผลการคำนวณทางคณิตศาสตร์ของเชลล์


17

ในความคิดเห็นต่อคำถามล่าสุด Stéphane Chazelas กล่าวว่ามีผลกระทบด้านความปลอดภัยต่อวงเล็บทางคณิตศาสตร์สองหลักเช่น:

x=$((1-$x))

บนเปลือกหอยส่วนใหญ่

ทักษะ Google ของฉันดูเหมือนจะเป็นสนิมและฉันไม่พบสิ่งใดเลย ผลกระทบด้านความปลอดภัยของเลขคณิตวงเล็บคู่คืออะไร?

คำตอบ:


22

ปัญหาคือในกรณีที่เนื้อหาของ$xไม่ได้ถูกสุขอนามัยและมีข้อมูลที่อาจอยู่ภายใต้การควบคุมของผู้โจมตีในกรณีที่รหัสเชลล์อาจถูกใช้ในบริบทการยกระดับสิทธิ์ (เช่นสคริปต์ที่เรียกโดย setuid แอปพลิเคชันสคริปต์ sudoers หรือใช้เพื่อประมวลผลข้อมูลนอกเครือข่าย (CGI, DHCP hook ... ) โดยตรงหรือโดยอ้อม)

ถ้า:

x='(PATH=2)'

แล้ว:

x=$((1-$x)))

มีผลข้างเคียงของการตั้งค่าPATHเป็น2(เส้นทางสัมพัทธ์ที่อาจอยู่ภายใต้การควบคุมของผู้โจมตีได้เป็นอย่างดี) คุณสามารถแทนที่PATHด้วยLD_LIBRARY_PATHหรือIFS... สิ่งเดียวกันนี้เกิดขึ้นกับx=$((1-x))ใน bash, zsh หรือ ksh (ไม่ใช่เส้นประหรือ yash ที่ยอมรับค่าคงที่ตัวเลขในตัวแปรที่มีเท่านั้น)

โปรดทราบว่า:

x=$((1-$x))

จะไม่ทำงานอย่างถูกต้องสำหรับค่าลบของ$xในเชลล์บางตัวที่ใช้ตัวดำเนินการ (เป็นทางเลือกตาม POSIX) --(ลดลง) (เช่นเดียวกับx=-1ที่หมายถึงการขอให้เชลล์ประเมิน1--1ค่านิพจน์ทางคณิตศาสตร์) "$((1-x))"ไม่ได้มีปัญหาตามที่ได้xรับการขยายเป็นส่วนหนึ่งของการประเมินเลขคณิต (ไม่ใช่ก่อนหน้านี้)

ในbash, zshและksh(ไม่ใช่dashหรือyash) ถ้าxเป็น:

x='a[0$(uname>&2)]'

จากนั้นการขยาย$((1-$x))หรือ$((1-x))ทำให้unameคำสั่งนั้นถูกเรียกใช้งาน (สำหรับzsh, aจะต้องเป็นตัวแปรอาร์เรย์ แต่สามารถใช้psvarเพื่ออินสแตนซ์สำหรับสิ่งนั้นได้)

สรุปหนึ่งไม่ควรใช้ uninitialised หรือไม่ปรุงแต่งข้อมูลภายนอกในการแสดงออกทางคณิตศาสตร์ในเปลือกหอย (หมายเหตุว่าการประเมินทางคณิตศาสตร์สามารถทำได้โดย$((...))(หรือที่รู้จัก$[...]ในbashหรือzsh) แต่ยังขึ้นอยู่กับเปลือกในที่let, [/ test, declare/typeset/export..., return, break, continue, exit, printf, printบิวด์อิน, ดัชนีอาเรย์((..))และ[[...]]โครงสร้างเพื่อตั้งชื่อไม่กี่)

ในการตรวจสอบว่าตัวแปรมีตัวเลขจำนวนเต็มฐานสิบที่แท้จริงคุณสามารถใช้ POSIXly:

case $var in
  ("" | - | *[!0123456789-]* | ?*-*) echo >&2 not a valid number; exit 1;;
esac

ระวังว่า[0-9]ในบางพื้นที่ตรงกับมากกว่า 0123456789 [[:digit:]]ควรจะตกลง แต่ฉันจะไม่เดิมพัน

โปรดจำไว้ว่าตัวเลขที่มีเลขศูนย์นำหน้าจะถือว่าเป็นเลขฐานแปดในบางบริบท ( 010บางครั้งคือ 10, บางครั้ง 8) และระวังว่าการตรวจสอบข้างต้นจะให้ตัวเลขที่อาจใหญ่กว่าจำนวนเต็มสูงสุดที่ระบบของคุณสนับสนุน ใช้จำนวนเต็มนั้นใน; bash เช่นถือว่า 18446744073709551616 เป็น 0 เท่ากับ 2 64 ) ดังนั้นคุณอาจต้องการเพิ่มการตรวจสอบพิเศษในคำสั่งกรณีข้างต้นเช่น:

(0?* | -0?*)
  echo >&2 'Only decimal numbers without leading 0 accepted'; exit 1;;
(-??????????* | [!-]?????????*)
  echo >&2 'Only numbers from -999999999 to 999999999 supported'; exit 1;;

ตัวอย่าง:

$ export 'x=psvar[0$(uname>&2)]'
$ ksh93 -c 'echo "$((x))"'
Linux
ksh93: psvar: parameter not set
$ ksh93 -c '[ x -lt 2 ]'
Linux
ksh93: [: psvar: parameter not set
$ bash -c 'echo "$((x))"'
Linux
0
$ bash -c '[[ $x -lt 2 ]]'
Linux
$ bash -c 'typeset -i a; export a="$x"'
Linux
$ bash -c 'typeset -a a=([x]=1)'
Linux
$ bash -c '[ -v "$x" ]'
Linux
$ mksh -c '[[ $x -lt 2 ]]'
Linux
$ zsh -c 'echo "$((x))"'
Linux
0
$ zsh -c 'printf %d $x'
Linux
0
$ zsh -c 'integer x'
Linux
$ zsh -c 'exit $x'
Linux

อ่านเพิ่มเติมได้ที่:


x='P=3'; : $(($x + 5))จะตั้งP8 แต่x='P=3'; : $((x + 5))จะตั้งPไป3(ในzsh, kshหรือbash) "สิ่งเดียวกันนี้เกิดขึ้นกับ$((x + 1))... " ไม่ถูกต้องในขณะนี้ มันจะตั้งPATHเพื่อ2เป็นของเดิม
mosvy

@mosvy ขอขอบคุณที่ถูกต้อง (และสำหรับการแก้ไขก่อนหน้าของคุณ) ถูกต้องแล้ว
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.