env x = '() {:;}; ทุบตีคำสั่งทำและทำไมมันไม่ปลอดภัย?


237

เห็นได้ชัดว่ามีช่องโหว่ (CVE-2014-6271) ใน bash: Bash ที่สร้างขึ้นเป็นพิเศษสำหรับการโจมตีการฉีดโค้ดตัวแปรสภาพแวดล้อม

ฉันพยายามที่จะคิดออกว่าเกิดอะไรขึ้น แต่ฉันไม่แน่ใจว่าฉันเข้าใจ จะสามารถechoดำเนินการตามที่อยู่ในเครื่องหมายคำพูดเดียวได้อย่างไร

$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
vulnerable
this is a test

แก้ไข 1 : ระบบที่ถูกปะแก้มีลักษณะดังนี้:

$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `x'
this is a test

แก้ไข 2 : มีช่องโหว่ / patch ที่เกี่ยวข้อง: CVE-2014-7169ซึ่งใช้การทดสอบที่แตกต่างกันเล็กน้อย:

$ env 'x=() { :;}; echo vulnerable' 'BASH_FUNC_x()=() { :;}; echo vulnerable' bash -c "echo test"

เอาท์พุทที่ไม่ตรงกัน :

vulnerable
bash: BASH_FUNC_x(): line 0: syntax error near unexpected token `)'
bash: BASH_FUNC_x(): line 0: `BASH_FUNC_x() () { :;}; echo vulnerable'
bash: error importing function definition for `BASH_FUNC_x'
test

เอาต์พุต patched บางส่วน (เวอร์ชั่นก่อนหน้า) :

bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `x'
bash: error importing function definition for `BASH_FUNC_x()'
test

เอาต์พุตที่ได้รับการปรับปรุงแล้วและรวมถึง CVE-2014-7169:

bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `BASH_FUNC_x'
test

แก้ไข 3 : เรื่องราวดำเนินต่อไปด้วย:


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

26
eyoung100 ไม่มีเสียงสะท้อนใด ๆถูกประหารชีวิต คุณสามารถเห็นว่ามันกำลังดำเนินการเพราะคำที่vulnerableปรากฏในการส่งออก ปัญหาหลักคือการทุบตีจะแยกวิเคราะห์และรันรหัสหลังจากนิยามฟังก์ชั่นเช่นกัน ดู/bin/idส่วนของseclists.org/oss-sec/2014/q3/650สำหรับตัวอย่างอื่น
มิเคล

4
เพียงแค่แสดงความคิดเห็นด้านข้างอย่างรวดเร็ว Red Hat ได้แนะนำว่าแพทช์ที่ได้รับการเผยแพร่นั้นเป็นเพียงแพทช์บางส่วนและทำให้ระบบยังมีความเสี่ยง
ปีเตอร์

2
@ eyoung100 ความแตกต่างก็คือรหัสภายในฟังก์ชั่นจะทำงานเมื่อมีการเรียกตัวแปรสภาพแวดล้อมอย่างชัดเจน โค้ดหลังจากนิยามฟังก์ชันจะทำงานทุกครั้งที่กระบวนการ Bash ใหม่เริ่มต้นขึ้น
David Farrell

1
ดูstackoverflow.com/questions/26022248/…สำหรับรายละเอียดเพิ่มเติม
Barmar

คำตอบ:


204

bash เก็บนิยามฟังก์ชันที่เอ็กซ์พอร์ตเป็นตัวแปรสภาวะแวดล้อม ฟังก์ชั่นที่ส่งออกมีลักษณะเช่นนี้:

$ foo() { bar; }
$ export -f foo
$ env | grep -A1 foo
foo=() {  bar
}

นั่นคือตัวแปรสภาพแวดล้อมfooมีเนื้อหาตามตัวอักษร:

() {  bar
}

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

$ export foo='() { echo "Inside function"; }'
$ bash -c 'foo'
Inside function

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

$ export foo='() { echo "Inside function" ; }; echo "Executed echo"'
$ bash -c 'foo'
Executed echo
Inside function

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

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

นี่คือตัวอย่างของการโจมตีได้ คุณเรียกใช้เว็บเซิร์ฟเวอร์ที่เรียกใช้เชลล์ที่มีช่องโหว่ซึ่งเป็นส่วนหนึ่งของอายุการใช้งาน เว็บเซิร์ฟเวอร์นี้ส่งผ่านตัวแปรสภาพแวดล้อมไปยังสคริปต์ทุบตีตัวอย่างเช่นหากคุณใช้ CGI ข้อมูลเกี่ยวกับคำขอ HTTP มักจะถูกรวมไว้เป็นตัวแปรสภาพแวดล้อมจากเว็บเซิร์ฟเวอร์ ตัวอย่างเช่นHTTP_USER_AGENTอาจถูกตั้งค่าเป็นเนื้อหาของตัวแทนผู้ใช้ของคุณ ซึ่งหมายความว่าหากคุณหลอกตัวแทนผู้ใช้ของคุณให้เป็นเช่น '() {:; }; echo foo ', เมื่อเชลล์สคริปต์นั้นทำงาน, echo fooจะถูกประมวลผล อีกครั้งecho fooอาจเป็นอะไรก็ได้ที่เป็นอันตรายหรือไม่


3
สิ่งนี้จะส่งผลกระทบต่อเปลือกหอยเหมือนทุบตีอื่น ๆ เช่น Zsh?
Amelio Vazquez-Reina

3
@ user815423426 ไม่ zsh ไม่มีฟีเจอร์นี้ Ksh มี แต่ใช้งานแตกต่างกันฉันคิดว่าฟังก์ชั่นสามารถส่งในสถานการณ์ที่แคบมากเท่านั้นหากเชลล์ใช้งานไม่ใช่ผ่านสภาพแวดล้อม
Gilles

20
@ user815423426 rc เป็นเชลล์อื่นที่ส่งผ่านฟังก์ชันในสภาพแวดล้อม แต่มีตัวแปรที่มีชื่อนำหน้าด้วย "fn_" และพวกมันจะถูกตีความเมื่อเรียกใช้เท่านั้น
Stéphane Chazelas

18
@ StéphaneChazelas - ขอบคุณสำหรับการรายงานข้อผิดพลาด
Deer Hunter

13
@gnclmorais คุณหมายความว่าคุณทำงานexport bar='() { echo "bar" ; }'; zsh -c barและจะแสดงbarมากกว่าzsh:1: command not found: bar? คุณแน่ใจหรือไม่ว่าคุณไม่สับสนกับเชลล์ที่คุณใช้เรียกเชลล์ที่คุณใช้ในการตั้งค่าการทดสอบ
Gilles

85

สิ่งนี้อาจช่วยแสดงให้เห็นถึงสิ่งที่เกิดขึ้น:

$ export dummy='() { echo "hi"; }; echo "pwned"'
$ bash
pwned
$

หากคุณใช้เชลล์ที่มีช่องโหว่จากนั้นเมื่อคุณเริ่มเชลล์ย่อยใหม่ (ที่นี่เพียงแค่ใช้คำสั่ง bash) คุณจะเห็นว่าโค้ดอาร์บิทรารี่ ( echo "pwned") นั้นถูกเรียกใช้งานโดยทันทีซึ่งเป็นส่วนหนึ่งของการเริ่มต้น เห็นได้ชัดว่าเชลล์เห็นว่าตัวแปรสภาพแวดล้อม (ดัมมี่) มีความหมายของฟังก์ชั่นและประเมินความหมายเพื่อกำหนดฟังก์ชั่นในสภาพแวดล้อมของมัน (โปรดทราบว่ามันไม่ได้ดำเนินการฟังก์ชั่น: ที่จะพิมพ์ 'hi')

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

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

$ declare -f
dummy ()
{
    echo "hi"
}
declare -fx dummy
$ dummy
hi
$echo $dummy
$

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


17
ในขณะที่คำตอบที่ยอมรับนั้นจริง ๆ แล้วพูดอย่างนี้ถ้าคุณอ่านอย่างระมัดระวังฉันพบว่าคำตอบนี้ชัดเจนและเป็นประโยชน์มากขึ้นในการทำความเข้าใจว่าเป็นการประเมินคำนิยาม (แทนที่จะเรียกใช้ฟังก์ชันเอง) ที่เป็นปัญหา
natevw

1
ทำไมตัวอย่างนี้มีexportคำสั่งในขณะที่คนอื่นมีenv? ฉันคิดว่าenvกำลังถูกใช้เพื่อกำหนดตัวแปรสภาพแวดล้อมที่จะถูกเรียกเมื่อมีการเปิดตัว bash shell อื่น แล้วมันทำงานอย่างไรกับexport
Haris

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

@ralph - คำจำกัดความทั้งสภาพแวดล้อมenvและการexportส่งออกเพื่อให้พวกเขามีอยู่ในเปลือกย่อย ปัญหาคือวิธีการนำเข้าคำจำกัดความที่ส่งออกเหล่านี้ไปยังสภาพแวดล้อมของ subshell และโดยเฉพาะในกลไกที่นำเข้าคำจำกัดความของฟังก์ชัน
sdenham

1
@ralph - envเรียกใช้คำสั่งพร้อมตัวเลือกและชุดตัวแปรสภาพแวดล้อม โปรดทราบว่าในตัวอย่างคำถามดั้งเดิมให้envตั้งค่าxเป็นสตริงและเรียกใช้bash -cพร้อมกับคำสั่งให้เรียกใช้ หากคุณเป็นenv x='foo' vimกลุ่มจะเรียกใช้และในนั้นคุณสามารถโทรหาเชลล์ที่มี / สภาพแวดล้อมด้วย!echo $xและมันจะพิมพ์fooแต่ถ้าคุณออกแล้วและecho $xมันจะไม่ถูกกำหนดตามที่มันมีอยู่เฉพาะในขณะที่เรียกใช้เป็นกลุ่ม ผ่านenvคำสั่ง exportคำสั่งแทนชุดค่าถาวรในสภาพแวดล้อมปัจจุบันเพื่อให้การทำงาน subshell ต่อมาจะใช้พวกเขา
Gary Fixler

72

ฉันเขียนสิ่งนี้เป็นรูปแบบการสอนใหม่ของคำตอบที่ยอดเยี่ยมโดย 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":

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

นี่คือข้อกำหนดอีกครั้ง:

  1. ทุบตีใหม่จะเกิดขึ้น
  2. ตัวแปรสภาพแวดล้อมถูกกลืนเข้า
  3. ตัวแปรสภาพแวดล้อมนี้เริ่มต้นด้วย "()" จากนั้นจะมีส่วนของฟังก์ชั่นภายในวงเล็บปีกกาจากนั้นก็มีคำสั่งหลังจากนั้น

ในกรณีนี้การทุบตีที่มีช่องโหว่จะดำเนินการคำสั่งหลัง

ตัวอย่าง:

$ 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"

12
คำอธิบายที่ดี คำถามนี้ได้รับมุมมองมากมาย (อาจไม่ใช่ทุกคนที่มีความเชี่ยวชาญในการทุบตีเหมือนคนอื่น ๆ ) และฉันเชื่อว่าไม่มีใครเลยที่ใช้คำสองคำในสิ่งที่{ :;};พูดจริง ๆ นั่นจะเป็นการเพิ่มคำตอบของคุณในความคิดของฉัน อาจอธิบายว่าคุณได้ตัวอย่างจากคำสั่งเดิมในคำถามได้ไหม?
jippie

20

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

อย่างไรก็ตาม CVE-2014-6271 นั้นแตกต่างกัน

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

ตัวอย่างที่ได้รับการหยิบยกในบริบทของ CVE-2014-6271 คือสคริปต์ที่ใช้สำหรับการแยกวิเคราะห์ไฟล์บันทึก สิ่งเหล่านั้นอาจมีความต้องการที่ถูกต้องตามกฎหมายในการส่งผ่านข้อมูลที่ไม่น่าเชื่อถือในตัวแปรสภาพแวดล้อม แน่นอนว่าชื่อของตัวแปรสภาพแวดล้อมดังกล่าวได้รับการคัดเลือกจนไม่มีผลกระทบใด ๆ

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

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

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

ผู้โจมตีที่รู้ชื่อที่แน่นอนของตัวแปรที่ส่งออกโดยprogram1และชื่อของตัวแปรที่ตีความโดยprogram2ไม่สามารถใช้ประโยชน์จากความรู้นี้เพื่อปรับเปลี่ยนพฤติกรรมของ 'program2` หากไม่มีการซ้อนทับกันระหว่างชุดของชื่อ

แต่นี่พังถ้าprogram2เป็นbashสคริปต์เพราะเนื่องจากข้อผิดพลาดนี้bashจะตีความทุกตัวแปรสภาพแวดล้อมเป็นรหัส


1
"ตัวแปรสภาพแวดล้อมทุกตัวกลายเป็นเวคเตอร์การโจมตี" - นั่นคือส่วนที่ฉันหายไป ขอบคุณ
wrschneider

9

มีการอธิบายในบทความที่คุณเชื่อมโยง ...

คุณสามารถสร้างตัวแปรสภาพแวดล้อมด้วยค่าที่สร้างขึ้นเป็นพิเศษก่อนเรียก bash shell ตัวแปรเหล่านี้สามารถมีรหัสซึ่งได้รับการดำเนินการทันทีที่เชลล์ถูกเรียกใช้

ซึ่งหมายถึงการทุบตีที่เรียกว่า-c "echo this is a test"รันรหัสในคำพูดเดียวเมื่อมันถูกเรียก

Bash มีฟังก์ชั่นแม้ว่าจะมีการใช้งานค่อนข้าง จำกัด และเป็นไปได้ที่จะนำฟังก์ชัน bash เหล่านี้ไปใช้ในตัวแปรสภาพแวดล้อม ข้อบกพร่องนี้จะเกิดขึ้นเมื่อมีการเพิ่มรหัสพิเศษในตอนท้ายของคำนิยามฟังก์ชั่นเหล่านี้ (ภายในตัวแปร enivronment)

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

สิ่งพิเศษจริง ๆ เกี่ยวกับข้อมูลโค้ดที่คุณโพสต์ตามที่ฉันเข้าใจคือการใช้การกำหนดฟังก์ชั่นก่อนรหัสที่เราต้องการเรียกใช้กลไกการรักษาความปลอดภัยบางอย่างสามารถหลีกเลี่ยงได้

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