1 คะแนน โดย GN⁺ 2024-03-27 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • CVE-2024-1086 ใน nf_tables ของเคอร์เนล Linux เกิดจากการตรวจสอบอินพุต verdict ของ Netfilter ล้มเหลว ทำให้เกิดการ free ซ้ำของ sk_buff และเมื่อเงื่อนไขเหมาะสมอาจนำไปสู่การยกระดับสิทธิ์ภายในเครื่องได้
  • ลำดับการโจมตีทำให้ skb ที่ถูก free ระหว่างการประมวลผล NF_DROP ยังถูกประมวลผลต่อเหมือน NF_ACCEPT ส่งผลให้อ็อบเจ็กต์เดียวกันถูก free อีกครั้งในเส้นทางถัดไป
  • PoC ได้รับการตรวจสอบบน KernelCTF mitigation, Debian, Ubuntu และเคอร์เนล vanilla โดยอย่างน้อยช่วง v5.14.21~v6.6.14 จะอยู่ในข่ายได้รับผลกระทบตามค่า kconfig และมีการปล่อยแพตช์แก้ไขใน stable branch เมื่อเดือนกุมภาพันธ์ 2024
  • แกนหลักคือ Dirty Pagedirectory ซึ่งเป็นเทคนิค KSMA แบบ data-only ที่จัดสรรหน้า PTE และ PMD ซ้ำลงบน physical page เดียวกัน เพื่อเข้าถึง physical address ใดก็ได้ด้วยการอ่าน/เขียนจาก user space เท่านั้น
  • มีการผสานการค้นหา physical KASLR, การเลี่ยง modprobe_path, fileless execution และการเชื่อม file descriptor เพื่อหลุดออกจาก namespace จนเป็นกรณี LPE เชิงปฏิบัติที่มุ่งโจมตีทั้งการจัดการหน่วยความจำของเคอร์เนลและ network subsystem

เงื่อนไขของช่องโหว่และขอบเขตผลกระทบ

  • บั๊ก nf_tables ถูกจดทะเบียนเป็น CVE-2024-1086 โดยประเด็นหลักคือการตรวจสอบอินพุตในกระบวนการจัดการ verdict ของ Netfilter ในเคอร์เนล Linux ล้มเหลว ทำให้อนุญาต ค่า drop error แบบบวก ได้
  • การ exploit ต้องมีเงื่อนไขต่อไปนี้
    • ต้องเปิดใช้งาน nf_tables
    • ต้องเปิดใช้งาน unprivileged user namespace
    • ในดิสโทรหลักอย่าง Debian และ Ubuntu หากการตั้งค่านี้เปิดเป็นค่าเริ่มต้น จะถือเป็นสมมติฐานของการโจมตี
  • จากการทดสอบ stable branch linux-5.15.y, linux-6.1.y, linux-6.6.y อยู่ในข่ายได้รับผลกระทบ และ linux-6.7.1 ก็มีความเป็นไปได้เช่นกัน
  • มีการปล่อย การแก้ไขบั๊ก ใน stable branch เมื่อเดือนกุมภาพันธ์ 2024
  • ซอร์สโค้ด PoC เผยแพร่ไว้ที่ CVE-2024-1086 PoC repository

ลำดับโค้ดที่ทำให้เกิด double-free

  • verdict ของ Netfilter คือค่าที่ตัดสินว่าจะ drop, accept หรือส่งแพ็กเก็ตเข้าคิว เป็นต้น
  • ลำดับที่มีช่องโหว่เริ่มจาก nft_verdict_init() ไม่ได้จำกัดค่า verdict ที่รับจากผู้ใช้อย่างเพียงพอ ทำให้สามารถตั้งค่าที่ดูเหมือน NF_DROP แต่มี drop error เป็นค่าบวกได้
  • เมื่อ nf_hook_slow() ดูบิตล่างของ verdict แล้วตัดสินว่าเป็น NF_DROP จะ free skb ก่อนด้วย kfree_skb_reason()
  • หลังจากนั้น หากผลลัพธ์ของ NF_DROP_GETERR() คืนค่าเป็นค่าที่สอดคล้องกับ NF_ACCEPT ฝั่ง caller จะมองว่าแพ็กเก็ตถูก accept และประมวลผลต่อ
  • ผลคือ skb ที่ถูก free ไปแล้วถูก free ซ้ำอีกครั้งในเส้นทางถัดไป ก่อให้เกิด double-free primitive

อ็อบเจ็กต์ที่เสียหายและวิธีประมวลผลแพ็กเก็ต

  • double-free ส่งผลต่อ struct sk_buff ใน skbuff_head_cache และอ็อบเจ็กต์ sk_buff->head
  • sk_buff->head เก็บเนื้อหาแพ็กเก็ตจริง และตามขนาดของแพ็กเก็ต IPv4 อาจถูกจัดสรรตั้งแต่ kmalloc-256 ไปจนถึง order 4 page ของ buddy allocator
  • PoC ถูกออกแบบให้ใช้แพ็กเก็ต IP ขนาดใหญ่เพื่อให้เข้าสู่เส้นทาง buddy allocator แทน slab allocator
  • fragmentation queue ของ IPv4 ถูกใช้เพื่อหน่วงเวลาการ free ครั้งที่สองของ skb หรือชักนำให้เกิดขึ้นในเวลาที่ต้องการ
  • หากฟิลด์ skb ที่เสียหายถูกใช้งานในเส้นทางแพ็กเก็ต อาจทำให้เคอร์เนล panic ได้ จึงหลีกเลี่ยง TCP/UDP stack และใช้เส้นทางข้อผิดพลาดของ IP fragment เฉพาะแทน

ขอบเขตการทดสอบและอัตราความสำเร็จ

  • มีการทดสอบเคอร์เนลหลายเวอร์ชันและการตั้งค่าหลายแบบในสภาพแวดล้อม vanilla kernel, KernelCTF, Debian และ Ubuntu
  • กรณีที่สำเร็จรวมถึงสภาพแวดล้อมต่อไปนี้
    • Linux v5.14.21, v5.15.148, v5.16.20, v5.17.15, v5.18.19, v5.19.17, v6.0.19
    • Linux v6.1.55 ของ KernelCTF Mitigation v3
    • Linux v6.1.69 ของ Debian Bookworm 6.1.0-17
    • Linux v6.1.72 ของ KernelCTF LTS
    • Ubuntu Jammy v6.2.0-37
    • Linux v6.2.16, v6.3.13
  • กรณีที่ล้มเหลวรวมถึง v5.4.270, v5.10.209, v6.4.16, Ubuntu Jammy v6.5.0-15, v6.5.13, v6.6.14, v6.7.1 เป็นต้น
  • ความล้มเหลวบางส่วนหลัง v6.4.0 เชื่อมโยงกับการตรวจจับ bad_page() จาก CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y
  • ในสภาพแวดล้อม v6.4.16 อัตราความสำเร็จอยู่ที่ 99.4% และบางกรณีลดลงถึง 93.0% โดยจำนวนตัวอย่างคือ n=1000 ทั้งสองกรณี

วิธีแก้ไข

  • การแก้ไขที่เสนอในตอนแรกอาจสร้าง breaking change กลาง Netfilter stack ได้
  • การแก้ไขของ maintainer ของ Netfilter จำกัด verdict ที่มาจากอินพุตผู้ใช้ให้เข้มงวดขึ้นในระดับ API
  • แพตช์ใช้วิธีปฏิเสธพารามิเตอร์ verdict แบบ DROP/QUEUE สำหรับอินพุตจาก userland
  • ตามคำอธิบาย CVE nft_verdict_init() อนุญาตค่าบวกเป็น drop error ภายใน hook verdict และ nf_hook_slow() อาจสร้าง double free ได้ในสถานการณ์ที่ NF_DROP และ NF_ACCEPT ทับซ้อนกัน
  • ดูแพตช์แก้ไขได้ที่ [PATCH nf] netfilter: nf_tables: reject QUEUE/DROP verdict parameters.

Dirty Pagedirectory และการเข้าถึง physical memory ใดก็ได้

  • เทคนิคหลักของ PoC คือ Dirty Pagedirectory ซึ่งเป็นรูปแบบดัดแปลงของเทคนิค Dirty Pagetable เดิม
  • ไอเดียหลักคือการจัดสรรหน้า PTE และหน้า PMD ซ้ำให้ชี้ไปยัง physical page เดียวกัน
  • เมื่อเขียนค่าที่ดูเหมือนค่า PTE ลงในพื้นที่ virtual address หนึ่ง พื้นที่ virtual address อีกส่วนจะตีความค่านั้นเป็น page table entry และแมป physical page ที่ระบุ
  • วิธีนี้ทำงานเป็น kernel-space mirroring attack ที่เข้าถึง physical address ใดก็ได้และกำหนด permission flags ได้ด้วยการอ่าน/เขียน address ใน user space เท่านั้น
  • ใช้สำหรับเลี่ยงมาตรการบรรเทาอย่าง virtual KASLR, KPTI, SMAP, SMEP และ CONFIG_STATIC_USERMODEHELPER

การจัดการ page allocator

  • การจัดสรรหน้าของเคอร์เนลเกี่ยวข้องกับ slab allocator, buddy allocator และ PCP allocator
  • skb head ขนาดใหญ่ใช้ order 4 page ของ buddy allocator แต่หน้า PTE/PMD เป็น order 0 page จึงไม่เข้ากันโดยตรง
  • เพื่อแก้ปัญหานี้ มีการใช้วิธี page conversion สองแบบ
    • PCP list draining: ใส่ order 4 page ลงใน buddy freelist แล้วทำให้ PCP order 0 freelist ว่าง เพื่อให้ buddy allocator เติมกลับมาเป็น order 0 page
    • race condition: ใช้การแข่งขันระหว่างการ free ครั้งที่สองเพื่อใส่ order 4 page ลงใน order 0 freelist
  • PCP list draining เป็นวิธีที่เรียบง่าย เสถียรกว่า และเร็วกว่า
  • วิธี race condition เคยใช้ใน exploit ช่วงแรกของ KernelCTF แต่ถูกจัดว่า obsolete เพราะพึ่งพาสภาพแวดล้อมที่มีความหน่วง serial TTY สูงอย่าง QEMU VM

การเลี่ยง KernelCTF mitigation

  • ในสภาพแวดล้อม KernelCTF mitigation มาตรการบรรเทาที่ต้องเลี่ยงอย่างจริงจังคือการตรวจสอบ freelist corruption ของ sk_buff
  • เนื่องจาก skbuff_head_cache->offset == 0x70 ทำให้ freelist next pointer ทับกับ skb->len
  • หลังจาก free skb ครั้งแรก skb->len จะถูกเขียนทับด้วยบางส่วนของ freelist pointer และระหว่างการ parse แพ็กเก็ตถัดมา ค่านี้อาจเปลี่ยนจนถูกตรวจจับด้วย corruption check
  • เลี่ยงการตรวจจับโดย free skb ปกติเพิ่มเติมทับบน skb ที่เสียหาย เพื่อเขียนทับ freelist head
  • ผู้พัฒนา KernelCTF เห็นว่าหากตรวจสอบ freelist head next pointer ตอน free ด้วย ก็อาจบรรเทาการเลี่ยงนี้ได้

TLB flush และการค้นหา physical KASLR

  • เมื่อเปลี่ยน page table ด้วย Dirty Pagedirectory ในรูปแบบที่ไม่คาดคิด ข้อมูล translation เก่าอาจค้างอยู่ใน TLB cache ของ CPU
  • ทำการ flush TLB จาก user space โดยให้ child หลัง fork() เรียก munmap() แล้วเข้าสู่สถานะ sleep
  • วิธีนี้ได้รับการยืนยันว่าทำงาน 100% บน CPU AMD และ QEMU VM
  • การค้นหา physical KASLR ลดช่วงค้นหาโดยอาศัยข้อเท็จจริงที่ว่า kernel physical base address ถูก align ตาม CONFIG_PHYSICAL_START หรือ CONFIG_PHYSICAL_ALIGN
  • หากสมมติ physical memory 8GiB และ alignment 16MiB จะมี candidate 512 ค่า และใช้สคริปต์ get-sig สร้าง kernel base signature เพื่อระบุค่า

modprobe_path และการได้ root shell

  • หลังจากได้สิทธิ์อ่าน/เขียน physical memory ใดก็ได้ PoC จะสแกนช่วงประมาณ 80MiB หลัง kernel base เพื่อหา modprobe_path
  • ในการตั้งค่าทั่วไป จะค้นหาแพตเทิร์น "/sbin/modprobe" พร้อม null padding และตรวจสอบตัวแปรจริงจากการสะท้อนไปยัง /proc/sys/kernel/modprobe
  • หากเปิดใช้ CONFIG_STATIC_USERMODEHELPER จะเล็งสตริง "/sbin/usermode-helper" แทน
  • เพื่อให้ได้ root shell จะเขียนทับ modprobe_path หรือสตริง static usermode helper ด้วยพาธ memfd ในรูป /proc/<pid>/fd/<fd>
  • สคริปต์ privilege escalation เชื่อม file descriptor ของ exploit เข้ากับ stdin/stdout ของ shell เพื่อให้ทำงานได้ทั้งบน local terminal และ reverse shell

Fileless execution และโครงสร้าง PoC

  • PoC รองรับ fileless execution ที่ไม่เขียนไฟล์ลงดิสก์
  • หากเป้าหมายมี Perl สามารถใช้ memfd_create() โหลดไบนารี exploit ขึ้นหน่วยความจำและรันผ่าน /proc/$$/fd/<fd> ได้
  • dependency สำหรับคอมไพล์คือ libnftnl-dev และ libmnl-dev
  • static build สำหรับ KernelCTF ใช้ musl-gcc ซึ่งเป็นการเลือกเพื่อหลีกเลี่ยงปัญหา static linking ของ glibc และปัญหา AVX512 opcode ใน QEMU
  • ซอร์ส exploit แบ่งเป็นหลายไฟล์ และเนื่องจากมีเป้าหมายเป็นไบนารีรันเดี่ยว เมื่อเกิดข้อผิดพลาดจึงเลือก crash/exit มากกว่าการคืน error code

ความเสถียรและข้อจำกัด

  • สถานะ pagetable ของโปรเซส exploit อาจไม่เสถียร ดังนั้นหลังสำเร็จหรือล้มเหลวจะไม่จบ child process แต่ปล่อยให้อยู่ในสถานะ sleep เพื่อลดความไม่เสถียรของเคอร์เนล
  • หากมีกิจกรรมเครือข่าย จะเกิด noise ใน skb freelist และส่งผลต่อความเสถียรได้
  • ในสภาพแวดล้อม SSH หรือ reverse shell จะลดการพิมพ์ stdout รอบช่วง double-free เพื่อลดการจัดสรร/ปล่อย skb จากเครือข่าย
  • ในการทดสอบบนฮาร์ดแวร์บางเครื่อง ระบบ crash หลังผ่านไปไม่กี่วินาที และเนื่องจาก WiFi frame ก็ใช้ skb เช่นกัน จึงเป็นไปได้ว่ากิจกรรม WiFi มีผลกระทบ
  • เมื่อปิดใช้งานอะแดปเตอร์ WiFi ใน BIOS exploit ทำงานได้ตามปกติในสภาพแวดล้อมดังกล่าว

ข้อสรุปจากกระบวนการวิจัย

  • PoC ถูกปรับแต่งโดยมีเป้าหมายเพื่อความเข้ากันได้กว้าง ความเสถียรสูง และการรันแบบซ่อนร่องรอย
  • ใช้เวลาพัฒนา 2 เดือน และใช้อีก 2 เดือนเพื่อปรับปรุงความเสถียรและความเข้ากันได้
  • ตัว exploit เองไม่ได้พึ่งพาพฤติกรรมของ slab allocator มากนัก แต่ประกอบขึ้นจากฟังก์ชันที่มักเปิดใช้แพร่หลายอย่าง IPv4 subsystem และ virtual memory
  • บั๊กตั้งต้นต้องมี unprivileged user namespace และ nftables แต่เทคนิคอย่าง Dirty Pagedirectory และ PCP draining สามารถนำไปใช้กับ exploit จริงอื่น ๆ ได้ด้วย
  • งานนี้เป็นกรณีศึกษาที่เจาะลึกทั้ง networking subsystem และ memory management subsystem ของเคอร์เนล Linux ไปพร้อมกัน

1 ความคิดเห็น

 
GN⁺ 2024-03-27
ความคิดเห็นจาก Hacker News
  • วันนี้มีการเผยแพร่ exploit แบบพิสูจน์แนวคิดของ CVE-2024-1086 แล้ว และทำงานได้บน Debian, Ubuntu เป็นต้น
    เวอร์ชันที่ได้รับผลกระทบคือ Linux kernel v5.14~v6.6 และการรองรับ v6.4~v6.6 ขึ้นอยู่กับการตั้งค่า kernel CONFIG_INIT_ON_ALLOC_DEFAULT_ON
    บั๊กนี้ถูกแพตช์ไปแล้วในเดือนกุมภาพันธ์ 2024 ดังนั้นควรอัปเดตเครื่อง Linux

    • ซอร์สดีและงานก็ยอดเยี่ยม อยากรู้ว่าจะมีปฏิกิริยาที่ใหญ่กว่านี้ออกมาอย่างไร
      https://github.com/Notselwyn/CVE-2024-1086/blob/main/src/mai...
    • ถ้าในขั้นตอนหลัง exploit ได้ primitive operation ที่คล้าย KSMA มาแล้ว ก็สงสัยว่าทำไมยังใช้วิธี modprobe_path และถึงกับใส่การ brute-force pid เพราะต้องการวิธีแบบไร้ไฟล์
      เช่น อยากถามว่าทำไมไม่เลือกวิธีแพตช์ kernel .text ด้วย shellcode สั้น ๆ เพื่อกลายเป็น root และหลุดออกจาก namespace
    • อยากรู้ว่า เส้นทางโจมตีและผลกระทบ ที่เป็นไปได้ของช่องโหว่นี้คืออะไร
    • ในเนื้อหาบอกว่าเวอร์ชัน exploit ที่ได้รับผลกระทบคือตั้งแต่ Linux kernel v5.14 ถึง v6.4 แต่หน้าที่ลิงก์ไปบอกว่าตั้งแต่ v5.14 ถึง v6.6
      อย่างไรก็ตาม ระบุว่ายกเว้น branch ที่แพตช์แล้วคือ v5.15.149>, v6.1.76>, v6.6.15>
  • ในแพตช์มีประโยคนี้: “This reverts commit e0abdadcc6e1. [...] Its not clear to me why this commit was made.”
    อยากรู้ว่ามีใครค้นหา ประวัติความเป็นมา ของ commit นี้แล้วหรือยัง
    [1] https://lore.kernel.org/all/20240120215012.129529-1-fw@strle...

    • commit เดิมเป็น commit ที่เก่ากว่า 10 ปี แล้ว จึงมีโอกาสสูงที่จะถูกกลบไปตามกาลเวลา
      ลองค้นในรายชื่อเมล netdev เก่า ๆ แล้วแต่ไม่พบอะไร และอาจเป็นแพตช์ที่ส่งตรงไปยัง committer คือ Pablo Neira Ayuso ก็ได้
      ผู้เขียนเดิมคือ Patrick McHardy ซึ่งปัจจุบันกลายเป็นบุคคลที่ผู้คนหลีกเลี่ยง และหาก Pablo จำไม่ได้หรือค้นหาในอีเมลไม่เจอ ก็ดูเหมือนว่าการใช้งานจริงที่แน่ชัดจะยากจะเปิดเผยได้ด้วยการสืบค้นพื้นฐาน
    • commit ที่เกี่ยวข้องคือ: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin...
      คือ netfilter: nf_tables: accept QUEUE/DROP verdict parameters ซึ่งทำให้ user space สามารถระบุหมายเลข queue หรือรหัส errno ให้กับ verdict แบบ QUEUE และ DROP ได้
  • บทความนี้น่าประทับใจมากแม้ในมุมมองของ การเขียนด้านความปลอดภัย
    เวลาจะเขียนบล็อกความปลอดภัย มักต้องคิดเสมอว่า “จะสมมติว่าผู้อ่านมีความรู้พื้นฐานและความรู้เดิมมากแค่ไหน” และการหาสมดุลให้ทั้งเข้าถึงได้และเขียนได้จริงนั้นทำได้ยาก
    จุดที่ดีมากคือกำหนดกลุ่มผู้อ่านเป้าหมายก่อนแล้วให้คำอธิบายพื้นฐานอย่างเพียงพอ จึงบุ๊กมาร์กไว้เพื่อใช้เป็นเอกสารแนะนำให้นักวิจัยหน้าใหม่ที่เจอทุกปี

  • exploit นี้พึ่งพาการเข้าถึง unprivileged user namespace: sysctl kernel.unprivileged_userns_clone = 1
    นี่เป็นค่าเริ่มต้นของ kernel บน Debian/Ubuntu และ Arch Linux และถ้าไม่มีความจำเป็นอย่างการรันคำสั่ง Docker โดยไม่ใช้ sudo ก็ควรปิดไว้

    • การตั้งค่านี้ไม่ได้ใช้แค่ Docker หรือ Pacman
      Chrome sandbox ที่ใช้ในแอป Electron หรือ 1Password ก็เกี่ยวข้องด้วย และ binary ช่วยงานของ sandbox ยังทำงานเป็นโปรแกรม setuid ได้ด้วย
      ถ้า Proton จะเริ่มใช้ user namespace ในอนาคตก็คงไม่น่าแปลกใจ ดังนั้นบน Linux เดสก์ท็อปทั่วไป อาจจะดีกว่าที่จะไม่ปิดไว้
      ส่วนบนเซิร์ฟเวอร์หรือ Linux ที่ harden เป็นพิเศษ หลายกรณีก็ไม่เสียหายที่จะปิด
    • การตั้งค่านี้ดีตรงที่ทำให้ผู้คนรันสิ่งอย่าง container ได้โดยไม่ต้องมีสิทธิ์ root แต่ก็น่าเสียดายจริง ๆ ที่สุดท้ายเคยนำไปสู่ ช่องโหว่ยกระดับสิทธิ์เป็น root
    • ปัญหาจริง ๆ ไม่ใช่ตัวการตั้งค่านั้นเอง แต่คือ namespace เดิมทีเป็นเครื่องมือสำหรับลดสิทธิ์
      ปัญหาคือเพื่อให้ container “ทำงานได้ทันที” ต้องมีการแฮ็กมากมายในฝั่งเครือข่าย และจุดขายหลักของ Docker ก็แทบจะเป็นการทำแฮ็กอันตรายเหล่านั้นแทนให้
      สุดท้ายโซลูชัน container แบบใดก็ตามก็ต้องยอมรับแฮ็กเหล่านั้น และกลายเป็นโครงสร้างที่แม้ฟังก์ชัน namespace ปกติจะลดการเข้าถึงลง แต่ kernel ก็เปิดประตูไว้เพราะการแฮ็กด้านเครือข่าย
    • ใน 6.1.65 ไม่มีตัวเลือกนั้น เลยสงสัยว่าเปลี่ยนชื่อไปแล้วหรือเปล่า
  • ไม่เข้าใจว่าทำไม unprivileged user namespace ถึงเปิดเป็นค่าเริ่มต้น
    แม้จะรันอยู่ภายใน namespace แบบ “ไม่มีสิทธิ์พิเศษ” แต่ก็สงสัยว่าทำไมถึงให้ความสามารถแก่ผู้ใช้ในการรันสิ่งอย่าง iptables หรือ mount เป็นค่าเริ่มต้น

    • unprivileged user namespace ทำให้โปรแกรมที่ไม่มีสิทธิ์สามารถสร้าง sandbox ได้
      ตัวอย่างเช่น Chrome ใช้ namespace เพื่อทำ process sandbox แต่ก็ติดตั้ง binary แบบ setuid-root เพื่อให้ทำได้แม้ไม่มี unprivileged namespace
      เนื่องจาก binary แบบ setuid-root เองก็เป็นความเสี่ยงด้านความปลอดภัย ในระยะยาวจึงควรไปในทางที่ Chrome ไม่ต้องติดตั้ง binary แบบนั้น
      แต่การจะทำเช่นนั้นได้ unprivileged user namespace ต้องมีให้ใช้อย่างแพร่หลาย และบั๊กแบบนี้ก็ทำให้อนาคตนั้นช้าลง
      อีกทั้งโปรแกรมที่สร้าง sandbox อิง namespace อย่าง Chrome มักใช้ seccomp ร่วมด้วย เพื่อป้องกันไม่ให้โค้ดภายใน sandbox ใช้ฟังก์ชัน kernel แปลก ๆ อย่าง namespace
      บนเดสก์ท็อปผู้ใช้คนเดียว ประโยชน์จากการบังคับแยกระหว่างผู้ใช้กับ root อย่างเข้มงวดไม่ได้มากนัก และสิ่งที่น่าสนใจส่วนใหญ่ก็เข้าถึงได้แม้จากบัญชีผู้ใช้
      ในทางกลับกัน sandbox อย่าง Chrome เป็นสิ่งจำเป็นต่อความปลอดภัยของเดสก์ท็อป ดังนั้นบนเดสก์ท็อปผู้ใช้คนเดียว จึงมักมองว่าการเปิด unprivileged user namespace จะเพิ่มความปลอดภัยโดยรวม
      แน่นอนว่าระบบหลายผู้ใช้เป็นอีกเรื่องหนึ่ง
  • ถ้าไม่มีบั๊กแบบนี้โผล่มาเรื่อย ๆ user namespace แบบไม่ต้องมีสิทธิ์พิเศษก็น่าจะเป็น ฟีเจอร์ด้านความปลอดภัย ที่ยอดเยี่ยม
    เช่น คงจะดีถ้าการรัน Flatpak ไม่จำเป็นต้องใช้ไบนารี setuid ของโฮสต์เพื่อแยกแอปออกจากกัน

    • ปัญหาคือปรัชญาแบบ “แค่ใช้ได้เลย”
      ค่าเริ่มต้นส่วนใหญ่ไม่ปลอดภัย และต้องอาศัยความรู้ทางเทคนิคกับการตั้งค่าเพิ่มความแข็งแกร่ง
  • คอมมิตที่ทำให้เกิดปัญหา: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin...
    ใน switch/case ที่ซ้อนกันมี return อยู่ในเส้นทางเริ่มต้น แต่โครงสร้างที่คาดหวังให้ fall-through หลังจากนั้นดูแปลก ๆ

    • สงสัยว่าเป็นคนเดียวกันหรือเปล่า
      https://lwn.net/Articles/882397/
    • ถ้าเปลี่ยนลิงก์เป็นรูปแบบ GitHub จะเป็นดังนี้: https://github.com/torvalds/linux/commit/f342de4e2f33e0e3916...
      https://bugs.launchpad.net/bugs/cve/2024-1086
      เป็นช่องโหว่แบบ use-after-free ในคอมโพเนนต์ netfilter: nf_tables ของเคอร์เนล Linux ซึ่งทำให้ยกระดับสิทธิ์ในเครื่องได้
      ฟังก์ชัน nft_verdict_init() อนุญาตให้ใช้ค่าบวกเป็น drop error ภายใน hook verdict ส่งผลให้ nf_hook_slow() ประมวลผล NF_DROP พร้อมกับ drop error ที่ดูเหมือน NF_ACCEPT จนอาจเกิดช่องโหว่ double-free ได้
      แนะนำให้อัปเกรดเป็นเวอร์ชันหลัง f342de4e2f33e0e39165d8639387aa6c19dff660
  • สงสัยว่าการ exploit แบบนี้ทำได้อย่างไร ทั้งที่มี mitigation สมัยใหม่อย่าง ASLR
    ตอนเรียนมหาวิทยาลัย เคยมีงานที่ได้รับไบนารีหลายตัวซึ่งต้องรันบน Ubuntu เวอร์ชันเฉพาะ แล้วต้องหาบั๊กอย่าง use-after-free หรือ buffer overflow เพื่อนำไป exploit ซึ่งยากมากจริง ๆ
    แค่หาข้อบกพร่องก็ยากแล้ว และการเขียน shellcode ที่ถูกต้องเพื่อให้ข้อบกพร่องนั้นทำงานที่เป็นประโยชน์ได้ยิ่งยากกว่า
    ในขั้นที่ยากขึ้น ยังเปิด mitigation อย่าง ASLR และ stack canary ไว้ด้วย และแม้ในสภาพแวดล้อมนักศึกษาที่ควบคุมได้ก็รู้สึกว่าแทบเป็นไปไม่ได้
    สุดท้ายต้อง 1) พบข้อบกพร่องที่ exploit ได้ และ 2) หาไบนารี payload ที่ถูกต้องซึ่งทำสิ่งที่เป็นประโยชน์ได้โดยไม่ทำให้โปรแกรมแค่แครช แต่ในโลกจริงน่าจะยากกว่านั้น จึงสงสัยว่าทำได้อย่างไร
    [0] https://web.stanford.edu/class/archive/cs/cs107/cs107.1194/a...

    • คนที่เรียนวิชานั้นเป็นมือใหม่ที่มีประสบการณ์น้อย และต้องหาบั๊กหลายตัวแล้ว exploit ให้ได้ภายในคอร์ส 10–15 สัปดาห์
      เมื่อเรียนควบคู่กับวิชาอื่น เวลาที่ใช้กับบั๊กหนึ่งตัวคงมีตั้งแต่ไม่กี่ชั่วโมงถึงไม่กี่วัน
      Zerodium จ่าย 50,000 ดอลลาร์ สำหรับการยกระดับสิทธิ์ในเครื่องบน Linux ทั่วไป และถ้าคิดตามต้นทุนของนักพัฒนา exploit ที่มีทักษะ ก็ซื้อเวลาได้ราว 200 ชั่วโมง-คน
      กล่าวคือผู้เชี่ยวชาญใช้เวลามากกว่านักศึกษามือใหม่ 10–100 เท่า
      ให้นึกถึงความต่างระหว่างคนที่เพิ่งเข้าร้านงานไม้ครั้งแรกกับช่างไม้, มือใหม่หัดปั้นเครื่องปั้นดินเผากับช่างฝีมือ, หรือจิตรกรปีหนึ่งกับศิลปินมืออาชีพ
      แถมยังทุ่มเวลาเพิ่มอีก 10–100 เท่าด้วย
      [1] https://zerodium.com/program.html
    • mitigation สมัยใหม่ทำให้การ exploit ยากขึ้นมาก แต่นักวิจัยก็ยังหาวิธีเลี่ยง mitigation เหล่านั้นได้อย่างต่อเนื่อง
      มีเทคนิค “คลาสสิก” ที่เลี่ยงกลไกป้องกันสมัยใหม่ส่วนใหญ่ได้ และถ้าไม่มีก็มักมีการโจมตีหรือวิธีเลี่ยงแบบใหม่เกิดขึ้น
      ตัวอย่างเช่น การเลี่ยงการป้องกันของ heap ดูได้จาก how2heap[0], เคยมีกรณี exploit ที่เลี่ยง KASLR[1], และ exploit ครั้งนี้ดูเหมือนจะใช้เทคนิค dirty pagetable[2]
      โครงสร้างจึงเป็น เกมแมวจับหนู ต่อเนื่องระหว่างการเพิ่ม mitigation กับนักวิจัยที่หาทางเลี่ยงมัน
      [0] https://github.com/shellphish/how2heap
      [1] https://www.willsroot.io/2022/12/entrybleed.html
      [2] https://pwning.tech/nftables/#452-the-technique
    • พูดสั้น ๆ คือมีคนที่เก่ง มีคนที่โชคดี และมีคนที่ทั้งเก่งทั้งโชคดี
      การค้นพบสิ่งแบบนี้ต้องการเพียงคนประเภทสุดท้ายสักคนก็พอ
      ทุกวันนี้มันยากมาก และแม้ปิด mitigation แล้ว การหาช่องโหว่และเขียน exploit ก็ยังไม่ใช่เรื่องง่าย
      แต่หลายคนที่ค้นหาช่องโหว่เหล่านี้ทำงานเป็นทีม สามารถรัน fuzzing แบบขนาน และผสานหรือ chain ความรู้กับ exploit อื่น ๆ ได้
      ความเชี่ยวชาญและพรสวรรค์ของนักวิจัยบางคนอยู่ในระดับน่าทึ่ง และในสาขานี้ ประสบการณ์หลายปีถึงหลายสิบปีมีคุณค่ามหาศาล
    • เคยทำงานในวิชาคล้าย ๆ กันที่ได้รับไบนารีมาแล้วต้องหาบั๊ก ซึ่งก็ยากเหมือนกัน
      แต่ถ้าคิดว่าบั๊กสำคัญ ๆ ที่ถูกค้นพบในปัจจุบันมาจากทีมที่มีเงินทุนมากหรือ actor ระดับรัฐ ก็เข้าใจได้มากขึ้นว่าการทุ่มกำลังคนและทรัพยากรจำนวนมากสามารถเลี่ยง mitigation ที่มีอยู่ได้
    • บล็อกโพสต์ที่ลิงก์ไว้ใน repository มีส่วนแยกต่างหากเกี่ยวกับ KASLR
  • ตามข้อมูลของ Ubuntu รุ่น LTS ทั้งหมดได้รับผลกระทบ และได้รับการแก้ไขแล้วในเคอร์เนลที่แพตช์แล้วปัจจุบัน: https://ubuntu.com/security/CVE-2024-1086
    Focal แก้ไขใน 5.4.0-174.193, Jammy ใน 5.15.0-101.111, Mantic ใน 6.5.0-26.26
    หากใช้การสนับสนุนแบบขยาย จะรวม Xenial และ Bionic ด้วย

  • ลองรันบนระบบ Debian ที่มีช่องโหว่แล้ว แม้จะไม่ได้ ยกระดับสิทธิ์ สำเร็จ แต่ในการรันครั้งที่สองทั้งระบบค้างไปเลย
    การรันครั้งแรกแค่ล้มเหลวเฉย ๆ ดังนั้นก็ยังคุ้มค่าพอที่จะใช้เวลาแพตช์

  • สามารถตรวจสอบการตั้งค่าเคอร์เนลปัจจุบันได้จากไฟล์อย่าง /boot/config หรือ /proc/config.gz