มาทำความเข้าใจกับช่องโหว่ ShellShock Step-by-Step
Author By: Mr. Chayutm Prapassaraporn | ACIS Cyber LAB Team
CISSP, SANS GIAC GPEN, OSCP, CBCI, ECSA, CEH, SMFE, CCSK, Security+, CCNA
Senior Information Security Consultant, Consulting Service Department (CSD)
Abstract
บทความนี้จะเน้นไปที่การสร้างความเข้าใจเกี่ยวกับ Shellshock (หรือเรียกอีกอย่างว่า Bashdoor) ที่เป็นรูปแบบการทำงานที่ผิดพลาดของ Shell ที่มีชื่อว่า Bash (Bourne Again Shell) ในระบบปฏิบัติการตระกูล UNIX ซึ่งส่งผลกระทบต่อความมั่นคงปลอดภัยของระบบในรูปแบบที่ทำให้ผู้บุกรุกสามารถรันคำสั่งเพื่อสั่งการเครื่องคอมพิวเตอร์ของเหยื่อให้กระทำการใด ๆ ได้ (Command Execution) ผ่านสิ่งที่เรียกว่า “Environment Variable” ซึ่งถูกเปิดเผยเมื่อวันที่ 24 กันยายน 2557 โดย Stephane Chazelas ซึ่งเป็น open source software developer ชาวสหราชอาณาจักร ฯ
Terminology, let it be clear
- Shell
Shell เป็นส่วนที่มีไว้ให้ผู้ใช้งานสามารถที่จะปฏิสัมพันธ์ (Interfacing) กับระบบปฏิบัติการของคอมพิวเตอร์ได้ เช่น การสั่งรันโปรแกรม การเข้าถึงระบบจัดการไฟล์ การเล่นเกม ฯลฯ ซึ่งโดยทั่วไปแล้ว Shell จะมีอยู่ด้วยกันใน 2 รูปแบบหลัก ๆ คือ Command-Line Interface (CLI, Text Shell, Command Processor และ Command Shell) ดังในรูปตัวอย่างที่ 1 และ Graphic User Interface (GUI และGraphic shell) ดังในรูปตัวอย่างที่ 2 ซึ่งในบทความนี้จะเน้นไปที่ CLI เนื่องจากเป็นเรื่องที่เกี่ยวข้องโดยตรง
รูปที่ 1: CLI Shell ของระบบปฏิบัติการของ Windows® (จาก http://en.wikipedia.org)
รูปที่ 2: Graphic Shell (Graphic User Interface) ของระบบปฏิบัติการ Ubuntu (จาก http://i.stack.imgur.com)Command-Line Interface (CLI) ถือได้ว่าเป็น User Interface รูปแบบแรกที่มีการใช้งานกับคอมพิวเตอร์ และสามารถทำหน้าที่ได้ดีกว่าในบางสถานการณ์ โดยเฉพาะเมื่อยังไม่ได้มีการสร้าง Graphic Interface หรือ Graphic Interface ไม่สามารถสร้างได้สำหรับบาง Function การทำงานของระบบปฏิบัติการ (แต่ก็มีหลายงานที่การสื่อสารผ่าน GUI ทำได้ดีกว่า เช่น เล่นเกมส์, Presentation) โดยในปัจจุบันระบบปฏิบัติการของ Desktop Computer จะมีทั้ง CLI และ GUI มาให้ ซึ่งในการใช้งาน CLI นั้น ผู้ใช้งานจำเป็นต้องจดจำคำสั่ง และรูปแบบของการใช้คำสั่งนั้นๆ ให้ได้ (ตัวอย่างรูปที่ 3 การบีบอัดไฟล์โดยใช้โปรแกรม ZIP ผ่าน CLI) ดังนั้นในการที่จะทำงานหลาย ๆ อย่างผ่าน CLI ก็จำเป็นที่จะต้องจำคำสั่งหล่านั้นเยอะพอสมควร รวมถึงต้องเรียนรู้ Scripting Language สำหรับ Shell นั้น ๆ ด้วย
รูปที่ 3: การบีบอัดไฟล์โดยใช้โปรแกรม ZIP ผ่าน CLICommand Shell นั้นมีการใช้งานอย่างมากในระบบปฏิบัติการตระกูล UNIX และก็มีประเภทของ Shell อยู่หลากหลาย เช่น Bourne Shell (sh), Almquist Shell (ash), Bourne Again Shell (bash), Korn Shell (ksh), Z Shell (zsh), C Shell (csh), TENEX C Shell (tcsh), Emacs Shell (eshell) Stand-alone Shell (sash) และ Remote Shell (rsh) เป็นต้น โดยถ้าเราอยากทราบว่ากำลังใช้ Shell ตัวใดอยู่นั้น ก็สามารถทำได้โดยการ เรียกใช้คำสั่ง “echo $0” ผ่าน Command Shell ที่เรากำลังใช้อยู่ หรือ พิมพ์คำสั่งที่ไม่มีอยู่จริง (พิมพ์มั่ว ๆ นั่นเอง) ลงไปใน shell แล้วสังเกตจาก Error Message ก็ได้ ดังในรูปที่ 4
รูปที่ 4: การตรวจสอบ Shell ที่ใช้งานอยู่ - Bourne Again shell (bash)
Bash เป็น Command Shell ตัวหนึ่งที่ถูกเขียนขึ้นโดย Brian Fox ในรูปแบบที่เป็น Free Software สำหรับ GNU Project เพื่อใช้งานในระบบปฏิบัติการตระกูล UNIX แทน Bourne Shell (sh) เช่นเดียวกับ Command Shell อื่น ๆ bash ก็จะทำงานโดยการรับคำสั่ง (Command) จากผู้ใช้งาน จากนั้นจะนำคำสั่งไปประมวลผลแล้วจึงแสดงผลลัพธ์ออกมา หรือ จะเรียกใช้คำสั่งที่อยู่ในไฟล์ก็ทำได้ โดยใช้ความสามารถของ bash ในการรันหลาย ๆ คำสั่งร่วมกันโดยมี Algorithm การทำงานเพื่อให้ได้ผลลัพธ์อย่างใดอย่างหนึ่ง เรามักเรียกว่า Script ดังในรูปที่ 5 เป็นการแสดงให้เห็นถึงการรัน Script จากผู้ใช้งาน และจากไฟล์ตามลำดับ
รูปที่ 5: แสดงให้เห็นถึงการรัน script จากผู้ใช้งาน และจากไฟล์ - Bash Function
โดยปกติในการเขียนโปรแกรม เมื่อมีการต้องเรียกใช้ Algorithm ในการประมวลผลใด ๆ ซ้ำ ๆ นั้น นักเขียนโปรแกรมก็จะประกาศ script ของ algorithm นั้นเป็น Function และตั้งขื่อให้กับ Function นั้น เพื่อการเรียกใช้งานที่ง่าย และ รวดเร็ว ไม่ต้องพิมพ์ บ่อย ๆ ครั้งดังรูปที่ 6
รูปที่ 6: การใช้งาน Function ใน bash - Environment Variable
คำว่าสภาพแวดล้อม (Environment) โดยทั่วไปหมายถึงสภาพที่อยู่รอบๆ และส่งผลต่อการกระทำของ คน สัตว์ สิ่งของ ซึ่งในทางคอมพิวเตอร์ จากกรณีของตัวอย่างที่ผ่านมาข้างต้น ผู้เขียนได้มีการใช้งานคำสั่งในระบบปฏิบัติการ KALI Linux ผ่าน Bash ดังนั้นผู้เขียนจึงทำงานอยู่ในสภาพแวดล้อมของ bash และต้องใช้คำสั่ง รวมถึง Syntax ต่าง ๆ ของ bash อย่างถูกต้องEnvironment Variable คือตัวแปรของสภาพแวดล้อมของระบบที่เราทำงานด้วย และส่งผลต่อ Process ที่ทำงานในเครื่องคอมพิวเตอร์ (เรียกกันสั้น ๆ ว่า ตัวแปรของระบบ) เราสามารถดูค่าตัวแปรของระบบเหล่านี้ โดยการรันคำสั่ง env ผ่าน bash ซึ่งผลลัพธ์ จะอยู่ในรูปแบบ NAME=VALUE ดังรูปที่ 7
รูปที่ 7: การใช้คำสั่ง env เพื่อดูค่าตัวแปรของระบบแล้วตัวแปรของระบบเหล่านี้ถูกนำไปใช้งานอย่างไร? ต้องตอบว่าได้หลายรูปแบบแล้วแต่ว่า Process ที่มาอ่านค่าตัวแปรจะนำค่าตัวแปรนั้น ๆ ไปใช้ทำอะไร เพื่อให้เกิดความเข้าใจเรามาดูตัวอย่างกัน โดยปกติในระบบปฏิบัติการ Linux เวลาเรา login เข้าระบบสำเร็จและมาถึงหน้าจอของ Command Shell เราจะอยู่ใน Directory ที่เรียกว่า “home directory” เสมอ หากสังเกตที่ Command Prompt จะมีเครื่องหมาย “~” ซึ่งบ่งบอกว่าเรากำลังอยู่ใน home directory ของ user account ดังรูปที่ 8
รูปที่ 8: Home Directory ของ user account “root” คือ “/root”home directory ของ user account “root” คือ “/root”เมื่อเราจะเปลี่ยน directory เราก็พิมพ์คำสั่ง “cd” ตามด้วย directory ที่ต้องการจะไป เช่น ”cd testtest” (เป็นการอ้างถึง directory แบบ relative) ดังรูปที่ 9
รูปที่ 9: การเปลี่ยน directoryทีนี้ถ้าเราต้องการกลับไปที่ home directory ของเรา เราสามารถพิมพ์คำสั่ง “cd /root” ตามปกติก็ได้ หรือ เราสามารถพิมพ์ คำสั่ง “cd ~” เพื่อกลับไปยัง home directory ของเราทันที ซึ่งในจุดนี้เอง เมื่อเราพิมพ์คำสั่ง “cd ~” ระบบจะรู้ว่าเราต้องการกลับไป home directory ระบบจะไปดูค่าของ Environment Variable ที่ชื่อว่า “HOME” ว่าเป็นค่าใด แล้วก็ทำการส่งเราไปที่นั่น ดังรูปที่ 10
ดังรูปที่ 10: ตัวอย่างการใช้งาน Environment Variable ที่ชื่อ HOME
What could go wrong? And How?
อย่างที่ได้กล่าวไปข้างต้น เกี่ยวกับ Environment Variable ซึ่ง Environment Variable เหล่านี้สามารถถูกแก้ไขเปลี่ยนแปลง เพิ่มเติมและ ลบทิ้งได้ ดังรูปที่ 11
รูปที่ 11: แก้ไขค่าตัวแปรของระบบ “HOME”
โดยทั่วไปนอกจากระบบที่เป็นตัวจัดการ Environment Variable แล้ว ผู้ใช้งาน หรือ Process สามารถสร้างหรือแก้ไข Environment Variable ได้ด้วย เพื่อให้การทำงานของ Program หรือ Process เป็นไปตามที่ต้องการ ซึ่งการสร้างหรือเปลี่ยนแปลงแก้ไข Environment Variable สามารถทำได้ใน 3 รูปแบบ หลัก ๆ
- ประกาศ Environment Variable เพียงอย่างเดียว ดังในรูปที่ 12
- ประกาศ Environment Variable โดยใช้คำสั่ง “export” ดังในรูปที่ 13
- ประกาศ Environment Variable โดยใช้คำสั่ง “env” ดังในรูปที่ 14
- การลบ Environment Variable สามารถทำได้โดยใช้คำสั่ง “unset” ดังรูปที่ 15
รูปที่ 12: ประกาศ Environment Variable แบบ NAME=VALUE เพียงอย่างเดียว
รูปที่ 13: ประกาศ Environment Variable โดยใช้คำสั่ง “export”
รูปที่ 14: ประกาศ Environment Variable โดยใช้คำสั่ง “env”
รูปที่ 15: การลบ Environment Variable
ทั้ง 3 วิธีก็มีความแตกต่างกันดังตัวอย่างต่อไปนี้
- วิธีแรกจะมีผลต่อ “bash shell ที่เราใช้กำหนดค่า variable นี้เท่านั้น หากเราเปิด bash instance ใหม่ ตัวแปรจะไม่ตามไปด้วย และเมื่อเรากลับมาที่ bash instance เดิม เราก็จะเรียกใช้ตัวเปรได้ตามเดิม” ดังรูปตัวอย่างที่ 16 เรามักใช้กับตัวแปรที่เราต้องการรันใน bash script ดังรูปตัวอย่างที่ 17
รูปที่ 16: ประกาศ Environment Variable แบบ NAME=VALUE
รูปที่ 17: การใช้งาน Environment Variable ใน Script - การตั้งค่า Environment Variable ผ่านคำสั่ง “export” นั้น “จะทำให้ bash instance อื่น ๆ เข้าถึงตัวแปรได้ด้วย” ดังรูปที่ 18
รูปที่ 18: การตั้งค่า Environment Variable ผ่านคำสั่ง “export” - การตั้งค่า Environment Variable ผ่านคำสั่ง “env” นั้น “เป็นการสร้างตัวแปรให้กับ bash instance หนึ่ง เพื่อรัน Process หรือ Command ใด ๆ และเมื่อ Command นั้นทำงานสำเร็จ ตัวแปรก็จะถูกลบไปในทันที” ดังรูปที่ 19
รูปที่ 19: การตั้งค่า Environment Variable ผ่านคำสั่ง “env”
ทีนี้การตั้งค่าให้กับ Environment Variable นั้น เราสามารถตั้งเป็น Bash Function ได้โดยอาศัยวิธีการ ตั้งค่าให้กับ Environment Variable จาก 3 วิธีข้างต้น และการเรียกใช้งานก็ต่างกันไปดังรูปที่ 20 (ตรงนี้ผู้เขียนพบว่าการตั้งค่าด้วยวิธีแบบที่ 1 ไม่สามารถเรียกใช้ Function ได้ ใน MAC OSX)
รูปที่ 20: การใช้งาน Bash Function ผ่าน Environment Variable
ประเด็นช่องโหว่ เกิดตอนที่เราทำการตั้งค่า Environment เป็น Function นี่แหละครับ โดยใน Bash Version ที่ยังไม่ได้อัพเดทหรือมีช่องโหว่อยู่นั้น จะมีการทำงานผิดพลาด ที่หากว่ามีการแทรกคำสั่งใด ๆ ลงไปหลังจาก Function ที่อยู่ใน Environment Variable นั้น คำสั่งที่ว่านั้นจะทำงานไปด้วย ดังรูปที่ 21
รูปที่ 21: การทำงานผิดพลาดที่ทำให้คำสั่งที่แทรกลงไปหลัง Function สามารถทำงานได้
แน่นอนครับ เห็นแบบนี้ครั้งแรกบางท่านอาจจะคิดว่า “ก็เราเรียกใช้ Function เองนี่นา มันจะทำงานแบบนั้นก็น่าจะถูกแล้วมั้ง?” เราลองมาดูรูปที่ 22 และ 23
รูปที่ 22: การทำงานผิดพลาดที่ทำให้คำสั่งที่แทรกลงไปหลัง Function สามารถทำงานได้ ทั้ง ๆ ที่ไม่ได้เรียก Function เพียงแค่เปิด bash instance ใหม่เฉย ๆ
รูปที่ 23: ถ้าคำสั่งไม่ไปแทรกอยู่หลัง Function ก็จะไม่ทำงาน จะเป็นแค่ String ธรรมดา ๆ
จากรูปที่ 22 เหตุการณ์ที่เกิดขึ้นสามารถอธิบายได้แบบนี้ครับ Bash เองก็คือโปรแกรมหนึ่งที่ถูกเขียนด้วยภาษา C (เหมือนโปรแกรม Utility ใน UNIX ส่วนใหญ่ทั่วไป) เมื่อมีการเรียกใช้งาน bash ด้วยคำสั่ง “bash” นั้น Bash Instance ใหม่ก็จะถูกสร้างขึ้นมา โดยในกระบวนการนี้เองจะมีขั้นตอนหนึ่งก่อนที่ Bash Instance นั้นจะถูกสร้างสำเร็จก็คือ จะมีการสแกนหา Environment Variable ที่ถูกประกาศเป็น Function (variable ที่ขึ้นต้นด้วย “()”) แล้วทำการแปลงค่าในตัวแปรนี้ไปเป็น Internal Function
โดยในขั้นตอนการแปลงค่าตัวแปรนี้ไปเป็น Internal Function จะเป็นการนำค่าที่อยู่ในตัวแปร ไปประกาศเป็น Function ใหม่แล้วทำการ Execute Function นี้ “โดยที่ไม่ได้ตรวจสอบว่าค่าในตัวแปรนั้น ไม่ได้เป็นเพียงการประกาศ Function แต่ยังมี command ติดตามมาอีกด้วย”
สำหรับ Function เจ้ากรรมของ bash ที่ทำการแปลง ค่าใน Environment Variable ไปเป็น internal Function ชื่อว่า “parse_and_execute()” อยู่ในไฟล์ Source Code ของ bash ที่ชื่อว่า “evalstring.c” นี่ก็เป็นที่มาของการที่แค่เราเปิด bash คำสั่งที่แฝงมาก็จะทำงานทันที
How can it be leveraged?
อย่างที่ได้กล่าวไปในหัวข้อก่อนหน้า ว่า ผู้ใช้งาน หรือ process ที่ทำงานอยู่ในเครื่องคอมพิวเตอร์ก็สามารถที่จะสร้าง Environment Variable ให้กับตัวเองเพื่อการใช้งานได้เช่นกัน ทีนี้เราลองคิดว่า ถ้าเป็นWeb Application ที่มีการรับ Input จากผู้ใช้งาน ซึ่งเป็น Untrusted Source แล้วนำ Input นั้นไปสร้าง Environment Variable ใน bash ล่ะ? ใช่ครับ เรากำลังพูดถึงตัวอย่างที่เข้าใจง่ายที่สุดก็คือ Common Gateway Interface (CGI) นั้นเอง ดังรูปที่ 24
รูปที่ 24: การรันคำสั่ง “ifconfig” ซึ่งเป็นคำสั่งที่ใช้ในการดูการตั้งค่าของ Network Interface Card ที่อยู่กับเครื่องคอมพิวเตอร์ ผ่าน CGI
เมื่อพิจารณา Source Code ของไฟล์ที่ชื่อ “flaw.sh” ในรูปที่ 25 จะเห็นว่าในตอนต้นของ Script มีการประกาศ “#!/bin/bash” (ตรงนี้เรียกว่า shebang) เพื่อบอกให้รู้ว่า Script ต่อจากนี้ไป จะถูก handle ด้วย bash ซึ่งใน Script นี้ก็จะทำการ echo ข้อความบรรทัดต่าง ๆ ที่เป็นภาษา html รวมทั้งการเรียกใช้คำสั่ง “ifconfig” เพื่อแสดงข้อมูล Network Interface Card ด้วย
รูปที่ 25: source code ของ flaw.sh
ทีนี้เราอยากรู้ว่า Web Applicationที่ใช้งานอยู่นี้ มี Environment Variable อะไรที่ใช้งานอยู่บ้าง ผู้เขียนจะทำการแก้ Source Code ของ flaw.sh เป็นดังรูปที่ 26 และผลลัพธ์จะแสดงให้เห็นในรูปที่ 27
รูปที่ 26: เพิ่มบรรทัด “/usr/bin/env” เพื่อเรียกดู Environment Variable ที่ Web Server ใช้งานอยู่
รูปที่ 27: Environment Variable ของ Web Server
จะเห็นว่ามีหลาย Environment Variable ที่รับค่ามาจาก HTTP Header ซึ่ง ผู้เข้าใช้งาน Website (ทั้งที่เป็นคนดี และไม่ดี) สามารถแก้ไขเองได้ เช่น HTTP_USER_AGENT, HTTP_ACCEPT_ENCODING, HTTP_ACCEPT_LANGUAGE, HTTP_REFERER และ HTTP_ACCEPT เป็นต้น
เราจะลองทดสอบ โดยการโจมตีผ่าน HTTP Header ที่ ชื่อว่า “Referrer” โดยใช้โปรแกรม CURL ในการโจมตีผ่าน Referrer header ดังรูปที่ 28 เพื่อสั่งให้เครื่องของเหยื่อ ทำการ ping ไปที่ www.acis.co.th เป็นจำนวน 5 ครั้ง
รูปที่ 28: การโจมตีช่องโหว่ Shellshock เพื่อทำการ Remote Command Execution สำเร็จ
นี่เป็นเพียงตัวอย่างหนึ่งของการอาศัย Shellshock เพื่อเข้าควบคุมเครื่องคอมพิวเตอร์เป้าหมาย เหตุการณ์นี้สามารถเกิดได้กับ service อื่น ๆ ที่…
- ทำงานร่วมกับ Bash ที่มีช่องโหว่ Shellshock
- Service ที่ว่านี้มีการใช้งาน Environment Variable
- ผู้ใช้งานสามารถส่งค่า Input ไปที่ Environment ได้ ไม่ทางใดก็ทางหนึ่ง
Am I affected by this flaw?
Bash ที่ได้รับผลกระทบเรื่องนี้ก็จะเป็นตั้งแต่ Version 1.14 ถึง 4.3 เลยทีเดียว
Why so many CVEs?
ตัวอย่างที่แสดงในบทความนี้ เป็นรูปแบบแรกของช่องโหว่ Shellshock ซึ่งได้รับหมายเลข CVE เป็น CVE-2014-6271 หลังจากนั้นก็ได้มี patch ออกมาแก้ไขปัญหานี้ในทันที แต่ทว่าเป็น patch ที่ไม่ได้แก้ปัญหาอย่างเด็ดขาด ยังมีคนพบวิธีการโจมตี Shellshock ในแบบอื่น ๆ และเหตุการณ์เช่นนี้ก็เกิดขึ้นเรื่อย ๆ เมื่อมี patch ออกมา ก็มีคนค้นพบวิธีการโจมตีแบบใหม่ ๆ จนได้มาเป็น CVE-2014-7169, CVE-2014-7186, CVE-2014-7187, CVE-2014-6277, CVE-2014-6278 ฯลฯ
How to protect my system from this threat?
การแก้ไขทำได้ 2 ทาง หลัก ๆ
-
- โดยการ patch โปรแกรม Bash ในเครื่องของเรา – ซึ่งต้องบอกว่า patch ที่มีในขณะเรียกได้ว่ายังไม่สมบูรณ์ แต่ก็มี patch ออกมาเรื่อย ๆ เพื่อแก้ไขเทคนิคที่ใช้โจมตี Shellshock ใหม่ ๆ โดยวิธีการโจมตี, patch และ ประกาศสำหรับ patch ล่าสุด สามารถดูได้ที่ https://shellshocker.net/
- ทำการ tuning อุปกรณ์หรือ Software IDS เพื่อให้ตรวจจับและป้องกันการโจมตี Shellshock จาก Remote Attacker หากเป็น IDS ที่มี Commercial Subscription ก็ให้ทำการ Update signature จาก Vendor ได้เลย
- วิธีการป้องกันรูปแบบอื่น ๆ สามารถศึกษาได้ที่ https://access.redhat.com/articles/1212303
Conclusion
สำหรับช่องโหว่ Shellshock นี้ เรียกได้ว่าเราต้องใช้วิธีการ Defense-In-Depth กันอย่างเต็มที่ ไล่ตั้งแต่ NIDS/NIPS เข้ามาถึงการป้องกันที่ server เช่น mod_security จนมาถึงการ patch ตัว bash เนื่องจาก patch ที่สมบูรณ์นั้นอาจจะต้องใช้เวลาอีกสักเล็กน้อย แต่หากเรามีการ Defense-In-Depth ที่รัดกุมแล้ว ก็จะช่วยลดความเสี่ยงต่อระบบของเราได้มากทีเดียว ด้วยเหตุนี้ เราจึงต้องติดตามข่าวสารเพื่อ update ช่องทางการโจมตีใหม่ ๆ เพราะอย่างที่ได้กล่าวไปในหัวข้อ “How can it be leveraged?” ที่ว่า service ใด ๆ ก็ตามที่มีการใช้งาน Environment Variable ผ่าน bash ที่มีช่องโหว่ และรับ Input มาจาก Untrusted Source มีสิทธ์โดนโจมตีได้เท่ากัน ไม่ใช่เฉพาะ Web CGI
References
- http://askubuntu.com/questions/205688/whats-the-difference-between-set-export-and-env-and-when-should-i-use-each
- https://shellshocker.net/
- http://security.stackexchange.com/questions/68448/where-is-bash-shellshock-vulnerability-in-source-code
- https://access.redhat.com/articles/1212303
- http://www.troyhunt.com/2014/09/everything-you-need-to-know-about.html