เหตุใด bash fork bombs จึงทำงานต่างกันและความสำคัญของมันคืออะไร?


16

ฉันรู้ว่า Fork fork แบบธรรมดาทำงานได้อย่างไร แต่ฉันไม่เข้าใจว่าทำไม & ในตอนท้ายของการวางระเบิด bash fork ทั่วไปและทำไมสคริปต์เหล่านี้จึงมีความแตกต่าง:

:(){ (:) | (:) }; :

และ

:(){ : | :& }; :

อดีตทำให้การใช้งาน cpu ขัดขวางก่อนที่จะทิ้งฉันกลับไปที่หน้าจอเข้าสู่ระบบ หลังทำให้ระบบของฉันค้างแข็งแทนที่จะบังคับให้ฉันรีบูตยาก ทำไมถึงเป็นอย่างนั้น? ทั้งสองอย่างต่อเนื่องสร้างกระบวนการใหม่ดังนั้นทำไมระบบทำงานแตกต่างกันอย่างไร

สคริปต์ทั้งสองยังทำงานแตกต่างจาก

:(){ : | : }; :

ซึ่งไม่ได้ทำให้เกิดปัญหาใด ๆ เลยถึงแม้ว่าฉันจะคาดหวังให้พวกเขาเหมือนกัน หน้าคู่มือทุบตีระบุว่าคำสั่งในไปป์ไลน์ถูกดำเนินการแล้วใน subshell ดังนั้นฉันจึงเชื่อว่า: | : ควรเพียงพอแล้ว ฉันเชื่อ & ควรเรียกใช้ไปป์ไลน์ใน subshell ใหม่ แต่ทำไมมันถึงเปลี่ยนไปมาก

แก้ไข: การใช้ htop และการ จำกัด จำนวนกระบวนการฉันสามารถเห็นว่าตัวแปรแรกสร้างแผนผังกระบวนการจริงตัวแปรที่สองสร้างกระบวนการทั้งหมดในระดับเดียวกันและตัวแปรสุดท้ายดูเหมือนจะไม่สร้างกระบวนการใด ๆ เลย สิ่งนี้ทำให้ฉันสับสนมากขึ้น แต่อาจช่วยได้บ้าง


2
ฉันคิดว่าตัวแปรสุดท้ายของคุณไม่มีเครื่องหมายอัฒภาค::(){ : | :; }; :
adonis

คำตอบ:


22

คำเตือนอย่าพยายามเรียกใช้สิ่งนี้บนเครื่องผลิต เพียงแค่ไม่ คำเตือน: เพื่อลองใช้ "ระเบิด" ใด ๆ ตรวจสอบให้แน่ใจว่าulimit -uมีการใช้งานอยู่ อ่านด้านล่าง[เป็น]

มากำหนดฟังก์ชั่นเพื่อรับ PID และวันที่ (เวลา):

bize:~$ d(){ printf '%7s %07d %s\n' "$1" "$BASHPID" "$(date +'%H:%M:%S')"; }

bombฟังก์ชั่นที่เรียบง่ายและไม่เป็นปัญหาสำหรับผู้ใช้ใหม่ (ป้องกันตัวคุณเอง: อ่าน[a] ):

bize:~$ bomb() { d START; echo "yes"; sleep 1; d END; } >&2

เมื่อฟังก์ชั่นนั้นถูกเรียกให้เรียกใช้งานจะทำงานดังนี้

bize:~$ bomb
  START 0002786 23:07:34
yes
    END 0002786 23:07:35
bize:~$

คำสั่งdateถูกเรียกใช้งานจากนั้นจะพิมพ์คำว่า "ใช่" ออกจากโหมดสลีปเป็นเวลา 1 วินาทีจากนั้นคำสั่งปิดdateและในที่สุดฟังก์ชั่นจะออกจากการพิมพ์พรอมต์คำสั่งใหม่ ไม่มีอะไรแฟนซี

| ท่อ

เมื่อเราเรียกใช้ฟังก์ชันดังนี้:

bize:~$ bomb | bomb
  START 0003365 23:11:34
yes
  START 0003366 23:11:34
yes
    END 0003365 23:11:35
    END 0003366 23:11:35
bize:~$

คำสั่งเริ่มต้นสองคำสั่งในบางครั้งทั้งสองสิ้นสุด 1 วินาทีในภายหลังจากนั้นพรอมต์จะกลับมา

นั่นเป็นเหตุผลสำหรับไปป์ไลน์|เพื่อเริ่มต้นกระบวนการสองกระบวนการแบบขนาน

& พื้นหลัง

หากเราเปลี่ยนการโทรเพิ่มการสิ้นสุด&:

bize:~$ bomb | bomb &
[1] 3380
bize:~$
  START 0003379 23:14:14
yes
  START 0003380 23:14:14
yes
    END 0003379 23:14:15
    END 0003380 23:14:15

พรอมต์จะส่งกลับทันที (การดำเนินการทั้งหมดจะถูกส่งไปยังพื้นหลัง) และคำสั่งสองคำสั่งจะถูกดำเนินการเหมือนเมื่อก่อน โปรดทราบค่าของ "จำนวนงานที่" [1]พิมพ์ก่อน PID 3380ของกระบวนการ ในภายหลังหมายเลขเดียวกันจะถูกพิมพ์เพื่อระบุว่าไปป์ได้สิ้นสุดแล้ว:

[1]+  Done                    bomb | bomb

&นั่นคือผลกระทบของ

นั่นคือเหตุผลที่&ทำให้กระบวนการเริ่มต้นเร็วขึ้น

ชื่อที่เรียบง่าย

เราสามารถสร้างฟังก์ชั่นที่เรียกว่าเพียงbเพื่อรันสองคำสั่ง พิมพ์เป็นสามบรรทัด:

bize:~$ b(){
> bomb | bomb
> }

และดำเนินการเป็น:

bize:~$ b
  START 0003563 23:21:10
yes
  START 0003564 23:21:10
yes
    END 0003564 23:21:11
    END 0003563 23:21:11

โปรดทราบว่าเราไม่ได้ใช้;คำจำกัดความของb(บรรทัดใหม่ถูกใช้เพื่อแยกองค์ประกอบ) อย่างไรก็ตามสำหรับคำนิยามในหนึ่งบรรทัดเป็นเรื่องปกติที่จะใช้;เช่นนี้

bize:~$ b(){ bomb | bomb ; }

ช่องว่างส่วนใหญ่ยังไม่ได้บังคับเราสามารถเขียนเทียบเท่า (แต่ชัดเจนน้อยกว่า):

bize:~$ b(){ bomb|bomb;}

นอกจากนี้เรายังสามารถใช้ a &เพื่อแยก}(และส่งสองกระบวนการไปยังพื้นหลัง)

การวางระเบิด

หากเราทำให้ฟังก์ชันกัดหางของมัน (โดยเรียกชื่อตัวเอง) เราจะได้รับ "fork bomb":

bize:~$ b(){ b|b;}       ### May look better as b(){ b | b ; } but does the same.

และเพื่อให้การเรียกใช้ฟังก์ชั่นอื่น ๆ เร็วขึ้นส่งท่อไปยังพื้นหลัง

bize:~$ b(){ b|b&}       ### Usually written as b(){ b|b& }

หากเราต่อท้ายการเรียกครั้งแรกไปยังฟังก์ชั่นหลังจากที่จำเป็น;และเปลี่ยนชื่อที่:เราได้รับ:

bize:~$ :(){ :|:&};:

มักเขียนว่า :(){ :|:& }; :

หรือเขียนด้วยวิธีสนุกโดยใช้ชื่ออื่น (มนุษย์หิมะ):

☃(){ ☃|☃&};☃

ulimit (ซึ่งคุณควรตั้งไว้ก่อนใช้งาน) จะทำให้พรอมต์กลับมาอย่างรวดเร็วหลังจากข้อผิดพลาดมากมาย (กด Enter เมื่อรายการข้อผิดพลาดหยุดเพื่อรับพรอมต์)

สาเหตุของการถูกเรียกว่า "fork bomb" คือวิธีที่เชลล์เริ่มเชลล์ย่อยคือการฟอร์กเชลล์ที่กำลังทำงานและจากนั้นเรียก exec () ไปยังกระบวนการ forked โดยมีคำสั่งให้เรียกใช้

ไปป์จะ "แยก" สองกระบวนการใหม่ การทำอย่างไม่มีที่สิ้นสุดทำให้เกิดการระเบิด
หรือว่ากระต่ายอย่างที่เคยเรียกกันมาเพราะมันแพร่พันธุ์อย่างรวดเร็ว


เวลา:

  1. :(){ (:) | (:) }; time :
    สิ้นสุดแล้ว
    0m45.627s จริง

  2. :(){ : | :; }; time :
    สิ้นสุดแล้ว
    0m15.283s จริง

  3. :(){ : | :& }; time :
    จริง 0m00.002 s
    ยังคงทำงานอยู่


ตัวอย่างของคุณ:

  1. :(){ (:) | (:) }; :

    ไหนปิดที่สอง)แยกเป็นรุ่นที่ซับซ้อนมากขึ้นของ} :(){ :|:;};:แต่ละคำสั่งในไพพ์เรียกว่าภายในเชลล์ย่อยอย่างไรก็ตาม ()ซึ่งเป็นผลกระทบจากการที่

  2. :(){ : | :& }; :

    เป็นรุ่นที่เร็วขึ้นเขียนขึ้นเพื่อไม่มีช่องว่าง: :(){(:)|:&};:(13 ตัวอักษร)

  3. :(){ : | : }; : ### ใช้งานได้ใน zsh แต่ไม่ใช่ใน bash

    มีไวยากรณ์ผิดพลาด (ในทุบตี) ซึ่งเป็น metacharacter เป็นสิ่งจำเป็นก่อนปิด},
    เช่นนี้:

    :(){ : | :; }; :

[a] สร้างผู้ใช้ใหม่ที่สะอาด (ฉันจะโทรหาฉันbize) เข้าสู่ระบบผู้ใช้ใหม่ในคอนโซลอย่างใดอย่างหนึ่งsudo -i -u bizeหรือ:

$ su - bize
Password: 
bize:~$

ตรวจสอบจากนั้นเปลี่ยนmax user processesขีด จำกัด :

bize:~$ ulimit -a           ### List all limits (I show only `-u`)
max user processes              (-u) 63931
bize:~$ ulimit -u 10        ### Low
bize:~$ ulimit -a
max user processes              (-u) 1000

โดยใช้เพียง 10 bizeผลงานในฐานะเป็นเพียงหนึ่งในผู้ใช้ใหม่โดดเดี่ยว: มันง่ายกว่าในการโทรออกkillall -u bizeและกำจัดระบบกำจัดระเบิดส่วนใหญ่ (ไม่ใช่ทั้งหมด) โปรดอย่าถามคนที่ยังใช้งานได้อยู่ฉันจะไม่บอก แต่ก็ยังคง: ค่อนข้างต่ำ แต่ในด้านความปลอดภัยที่ปรับให้เข้ากับระบบของคุณ
นี้จะให้แน่ใจว่าเป็น "ระเบิดส้อม" จะไม่ยุบระบบของคุณ

อ่านเพิ่มเติม:

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