เหตุการณ์ XZ backdoor: ผลการวิเคราะห์เบื้องต้น
(securelist.com)- XZ Utils/liblzma backdoor ถูกเปิดเผยเมื่อวันที่ 29 มีนาคม 2024 บนเมลลิงลิสต์ Openwall OSS-security และมีความเป็นไปได้สูงว่าเป้าหมายสุดท้ายของผู้โจมตีคือฝังความสามารถในการรันโค้ดจากระยะไกลลงใน sshd ซึ่งเป็นเซิร์ฟเวอร์ OpenSSH บนดิสโทรที่ใช้ systemd
- ห่วงโซ่การติดเชื้อประกอบด้วยการ แทรกหลายชั้น โดยอาศัยไฟล์ทดสอบและโครงสร้างการบิลด์ของ XZ ผ่าน
build-to-host.m4,bad-3-corrupt_lzma2.xz,good-large_compressed.lzmaก่อนที่ไฟล์ออบเจ็กต์อันตรายจะถูกลิงก์เข้ากับliblzmaระหว่างการบิลด์ - XZ 5.6.0 และ 5.6.1 ที่มี backdoor ถูกแจกจ่ายในบิลด์เบต้าและบิลด์ทดลองของผู้จำหน่ายรายใหญ่บางราย และ CVE-2024-3094 ถูกจัดให้มีระดับความรุนแรง 10 คะแนน
- backdoor ระดับไบนารีถูกโหลดโดยอาศัย GLIBC IFUNC และเส้นทางการเรียก
cpuidจากนั้นพยายาม hook ฟังก์ชันที่เกี่ยวข้องกับ OpenSSL/libcrypto เพื่อเฝ้าดูการเชื่อมต่อของเครื่องที่ติดเชื้อ - โค้ดอันตรายจะตรวจสอบว่ามีการรัน
/usr/bin/sshdอยู่หรือไม่ และตรวจสอบตัวแปรแวดล้อม kill switch พร้อมใช้การประมวลผลสตริงแบบ trie, การ resolve สัญลักษณ์แบบไดนามิก และการแพตช์รันไทม์rtdl-auditเพื่อทำให้การวิเคราะห์และการตรวจจับยากขึ้น
ภาพรวมเหตุการณ์และขอบเขตผลกระทบ
- เมื่อวันที่ 29 มีนาคม 2024 มีการเปิดเผยการค้นพบ XZ backdoor ผ่านข้อความ ในเมลลิงลิสต์ Openwall OSS-security
- XZ คือ ยูทิลิตีบีบอัดข้อมูล ที่ถูกรวมอยู่ใน Linux distribution หลักหลายตัว
- ประเด็นสำคัญของความเสี่ยงคือ
liblzmaที่มี backdoor สามารถเชื่อมโยงกับโปรเซส OpenSSH serversshdได้ในบางดิสโทรที่ใช้ systemd- Ubuntu, Debian, RedHat/Fedora Linux มีการแพตช์ให้ OpenSSH ใช้ความสามารถของ systemd จึงพึ่งพาไลบรารีนี้
- สรุปได้ว่า Arch Linux และ Gentoo ไม่ได้รับผลกระทบ
- มีความเป็นไปได้สูงว่าเป้าหมายสุดท้ายของผู้โจมตีคือการใส่ความสามารถ sshd remote code execution ที่ผู้อื่นไม่สามารถใช้งานได้
- ต่างจากการโจมตี supply chain แบบอื่นที่มักเน้นแพตช์อันตรายเพียงตัวเดียว แพ็กเกจปลอม หรือแพ็กเกจ typo-squatting เหตุการณ์นี้ใกล้เคียงกับ ปฏิบัติการหลายขั้นตอนที่เกือบประสบความสำเร็จในการเจาะ SSH server ทั่วโลก
วิธีที่ backdoor ถูกใส่เข้าไป
- backdoor ใน
liblzmaถูกแทรกในสองระดับ โดยใช้ทั้งกระบวนการบิลด์และไฟล์ทดสอบร่วมกัน- ซอร์สโค้ดของโครงสร้างการบิลด์ ที่ใช้สร้างแพ็กเกจสุดท้ายถูกแก้ไขด้วยการเพิ่ม
build-to-host.m4 - สคริปต์และองค์ประกอบไบนารีที่ซ่อนอยู่ในไฟล์ test case ถูกดึงออกมาระหว่างการบิลด์
- ซอร์สโค้ดของโครงสร้างการบิลด์ ที่ใช้สร้างแพ็กเกจสุดท้ายถูกแก้ไขด้วยการเพิ่ม
- กระบวนการติดเชื้อดำเนินไปโดยมีสามไฟล์เป็นแกนหลัก
build-to-host.m4: สคริปต์บิลด์ที่ดึงสคริปต์ขั้นถัดไปออกมาbad-3-corrupt_lzma2.xz: ไฟล์ทดสอบที่ซ่อนเชลล์สคริปต์ไว้good-large_compressed.lzma: ไฟล์ทดสอบที่ซ่อนไบนารีออบเจ็กต์อันตรายไว้
- องค์ประกอบไบนารีอันตรายที่ถูกดึงออกมานั้นถูกลิงก์เข้ากับไลบรารีปกติระหว่างขั้นตอนคอมไพล์ และอาจถูกส่งต่อไปยัง repository ของ Linux ได้
- ผู้จำหน่ายหลักบางรายได้แจกจ่ายองค์ประกอบอันตรายนี้ใน บิลด์เบต้าและบิลด์ทดลอง
- การเจาะ XZ Utils นี้ได้รับการกำหนดเลข CVE-2024-3094 และมีระดับความรุนแรงสูงสุดที่ 10 คะแนน
ไทม์ไลน์หลัก
- 19 มกราคม 2024: เมนเทนเนอร์ใหม่
jiaT75ย้ายเว็บไซต์ XZ ไปยัง GitHub Pages - 15 กุมภาพันธ์ 2024: เพิ่ม
build-to-host.m4ลงใน.gitignore - 23 กุมภาพันธ์ 2024: เพิ่ม “ไฟล์ทดสอบ” สองไฟล์ที่มีขั้นตอนสคริปต์อันตรายอยู่ภายใน
- 24 กุมภาพันธ์ 2024: ออก XZ 5.6.0
- 26 กุมภาพันธ์ 2024: มีคอมมิตใน
CMakeLists.txtที่ขัดขวางความสามารถด้านความปลอดภัยของ Landlock - 4 มีนาคม 2024: backdoor ทำให้เกิดปัญหาที่เกี่ยวข้องกับ Valgrind
- 9 มีนาคม 2024: อัปเดตไฟล์ทดสอบทั้งสอง แก้ไขฟังก์ชัน CRC และ “แก้ไข” ปัญหา Valgrind
- 9 มีนาคม 2024: ออก XZ 5.6.1
- 28 มีนาคม 2024: พบบั๊กและแจ้ง Debian กับ RedHat
- 28 มีนาคม 2024: Debian rollback XZ 5.6.1 กลับไปเป็นเวอร์ชัน 5.4.5-0.2
- 29 มีนาคม 2024: มีการโพสต์อีเมลเปิดเผยต่อสาธารณะในเมลลิงลิสต์ OSS-security
- 29 มีนาคม 2024: RedHat ยืนยันว่า XZ ที่มี backdoor ถูกแจกจ่ายไปยัง Fedora Rawhide และ Fedora Linux 40 beta
- 30 มีนาคม 2024: Debian หยุดการบิลด์และเริ่มขั้นตอนการ rebuild
- 2 เมษายน 2024: นักพัฒนาหลักของ XZ ยอมรับเหตุการณ์ backdoor
3 ขั้นของห่วงโซ่การติดเชื้อ
-
ขั้นที่ 1:
build-to-host.m4ที่ถูกแก้ไข- รีลีสจาก GitHub repository ทางการของ XZ มีไฟล์ทดสอบอันตรายรวมอยู่ แต่ในตอนแรกยังไม่เป็นอันตรายเพราะไม่มีโอกาสถูกเรียกใช้
- โค้ดอันตรายที่ใช้ bootstrap การติดเชื้อดูเหมือนจะรวมอยู่ในรีลีสที่ให้บริการจาก
jiaT75ซึ่งควบคุมxz[.]tukaani.org - ดิสโทรส่วนใหญ่ใช้ URL นี้ และรีลีสที่ดาวน์โหลดมามีไฟล์
build-to-host.m4ที่มีโค้ดอันตรายอยู่ build-to-host.m4จะถูกรันระหว่างกระบวนการบิลด์ โดยใช้trแก้ข้อมูลที่ “เสีย” ในbad-3-corrupt_lzma2.xzแล้วแตกไฟล์ด้วยxz -d- ผลลัพธ์หลังแตกไฟล์มี เชลล์สคริปต์ ที่ภายหลังจะถูกรันด้วย
/bin/bash
-
ขั้นที่ 2: เชลล์สคริปต์ที่ถูกแทรก
- สคริปต์ที่ไฟล์
.m4อันตรายแทรกเข้ามาจะตรวจสอบว่ากำลังรันบนเครื่อง Linux หรือไม่ และรันอยู่ในกระบวนการบิลด์ที่ตั้งใจไว้หรือไม่ - ขั้นตอนถัดไปใช้
good-large_compressed.lzma - ไฟล์นี้ถูกบีบอัดด้วย XZ อย่างปกติ แต่ในข้อมูลหลังแตกมี junk data ปะปนอยู่
- สคริปต์จะรันชุดคำสั่ง
headผ่านevalเพื่อดึงข้อมูลออกมา - คำสั่ง
headแต่ละตัวจะข้าม 1024 ไบต์ถัดไป หรือดึง 2048 ไบต์ถัดไป หรือ 724 ไบต์ถัดไป - มีการดึงข้อมูลรวม 33,492 ไบต์
tailจะเก็บ 31,265 ไบต์ สุดท้ายของไฟล์ไว้และละทิ้งส่วนที่เหลือ- จากนั้นใช้
trทำการแทนที่พื้นฐานเพื่อคลายการ obfuscate แล้วใช้คำสั่ง XZ ครั้งที่สองแตกไบต์ที่แปลงแล้วเป็น rawlzmastream ก่อนส่งผลลัพธ์เข้าเชลล์
- สคริปต์ที่ไฟล์
-
ขั้นที่ 3: การดึงออบเจ็กต์ backdoor
- เชลล์สคริปต์ในขั้นสุดท้ายจะตรวจสอบหลายเงื่อนไขเพื่อยืนยันว่ากำลังรันอยู่ในสภาพแวดล้อมที่คาดไว้
- ตรวจสอบว่าโปรเจ็กต์ถูกตั้งค่าให้ใช้ IFUNC หรือไม่
- ตรวจสอบว่ามีการคอมไพล์ด้วย GCC หรือไม่
- จากนั้นตรวจสอบว่ามีไฟล์บางตัวที่สคริปต์ต้องใช้ในโปรเจ็กต์หรือไม่
- ขั้นนี้จะดึง ไฟล์ออบเจ็กต์ ที่ซ่อนอยู่ใน offset อื่นของ
good-large_compressed.lzma - ขั้นตอนการดึงจะรันหลายคำสั่งต่อเนื่องกัน โดยส่ง output ของแต่ละคำสั่งไปเป็น input ของคำสั่งถัดไป
- ใช้เครื่องมือ XZ แตก
good-large_compressed.lzma - ใช้
eval $iและการเรียกheadหลายครั้ง - ถอดรหัสข้อมูลไบนารีด้วยอัลกอริทึมแบบกำหนดเองที่คล้าย RC4
- แตกไฟล์ที่ถูกถอดรหัสแล้วอีกครั้งด้วย XZ
- ตัดไบต์บางส่วนช่วงต้นออก ก่อนบันทึกผลลัพธ์เป็น
liblzma_la-crc64-fast.o - สุดท้ายสคริปต์จะแก้ฟังก์ชัน
is_arch_extension_supportedในไฟล์crc_x86_clmul.hของliblzma - เปลี่ยนการเรียก
__get_cpuidเป็น_get_cpuidโดยลบขีดล่างออกหนึ่งตัว - การเปลี่ยนแปลงนี้ทำให้ออบเจ็กต์ backdoor สามารถถูกลิงก์เข้าไลบรารีได้
วิธีโหลด backdoor ระดับไบนารี
- โค้ด XZ เดิมมีฟังก์ชันพิเศษสำหรับคำนวณ CRC คือ
lzma_crc32และlzma_crc64 - ฟังก์ชันทั้งสองถูกเก็บใน ELF symbol table เป็นชนิด IFUNC ของ GLIBC
- IFUNC ทำให้ dynamic linker สามารถเลือกฟังก์ชันที่จะใช้แบบไดนามิกขณะโหลด shared library
- XZ ใช้ความสามารถนี้เพื่อตัดสินใจว่าจะใช้ฟังก์ชัน CRC ที่ปรับแต่งแล้วหรือไม่ หลังตรวจสอบความสามารถของโปรเซสเซอร์สมัยใหม่อย่าง CLMUL, SSSE3, SSE4.1
- การตรวจสอบความสามารถของโปรเซสเซอร์ต้องใช้คำสั่ง
cpuidและมีการเรียก wrapper__get_cpuidที่ GLIBC จัดเตรียมไว้ - เมื่อสคริปต์อันตรายเปลี่ยน
__get_cpuidเป็น_get_cpuidการเรียกจากโค้ดจึงถูกเชื่อมไปยัง สัญลักษณ์_get_cpuidภายในไฟล์ออบเจ็กต์ backdoor - โค้ด backdoor ระยะแรกจะถูกเรียกสองครั้ง เพราะทั้ง
lzma_crc32และlzma_crc64ใช้ฟังก์ชัน_get_cpuidที่ถูกแก้ไขตัวเดียวกัน- มันใช้เคาน์เตอร์อย่างง่ายเพื่อตรวจสอบว่าเคยรันแล้วหรือยัง
- เมื่อ
lzma_crc64IFUNC เรียก_get_cpuidและค่าเคาน์เตอร์เท่ากับ 1 จะเริ่มขั้นตอน redirect ไปยัง entry point สุดท้าย
พฤติกรรมขณะรันและเป้าหมายการ hook
- backdoor จะเริ่มต้นโครงสร้างข้อมูลที่เก็บข้อมูลสำคัญของโปรเซสที่กำลังรันอยู่
- มันค้นหาที่อยู่ของ Global Offset Table(GOT) จาก offset ที่ hardcode ไว้ แล้วค้นหาพอยน์เตอร์
cpuidภายในนั้น - จากนั้นแทนที่พอยน์เตอร์ด้วยฟังก์ชันอันตรายหลัก เพื่อให้เรียกฟังก์ชันอันตรายได้ ราวกับเป็นการเรียก
cpuid - เป้าหมายหลักคือ hook ฟังก์ชันบางตัวเพื่อเฝ้าดูการเชื่อมต่อที่เข้ามายังเครื่องที่ติดเชื้อ
- ฟังก์ชันเป้าหมายเกี่ยวข้องกับ
libcryptoRSA_public_decrypt: ใช้ถอดรหัส ciphertext ที่ถูกเซ็นด้วย private keyEVP_PKEY_set1_RSA: ใช้ตั้งค่า RSA key ของ context บางตัว- ไม่มีใน SSH server เวอร์ชันใหม่ แต่ใช้ในเครื่องมือสร้าง SSH key แบบแยกต่างหาก
RSA_get0_key: ใช้ดึง RSA key
- กระบวนการ hook ต้องอาศัยการเข้าถึงโครงสร้างภายในหลายตัวและการจัดการโครงสร้างเฉพาะโปรเซส โดยโค้ดอันตรายยังทำการตรวจสอบเพื่อหลบเลี่ยงการวิเคราะห์ควบคู่กันไป
การตรวจสอบสภาพแวดล้อมการรันและเทคนิคหลบเลี่ยงการวิเคราะห์
-
การตรวจสอบ
sshdและ kill switch- backdoor จะเริ่มจากการ parse dynamic linker
ld-linuxเพื่อ resolve ที่อยู่ของสัญลักษณ์แบบไดนามิก - จากนั้นตรวจสอบว่าสภาพแวดล้อมการรันปัจจุบันตรงตามเกณฑ์หรือไม่
- ตรวจสอบว่าโปรเซสปัจจุบันคือ
/usr/bin/sshdหรือไม่ - ตรวจสอบว่ามี kill switch อยู่หรือไม่
- ชื่อโปรเซสปัจจุบันถูกดึงมาจาก
argv[0] - มีการ parse ตัวแปรแวดล้อมของโปรเซสทั้งหมด แล้วตรวจสอบว่าตรงกับค่าที่อยู่ใน internal string table หรือไม่
- หากพบตัวแปรแวดล้อมที่ตรงกัน หรือโปรเซสไม่ใช่
sshdโค้ดอันตรายจะหยุดทำงาน - ตัวแปรเดียวในตารางภายในคือ
yolAbejyiejuvnup=Evjtgvsh5okmkAvjซึ่งทำหน้าที่เป็น kill switch ในบริบทนี้
- backdoor จะเริ่มจากการ parse dynamic linker
-
การประมวลผลสตริงแบบ trie
- หนึ่งในลักษณะเด่นของ backdoor คือใช้โครงสร้าง trie เดียวสำหรับงานเกี่ยวกับสตริง
- แทนที่จะเทียบค่าคงที่อย่างชื่อฟังก์ชันโดยตรง หรือเปรียบเทียบผ่าน string hash มันจะตรวจว่าผลลัพธ์จากการค้นหาใน trie เท่ากับค่าคงที่ที่กำหนดหรือไม่
- magic value ของ ELF header จะทำให้ trie คืนค่า 0x300
- ชื่อฟังก์ชัน
systemจะจับคู่กับค่าที่คืนกลับ 0x9F8 - trie ไม่ได้ใช้แค่การเปรียบเทียบ แต่ยังใช้ค้นหาสตริงภายใน host binary ด้วย
- ตัวอย่างเช่น ฟังก์ชันที่ใช้พอยน์เตอร์สตริงอย่าง
ssh-2.0จะค้นหาสตริงจาก host binary เพื่อไม่ให้มีข้อมูลน่าสงสัยค้างอยู่ในตัว backdoor เอง - การใช้งานนี้ใช้บิตแมสก์ขนาด 16 ไบต์
- แต่ละครึ่งสอดคล้องกับช่วงไบต์อินพุต
0x00-0x3F,0x40-0x7F - trie leaf node มีขนาด 2 ไบต์ โดย 3 บิตใช้เป็น flag และส่วนที่เหลือสงวนไว้สำหรับค่า หรือชี้ไปยังตำแหน่งของโหนดถัดไป
การ resolve สัญลักษณ์และการ hook ผ่าน rtdl-audit
-
รูทีน resolve สัญลักษณ์
- backdoor มี รูทีน resolve สัญลักษณ์ อย่างน้อย 3 ตัวสำหรับค้นหาโครงสร้าง ELF Symbol
- ฟังก์ชัน resolve สัญลักษณ์ทั้งหมดรับคีย์สำหรับค้นหาใน trie เป็นอินพุต
- หนึ่งในฟังก์ชัน resolve จะไล่ดูสัญลักษณ์ทั้งหมดเพื่อหาตัวที่มีคีย์ที่ต้องการ และเมื่อพบจะคืนค่าโครงสร้าง
Elf64_Sym - จากนั้นโครงสร้างนี้จะถูกใช้เติมค่าในโครงสร้างภายในของ backdoor เพื่อเก็บฟังก์ชันพอยน์เตอร์ที่จำเป็น
- กระบวนการนี้คล้ายกับ API hashing routine ที่พบได้บ่อยในภัยคุกคามฝั่ง Windows
- จากนั้น backdoor จะค้นหาฟังก์ชัน OpenSSL
libcryptoจำนวนมากเพื่อนำไปใช้ในรูทีนเข้ารหัส - มันติดตามจำนวนฟังก์ชันที่ค้นหาและ resolve สำเร็จ และใช้ค่านี้ตัดสินใจว่าจะทำงานต่ออย่างปกติหรือหยุดทำงาน
-
fake allocator ที่อาศัย
lzma_alloc- ตัว resolve สัญลักษณ์อีกตัวหนึ่งอาศัยฟังก์ชัน
lzma_allocของliblzmaเอง lzma_allocเป็นฟังก์ชันที่ช่วยจัดสรรหน่วยความจำอย่างมีประสิทธิภาพ โดยใช้ allocator ปกติอย่างmallocหรือ custom allocator- ใน XZ backdoor ฟังก์ชันนี้ถูกทำให้ทำงานราวกับเป็นตัว resolve สัญลักษณ์อีกตัวผ่าน fake allocator
- พารามิเตอร์ที่เดิมใช้เป็น “ขนาดการจัดสรร” กลับถูกใช้เป็นคีย์สัญลักษณ์ภายใน trie
- กลเม็ดนี้มีจุดประสงค์เพื่อทำให้การวิเคราะห์ backdoor ยากขึ้น
- backdoor ทำการ resolve สัญลักษณ์แบบไดนามิกระหว่างรัน โดยไม่ได้จำกัดว่าจะต้อง resolve ทั้งหมดครั้งเดียวหรือ resolve เฉพาะเมื่อจำเป็นเท่านั้น
- เป้าหมายที่ถูก resolve มีตั้งแต่ฟังก์ชัน OpenSSL ปกติไปจนถึง
systemที่ใช้รันคำสั่งบนเครื่อง
- ตัว resolve สัญลักษณ์อีกตัวหนึ่งอาศัยฟังก์ชัน
-
การแพตช์รันไทม์
rtdl-audit- backdoor ใช้ความสามารถ rtdl-audit ของ dynamic linker เพื่อทำ function hooking
- ปกติแล้ว
rtdl-auditเป็นความสามารถที่ช่วยให้นักพัฒนาสร้าง shared library แบบกำหนดเองเพื่อรับการแจ้งเหตุการณ์ของ linker เช่น การ resolve สัญลักษณ์ - XZ backdoor ไม่ได้สร้าง shared library แยกต่างหาก แต่เลือก แพตช์อินเทอร์เฟซเริ่มต้นที่ลงทะเบียนอยู่ในหน่วยความจำในขณะรัน เพื่อยึดการทำงานของรูทีน resolve สัญลักษณ์
- โครงสร้าง
audit_ifaceอันตรายที่เก็บอยู่ในตัวแปร globaldl_auditในหน่วยความจำของ dynamic linker มีที่อยู่ callback ของsymbind64 - เมื่อ dynamic linker เรียก callback นี้ ข้อมูลสัญลักษณ์ทั้งหมดจะถูกส่งไปอยู่ภายใต้การควบคุมของ backdoor และสำหรับฟังก์ชันเป้าหมายก็จะคืนที่อยู่อันตรายกลับมา ทำให้การ hook สำเร็จ
- ที่อยู่ของ
dl_auditและdl_nauditได้มาจากการ disassemble ฟังก์ชันdl_mainและdl_audit_symbind_alt - backdoor มี disassembler ขนาดย่อมภายในสำหรับถอดรหัสคำสั่ง และถูกใช้อย่างกว้างขวางเมื่อต้องค้นหาค่าบางอย่าง เช่น ที่อยู่
*audit
ชุดแจกจ่ายที่ยืนยันว่ามี backdoor และข้อมูลการตรวจจับ
-
ซอร์สแพ็กเกจที่มี backdoor
- xz-5.6.0
- MD5:
c518d573a716b2b2bc2413e6c9b5dbde - SHA1:
e7bbec6f99b6b06c46420d4b6e5b6daa86948d3b - SHA256:
0f5c81f14171b74fcc9777d302304d964e63ffc2d7b634ef023a7249d9b5d875
- MD5:
- xz-5.6.1
- MD5:
5aeddab53ee2cbd694f901a080f84bf1 - SHA1:
675fd58f48dba5eceaf8bfc259d0ea1aab7ad0a7 - SHA256:
2398f4a8e53345325f44bdd9f0cc7401bd9025d736c6d43b372f4dea77bf75b8
- MD5:
- xz-5.6.0
-
อาร์ติแฟกต์หลักที่ถูกวิเคราะห์
bad-3-corrupt_lzma2.xz:86fc2c94f8fa3938e3261d0b9eb4836be289f8aebuild-to-host.m4:b4dd2661a7c69e85f19216a6dbbb1664good-large_compressed.lzma:540c665dfcd4e5cfba5b72b4787fec4fliblzma_la-crc64-fast.o:212ffa0b24bb7d749532425a46764433
-
ไลบรารีที่ทราบว่ามี backdoor
- Debian Sid
liblzma.so.5.6.0- MD5:
4f0cf1d2a2d44b75079b3ea5ed28fe54 - SHA1:
72e8163734d586b6360b24167a3aff2a3c961efb - SHA256:
319feb5a9cddd81955d915b5632b4a5f8f9080281fb46e2f6d69d53f693c23ae
- MD5:
- Debian Sid
liblzma.so.5.6.1- MD5:
53d82bb511b71a5d4794cf2d8a2072c1 - SHA1:
8a75968834fc11ba774d7bbdc566d272ff45476c - SHA256:
605861f833fc181c7cdcabd5577ddb8989bea332648a8f498b4eef89b8f85ad4
- MD5:
- Debian Sid
-
ชื่อการตรวจจับ
- ผลิตภัณฑ์ของ Kaspersky ตรวจจับออบเจ็กต์อันตรายที่เกี่ยวข้องกับการโจมตีนี้เป็น HEUR:Trojan.Script.XZ และ Trojan.Shell.XZ
- Kaspersky Endpoint Security for Linux ตรวจจับโค้ดอันตรายในหน่วยความจำของโปรเซส SSHD เป็น MEM:Trojan.Linux.XZ ซึ่งเป็นส่วนหนึ่งของงาน Critical Areas Scan
- กฎ Yara ที่ให้มาคือกฎ
liblzma_get_cpuid_functionสำหรับค้นหาฟังก์ชันget_cpuidอันตรายที่เกี่ยวข้องกับ CVE-2024-3094
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ประโยคนี้ดูเหมือนจะพูดให้สิ่งที่เกิดขึ้นจริงเบาลงเสียด้วยซ้ำ
สิ่งที่น่ากลัวกว่าด้านเทคนิคของแบ็กดอร์คือ ปริมาณและระดับของวิศวกรรมสังคม แบ็กดอร์เป็นเพียงผลลัพธ์สุดท้าย และที่มันถูกใส่เข้าไปได้ก็เพราะ ณ จุดนั้น โปรเจกต์ xz ทั้งหมดถูกผู้ไม่หวังดี คือ “Jia Tan” และคนรอบตัว เข้าควบคุมมาเป็นเวลานานแล้ว มีการทำสงครามจิตวิทยากับผู้ดูแลนานกว่าหนึ่งปี โดยที่ผู้ดูแลหรือคนอื่น ๆ ไม่ทันสังเกตเห็น
มันเหมือนเรื่องในนิยายสายลับ และถ้าเรื่องแบบนี้เป็นไปได้ ก็ทำให้อยากรู้ว่าตอนนี้ในโปรเจกต์อื่น ๆ กำลังเกิดอะไรขึ้นอีกบ้าง
แนวคิดแบบเดียวกันนี้ยังปรากฏอยู่ในโค้ดแบ็กดอร์เองด้วย ไม่ใช่แค่พยายามทำให้ดูไม่มีพิษภัย แต่ยังสร้าง เรื่องเล่า อย่างแข็งขันผ่านข้อความ commit, คอมเมนต์, ชื่อตัวแปร, การเลือกคำสั่ง ฯลฯ ว่าภายนอกดูเหมือนมันทำอะไร ทั้งที่จริงแล้วถูกทำให้ทำอีกอย่างโดยสิ้นเชิง โครงสร้างคือทำให้คนที่ตรวจโค้ดก่อนสงสัยความเข้าใจของตัวเอง จากนั้นสงสัยว่าเป็นบั๊ก แล้วกว่าจะสงสัยว่าเป็นเจตนาร้ายก็ผ่านไปนานแล้ว
หวังว่าหน่วยข่าวกรองใด ๆ ก็ตามจะกำลังสืบสวนคดีนี้ให้ลึกขึ้น
เห็นทุกเธรด HN เรื่องแบ็กดอร์แล้วหงุดหงิดที่ความเป็นไปได้นี้ถูกปฏิเสธว่าเป็นความหวาดระแวงหรือพวกหมวกฟอยล์อะลูมิเนียม ทำเหมือนเป็นเรื่องที่ไม่เกิดขึ้น แต่ครั้งนี้เป็นแค่กรณีรูปธรรมที่ถูกจับได้เท่านั้น ยังมีอีกนับไม่ถ้วนที่ยังไม่ถูกจับได้
กรณีนี้เป็นโปรเจกต์โอเพนซอร์ส จึงนับว่าค้นพบได้ค่อนข้างง่ายกว่า แต่ก็ยังถือว่าโชคดี ลองนึกถึงผลิตภัณฑ์ซอร์สปิดดู การใส่แบ็กดอร์จะลดรูปเหลือแค่ปัญหาการแทรกซึมหรือกดดันองค์กรหนึ่ง เรื่องแบบนี้เกิดขึ้นบ่อย ไม่มีใครอยากเชื่อ แต่เป็นเรื่องทั่วไป คนที่เคยทำงานในบริษัทโครงสร้างพื้นฐานทางเทคโนโลยีน่าจะมีเรื่องเล่าอยู่บ้าง แค่พูดยากเพราะ NDA หรือเหตุผลที่หนักกว่านั้น แต่มันเกิดขึ้นจริง
อาจเป็นผลลัพธ์จากความหมกมุ่นของใครสักคน หรืออาจเป็นงานของบริษัทรักษาความปลอดภัยเอกชนหรือผู้เล่นระดับรัฐที่ทำเรื่องแบบนี้กับหลายโปรเจกต์เป็นงานประจำ 9 โมงถึง 5 โมงก็ได้
เป็นเรื่องธรรมดาที่ความสนใจจนถึงตอนนี้จะไปอยู่ที่ว่าแบ็กดอร์ทำงานสำเร็จอย่างไรและบรรลุเป้าหมายด้วยวิธีไหน
ถึงอย่างนั้นก็อยากเห็นการวิเคราะห์ที่ลึกขึ้นเกี่ยวกับข้อผิดพลาดและส่วนที่ออกแบบเกินจำเป็น ในบทสัมภาษณ์ Bryan Cantril [1] Andrés บอกว่าสิ่งนี้ดูเหมือนเป็น ชิ้นส่วนแบ็กดอร์สำเร็จรูป ที่ไม่ได้สร้างขึ้นโดยรู้รายละเอียดถึงวิธีแพ็กเกจและเผยแพร่ จึงมีจุดโง่ ๆ อยู่มาก ตัวอย่างเช่นการค้นหา symbol table ที่ทำให้เขาเริ่มสืบสวน
เช่นเดียวกัน ก็สงสัยว่าทำไมถึงตัด 48 ไบต์ออกด้วย RC4 [2]
อยากฟังเรื่องว่าถ้ามีเวลามากกว่านี้หรือมีทีมที่ดีกว่านี้ จะทำให้ดีขึ้นได้อย่างไร หรือพลาดหนักกว่านี้ตรงไหน
[1] https://youtu.be/jg5F9UupL6I?si=gvXsYFXgagkGOMd4
[2] https://twitter.com/matthew_d_green/status/17744729080201014...
ถ้าผมเข้าใจถูก มาตรการเสริมความแข็งแกร่งที่มีประโยชน์น่าจะเป็นการให้ไลบรารีลิงก์แบบไดนามิกแต่ละตัวมี GOT ของตัวเอง และหลังจากการลิงก์แบบไดนามิกเสร็จแล้วให้ทำเครื่องหมายตารางเป็นอ่านอย่างเดียว กล่าวคือเป็นวิธีที่ทำให้ไม่สามารถแพตช์รายการ ifunc ของอีกฝั่งข้ามขอบเขตไดนามิกได้
วิธีนี้อาจช่วยปรับปรุง ความปลอดภัยของซัพพลายเชน สำหรับโค้ดที่ถูกลิงก์เข้าไปที่ใดที่หนึ่งแต่ไม่ได้ถูกเรียกใช้งาน
ยิ่งไปกว่านั้น อาจดีกว่าถ้า implement ifunc ในรูปแบบ declarative เพื่อไม่ให้เกิดการรันโค้ดตามอำเภอใจจากแต่ละไลบรารีที่ลิงก์เข้ามา ตอนนี้คงทำได้ยากเพราะต้องรักษาความเข้ากันได้ย้อนหลัง แต่ในระยะยาวดูเหมือนจะค่อย ๆ นำมาใช้เป็นชั้น ๆ ได้ เช่น ถ้าสร้างไลบรารีหนึ่งด้วยบิตฟีเจอร์ “declarative linked ifunc” ก็ให้ dynamic linker ทำให้การรันล้มเหลวเมื่อไลบรารีทั้งหมดที่เชื่อมต่ออยู่ไม่มีแฟล็กฟีเจอร์เดียวกัน
ปัจจุบันการบิลด์ไลบรารีส่วนใหญ่เป็นการรันสคริปต์ที่ซับซ้อนและเข้าใจยากมาก ซึ่งต้องการสภาพแวดล้อมแบบ Turing-complete สิ่งนี้ให้พื้นผิวโจมตีไม่รู้จบแก่ผู้โจมตี และถ้ากระบวนการบิลด์ถูกยึด ก็จะเปิดโอกาสขึ้นมา
การย้ายไปสู่ กระบวนการบิลด์แบบ declarative ที่ตัวรันเนอร์เป็นเพียง state machine แบบจำกัดน่าจะช่วยได้ ข้อกำหนดที่ว่าก้อนซอร์สทั้งหมดต้องทำซ้ำได้ก็น่าพิจารณาเช่นกัน
ตั้งแต่จุดนั้น การป้องกันทั้งหมดก็ไร้ผล ถ้าต้องการก็สามารถแมป GOT ให้เขียนได้อีกครั้ง และแม้พฤติกรรมแบบนั้นอาจถูกตรวจพบว่า “น่าสงสัย” หรือระบบปฏิบัติการอาจขัดขวางการสลับนั้นได้ แต่โค้ดที่ถูกฉีดเข้าไปก็ยังสามารถพลิก control flow ได้อีกหลายร้อยวิธี อ่าน/เขียนตามอำเภอใจ รันโค้ด ทุกอย่างเป็นไปได้ ไม่มี security mitigation ใดที่จะหยุดการถูกเจาะในระดับนี้ได้ ถ้าต้องการก็สามารถรั่วไพรเวตคีย์แล้วส่งให้ผู้โจมตีโดยตรง หรือเปิดเชลล์ก็ได้ การพยายามออกแบบการป้องกันในขั้นนี้เป็นความพยายามที่เปล่าประโยชน์
function pointer ที่ถูกต้องจะถูกโหลดเมื่อถูกเรียกครั้งแรก แล้วใส่ลงในตารางแทน stub และจุดเวลานั้นอาจอยู่ไกลในอนาคตแบบไม่แน่นอนก็ได้ ที่จริงแล้วในระบบนิเวศไลบรารีขนาดใหญ่เช่นแอป gtk ฟังก์ชันส่วนใหญ่ที่ถูกลิงก์เข้ามาไม่เคยถูกเรียกเลย
-march=nativeและถ้าตั้งค่า-multiarchใน USE flag ของ glibc ก็ปิด ifunc ได้ง่าย ผมไม่เห็นผลกระทบเชิงลบใด ๆสำหรับ 3 ขั้นตอนแรก บทความนี้ไม่ได้เพิ่มอะไรมากจากสิ่งที่รู้กันในช่วง 2 สัปดาห์ที่ผ่านมา ถือว่าเป็นบทความสรุปที่ดีพร้อมผังการทำงาน
แต่ส่วนที่วิเคราะห์ไบนารีได้ละเอียดขนาดนั้นดูเหมือนจะเป็นเรื่องใหม่
ซอร์สโค้ดที่อยู่ในนั้นถูกสร้างขึ้นมาได้อย่างไร? รัน disassembler แล้วทำความเข้าใจว่าโค้ดทำอะไร จากนั้นเปลี่ยนชื่อทั้งหมดให้เป็นชื่อเชิงอธิบายหรือ? ถ้าทำได้ภายใน 2 สัปดาห์ก็ดูเป็นผลงานที่น่าทึ่งทีเดียว
Global Research & Analysis Team, Kaspersky Lab
https://securelist.com/author/great/
ดูเหมือนผู้เขียนบทความจะเป็นทีมวิเคราะห์มัลแวร์ของ Kaspersky Lab จึงมีความเป็นไปได้สูงว่าจะเชี่ยวชาญด้าน reverse engineering ไบนารีมาก
https://hex-rays.com/ida-pro/
สิ่งที่อยากรู้จริง ๆ คือ ดีเลย์เริ่มต้นของ SSH ที่เป็นชนวนให้มีการสอบสวน xz นั้นเกิดจากอะไรกันแน่ มีใครหาคำตอบได้หรือยัง?
ตามที่คนที่ reverse engineer โค้ดบอกไว้ ข้อความคำสั่งต้องถูกผูกกับ SSH host key ด้วย ดังนั้นถ้า host key เป็นคีย์ RSA ก็อาจมีการถอดรหัส RSA เพิ่มเติมต่อการเชื่อมต่อแต่ละครั้งด้วย
ถ้าเป็นแบบนั้นก็น่าจะเพียงพอที่จะเป็นสาเหตุของดีเลย์
เป็นวิธีง่าย ๆ ในการตรวจจากภายนอกว่าเซิร์ฟเวอร์ติดเชื้อหรือไม่ โดยไม่ต้องพยายามฉีดโค้ดก่อน
ผู้เขียนเข้าใจ โครงสร้างภายในของ glibc อย่างลึกมาก เป็นเนื้อหาแบบที่ต้องจมอยู่กับซอร์สโค้ดจนถึงคอถึงจะรู้ได้ และยังมีเทคนิคใหม่ ๆ หลายอย่าง
custom ELF parser กับ disassembler นั้นซับซ้อนมากจนยากจะจินตนาการว่าโค้ดนั้นไม่เคยถูกใช้ที่อื่นมาก่อน หรือจะไม่ถูกนำไปใช้อีกในอนาคต
สงสัยว่าเหตุการณ์นี้จะได้รับการสอบสวนอย่างจริงจังในระดับที่สมควรหรือไม่ แต่ก็ดูไม่น่าจะเป็นเช่นนั้น
มีใครวิเคราะห์ บั๊กของแบ็กดอร์ ที่ทำให้เกิดข้อผิดพลาดใน Valgrind และทำให้ SSH ช้าลง จนสุดท้ายทำให้เรื่องนี้ถูกเปิดโปงหรือยัง?
“การแก้” สำหรับ Valgrind คือการปิดใช้งาน ifunc และผลก็คือแบ็กดอร์ถูกปิดใช้งาน ทำให้ข้อผิดพลาดหายไป
เท่าที่ผมรู้ ความช้าลงมาจาก การค้นหา symbol และ instruction ทั้งหมดที่แบ็กดอร์ทำ
ถ้ามองแบบหักมุม เมื่อคิดถึงความพยายามที่ผู้โจมตีใส่ลงไปเพื่อหลบเลี่ยงการตรวจจับในสคริปต์และโค้ด โปรเจกต์ทั้งหมดนี้อาจเป็น ตัวเบี่ยงความสนใจ หรือเป็นแผนสำรองระหว่างที่มีความพยายามหลายอย่างดำเนินอยู่พร้อมกันก็ได้
เราจะนำหน้าเรื่องแบบนี้ไปได้อย่างไร? การที่ชุมชนไปโฟกัสที่ SSHD จะส่งผลต่อส่วนอื่นของระบบโดยรวมหรือไม่? แล้วด้านเทคนิคอื่น ๆ หรือด้านสังคมล่ะ?
หมวกฟอยล์อะลูมิเนียมนี่สนุกดี
หรือไม่ก็ซื้อ closed source จากที่อย่าง Microsoft แล้วหวังว่าพวกเขาจะมีทรัพยากรและความตั้งใจที่จะตรวจทานโค้ดเข้มงวดกว่า
และแนวทางที่มีได้เสมอคือการมี ทีมปฏิบัติการความปลอดภัย ที่ยอดเยี่ยมเพื่อตรวจจับกิจกรรมเครือข่ายผิดปกติและความพยายามยกระดับสิทธิ์
แต่ผู้มีส่วนร่วมที่ไม่มีประวัติหรือร่องรอยอื่นใดนอกจากโปรเจกต์ที่กำลังทำอยู่ ควรถูกมองเป็น สัญญาณอันตราย ในอนาคต
กลุ่มยุคก่อนอย่าง GOBBLES, ADM, ac1db1tch3z, ~el8 ก็เคยทำเรื่องแบบนี้ และ “นักวิจัยความปลอดภัย” ภาคเอกชนอย่าง isec.pl ก็เคยทำเช่นกัน
ที่ครั้งนี้เป็นปัญหาก็เพราะผู้ปฏิบัติการรัฐกำลังฉวยประโยชน์จากทุนนิยมองค์กรที่สร้างยุคซึ่งโปรเจกต์โครงสร้างพื้นฐานถูกค้ำจุนด้วยค่าตอบแทนต่ำ ผู้ไม่หวังดีมีทรัพยากรแทบไม่จำกัดเพื่อบรรลุเป้าหมาย
เรื่องนั้นเองที่นำไปสู่ความต้องการและการถือกำเนิดขององค์กรอย่าง NSO, Zerodium
ก่อนหน้านั้น exploit และแบ็กดอร์แทบไม่มีมูลค่า และแฮ็กเกอร์ก็คาดหวังการสนับสนุนหรือการจ้างงานจากบริษัทอย่าง Qualys
ผมเคยดูการวิเคราะห์การแฮ็กช่องโหว่ zero-day ของ Google อยู่หลายครั้ง ซึ่งพวกนั้นก็ยอดเยี่ยมจนดูเหนือจริงแล้ว แต่การแฮ็กครั้งนี้น่าจะเป็นหนึ่งในระดับตลอดกาล
ผมเห็นว่า repository ของ xz กลับขึ้น GitHub แล้ว และ Lasse กับผู้มีส่วนร่วมคนใหม่กำลังจัดการอยู่ พวกเขาลบการรองรับ ifunc ออก และ commit โค้ดสร้างไฟล์ทดสอบ ลง repository เพื่อให้สร้างไฟล์ทดสอบได้โดยไม่ต้องมี blob ดูเหมือนกำลังทำงานไปในทิศทางที่ดี