1 คะแนน โดย GN⁺ 2024-03-31 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • แบ็กดอร์ xz/liblzma ไม่ได้เป็นปัญหาเฉพาะ payload ไบนารีสุดท้ายเท่านั้น แต่ยังมี ขั้นตอน Bash ที่ทำงานระหว่างการ build ซึ่งถูกซ่อนด้วยกระบวนการดึงข้อมูลและถอดรหัสหลายชั้น ทำให้วิเคราะห์ได้ยาก
  • เวอร์ชันที่ได้รับผลกระทบคือ xz/liblzma 5.6.0 และ 5.6.1 โดยสคริปต์ที่ถูกพรางและ payload ไบนารีอยู่ในไฟล์สองไฟล์ที่ดูเหมือนไฟล์ทดสอบ
  • Stage 0 เริ่มจาก m4/build-to-host.m4 โดยกู้คืน xz stream ที่ดูเหมือนเสียหาย แล้วใช้ xz -d ดึง สคริปต์ Stage 1 ออกมารัน
  • Stage 1 ตัดและแทนที่ข้อมูลจาก good-large_compressed.lzma แล้วรัน สคริปต์ Bash Stage 2 และใน 5.6.1 มีการเพิ่มโค้ดตรวจสอบว่ารันบน Linux หรือไม่ 5 ครั้ง
  • Stage 2 ใช้ file carving, cipher แบบแทนที่ และ การถอดรหัส RC4 แบบดัดแปลงด้วย AWK เพื่อแทรกไฟล์ .o เข้าไปในกระบวนการ build/link และใน 5.6.1 ยังมีโครงสร้างส่วนขยายที่สามารถรันสคริปต์เพิ่มเติมในอนาคตได้ด้วย

ขอบเขตของขั้นตอน Bash ในแบ็กดอร์ xz/liblzma

  • Andres Freund แจ้งการค้นพบแบ็กดอร์ xz/liblzma ใน mailing list oss-security และแบ็กดอร์นี้ส่งผลต่อ OpenSSH server
  • สิ่งที่วิเคราะห์ไม่ใช่ตัวแบ็กดอร์ไบนารีเอง แต่เป็น ขั้นตอนเริ่มต้นที่ใช้ Bash และวิธีการพรางโค้ดที่ทำงานก่อนหน้านั้น
  • โฟลว์ Bash ต่อเนื่องตั้งแต่ Stage 0 ถึง Stage 2 และยังเผยให้เห็นบางส่วนของ Stage 3 ที่เป็นไปได้ซึ่งดูเหมือนยังไม่ได้ implement สมบูรณ์
  • ขั้นตอนที่ถูกพราง/เข้ารหัสและแบ็กดอร์ไบนารีหลังจากนั้นถูกซ่อนอยู่ในไฟล์ทดสอบสองไฟล์
    • tests/files/bad-3-corrupt_lzma2.xz
    • tests/files/good-large_compressed.lzma
  • เวอร์ชันที่ได้รับผลกระทบคือ 5.6.0 และ 5.6.1 โดยมีความแตกต่างเล็กน้อยระหว่างสองเวอร์ชัน

Stage 0: กู้คืน xz stream ที่ดูเหมือนไฟล์ทดสอบเสียหาย

  • จุดเริ่มต้นคือโค้ดใน m4/build-to-host.m4 และดูเหมือนว่าจะถูกเรียกใช้ที่ใดที่หนึ่งระหว่างกระบวนการ build
  • pipeline หลักเชื่อม sed, tr, xz -d เพื่อดึง สคริปต์ Stage 1 ออกมา
  • หลังจากอ่านไบต์ของ tests/files/bad-3-corrupt_lzma2.xz แล้ว จะใช้ tr "\t \-_" " \t_\-" เพื่อแทนที่ไบต์บางตัว
    • แท็บ 0x09 ถูกเปลี่ยนเป็นช่องว่าง 0x20
    • ช่องว่าง 0x20 ถูกเปลี่ยนเป็นแท็บ 0x09
    • ไฮเฟน 0x2d ถูกเปลี่ยนเป็น underscore 0x5f
    • underscore 0x5f ถูกเปลี่ยนเป็นไฮเฟน 0x2d
  • การแทนที่นี้มีหน้าที่ทำให้ bad-3-corrupt_lzma2.xz กลับมาเป็น xz stream ปกติ
  • ท้ายที่สุดจะคลาย stream ที่กู้คืนแล้วด้วย xz โดยไม่สนใจ error จากนั้นได้สคริปต์ Stage 1 และรันในทันที
    • stream ดูเหมือนถูกตัดขาด แต่ output ที่มีความหมายถูกเขียนออกมาแล้ว จึงไม่เป็นปัญหา

Stage 1: ดึง Stage 2 ออกจาก good-large_compressed.lzma

  • Stage 1 เป็นไฟล์ Bash สั้น ๆ ที่ขึ้นต้นด้วย ####Hello#### ในอีเมลของ Andres Freund
  • ความแตกต่างแรกของสองเวอร์ชันคือไบต์สุ่มในคอมเมนต์บรรทัดที่สอง
    • 5.6.0: 86 F9 5A F7 2E 68 6A BC
    • 5.6.1: E5 55 89 B7 24 04 D8 17
  • ใน 5.6.1 มีการเพิ่ม การตรวจสอบ Linux ในรูปแบบ [ ! $(uname) = "Linux" ] && exit 0 และการตรวจสอบเดียวกันนี้ถูกทำซ้ำ 5 ครั้ง
  • การดึง Stage 2 เริ่มจากบรรทัดยาว export i=... โดยค่านี้ถูกใช้เหมือนฟังก์ชันชนิดหนึ่งที่เรียกภายหลังด้วย eval $i
  • โฟลว์การดึงข้อมูลเป็นโครงสร้าง pipeline ที่เชื่อม standard output และ standard input ต่อเนื่องกัน
    • ก่อนอื่นคลาย good-large_compressed.lzma ด้วย xz -dc
    • eval $i เรียก head ซ้ำ ๆ ในรูปแบบที่ทิ้ง 1024 ไบต์ แล้ว output 2048 ไบต์
    • ที่ท้ายไฟล์ 5.6.0 output 724 ไบต์ ส่วน 5.6.1 output 939 ไบต์
  • chain ของ head นี้ตัดส่วนที่เป็นพื้นที่ entropy ต่ำและว่างเปล่าออก แล้วต่อเฉพาะพื้นที่ที่มีข้อมูลจริงเข้าด้วยกัน
  • จากนั้นใช้ tail -c +31233 ทิ้งส่วนต้น
    • ส่วนต้นนี้ซ่อน แบ็กดอร์ไบนารี ที่จะถูกดึงออกมาในขั้นตอนถัดไป
    • ใน 5.6.0 มีผลเท่ากับทิ้ง 31264 ไบต์แรก ส่วนใน 5.6.1 ทิ้ง 31232 ไบต์แรก
  • ถัดมา tr ถูกใช้เหมือน cipher แบบแทนที่อย่างง่าย โดย mapping key ของ 5.6.0 และ 5.6.1 ต่างกัน
    • 5.6.0: tr "\5-\51\204-\377\52-\115\132-\203\0-\4\116-\131" "\0-\377"
    • 5.6.1: tr "\114-\321\322-\377\35-\47\14-\34\0-\13\50-\113" "\0-\377"
  • ข้อมูลที่ถูกแทนที่จะถูกคลายอีกครั้งด้วย xz -F raw --lzma1 -dc และ Stage 2 ที่ได้จะถูกรันทันที

Stage 2: การแก้ไขกระบวนการ build และกลไกส่วนขยาย

  • Stage 2 คือไฟล์ infected.txt ที่ Andres Freund แนบไว้ในอีเมลต้นฉบับ และเป็นสคริปต์ Bash ที่เกิด การแก้ไขกระบวนการ compile จริง
  • จากมุมมองการพรางโค้ด Stage 2 มีชิ้นส่วนที่น่าสนใจสามส่วน โดยสองส่วนในนั้นปรากฏ เฉพาะใน 5.6.1
  • สองชิ้นส่วนของ 5.6.1 แทบจะเหมือนกัน และค้นหาไฟล์ที่มี signature เฉพาะใน tests/files/
    • Fragment 1 ค้นหา ~!:_ W และ |_!{ -
    • Fragment 3 ค้นหา jV!.^% และ %.R.1Z
  • เมื่อพบ signature จะดึงชื่อไฟล์และ offset ออกมา แล้วตัดช่วงไบต์ตั้งแต่หลัง signature แรกจนถึงก่อน signature ที่สอง
  • ข้อมูลที่ตัดออกมาจะถูกถอดรหัสแบบแทนที่ด้วยคีย์ tr ที่ใช้ใน 5.6.0 จากนั้นคลายด้วย xz -F raw --lzma2 -dc และรันด้วย eval
  • ในไฟล์ TAR archive ของ 5.6.0 และ 5.6.1 ที่ตรวจสอบ ไม่พบไฟล์ที่มี signature ดังกล่าว
  • โครงสร้างนี้ดูเหมือน ระบบส่วนขยาย/แพตช์ ที่ทำให้สามารถรันสคริปต์ในอนาคตได้โดยเพิ่มเฉพาะไฟล์ทดสอบใหม่ โดยไม่ต้องแก้ไฟล์ทดสอบที่ใช้ขนส่ง payload เดิมอีกครั้ง

Stage 2: ดึงไฟล์ .o ของแบ็กดอร์ไบนารี

  • แกนหลักอีกอย่างของ Stage 2 คือโค้ดที่ดึงไฟล์ .o แล้วแทรกเข้าไปในกระบวนการ compile/link
  • ผลลัพธ์เป้าหมายที่ถูกดึงออกมาคือ liblzma_la-crc64-fast.o ซึ่งเป็น แบ็กดอร์ไบนารี สุดท้าย
  • ขั้นตอนการดึงข้อมูลเชื่อมเครื่องมือมาตรฐานหลายตัวเป็น pipeline เหมือนขั้นตอนก่อนหน้า
    • คลาย good-large_compressed.lzma ด้วย xz -dc
    • ใช้ chain head ของ eval $i ที่ใช้ใน Stage 1 อีกครั้ง
    • ใช้ sed "s/\(.\)/\1\n/g" ใส่ newline หลังแต่ละไบต์ ทำให้อยู่ในรูปแบบหนึ่งบรรทัดต่อหนึ่งไบต์
  • สคริปต์ AWK ถัดไปทำ การถอดรหัสที่คล้าย RC4 กับ input stream
    • key array สร้างด้วย ((i * 7) + 5) % 256
    • 5.6.0 ข้าม 4096 ไบต์แรกของ PRNG stream ที่ output
    • 5.6.1 ข้าม 8192 ไบต์แรก
    • RC4 ดั้งเดิมใช้ XOR แต่โค้ดนี้ใช้การบวกในรูปแบบ (v + k) % 256
  • ข้อมูลที่ถอดรหัสแล้วถูกคลายด้วย xz -dc --single-stream
  • จากนั้นใช้ head ตัดเฉพาะช่วงไบต์ที่ต้องการและบันทึกเป็น liblzma_la-crc64-fast.o
    • ขนาด payload ที่ถูกบีบอัดแต่มีการดัดแปลงบางส่วนคือ 88792 ไบต์ใน 5.6.0 และ 88664 ไบต์ใน 5.6.1
    • ช่วงที่ดึงออกมาสุดท้ายมีขนาดราว 86KB

ลักษณะของการพรางโค้ดที่สร้างด้วยเครื่องมือมาตรฐานเท่านั้น

  • ขั้นตอน Bash ทั้งหมดประกอบขึ้นจากเครื่องมือ command line มาตรฐานเท่านั้น
    • sed
    • tr
    • xz
    • head
    • tail
    • awk
    • grep
    • cut
    • expr
  • payload ถูกซ่อนอยู่ในไฟล์ไบนารีที่ดูเหมือนไฟล์ทดสอบปกติ และใช้วิธี file carving เพื่อดึงเฉพาะพื้นที่ที่ต้องการออกมา
  • การพรางโค้ดใช้ทั้ง cipher แบบแทนที่อย่างง่ายและการถอดรหัส RC4 แบบดัดแปลงที่ใช้ AWK
  • การรันต่อเนื่องกันทั้งหมด 3 ขั้นตอน และใน 5.6.1 มีโครงสร้างที่สามารถเพิ่มไฟล์ทดสอบแยกต่างหากในภายหลังเพื่อรันสคริปต์ได้
  • โครงสร้างทั้งหมดเป็นรูปแบบที่ทุ่มเทความพยายามอย่างมากให้ดูไม่เป็นอันตรายและซ่อนตัวได้ดี และหากกรณีนี้ถูกพบโดยบังเอิญ ก็ทิ้งคำถามว่ายังมีกรณีคล้ายกันที่ยังไม่ถูกค้นพบเหลืออยู่อีกมากเพียงใด

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

 
GN⁺ 2024-03-31
ความคิดเห็นจาก Hacker News
  • ด้วยคำอธิบายแบบทำให้ง่ายขึ้นและการเปรียบเทียบกับภาพ noise ทำให้พอเข้าใจแล้วว่าที่คนพูดถึง ความประณีตซับซ้อน นั้นหมายถึงอะไร
    ผมยังเห็นใน reddit ด้วยว่า วิธี “sandboxing” ถูกทำให้พังด้วยจุดเพียงจุดเดียว และเห็นจุดอยู่ชิดซ้ายสุดของบรรทัดถัดจาก #include ทันที
    https://git.tukaani.org/?p=xz.git;a=commitdiff;h=328c52da8a2...
    https://old.reddit.com/r/linux/comments/1brhlur/xz_utils_bac...

    • ร้ายกาจจริง ๆ ใน diff มันดูเหมือนแค่ +, +., + และ จุดเดียว นั้นแทบไม่สะดุดตา
    • ผมเกลียดการใช้ เงื่อนไขตอนคอมไพล์/บิลด์ แบบนี้มาก ทดสอบได้ยากว่ามันเปิดเมื่อควรเปิดและปิดเมื่อควรปิด โดยเฉพาะถ้าอยู่ในระบบบิลด์ที่ไม่มีเฟรมเวิร์ก unit test
      แค่ความผิดพลาดธรรมดาที่ทำให้การทดสอบล้มเหลวตลอดหรือสำเร็จตลอดก็ปวดหัวแล้ว และเห็นได้เลยว่าทำไมมันถึงเป็นเป้าหมายที่ดีสำหรับพฤติกรรมไม่ประสงค์ดี
    • เรื่องนี้มีโอกาสสูงมากที่จะเป็นแค่ความผิดพลาดธรรมดา ไม่ใช่เจตนา
      a) แทบไม่มีใครเลยที่บิลด์แพ็กเกจนี้ด้วย cmake
      b) ถ้าลองบิลด์ด้วย cmake และ -DENABLE_SANDBOX=landlock ก็จะบิลด์ไม่ผ่านเลย: https://i.imgur.com/7xbeWFx.png
      จุดนั้นไม่ได้ปิด sandboxing แต่แค่ทำให้บิลด์ด้วย cmake ไม่ได้เท่านั้น จริง ๆ ถ้ามีใครลองบิลด์ด้วย cmake ก็คงเห็น error แล้วรู้ว่ามีอะไรผิดปกติ ดังนั้นจึงไม่สมเหตุสมผลนักที่จะมองว่าเป็นความพยายามร้ายเพื่อทำให้ความปลอดภัยลดลง
  • คำถามที่สำคัญที่สุดคือ “ถ้าเรื่องนี้ถูกค้นพบโดยบังเอิญ แล้วที่ยังไม่ถูกค้นพบยังเหลืออยู่อีกเท่าไร”
    ไม่น่าเป็นไปได้ที่ Andres Freund จะบังเอิญเจอแค่โปรเจกต์โอเพนซอร์สยอดนิยมหนึ่งเดียวที่ถูกฝัง backdoor ไว้ ถ้าตอนนี้มีของแบบนี้อยู่จริงในโลกสักโหลหนึ่งก็ไม่ใช่เรื่องแปลก

    • ไม่ใช่ว่าพบโดยบังเอิญ แต่ใกล้เคียงกับว่า รู้สึกได้ มากกว่า สองอย่างนี้ต่างกัน
      ผู้โจมตีรายถัดไปคงไม่สะเพร่าเหลือร่องรอยอย่างเวลารันที่เพิ่มขึ้นมากนัก
    • ก็เป็นไปได้ แต่ในความเป็นจริงอาจไม่ได้มีกรณีร้ายแรงมากนักก็ได้ ถ้ามีเยอะ เราน่าจะเจอสถานการณ์แบบนี้บ่อยกว่านี้
  • ความคิดที่ชวนไม่สบายใจตรงนี้คือ unit test เป็นสิ่งที่เปิดเส้นทางโจมตีให้ ถ้าไม่มีการทดสอบ ก็คงซ่อนแบบนี้ได้ยากกว่านี้มาก

    • ผู้โจมตียังกลบร่องรอยของ payload แรกเริ่มไว้ด้วยย่อหน้าใน README ที่ดูไม่มีพิษภัย เหมือนบอกว่า “ไม่มีอะไรให้ดู!”
      bad-3-corrupt_lzma2.xz มีสามสตรีม โดยสตรีมแรกและสตรีมที่สามเป็น xz stream ที่ถูกต้อง ส่วนสตรีมตรงกลางมี stream header, block header, index และ stream footer ที่ถูกต้อง มีเพียงข้อมูล LZMA2 เท่านั้นที่เสียหาย และระบุไว้ว่าถ้าใช้ --single-stream ก็ควรถูก decompress ได้
      สตริง ####Hello#### และ ####World#### ช่วยให้ได้ผลลัพธ์ปกติที่ดูสมเหตุสมผลเมื่อทำตามคำสั่งใน README จริง ๆ
      $ cat tests/files/bad-3-corrupt_lzma2.xz | xz -d --single-stream
      ####Hello####
      สตริงเหล่านี้เป็นคอมเมนต์ของ shell จึงไม่รบกวนการรัน payload
      สุดท้าย มันยังทำหน้าที่เป็น เครื่องหมาย เพื่อให้ภายหลัง regex หาไฟล์เจอได้โดยไม่ต้องอ้างชื่อไฟล์โดยตรงหรือใช้สตริง Hello/World จริง ๆ
      $ gl_am_configmake=\grep -aErls "#{4}[[:alnum:]]{5}#{4}$" $srcdir/ 2>/dev/null``
      $ echo $gl_am_configmake
      ./tests/files/bad-3-corrupt_lzma2.xz
    • สำหรับโปรเจกต์ที่สำคัญด้านความปลอดภัย ดูเหมือนว่าควรตั้งค่าโครงสร้างพื้นฐานการบิลด์ให้เกิด error หรืออย่างน้อย warning เมื่อมี ไฟล์ไบนารี รวมอยู่ในการบิลด์
      การตรวจนี้ควรใช้แบบ transitive ด้วย ดังนั้นแม้แต่ตอนที่ดิสโทรลินุกซ์พยายามอัปเดตเป็น liblzma เวอร์ชันใหม่ การบิลด์ก็ควรล้มเหลวหรือแจ้งเตือนเพราะมี dependency ไบนารีใหม่
      ผมไม่รู้ว่าแนวปฏิบัติแบบนี้พบได้ทั่วไปแค่ไหนในการบิลด์ของดิสโทรลินุกซ์ ถ้าพบได้ทั่วไป งานเก็บกวาดคงมหาศาล และไม่แน่ใจด้วยซ้ำว่าเป็นไปได้หรือไม่ สำหรับ bazel ดูเหมือนจะทำได้ แต่ระบบบิลด์อื่น ๆ ผมไม่แน่ใจ
  • สงสัยว่ามีใครลองค้นหา ทริก head | tail แบบคล้าย ๆ กันบน GitHub แล้วหรือยัง ยากที่จะเชื่อว่านี่ถูกคิดขึ้นมาใหม่เพื่อเรื่องนี้โดยเฉพาะ

    • เคยเห็นวิธีแบบนี้ค่อนข้างบ่อยในตัวติดตั้ง Unix ของผู้ขายซอฟต์แวร์เชิงพาณิชย์
      ไฟล์ .sh ขนาดมหึมาจะแสดงไลเซนส์และขอให้ยอมรับ จากนั้น cat ตัวเองแล้วส่งผ่าน pipe head/tail ไปยัง cpio เพื่อแตก asset จริงออกมา
    • ฉลาดก็จริง แต่ไม่ได้ใหม่เอี่ยม และค่อนข้างใกล้เคียงกับ use case ที่ตั้งใจไว้ของเครื่องมือพวกนี้
    • เป็นโอกาสเขียนเปเปอร์ได้เลย
  • แม้จะไม่มีคำตอบที่ดีกว่า แต่ผมก็รู้สึกว่า กอง bash ที่อ่านยากแบบนี้เองมันไม่น่าไว้ใจอยู่แล้วหรือเปล่า
    เหมือนกับในหลายส่วนของโลกการพัฒนา ซอฟต์แวร์นี้เขียนให้โปร่งใสน้อยลงไม่ได้หรือ เพื่อให้เห็นชัดขึ้นว่าเกิดอะไรขึ้น?
    ผมรู้ว่าผู้ดูแลสามารถใส่โค้ดมุ่งร้ายเข้าไปได้โดยไม่ต้องผ่านการตรวจสอบเข้มงวดเท่าผู้มีส่วนร่วมภายนอก แต่ก็น่าจะมีทางที่ดีกว่ากองโค้ดที่ “กระชับ” ซึ่งแทบจะดูเหมือนการทำให้สับสนโดยไม่ได้ตั้งใจแบบนี้

    • นี่เป็นกลิ่นไม่ดีที่เก่าแก่มาก
      ปัญหาหลักคือในยุค 80–90 มีระบบตระกูล Unix จำนวนมากที่แต่ละตัวมีข้อบกพร่องและฟีเจอร์ที่ขาดหายไปต่างกัน และผู้เขียนซอฟต์แวร์ก็อยากลด dependency สำหรับการ build ให้น้อยที่สุด
      ดังนั้นหลายชุมชนจึงทำให้การ build อัตโนมัติด้วย เชลล์สคริปต์ ที่ทำงานได้ทุกที่กลายเป็นมาตรฐาน แต่การเขียนเชลล์สคริปต์นั้นเจ็บปวด และผู้คนก็เริ่มใช้เครื่องมืออย่างตัวพรีโปรเซสเซอร์มาโคร M4 เพื่อสร้างเชลล์สคริปต์ขึ้นมา
      ผลก็คือหลายโปรเจกต์มีก้อนเชลล์สคริปต์ขนาดใหญ่และทึบแสง เผื่อกรณีที่ใครบางคนอยากรันโค้ดบน AIX หรือ Unix โบราณที่พัง ๆ
      หากจะกำจัดพงหนามเชลล์ที่เจาะยากนี้ ต้องลดจำนวนแพลตฟอร์มที่รองรับลงอย่างมาก ทำให้เครื่องมือ build ที่สะอาดกว่ากลายเป็นมาตรฐาน และสร้างโครงสร้างพื้นฐานหลักให้มากขึ้นด้วยภาษาที่ไม่ต้องพึ่งเชลล์สำหรับการ build แบบพกพา
      แต่นี่เป็นงานมหึมา และไลบรารี C หลักจำนวนไม่น้อยก็มีอาสาสมัครที่ไม่ได้รับค่าจ้างเพียงหนึ่งหรือสองคนดูแลอยู่ การเลิกซัพพอร์ต “Obscurnix-1997” ก็มักกลายเป็นการตัดสินใจที่ถกเถียงกันพอสมควร
      ดังนั้นโครงสร้างพื้นฐานหลักจำนวนมากจึงยังถูกล้อมรอบด้วยบึงเชลล์สคริปต์ที่สร้างโดยเครื่องและไม่รู้ที่มาที่ไป
    • เชลล์นั้นไม่ได้เขียนโดยมนุษย์ แต่เป็น โค้ดที่ถูกสร้างขึ้น เนื่องจาก autoconf ถูกใช้อย่างแพร่หลาย จึงมีโค้ดตั้งค่าเชลล์ที่สร้างขึ้นกองเป็นภูเขา และ autoconf ก็สร้างเชลล์สคริปต์แบบพกพาที่อ่านยากหลายพันบรรทัดจากสคริปต์ตัวพรีโปรเซสเซอร์มาโคร M4
      มีเครื่องมือจำนวนไม่น้อยที่ build ด้วยวิธีนี้
    • แค่ความจริงที่ว่า bash ดูอ่านยากเมื่อมองผ่าน ๆ นั้น ผมไม่ถือว่าเป็นสัญญาณผิดปกติ สคริปต์ bash ที่เขียนแน่น ๆ บางครั้งก็ดูแบบนั้นได้ ส่วนจำเป็นต้องเขียนให้แน่นขนาดนั้นไหมเป็นอีกประเด็นหนึ่ง
      สิ่งที่ดูน่าสงสัยคือการเรียก tr ซ้ำ ๆ เวลาเห็นแบบนั้นผมจะคิดว่ามีใครบางคนพยายามทำตัวฉลาด และในที่นี้ “ฉลาด” เป็นความหมายเชิงลบ ถ้าผมเป็นผู้ดูแล ผมคงขอให้เขาอธิบายว่าโค้ดแบบนั้นทำอะไรตอนที่มันถูกส่งเข้ามา เพราะแทบจะมีวิธีที่ดีกว่าในการหลีกเลี่ยงการ chain แบบนั้นอยู่เสมอ
      ปัญหาจริง ๆ คือไม่มีผู้ดูแลคนอื่นมาช่วยดูตอนที่โค้ดนี้เข้ามา องค์ประกอบสำคัญของสแตกต้องพึ่งพาคนคนเดียว และในกรณีนี้คนคนนั้นมีเจตนาร้าย
    • ไม่ใช่ “โดยไม่ได้ตั้งใจ” ประเด็นสำคัญคือมันถูก ทำให้สับสนโดยตั้งใจ
    • ผมอาจเข้าใจความเสี่ยงของ backdoor ใน XV ผิดไป แต่มีวิธีรัน bash โดยไม่ให้มัน exec อะไรบางอย่างได้ไหม? ผมคิดว่าถ้า bash มีอะไรอย่าง “โหมดปลอดภัย” ก็คงดี
      แต่พอมาคิดดู ผมนึกไม่ออกว่าจะรัน bash สำหรับสคริปต์ configure ของ xv ด้วย “โหมดปลอดภัย” ในจินตนาการแบบนั้นได้อย่างไร งั้นขอถอนคำพูด
  • https://github.com/tukaani-project/.github/issues/2

    • ค่อนข้างตลกดี เรื่องนี้ไม่ใช่แค่ backdoor โดยเจตนาที่เลวร้ายเท่านั้น แต่ยังเป็น การละเมิด GPL ด้วย เพราะ backdoor เป็นงานดัดแปลง แต่ไม่ได้รวมซอร์สไว้ และไม่ได้เผยแพร่ใน “รูปแบบที่เหมาะสมสำหรับการแก้ไข”
  • ระบบนิเวศ C ทั้งหมด รวมถึงเครื่องมือ build และยูทิลิตี Unix เก่า ๆ เป็น ความโกลาหลด้านความปลอดภัย ที่รอวันถูกฉวยใช้ และสุดท้ายก็จะถูกฉวยใช้
    แค่ดูว่าจุดหนึ่งจุดทำให้ทุกอย่างพังได้ง่ายแค่ไหนก็พอ ตอนนี้ผู้คนควรตระหนักได้แล้วว่าเราไม่สามารถฝากความปลอดภัยของโลกไว้กับ C ได้
    หวังว่าจะใช้ Ada หรือ Rust ที่มี toolchain สมัยใหม่

    • ถ้าเพิ่มจุดใน Rust แล้วจะไม่พังเหรอ?
  • ผมไม่เข้าใจเลยว่าสิ่งนี้ผ่าน code review แล้ว merge เข้ามาได้อย่างไร ถ้าผมไม่ได้พลาดอะไรไป มันก็ดูสะเพร่าอย่างเหลือเชื่อ

    • ผู้กระทำมุ่งร้ายเป็น ผู้ดูแลร่วม ของ repository และช่วงหนึ่งก็ active กว่าผู้ดูแลเดิมเสียอีก แถมมีสิทธิ์ commit เต็มรูปแบบ มันถูก commit ตรงเข้า master โดยไม่มี PR หรือ review
      ยิ่งไปกว่านั้น มันถูกซ่อนอย่างหนักอยู่ในไฟล์ไบนารีที่ระบุว่าเป็นไฟล์ทดสอบ คือไฟล์ทดสอบบีบอัด xz แบบ “good”/“bad” ถ้าไม่รู้ว่าต้องมองหาอะไร ก็ไม่มีทางสังเกตเห็น
    • ข้อความ commit ของไฟล์ทดสอบอ้างว่าสร้างด้วยตัวสร้างเลขสุ่ม คนที่สร้าง release tarball ใส่บรรทัดสุดท้ายไว้ในตำแหน่งที่เหมาะสม แต่ไม่ได้ check in เข้า repository
    • แพ็กเกจที่มีผู้ดูแล active เพียงคนเดียวไม่มี code review
  • การใช้ ดิสโทร LTS อาจช่วยป้องกันได้ในระดับหนึ่ง ดูเหมือนว่า Slackware จะใช้ lzma หรือก็คือ tar.xz ในแพ็กเกจ แต่ release stable ล่าสุดยกเว้น -current ไม่มีปัญหานี้
    ถ้าอยากก้าวไปทางซอฟต์แวร์เสรีอีกขั้น Hyperbola GNU ก็ไม่มีปัญหานี้เช่นกัน
    นอกจากนี้ Slackware -current ก็ไม่ได้ลิงก์ sshd กับ xz และไม่ได้ใช้ systemd ด้วย