2 คะแนน โดย GN⁺ 2023-10-12 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • CVE-2023-38545 ที่ถูกเปิดเผยพร้อมกับการออก curl 8.4.0 คือช่องโหว่ heap buffer overflow ที่เกิดขึ้นระหว่างการจัดการพร็อกซี SOCKS5 และเป็นช่องโหว่ระดับความรุนแรง HIGH ที่พบได้ไม่บ่อยในประเด็นความปลอดภัยของ curl
  • ปัญหานี้ถูกใส่เข้ามาระหว่างการเปลี่ยนโค้ดการเชื่อมต่อ SOCKS5 ในปี 2020 จากการเรียกแบบบล็อกกิงเป็น non-blocking state machine และได้รับผลกระทบตั้งแต่ curl 7.69.0
  • ตรรกะเดิมที่เปลี่ยนการแปลชื่อแบบรีโมตเป็นการแปลชื่อแบบโลคัลเมื่อพบชื่อโฮสต์ที่ยาวเกิน 255 ไบต์ ในโหมดแปลชื่อระยะไกล ได้ไปทำงานร่วมกับการเรียก state machine ซ้ำ จนทำให้สามารถคัดลอก ชื่อโฮสต์ที่ยาวเกินไป ลงในบัฟเฟอร์ขนาดเล็กได้
  • เพื่อให้การโจมตีเกิดขึ้นได้ ไคลเอนต์ libcurl ต้องใช้ SOCKS5 proxy-resolver-mode และการติดตามรีไดเร็กต์อัตโนมัติ ขณะที่เซิร์ฟเวอร์ HTTPS ที่ผู้โจมตีควบคุมต้องส่ง HTTP 30x Location: ที่มีชื่อโฮสต์ยาวเกิน 16KB แต่ไม่เกิน 64KB
  • ตั้งแต่ curl 8.4.0 เป็นต้นไป หากพบชื่อโฮสต์ที่ยาวเกินไปจะไม่เปลี่ยนจากการแปลชื่อแบบรีโมตเป็นแบบโลคัลอีก แต่จะคืนข้อผิดพลาดแทน และมีการเพิ่มการทดสอบเพื่อป้องกันสถานการณ์เดียวกันนี้

CVE-2023-38545 และ curl 8.4.0

  • มีการเผยแพร่คำแนะนำด้านความปลอดภัยและรายละเอียดของ CVE-2023-38545 พร้อมกับการออก curl 8.4.0
  • ปัญหานี้ถูกประเมินว่าเป็นปัญหาความปลอดภัยที่ร้ายแรงที่สุดของ curl ในรอบนาน และถูกจัดระดับความรุนแรงเป็น HIGH
  • ข้อบกพร่องหลักคือ heap buffer overflow ที่เกิดขึ้นภายใต้เงื่อนไขเฉพาะระหว่างการจัดการการเชื่อมต่อพร็อกซี SOCKS5

SOCKS5 และวิธีการแปลชื่อ

  • curl รองรับ SOCKS5 มาตั้งแต่เดือนสิงหาคม 2002
  • SOCKS5 เป็นโปรโตคอลพร็อกซีที่ใช้ตั้งค่าการสื่อสารเครือข่ายผ่านเซิร์ฟเวอร์ตัวกลาง
    • สามารถใช้ตั้งค่าการสื่อสารผ่าน Tor ได้
    • ยังสามารถใช้เมื่อเข้าถึงอินเทอร์เน็ตจากภายในองค์กรหรือบริษัทได้ด้วย
  • SOCKS5 มีวิธีแปลชื่อโฮสต์อยู่ 2 แบบ
    • ไคลเอนต์ทำการ แปลชื่อในเครื่อง ก่อน แล้วส่งที่อยู่ที่แปลแล้วให้พร็อกซี
    • ไคลเอนต์ส่งชื่อโฮสต์ทั้งหมดให้พร็อกซี และให้พร็อกซี แปลชื่อจากระยะไกล

การเปลี่ยนแปลงในปี 2020 ที่นำช่องโหว่เข้ามา

  • ต้นปี 2020 ฟังก์ชันที่ใช้เชื่อมต่อกับพร็อกซี SOCKS5 ถูกเปลี่ยนจากการเรียกแบบบล็อกกิงเป็น non-blocking state machine
  • การเปลี่ยนแปลงนี้ถูกใส่เข้า master เมื่อวันที่ 14 กุมภาพันธ์ 2020 และรวมอยู่ใน curl 7.69.0
  • curl 7.69.0 จึงเป็นทั้งรุ่นแรกที่มีการปรับปรุงนี้ และกลายเป็นรุ่นแรกที่มีช่องโหว่ CVE-2023-38545 ด้วย
  • เป้าหมายของการเปลี่ยนแปลงคือเพื่อให้เห็นประสิทธิภาพที่ดีขึ้นชัดเจนขึ้นเมื่อมีการส่งข้อมูลแบบขนานจำนวนมากที่ต้องผ่าน SOCKS5 ทั้งหมด

โหมดการแปลชื่อที่พังใน state machine

  • ฟังก์ชัน state machine จะถูกเรียกซ้ำทุกครั้งที่มีข้อมูลเครือข่ายเข้ามาเพิ่ม จนกว่าการเชื่อมต่อจะถูกสร้างเสร็จ
  • ตัวแปรภายใน socks5_resolve_local ที่ส่วนต้นของฟังก์ชันบ่งบอกว่า curl จะเป็นผู้แปลชื่อโฮสต์เอง หรือจะส่งชื่อให้พร็อกซี
  • ตัวแปรนี้จะถูกตั้งค่าใหม่ทุกครั้งที่ state machine ทำงาน โดยอิงจากโหมดพร็อกซีตั้งแต่ต้นการเรียกฟังก์ชัน
  • เงื่อนไขในสถานะ INIT เป็นต้นตอของปัญหา
    • ฟิลด์ชื่อโฮสต์ของ SOCKS5 อนุญาตได้สูงสุดเพียง 255 ไบต์
    • หากชื่อโฮสต์ยาวเกิน 255 ไบต์ พร็อกซี SOCKS5 จะไม่สามารถแปลชื่อนั้นได้
    • โค้ดเดิมของ curl จะเปลี่ยน socks5_resolve_local เป็น TRUE เพื่อสลับไปใช้ โหมดแปลชื่อในเครื่อง เมื่อพบชื่อโฮสต์ที่ยาวเกินไปในโหมดแปลชื่อจากระยะไกล
  • หากผู้ใช้ร้องขอการแปลชื่อจากระยะไกล curl ควรล้มเหลวแทนที่จะเปลี่ยนโหมด แต่พฤติกรรมการสลับนี้ที่ถูกเพิ่มไว้นานแล้วก็ยังคงอยู่

ลำดับที่ทำให้เกิด heap overflow

  • หากเซิร์ฟเวอร์ SOCKS5 ไม่เร็วพอจน state machine ไม่ได้รับข้อมูลเครือข่ายเพิ่มเติมให้ทำงานต่อ ฟังก์ชันจะคืนค่ากลับ
  • หลังจากนั้นเมื่อมีข้อมูลเข้ามาอีก ฟังก์ชัน state machine เดิมจะถูกเรียกซ้ำ
  • เมื่อถูกเรียกซ้ำ socks5_resolve_local จะถูกตั้งค่าใหม่ที่ส่วนต้นของฟังก์ชันตามโหมดพร็อกซีอีกครั้ง
    • ค่าที่เคยถูกเปลี่ยนเป็น TRUE ในการเรียกก่อนหน้าเพราะชื่อโฮสต์ยาวเกินไปจะไม่ถูกเก็บไว้
    • ค่าจึงกลับไปเป็นสถานะที่ พร็อกซีต้องแปลชื่อจากระยะไกล อีกครั้ง
  • curl จะสร้างเฟรมโปรโตคอลที่จะส่งให้พร็อกซีไว้ในบัฟเฟอร์หน่วยความจำ แล้วคัดลอกข้อมูลปลายทางลงไป
  • เพราะค่าสถานะผิดพลาดนี้ เมื่อพยายามส่งชื่อโฮสต์ที่ยาวเกินไปตามเดิม ก็อาจเขียนทับเลยบัฟเฟอร์ปลายทางที่จัดสรรไว้ไปยัง หน่วยความจำฮีป ที่อยู่ติดกันได้

ขนาดบัฟเฟอร์และข้อจำกัดของชื่อโฮสต์

  • curl นำบัฟเฟอร์ดาวน์โหลดทั่วไปกลับมาใช้ซ้ำเมื่อสร้างเฟรมโปรโตคอลที่จะส่งให้พร็อกซี
  • เงื่อนไขของขนาดบัฟเฟอร์ดาวน์โหลดมีดังนี้
    • ค่าเริ่มต้นคือ 16KB
    • เครื่องมือ curl ตั้งขนาดบัฟเฟอร์ไว้ที่ 100KB
    • แอปพลิเคชันสามารถใช้ขนาดอื่นได้ตามที่ร้องขอ
    • ขนาดต่ำสุดที่อนุญาตคือ 1024 ไบต์
  • หากขนาดบัฟเฟอร์ เล็กกว่า 65541 ไบต์ ก็มีโอกาสเกิด overflow นี้ได้
  • ยิ่งบัฟเฟอร์เล็ก ขนาดของ overflow ที่เป็นไปได้ก็ยิ่งมาก
  • แม้ฟิลด์ชื่อโฮสต์ของ URL จะไม่มีข้อจำกัดด้านขนาดในทางปฏิบัติ แต่ตัวแยกวิเคราะห์ URL ของ libcurl จะปฏิเสธชื่อที่ยาวเกิน 65535 ไบต์
  • DNS อนุญาตชื่อโฮสต์ได้สูงสุดเพียง 253 ไบต์
  • ชื่อที่ถูกต้องตามกฎหมายซึ่งยาวเกิน 253 ไบต์นั้นพบได้น้อย และชื่อจริงที่ยาวเกิน 1024 ไบต์แทบไม่พบเลย ดังนั้นการโจมตีจึงต้องใช้ชื่อโฮสต์ที่ยาวมากแบบจงใจ
  • ในฟิลด์ชื่อโฮสต์ของ URL อนุญาตได้เพียงบาง octet เท่านั้น และค่าของไบต์ที่ไม่ถูกต้องจะถูกตัวแยกวิเคราะห์ URL ปฏิเสธ
  • หาก libcurl ถูกคอมไพล์ให้ใช้ไลบรารี IDN ไลบรารีนั้นก็อาจปฏิเสธชื่อโฮสต์ที่ไม่ถูกต้องได้เช่นกัน

เงื่อนไขที่ทำให้การโจมตีเกิดขึ้นได้

  • ผู้โจมตีต้องควบคุมเซิร์ฟเวอร์ HTTPS ที่ไคลเอนต์ซึ่งใช้ libcurl เข้าถึงผ่านพร็อกซี SOCKS5 ใน proxy-resolver-mode
  • เซิร์ฟเวอร์ของผู้โจมตีต้องสามารถส่งรีไดเร็กต์ที่ถูกปรับแต่งในรูปแบบการตอบกลับ HTTP 30x ได้
  • เฮดเดอร์ Location: ของรีไดเร็กต์สามารถมีชื่อโฮสต์ที่ยาวมากในลักษณะดังนี้
  • หากฝั่งไคลเอนต์ libcurl เปิดการติดตามรีไดเร็กต์อัตโนมัติไว้ และพร็อกซี SOCKS5 “ช้าพอ” ที่จะทำให้เกิดปัญหาของตัวแปรภายในนี้ ชื่อโฮสต์ที่ถูกปรับแต่งก็จะถูกคัดลอกลงในบัฟเฟอร์ที่เล็กเกินไป
  • เมื่อเงื่อนไขเหล่านี้ตรงกัน ก็จะเกิดการเขียนไปยังหน่วยความจำฮีปที่อยู่ติดกัน และเกิด heap buffer overflow ขึ้น

การแก้ไขและบั๊กบาวน์ตี

  • ตั้งแต่ curl 8.4.0 เป็นต้นไป หากชื่อโฮสต์ยาวเกินไป จะไม่เปลี่ยนโหมดจากการแปลชื่อจากระยะไกลเป็นการแปลชื่อในเครื่อง แต่จะคืนข้อผิดพลาดแทน
  • มีการเพิ่มกรณีทดสอบเฉพาะสำหรับสถานการณ์เดียวกันนี้ด้วย
  • ปัญหานี้ถูกรายงาน วิเคราะห์ และแพตช์โดย Jay Satiro
  • ช่องโหว่นี้ได้รับบั๊กบาวน์ตีมูลค่าสูงที่สุดเท่าที่เคยจ่ายโดย curl จนถึงตอนนี้
    • ผู้รายงานได้รับ 4,660 ดอลลาร์
    • โครงการ curl ได้รับ 1,165 ดอลลาร์ ตาม นโยบาย IBB

C และภาษาแบบ memory-safe

  • หาก curl ไม่ได้เขียนด้วย C แต่เขียนด้วย ภาษาแบบ memory-safe ข้อบกพร่องประเภทนี้คงไม่เกิดขึ้น
  • อย่างไรก็ตาม การพอร์ต curl ไปเป็นภาษาอื่นไม่ได้อยู่ในวาระตอนนี้
  • แนวทางที่เป็นจริงจึงเหลืออยู่ 2 แบบ
    • ยอมรับ ใช้งาน และสนับสนุน dependency ที่เขียนด้วยภาษาแบบ memory-safe มากขึ้น
    • ค่อย ๆ แทนที่บางส่วนของ curl อย่างการนำ hyper มาใช้
  • การพัฒนาในลักษณะนี้ยังดำเนินไปอย่างช้ามากในปัจจุบัน และอุปสรรคที่เกี่ยวข้องก็ปรากฏชัดเจน
  • ในอนาคตอันใกล้ curl จะยังคง เขียนด้วย C ต่อไป
  • เมื่อนับรวม CVE ล่าสุด 2 รายการที่รายงานใน curl 8.4.0 จนถึงตอนนี้มีช่องโหว่ความปลอดภัยที่พบใน curl ราว 41% ที่อาจไม่เกิดขึ้นหากใช้ภาษาแบบ memory-safe
  • อย่างไรก็ตาม ในช่วงเวลาที่ปัญหาที่เกี่ยวกับ C ราว 80% ในช่วงแรกถูกนำเข้ามา Rust ยังไม่ใช่ตัวเลือกที่ใช้งานได้จริงสำหรับจุดประสงค์นี้

ข้อบกพร่องที่ถูกค้นพบหลังผ่านไป 1315 วัน

  • ข้อบกพร่องนี้ค้างอยู่ในโค้ดนาน 1315 วัน
  • หากมีชุดการทดสอบที่ดีกว่านี้ก็อาจถูกค้นพบได้
  • curl รัน static code analyzer หลายตัวซ้ำ ๆ แต่ไม่มีตัวใดตรวจพบปัญหาในฟังก์ชันนี้
  • การปล่อยโค้ดที่มี heap overflow ออกไปยังสภาพแวดล้อมการติดตั้งมากกว่า 20,000 ล้านแห่ง ไม่ใช่ประสบการณ์ที่แนะนำให้ใครทำตาม
  • สามารถดูขั้นตอนการรายงานและการจัดการก่อนเปิดเผยสู่สาธารณะได้ใน รายงานบน HackerOne

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

 
GN⁺ 2023-10-12
ความคิดเห็นจาก Hacker News
  • ยังน่าทึ่งอยู่เลยที่อุปกรณ์จำนวนมากขนาดนี้พึ่งพา ไลบรารีที่แทบทั้งหมดเขียนโดยคนเพียงคนเดียว ภาระคงหนักมาก และประโยคด้านล่างก็สะท้อนเรื่องนั้นได้ดี
    “พออ่านโค้ดตอนนี้แล้ว มันเป็นไปไม่ได้เลยที่จะมองไม่เห็นบั๊ก ผมรู้สึกเจ็บปวดจริง ๆ ที่ต้องยอมรับว่าผมไม่ทันสังเกตความผิดพลาดนี้ และข้อบกพร่องนั้นไม่ถูกพบในโค้ดอยู่นานถึง 1,315 วัน ขอโทษครับ ผมก็เป็นเพียงมนุษย์คนหนึ่งเท่านั้น”
    ถ้า Daniel ได้เห็นสิ่งนี้ ก็อยากบอกว่าขอบคุณที่ทำงานหนัก และไม่จำเป็นต้องขอโทษเลย สุดท้ายแล้วซอร์สก็เปิดให้ทุกคนอ่านและตรวจสอบได้อยู่แล้ว

    • ทำให้นึกถึงสมัยก่อนที่ OpenSSL ก็เคยมีคนชื่อ Steve สองคนเป็นผู้พัฒนาและดูแลอยู่พักหนึ่ง ตอนนี้ดูเหมือนอย่างน้อยก็เพิ่มเป็นทีมราว 7 คนแล้ว
      https://www.buzzfeed.com/chrisstokelwalker/the-internet-is-b...
    • ถูกต้องมาก เห็นได้ชัดจากประโยคนี้ในรายงาน HackerOne เช่นกัน
      “รายงานนี้ดูถูกต้องทุกประการ และมันเจ็บปวดลึก ๆ ในใจ”
      รวมถึงประโยคในบล็อกที่ว่า “ผมไม่แนะนำประสบการณ์การปล่อย heap overflow ในโค้ดที่ถูกติดตั้งไปแล้วมากกว่า 2 พันล้านสภาพแวดล้อม” การต้องแบกรับความรับผิดชอบใหญ่ขนาดนี้โดยได้รับผลตอบแทนน้อยขนาดนั้นช่างโหดร้าย
    • รู้สึกว่าประโยคแรกของข้อความที่ยกมานั้นสรุปบางส่วนของวิธีที่ การเรียนรู้และความใส่ใจ ของมนุษย์ทำงานได้ดี
      โดยส่วนตัวแล้ว ผมมักคิดอยู่บ่อย ๆ ว่าต่อให้พยายามจดจ่อแค่ไหน เราก็พลาดสิ่งต่าง ๆ ไปมากเพียงใด กิจกรรมอย่าง code review ดูเป็นวิธีที่ดีสำหรับเรื่องนี้ คือการฝึกความใส่ใจ หรือการพัฒนาการรับรู้ภาพรวมและบริบท ตัวอย่างเช่น มีแปลงดอกไม้ที่มีคนดูแลอย่างดีอยู่ในที่ที่ผมเดินผ่านบ่อย ๆ มาหลายปี หลังจากสังเกตเห็นครั้งแรก ผมก็หยุดดูอยู่ราวหนึ่งปี แต่เมื่อไม่กี่เดือนก่อน ผมเพิ่งสังเกตเป็นครั้งแรกว่ามีแปลงดอกไม้อีกแปลงหนึ่งที่อยู่คู่กัน ห่างออกไปแค่ไม่กี่ฟุต มีความเป็นไปได้สูงว่าแปลงนั้นอยู่มาตั้งแต่แรกแล้ว แต่เพราะผมไม่รู้ด้วยซ้ำว่ามันมีอยู่ จึงไม่อาจรู้ได้แน่ชัด
      ถ้าขยายความคิดนี้ไปสู่ความรู้ของมนุษยชาติทั้งหมด บางครั้งดูเหมือนว่ามีเพียงคนเดียวที่สังเกตเห็นบางอย่าง แล้วการสังเกตนั้นเปลี่ยนพฤติกรรม และเริ่มแพร่ไปถึงพวกเราทุกคน บางครั้งก็ต้องใช้ความพยายามหลายครั้งและเวลายาวนาน ผมยังสงสัยด้วยว่ายังมี ผลไม้ที่เอื้อมถึงได้ง่าย อีกมากแค่ไหนที่ยังไม่มีใครสังเกตเห็น และเชื่อว่าแค่ผนวกรวมแนวปฏิบัติที่ดีที่เรียบง่ายและเป็นพื้นฐาน ซึ่งคนที่ใส่ใจอยู่แล้วได้สร้างไว้ ก็สามารถยกระดับมาตรฐานขั้นต่ำของทุกคนได้
      เรื่องของ Julia Evans ที่ https://jvns.ca/blog/2023/10/06/new-talk--making-hard-things... และของ Dan Luu ที่ https://danluu.com/p95-skill/ ก็สอดคล้องกับประเด็นนี้ สรุปแล้วรู้สึกขอบคุณ Stenberg และ curl รวมถึงผู้คนที่สร้างส่วนต่าง ๆ มากมายของโครงสร้างพื้นฐานอินเทอร์เน็ตที่เราใช้กันทุกวันราวกับเป็นเรื่องปกติ
    • https://xkcd.com/2347/ เป็นความจริงมาหลายปีก่อนถูกโพสต์ และยังคงเป็นความจริงต่อไปอีกหลายปีหลังจากถูกโพสต์
      บางครั้งหลักการ “ถ้ายังไม่พังก็อย่าไปซ่อม” ถูกตีความเป็น “อย่าสนับสนุนจนกว่ามันจะพัง” ซึ่งนำไปสู่เรื่องน่าประหลาดใจที่ไม่น่ายินดีนัก
    • โปรเจกต์ที่มีนักพัฒนาเพียงคนเดียว มักให้ประสบการณ์ผู้ร่วมพัฒนาที่ยอดเยี่ยมเสมอ ขณะที่โปรเจกต์ของบริษัทหรือผู้ขายหลายครั้งค่อนข้างแย่ เมื่อดูความพยายามที่จะทำงานร่วมกันเพื่อปรับปรุงโปรเจกต์ ก็เห็นได้ชัดถึงความหลงใหล
      แม้จะมีปัญหาเรื่องความยั่งยืน แต่โปรเจกต์แบบนี้คงยังมีต่อไป และขอคารวะทุกคนที่ทำงานเพื่อชุมชน ไม่ใช่แค่เพื่อตัวเอง เช่นเดียวกับ Daniel
  • ข้อสรุปเกี่ยวกับความปลอดภัยของหน่วยความจำและ ภาษาแบบ memory-safe สมเหตุสมผลมาก ใจความคือ
    ถ้า curl เขียนด้วยภาษาแบบ memory-safe แทน C ข้อบกพร่องตระกูลนี้ก็คงเกิดขึ้นไม่ได้
    แนวทางที่ดูเป็นไปได้และสมเหตุสมผลในทิศทางนั้น คือการอนุญาต ใช้งาน และสนับสนุน dependency ที่เขียนด้วยภาษาแบบ memory-safe มากขึ้น และค่อย ๆ แทนที่บางส่วนของ curl ทีละชิ้น เหมือนกับการนำ hyper มาใช้
    อย่างไรก็ตาม การพัฒนาแบบนี้ตอนนี้ดำเนินไปช้าราวกับธารน้ำแข็ง และเผยให้เห็นปัญหายาก ๆ ที่เกี่ยวข้องอย่างเจ็บปวดและชัดเจน curl จะยังคงเป็น C ต่อไปในอนาคตที่คาดการณ์ได้ ใครไม่ชอบก็ลงมือพับแขนเสื้อทำเองได้
    หากรวม CVE ล่าสุดสองรายการที่รายงานใน curl 8.4.0 ด้วย จนถึงตอนนี้ 41% ของยอดสะสมช่องโหว่ด้านความปลอดภัยที่พบใน curl น่าจะไม่เกิดขึ้น หากใช้ภาษาแบบ memory-safe แต่ Rust ไม่ใช่ตัวเลือกที่ใช้งานได้จริงสำหรับงานนี้ในช่วงที่ปัญหาที่เกี่ยวกับ C ราว 80% แรกถูกนำเข้ามา
    มีการรัน static code analyzer หลายตัวซ้ำ ๆ แล้ว แต่ไม่พบปัญหาใด ๆ ในฟังก์ชันนี้

    • ดูเหมือนว่าสัปดาห์ที่แล้วจะมีการเพิ่ม fuzzer และการทดสอบ ใหม่ด้วย ถึงจุดนี้ ถ้าเป็นโปรเจกต์ C ที่ไม่ใช่งานด้านความปลอดภัยระดับวิกฤต ก็น่าจะเอา pipeline แบบนี้ไปทำตาม
    • การแทนที่บางส่วนของ curl ด้วย Rust คงเป็นไปไม่ได้ สภาพแวดล้อมส่วนใหญ่ต้องการ build จากซอร์ส และไม่อยากเพิ่มคอมไพเลอร์ตัวใหม่เข้าไปใน toolchain แถม Rust ยังรองรับแพลตฟอร์มเป้าหมายที่ C รองรับได้เพียงส่วนเล็กมากเท่านั้น
  • เป็นบทวิเคราะห์ที่ยอดเยี่ยม อย่างไรก็ตาม แม้อ่านถึง CVE แล้ว ก็ยังไม่แน่ชัดว่าได้รับผลกระทบในสถานการณ์ใดบ้าง เท่าที่เข้าใจ จะได้รับผลกระทบเมื่อเข้าเงื่อนไขด้านล่าง
    ใช้พร็อกซี SOCKS5, ให้พร็อกซีนั้น resolve ชื่อโฮสต์, ขนาดบัฟเฟอร์ถูกเปลี่ยนจากค่าเริ่มต้น 100KB เป็นน้อยกว่า 65541 ไบต์ และพร็อกซี SOCKS5 ช้าเกินกว่าจะประมวลผลคำขอได้ทันที
    แก้ไข: คำอธิบายเกี่ยวกับบัฟเฟอร์ไม่ถูกต้องนัก libcurl ใช้บัฟเฟอร์ดาวน์โหลดซ้ำ และค่าเริ่มต้นคือ 16KB แต่ตัว curl เองว่ากันว่าตั้งค่าเองเป็น 100KB เว้นแต่จะใช้ --limit-rate นอกจากนี้เงื่อนไขเรื่องความล่าช้าก็ผิดด้วย ตาม CVE แค่ความล่าช้าทั่วไปของเซิร์ฟเวอร์ก็มีแนวโน้ม “ช้า” พอที่จะทำให้เกิดบั๊กนี้ได้ และผู้โจมตีไม่จำเป็นต้องมีผลกระทบด้วยการทำ denial of service หรือควบคุมเซิร์ฟเวอร์ SOCKS
    ดังนั้นเส้นทางการโจมตีดูแคบมาก เลยสงสัยว่ามีอะไรที่พลาดไปหรือเปล่า

    • ผมลองทำความเข้าใจเงื่อนไขสุดท้ายคือ “พร็อกซี SOCKS5 ช้าเกินกว่าจะประมวลผลคำขอได้ทันที” แต่คำขอก็เกิด segmentation fault ทันทีโดยไม่มีการหน่วงหรือ redirect
      root@1aac5e228e16:/build/curl-7.74.0# curl -vvv -x socks5h://host.docker.internal:9050 $(python3 -c "print(('A'10000), end='')")
      Trying 192.168.65.254:9050...
      * SOCKS5: server resolving disabled for hostnames of length > 255 [actual len=10000]
      * SOCKS5 connect to AAAAA...
      * Send failure: Bad file descriptor
      * Failed to send SOCKS5 connect request.
      Segmentation fault
      https://gist.github.com/xen0bit/0dccb11605abbeb6021963e2b1a8...
    • ถ้ายึดตามเนื้อหาที่แก้ไขแล้ว ก็น่าจะถูก ต้องเกี่ยวข้องเป็นหลักกับซอฟต์แวร์ที่รันบนโครงสร้างพื้นฐานแบบใช้ร่วมกัน ส่งคำขอไปยัง URL ที่ผู้โจมตีให้มา และใช้ พร็อกซี SOCKS5 เช่นกรณีเว็บฮุก
    • ผมทำชุดเชลล์สคริปต์ชื่อ docker-proxy ซึ่งสร้าง อุโมงค์ SOCKS5 ผ่านคอนเทนเนอร์ Docker ที่รัน openconnect VPN มีประโยชน์เมื่อต้องจัดการ VPN หลายตัวของลูกค้าหลายราย โดยแต่ละ VPN ต้องการให้ทราฟฟิกทั้งหมดของเครื่องถูกส่งไปทางตัวเอง
      https://github.com/carlosonunez/docker-proxy
      เนื่องจากออกแบบให้การ resolve DNS เกิดขึ้นที่ฝั่งรีโมต CVE นี้จึงใช้กับกรณีนี้โดยตรง
    • นี่ดูเหมือน ส่วนผสมที่ชวนให้เกิดหายนะ เวลาเข้าเว็บดาร์กเน็ตอย่าง Tor
  • น่าจะเรียกได้ว่าเป็น บทวิเคราะห์ CVE ที่ดีที่สุดเท่าที่เคยอ่านมา อีกอย่าง แม้ผู้เขียนจะเป็นคนสร้างหนึ่งในซอฟต์แวร์สำคัญของยุคนี้ แต่ตลอดทั้งบทความก็ยังแสดงท่าทีถ่อมตัว น่าชื่นชมจริง ๆ นับถือมาก

  • if(!socks5_resolve_local && hostname_len > 255) {
    socks5_resolve_local = TRUE;
    }
    นี่เป็นความคิดที่แย่มาก สำหรับคนที่ใช้เครื่องมือเลี่ยงการเซ็นเซอร์เพื่อปกป้องความเป็นส่วนตัว อาจทำให้ ตัวตนรั่วไหล ผ่าน DNS ได้

    • “ถ้าผู้ใช้ร้องขอให้ resolve ฝั่งรีโมต curl ก็ควรทำตามนั้นหรือไม่ก็ล้มเหลว การเปลี่ยนโหมดแบบนี้ผมคิดว่าผิดอย่างชัดเจน แม้ในสถานการณ์ปกติ การสลับไปแบบนั้นก็ไม่น่าจะทำงานได้ดีอยู่แล้ว”
      ผู้เขียนก็คิดเช่นเดียวกัน
  • https://hackerone.com/reports/2187833

  • มีการบอกว่า “ถ้า curl เขียนด้วยภาษาที่ปลอดภัยด้านหน่วยความจำแทน C ข้อบกพร่องตระกูลนี้คงเป็นไปไม่ได้” แต่คงพูดได้แค่ว่าอาจเป็นไปไม่ได้ เราไม่มีทางรู้เสมอไปว่ามีวิธีหลุดออกจากกำแพงของ virtual machine ของภาษาหรือไม่ อย่างไรก็ดี ผู้เขียนเพิกเฉยต่อ ข้อจำกัดชื่อโฮสต์ DNS ที่ระบุไว้ใน RFC1123 อย่างชัดเจน และข้อจำกัดนี้ก็ถูก hardcode ไว้ในไลบรารี Java ด้วย
    https://www.rfc-editor.org/rfc/rfc1123

    • มีพูดถึงในส่วน “ความยาวของชื่อโฮสต์”
      “ชื่อโฮสต์ใน URL ไม่มีข้อจำกัดขนาดจริง ๆ แต่ตัว parser URL ของ libcurl จะปฏิเสธชื่อที่ยาวกว่า 65535 ไบต์ DNS อนุญาตชื่อโฮสต์ได้สูงสุด 253 ไบต์เท่านั้น ดังนั้นชื่อที่ถูกต้องตามกฎหมายซึ่งยาวกว่า 253 ไบต์จึงพบได้น้อย ชื่อจริงที่ยาวกว่า 1024 ไบต์แทบไม่เคยได้ยินมาก่อน”
      ทุกวันนี้แม้ DNS จะถูกใช้ใน 99.9% ของกรณี แต่ก็ไม่ใช่ กลไกเดียวสำหรับ resolve ชื่อโฮสต์เป็นที่อยู่
    • อยากทราบว่าพอจะอธิบายให้เจาะจงกว่านี้ได้ไหม ที่เห็นมีแค่ “ซอฟต์แวร์โฮสต์ต้องสามารถจัดการชื่อโฮสต์ได้ถึง 63 อักขระ และแนะนำให้จัดการชื่อโฮสต์ได้ถึง 255 อักขระ”
      หาไม่เจอว่า RFC กำหนดเพดานสูงสุดของขนาดชื่อโฮสต์ไว้ตรงไหน
    • ภาษาที่ปลอดภัยด้านหน่วยความจำ ไม่ได้หมายถึง virtual machine เสมอไป
      อาจหมายถึงคอมไพเลอร์พยายามพิสูจน์ว่าหน่วยความจำจะไม่ถูกเข้าถึงนอกขอบเขต, จะไม่ถูกเข้าถึงโดยไม่มี ownership ที่ชัดเจน และจะถูกเข้าถึงเฉพาะภายในอายุของอ็อบเจ็กต์พื้นฐานเท่านั้น ตัวอย่างเช่น Rust ทำแบบนั้น
      แน่นอนว่าคอมไพเลอร์อาจเพิ่มการตรวจสอบความล้มเหลวภายในและกลไกป้องกันในจุดสำคัญ ๆ ได้ Rust ไม่ได้ทำแบบนั้น แต่ในระบบที่ต้องกังวลเรื่อง bit flip ภายในรีจิสเตอร์ ก็น่าจะมีไว้จะดีกว่า เช่นสภาพแวดล้อมรังสีสูงอย่างเครื่องสแกน X-ray ซึ่ง ECC memory ตรวจจับไม่ได้
    • ตามถ้อยคำของ RFC การ ยอมรับชื่อโฮสต์ ที่ยาวกว่า 255 ไบต์นั้นอนุญาต แต่การสร้างชื่อแบบนั้นไม่ได้รับอนุญาต
  • เมื่อเทียบกับเรื่องที่ต้องมีเงื่อนไขเฉพาะเจาะจงมากจึงจะใช้โจมตีได้ ก็มี การพูดเกินจริงและดราม่า ค่อนข้างมาก

    • curl เคยเจอปัญหาเกี่ยวกับ CVE หลายครั้ง เลยดูเหมือนว่าต้องการแสดงให้เห็นว่าประเด็นความปลอดภัยจริง ๆ นั้นถูก扱อย่างจริงจังมาก ตัวอย่างเช่น ดู https://daniel.haxx.se/blog/2023/08/26/cve-2020-19909-is-eve...
    • และสิ่งนี้ถูกติดตั้งอยู่บน อุปกรณ์หลายหมื่นล้านเครื่อง เมื่อถูกกระจายในระดับนั้น ผมคิดว่าเงื่อนไขเฉพาะเจาะจงมาก ๆ ก็อาจเกิดขึ้นตามที่ต่าง ๆ ได้อย่างรวดเร็ว
  • อัปเดตความปลอดภัยของ Windows 10 ที่ออกมาเมื่อวานไม่ได้รวม curl เวอร์ชันใหม่มาด้วย curl ที่อยู่ใน Windows ยังเป็น 8.0.1

  • เป็นบทความที่ยาวและน่าสนใจ แต่สรุปได้ว่า “ดังนั้นไลบรารีระบบควรถูกเขียนด้วย ภาษาที่ปลอดภัย หรือได้รับการพิสูจน์ความถูกต้อง”
    ถ้าหนึ่งในโปรแกรมเมอร์ C ที่ยอดเยี่ยมที่สุด ใจดีที่สุด และโปร่งใสที่สุดในยุคของเรา กำลังเขียนบทความแบบนี้ เราก็ควรใส่ใจ

    • ถ้าโปรแกรมเมอร์คนนั้นเขียนว่า “การพัฒนาแบบนี้ในปัจจุบันดำเนินไปช้าแทบเหมือนธารน้ำแข็ง และทำให้เห็นปัญหายากที่เกี่ยวข้องอย่างชัดเจนจนเจ็บปวด curl จะยังคงเป็น C ต่อไปในอนาคตที่พอคาดการณ์ได้” ก็สงสัยว่าเราควรสรุปอย่างไรกันแน่