ฉันเขียนสิ่งนี้เป็นรูปแบบการสอนใหม่ของคำตอบที่ยอดเยี่ยมโดย Chris Down ด้านบน
ในทุบตีคุณสามารถมีตัวแปรเชลล์เช่นนี้
$ t="hi there"
$ echo $t
hi there
$
โดยค่าเริ่มต้นตัวแปรเหล่านี้ไม่ได้รับการสืบทอดโดยกระบวนการลูก
$ bash
$ echo $t
$ exit
แต่ถ้าคุณทำเครื่องหมายเพื่อส่งออก bash จะตั้งค่าสถานะซึ่งหมายความว่าพวกเขาจะเข้าสู่สภาพแวดล้อมของกระบวนการย่อย (แม้ว่าenvpพารามิเตอร์จะไม่เห็นมากนักmainในโปรแกรม C ของคุณมีสามพารามิเตอร์: main(int argc, char *argv[], char *envp[])โดยที่ตัวชี้อาร์เรย์สุดท้ายคืออาร์เรย์ ของตัวแปรเชลล์พร้อมคำจำกัดความ)
ลองส่งออกtดังนี้:
$ echo $t
hi there
$ export t
$ bash
$ echo $t
hi there
$ exit
ในขณะที่ข้างต้นtไม่ได้ถูกกำหนดใน subshell ตอนนี้มันปรากฏขึ้นหลังจากที่เราส่งออกมัน (ใช้export -n tถ้าคุณต้องการที่จะหยุดการส่งออก)
แต่ฟังก์ชั่นในการทุบตีเป็นสัตว์ที่แตกต่างกัน คุณประกาศให้พวกเขาเช่นนี้:
$ fn() { echo "test"; }
และตอนนี้คุณสามารถเรียกใช้ฟังก์ชันได้โดยเรียกมันราวกับว่ามันเป็นคำสั่งเชลล์อื่น:
$ fn
test
$
อีกครั้งหากคุณวางไข่ subshell ฟังก์ชันของเราจะไม่ถูกส่งออก:
$ bash
$ fn
fn: command not found
$ exit
เราสามารถส่งออกฟังก์ชันด้วยexport -f:
$ export -f fn
$ bash
$ fn
test
$ exit
นี่คือส่วนที่ยุ่งยาก: ฟังก์ชั่นที่ส่งออกเช่นfnถูกแปลงเป็นตัวแปรสภาพแวดล้อมเช่นเดียวกับการส่งออกตัวแปรเชลล์ของtเรา สิ่งนี้ไม่ได้เกิดขึ้นเมื่อfnเป็นตัวแปรโลคอล แต่หลังจากการส่งออกเราจะเห็นว่ามันเป็นตัวแปรเชลล์ อย่างไรก็ตามคุณยังสามารถมีตัวแปรเชลล์ (เช่นไม่ใช่ฟังก์ชัน) ปกติที่มีชื่อเดียวกัน bash แยกตามเนื้อหาของตัวแปร:
$ echo $fn
$ # See, nothing was there
$ export fn=regular
$ echo $fn
regular
$ 
ตอนนี้เราสามารถใช้envเพื่อแสดงตัวแปรเชลล์ทั้งหมดที่ทำเครื่องหมายเพื่อการส่งออกและทั้งปกติfnและฟังก์ชั่นการfnแสดง:
$ env
.
.
.
fn=regular
fn=() {  echo "test"
}
$
sub-shell จะรับทั้งคำจำกัดความ: หนึ่งเป็นตัวแปรปกติและอีกหนึ่งเป็นฟังก์ชั่น:
$ bash
$ echo $fn
regular
$ fn
test
$ exit
คุณสามารถกำหนดได้fnเหมือนที่เราทำด้านบนหรือโดยตรงเป็นการกำหนดตัวแปรตามปกติ:
$ fn='() { echo "direct" ; }'
หมายเหตุนี่เป็นสิ่งที่ผิดปกติอย่างมากที่ต้องทำ! โดยปกติเราจะกำหนดฟังก์ชั่นfnตามที่เราทำข้างต้นด้วยfn() {...}ไวยากรณ์ แต่เนื่องจากทุบตีส่งออกผ่านสภาพแวดล้อมเราสามารถ "ลัด" โดยตรงกับคำนิยามปกติข้างต้น โปรดทราบว่า (อาจขัดกับสัญชาตญาณของคุณ) สิ่งนี้ไม่ได้ส่งผลให้เกิดฟังก์ชั่นใหม่ที่fnมีอยู่ในเชลล์ปัจจุบัน แต่ถ้าคุณวางไข่เปลือก ** ย่อยแล้วมันจะ
มายกเลิกการส่งออกฟังก์ชั่นfnและปล่อยให้ปกติใหม่fn(ดังที่แสดงไว้ด้านบน) ไม่เป็นอันตราย
$ export -nf fn
ตอนนี้ฟังก์ชั่นfnจะไม่ถูกส่งออกอีกต่อไป แต่ตัวแปรปกติfnคือและมันมี() { echo "direct" ; }อยู่ในนั้น
ตอนนี้เมื่อ subshell เห็นตัวแปรปกติที่เริ่มต้นด้วย()มันตีความส่วนที่เหลือเป็นนิยามฟังก์ชั่น แต่นี่เป็นเพียงเมื่อเชลล์ใหม่เริ่มต้นขึ้น ดังที่เราเห็นด้านบนเพียงแค่กำหนดตัวแปรเชลล์ปกติที่เริ่มต้นด้วย()ไม่ทำให้มันทำงานเหมือนฟังก์ชั่น คุณต้องเริ่ม subshell
และตอนนี้บั๊ก "shellshock":
อย่างที่เราเพิ่งเห็นเมื่อเชลล์ใหม่นำเข้านิยามของตัวแปรปกติที่เริ่มต้นด้วย()การตีความมันเป็นฟังก์ชั่น อย่างไรก็ตามหากมีการกำหนดเพิ่มเติมหลังจากวงเล็บปิดที่กำหนดฟังก์ชั่นจะดำเนินการสิ่งที่มีเช่นกัน
นี่คือข้อกำหนดอีกครั้ง: 
- ทุบตีใหม่จะเกิดขึ้น
- ตัวแปรสภาพแวดล้อมถูกกลืนเข้า
- ตัวแปรสภาพแวดล้อมนี้เริ่มต้นด้วย "()" จากนั้นจะมีส่วนของฟังก์ชั่นภายในวงเล็บปีกกาจากนั้นก็มีคำสั่งหลังจากนั้น
ในกรณีนี้การทุบตีที่มีช่องโหว่จะดำเนินการคำสั่งหลัง
ตัวอย่าง:
$ export ex='() { echo "function ex" ; }; echo "this is bad"; '
$ bash
this is bad
$ ex
function ex
$
ตัวแปรที่ส่งออกปกติexถูกส่งผ่านไปยัง subshell ซึ่งถูกตีความว่าเป็นฟังก์ชั่นexแต่คำสั่งต่อท้ายได้รับการดำเนินการ ( this is bad) เป็น subshell วางไข่
อธิบายการทดสอบแบบบรรทัดเดียวที่ลื่นไหล
หนึ่งซับในที่นิยมสำหรับการทดสอบสำหรับช่องโหว่ Shellshock เป็นหนึ่งที่อ้างถึงในคำถามของ @ jippie:
env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
นี่คือการทำลายลงครั้งแรกในทุบตีเป็นเพียงชื่อย่อสำหรับ  : และทั้งคู่ประเมินว่า (คุณคาดเดา) จริงในทุบตี:truetrue:
$ if true; then echo yes; fi
yes
$ if :; then echo yes; fi
yes
$
ประการที่สองenvคำสั่ง (สร้างขึ้นใน bash) พิมพ์ตัวแปรสภาพแวดล้อม (ดังที่เราเห็นด้านบน) แต่ยังสามารถใช้เพื่อเรียกใช้คำสั่งเดียวด้วยตัวแปรส่งออก (หรือตัวแปร) ที่กำหนดให้กับคำสั่งนั้นและbash -cเรียกใช้คำสั่งเดียวจาก บรรทัดคำสั่ง:
$ bash -c 'echo hi'
hi
$ bash -c 'echo $t'
$ env t=exported bash -c 'echo $t'
exported
$
ดังนั้นการเย็บสิ่งทั้งหมดนี้เข้าด้วยกันเราสามารถรัน bash เป็นคำสั่งให้มันทำสิ่งที่ต้องทำ (เช่นbash -c echo this is a test) และส่งออกตัวแปรที่เริ่มต้นด้วย()ดังนั้น subshell จะตีความมันเป็นฟังก์ชั่น หาก shellshock มีอยู่ก็จะดำเนินการคำสั่งต่อท้ายใด ๆ ใน subshell ทันที เนื่องจากฟังก์ชั่นที่เราส่งผ่านนั้นไม่เกี่ยวข้องกับเรา (แต่ต้องแยกวิเคราะห์!) เราจึงใช้ฟังก์ชั่นที่สั้นที่สุดเท่าที่จะเป็นไปได้:
$ f() { :;}
$ f
$ 
ฟังก์ชั่นfที่นี่เพียงแค่รัน:คำสั่งซึ่งจะส่งกลับจริงและออก ตอนนี้ต่อท้ายว่าคำสั่ง "ความชั่วร้าย" และส่งออกตัวแปรปกติไปยัง subshell และคุณชนะ นี่คือหนึ่งซับอีกครั้ง:
$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
ดังนั้นxจะถูกส่งออกเป็นตัวแปรปกติที่มีฟังก์ชั่นที่ใช้งานง่ายและecho vulnerableติดอยู่กับส่วนท้าย สิ่งนี้ถูกส่งผ่านไปยัง bash และตีความ bash xเป็นฟังก์ชั่น (ซึ่งเราไม่สนใจ) แล้วอาจดำเนินการecho vulnerableถ้ามี shellshock อยู่
เราสามารถย่อขนาดหนึ่งซับให้สั้นลงได้โดยลบthis is a testข้อความ:
$ env x='() { :;}; echo vulnerable' bash -c :
สิ่งนี้ไม่รบกวนthis is a testแต่รัน:คำสั่งเงียบอีกครั้ง (ถ้าคุณออกไป-c :จากนั้นคุณนั่งใน subshell และต้องออกด้วยตนเอง) บางทีรุ่นที่เป็นมิตรกับผู้ใช้มากที่สุดน่าจะเป็นอันนี้:
$ env x='() { :;}; echo vulnerable' bash -c "echo If you see the word vulnerable above, you are vulnerable to shellshock"