Xzbot: บันทึก, ฮันนีพอต และเดโมเอ็กซ์พลอยต์สำหรับแบ็กดอร์ xz
(github.com/amlweems)- xzbot เป็นรีโพซิทอรีสำหรับสำรวจแบ็กดอร์ xz (CVE-2024-3094) โดยมีทั้งฮันนีพอต, แพตช์กุญแจสาธารณะ ED448, รูปแบบเพย์โหลดของแบ็กดอร์ และเดโมการรันโค้ดจากระยะไกลรวมอยู่ด้วย
- ฮันนีพอตใช้การแพตช์แบบง่ายกับ OpenSSH เพื่อบันทึก ความพยายามเชื่อมต่อ ที่เข้ามาด้วยค่า N ของกุญแจสาธารณะที่ตรงกับรูปแบบของแบ็กดอร์ลงในล็อก
sshd - แพตช์ ED448 เปลี่ยน กุญแจสาธารณะที่ฮาร์ดโค้ดไว้ ซึ่งแบ็กดอร์ใช้ตรวจสอบลายเซ็นและถอดรหัสเพย์โหลด ให้เป็นกุญแจที่ผู้ใช้สร้างเองเพื่อให้สามารถทริกเกอร์แบ็กดอร์ได้
- เพย์โหลดของแบ็กดอร์จะอยู่ในค่า N ของกุญแจลงนาม CA ของใบรับรอง SSH และสร้างประเภทคำขอด้วย
a * b + cโดย Type 2 จะรันคำสั่งที่ลงท้ายด้วย null ผ่านsystem() - เดโม CLI แสดงลำดับการเชื่อมต่อไปยังเซิร์ฟเวอร์ SSH ที่มีช่องโหว่เพื่อรันคำสั่ง และการโจมตีที่สำเร็จจะ ไม่สร้างล็อกระดับ INFO ขึ้นไป
ขอบเขตและองค์ประกอบของ xzbot
- xzbot เป็นโปรเจ็กต์สำหรับสำรวจ CVE-2024-3094 ซึ่งเป็น แบ็กดอร์ ของ xz
- รายการที่รวมอยู่มีสี่ส่วน
- honeypot: เซิร์ฟเวอร์ปลอมที่มีช่องโหว่สำหรับตรวจจับความพยายามเอ็กซ์พลอยต์
- ed448 patch: แพตช์
liblzma.soให้ใช้กุญแจสาธารณะ ED448 ของตนเอง - backdoor format: สรุปรูปแบบเพย์โหลดของแบ็กดอร์
- backdoor demo: CLI สำหรับทริกเกอร์ RCE โดยสมมติว่าทราบกุญแจส่วนตัว ED448
ฮันนีพอต: ตรวจจับความพยายามด้วยการแพตช์ OpenSSH
- openssh.patch เป็นแพตช์แบบง่ายที่ใช้กับ OpenSSH เพื่อบันทึกความพยายามเชื่อมต่อที่เข้ามาด้วยค่า N ของกุญแจสาธารณะที่ตรงกับรูปแบบของแบ็กดอร์
- ขั้นตอนคือโคลน
openssh-portableจากนั้นใช้แพตช์ แล้วรันautoreconf,configure,make - ความพยายามเชื่อมต่อจะปรากฏในล็อก
sshdเป็นxzbot: magic 1 [preauth]และชุดไบต์ของเพย์โหลด
แพตช์กุญแจสาธารณะ ED448
- แบ็กดอร์ใช้ กุญแจสาธารณะ ED448 ที่ฮาร์ดโค้ดไว้ สำหรับการตรวจสอบลายเซ็นและการถอดรหัสเพย์โหลด
- หากแทนที่กุญแจสาธารณะนี้ด้วยกุญแจที่ผู้ใช้มีอยู่ ก็จะสามารถทริกเกอร์แบ็กดอร์ได้
- รีโพซิทอรีแสดงทั้งกุญแจสาธารณะ ED448 ของผู้โจมตีและค่ากุญแจทดแทนที่สร้างด้วย
seed=0 - ขั้นตอนเริ่มต้นคือเตรียม shared object
liblzma.so.5.6.1ที่มีแบ็กดอร์รวมอยู่- มีการยกตัวอย่างแหล่งที่มาจาก Debian snapshot ของ
xz-utils 5.6.1-1 - หลังติดตั้ง
pwntoolsแล้ว ให้รันpatch.py liblzma.so.5.6.1เพื่อสร้างliblzma.so.5.6.1.patch - จากนั้นรัน
sshdโดยใช้ shared objectliblzma.so.5.6.1.patchที่แก้ไขแล้ว
- มีการยกตัวอย่างแหล่งที่มาจาก Debian snapshot ของ
- มีตัวอย่างอยู่ใน assets
รูปแบบเพย์โหลดของแบ็กดอร์
- แบ็กดอร์สามารถถูกทริกเกอร์ได้เมื่อเชื่อมต่อด้วยใบรับรอง SSH โดยใส่เพย์โหลดไว้ในค่า N ของกุญแจลงนาม CA ของใบรับรอง
- เพย์โหลดนี้ต้องถูก เข้ารหัสและลงลายเซ็น ด้วยกุญแจ ED448 ของผู้โจมตี
- โครงสร้างชั้นนอกประกอบด้วยฟิลด์ต่อไปนี้
a: 32 บิตb: 32 บิตc: 64 บิตciphertext: 240 ไบต์
- ประเภทคำขอถูกอนุมานจาก
a * b + c- ถ้าค่ามากกว่า 3 แบ็กดอร์จะข้ามการประมวลผล
- Type 1: ยังไม่ทราบการใช้งาน และคาดหวัง 0 ไบต์
- Type 2: รันเพย์โหลดที่ลงท้ายด้วย null ผ่าน
system() - Type 3: ยังไม่ทราบการใช้งาน และคาดหวังข้อมูลลายเซ็น 48 ไบต์
ciphertextถูกเข้ารหัสด้วย chacha20 โดยใช้ 32 ไบต์แรกของกุญแจสาธารณะ ED448 เป็น symmetric key- ด้วยเหตุนี้ ความพยายามเอ็กซ์พลอยต์จึงสามารถถอดรหัสได้ด้วยกุญแจ 32 ไบต์ที่ระบุไว้
- ภายใน
ciphertextที่ถอดรหัสแล้วจะมีลายเซ็น 114 ไบต์, ฟิลด์ 1 บิตxและy, ความยาว และสตริงคำสั่ง เป็นต้น- หากตั้งค่า
xหรือyจะไปยังเส้นทางโค้ดที่ต่างออกไปเล็กน้อย
- หากตั้งค่า
- ลายเซ็นเป็นลายเซ็น RFC-8032 ED448
- อินพุตจะรวมค่า magic 32 บิต, ฟิลด์ 5 ไบต์ก่อนคำสั่ง,
lengthไบต์ของคำสั่งแบบเลือกได้ และ 32 ไบต์แรกของแฮช SHA-256 ของ hostkey ฝั่งเซิร์ฟเวอร์
- อินพุตจะรวมค่า magic 32 บิต, ฟิลด์ 5 ไบต์ก่อนคำสั่ง,
เดโม CLI สำหรับเอ็กซ์พลอยต์
- ติดตั้ง CLI ด้วย Go
go install github.com/amlweems/xzbot@latest
xzbot -hแสดงตัวเลือกหลักสามรายการ-addr: ที่อยู่เซิร์ฟเวอร์ SSH, ค่าเริ่มต้น127.0.0.1:2222-seed: seed ของ ED448 ที่ต้องตรงกับกุญแจของแบ็กดอร์ xz, ค่าเริ่มต้น0-cmd: คำสั่งที่จะรันผ่านsystem(), ค่าเริ่มต้นid > /tmp/.xz
- ตัวอย่างจะแสดงการเชื่อมต่อไปยังเซิร์ฟเวอร์ SSH ที่มีช่องโหว่บน
127.0.0.1:2222เพื่อรันคำสั่งid > /tmp/.xz - หากตั้ง watchpoint ตรงตำแหน่งที่เรียก
system()บนเซิร์ฟเวอร์ที่มีช่องโหว่ จะเห็นว่าโปรเซสsshdรันid > /tmp/.xz - หลังรันเสร็จ มีตัวอย่างว่าใน
/tmp/.xzจะมีผลลัพธ์uid=0(root) gid=0(root) groups=0(root)
โพรเซสทรีและลักษณะของล็อก
- โพรเซสทรีของการเชื่อมต่อ SSH ปกติจะต่อไปยัง user session และเชลล์ของ
sshd - ตัวอย่างการรันแบ็กดอร์หลัง
xzbot -cmd 'sleep 60'จะปรากฏเป็นsshd: root [priv],sshd: root [net],sh -c sleep 60,sleep 60 - การโจมตีที่สำเร็จจะ ไม่สร้างรายการล็อกระดับ INFO ขึ้นไป
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ประเด็นที่น่าสนใจมากคือมันไม่ได้เป็นการรันโค้ดจากระยะไกลที่ใครก็เอาไปใช้ได้ แต่ต้องมี private key ของผู้โจมตี
ฟังดูย้อนแย้งเหมือนเป็นช่องโหว่ที่ใส่ใจความปลอดภัยมากเสียด้วยซ้ำ
ถ้าเจาะรูไว้ใหญ่จนใครก็เข้าได้ ก็คงถูกพบและอุดอย่างรวดเร็ว และถ้าไม่มีอาการประสิทธิภาพตก ก็อาจอยู่เงียบ ๆ ได้นานเพราะไม่ได้ถูกมองว่าสามารถถูกใช้โจมตีในวงกว้าง
งานที่ทำกับ xz ทั้งหมดก็ดูเหมือนถูกทำขึ้นเพื่อใส่ backdoor นี้ในท้ายที่สุด และถึงขั้นสร้างเฟรมเวิร์กทดสอบที่ใช้ซ่อน payload อันตรายได้ด้วย
ก่อนทำงานกับ xz ยังเคยมีส่วนร่วมกับ libarchive ของ BSD และที่นั่นก็เกิดช่องโหว่ขึ้นเช่นกัน
ทั้งการออกแบบและการลงมือทำประณีตมาก และที่มันถูกพบเพราะปัญหาด้านประสิทธิภาพก็ดูแทบจะเป็นเรื่องบังเอิญล้วน ๆ
ถ้าใช่ก็แปลว่าไม่สามารถหว่านไปทั่วเซิร์ฟเวอร์จำนวนมากได้ และแม้แต่การส่งไปยังโฮสต์เดียวก็มีต้นทุนการคำนวณพอสมควร
เหตุการณ์นี้วนอยู่ในหัวตลอดทั้งสุดสัปดาห์
กลไกของมันน่าสนใจ เป็นชุดการทำให้อ่านยากที่ทำได้ยอดเยี่ยม และส่วนของ social engineering ก็เป็นเรื่องที่คุ้นเคยจนชวนให้ผู้ดูแลโอเพนซอร์สรู้สึกละอาย
สิ่งที่น่าสนใจที่สุดคือการเลือก test data ที่ไม่ดี มาเป็นช่องทางโจมตี เพราะถ้าเตรียม archive ที่ดูปกติไว้ดี ๆ แล้วดัดแปลงเชิงโครงสร้าง ก่อนนำไปใช้เป็นข้อมูลเสียสำหรับ fuzz testing ขั้นตอนที่เหลือจะง่ายขึ้นมาก
ถ้าจะพูดเผื่ออนาคต การดัดแปลงแบบนี้น่าจะปรากฏให้เห็นในกราฟแพตเทิร์นของไบนารี
เทคนิคส่วนที่เหลือ หลังจากกำหนด payload แล้ว ก็ใกล้เคียงกับการ obfuscation ทั่วไปเสียเป็นส่วนใหญ่ แต่หมากที่ยอดเยี่ยมที่สุดคือการใช้แพตเทิร์นเดียวกันนี้เพื่อแอบเพิ่ม “แพตช์” หรือแม้แต่ backdoor ใหม่ลงในไฟล์ทดสอบอื่น ๆ โดยไม่ให้ใครสังเกตเห็น
การที่ GitHub ซ่อนและลบ repository ออกไปไม่ได้ช่วยอะไรเลย และกลับเป็นอุปสรรคต่อการวิเคราะห์เหตุการณ์นี้
พบว่า dependency ตัวนั้นชอบ xz เป็นพิเศษ และถ้ายังไม่ได้ติดตั้ง มันถึงขั้นติดตั้ง xz ลงบนเครื่องโฮสต์เหมือนเป็นฟีเจอร์อำนวยความสะดวก
แต่กลับไม่ทำแบบนั้นกับ dependency อื่น เลยดูแปลกอยู่มาก
สงครามยืดเยื้อแบบนี้น่ากลัว เพราะก่อนที่ “ความชั่วร้าย” จะทำงานจริง เราแทบไม่รู้ว่าอะไรเป็นสิ่งมุ่งร้าย และอะไรแค่ดูประหลาดเฉย ๆ
โดยเฉพาะถ้าเป็นไฟล์ “ขยะไบนารี” ที่ใช้พิสูจน์ว่าการทดสอบล้มเหลว มันก็เป็นที่ซ่อนเนื้อหาร้ายได้อย่างดีเยี่ยม
น่าประทับใจมากที่ชุมชน โดยเฉพาะ amlweems สามารถทำ proof of concept และเขียนเอกสารได้รวดเร็วขนาดนี้
ถ้าไม่มีช่องโหว่เพิ่มเติมในฟังก์ชันเข้ารหัสหรือการโหลด payload ก็อาจไม่ใช่ว่ามันเปิดช่องโหว่ให้ผู้โจมตีรายอื่นทั้งหมดใช้งานได้ ตราบใดที่คีย์ยังไม่ถูกถอดออกมา
ขั้นต่อไปคือการหาวิธีตรวจจับดิสโทรที่ได้รับผลกระทบ ซึ่งดูไม่ง่ายเลย และอาจพอใส่วิธีเฝ้าดูว่ามีใครกำลังสแกน SSH server แบบ active ด้วยคีย์ที่ฮาร์ดโค้ดไว้ลงไปใน upstream ได้ด้วย
proof of concept สำหรับเวอร์ชันต้นฉบับต้องใช้ private key ที่ยังไม่เปิดเผยของผู้โจมตี
สิ่งที่ทำได้ดีที่สุดคงเป็นการ benchmark ที่ละเอียดขึ้น แต่เราก็ไม่มีทางรู้ได้ว่าโฮสต์บนอินเทอร์เน็ตที่ตอบสนองช้านั้นเป็นเพราะมีช่องโหว่ อยู่ไกล หรือเพราะเครื่องมันช้าเอง
เราก็ไม่อาจเข้าถึงประวัติว่าเคยใช้เวลานานแค่ไหนในการเชื่อมต่อไปยังโฮสต์นั้นในอดีต และยังมีความผันผวนของ routing อีกด้วย
สงสัยว่ามีใครลองรัน proof of concept นี้กับเครื่องมือตรวจจับพฤติกรรมโปรเซสผิดปกติบ้างหรือยัง
เช่น Carbon Black, AWS GuardDuty, Sysdig และผลิตภัณฑ์แนวนี้ เพราะมันดูเป็นกรณีที่เหมาะมากสำหรับทดสอบว่าถ้าถูกนำไปใช้งานจริง จะมีใครจับได้ค่อนข้างเร็วหรือไม่
GuardDuty น่าจะจับยากเพราะไม่ได้มองลงไปถึงระดับโปรเซสเหมือน EDR อย่าง CrowdStrike หรือ Carbon Black ส่วน Sysdig เน้นคอนเทนเนอร์และโครงสร้างพื้นฐานคลาวด์ จึงดูไม่น่าจะจับตัว exploit เองได้ง่าย
อย่างไรก็ตาม หลังจากยกระดับสิทธิ์แล้ว ก็ยังมีโอกาสตรวจพบความผิดปกติจากพฤติกรรมต่อเนื่องของผู้คุกคามได้
สุดท้ายแล้ว สิ่งที่น่าจะมีโอกาสจับตัว exploit ได้มากที่สุดก็คือ EDR ที่เฝ้าดูโปรเซสบน endpoint หรือการทำ การประเมินซัพพลายเชนซอฟต์แวร์ ที่คอยติดตามปัญหาความปลอดภัยของซอฟต์แวร์เสรีและโอเพนซอร์สต้นน้ำ
ที่น่าสนใจคือมันโยงไปสู่ประเด็นความปลอดภัยที่ใหญ่กว่า
ทีมพัฒนาอาจไม่ชอบติดตั้ง EDR บนเซิร์ฟเวอร์เพราะกระทบประสิทธิภาพและอาจมีปัญหาด้านประสบการณ์ผู้ใช้เวลาต้องกักกันระบบ และอาจไม่ชอบนโยบายจำกัดการใช้ซอฟต์แวร์เสรีและโอเพนซอร์สด้วย
exploit นี้โจมตีเข้าใส่ “ช่องโหว่” ระดับองค์กรโดยตรง และขึ้นอยู่กับบริบทว่าเราจะมีเหตุผลทั้งในการคงการเปิดรับความเสี่ยงไว้หรือในการแก้ไขมัน
โดยบอกว่า “วิธีหนึ่งของการตรวจจับขณะรันไทม์ คือเฝ้าดูว่า SSHD โหลดไลบรารีที่เป็นอันตรายหรือไม่ ไลบรารี shared แบบนี้มักมีเลขเวอร์ชันอยู่ในชื่อไฟล์”
ในบล็อกยังมีเนื้อหา กฎการตรวจจับ จริง ๆ ซึ่งยังไม่เห็นจากผู้ให้บริการความปลอดภัยรายอื่น
https://sysdig.com/blog/cve-2024-3094-detecting-the-sshd-bac...
ผมอ่านลิงก์ด้านล่างผิด และเก็บข้อความต้นฉบับไว้เพื่อเป็นบันทึก
ถ้าดูต่อในเมลเธรดเดียวกัน จะมีคนบอกว่าคนที่คอมมิต backdoor ดูเหมือนจะมี ส่วนร่วมกับเคอร์เนล เมื่อไม่นานมานี้ด้วย แต่ตัวบทวิเคราะห์ต้นฉบับนั้นยอดเยี่ยมมาก จึงเป็นงานเขียนที่ควรอ่าน
https://www.openwall.com/lists/oss-security/2024/03/29/10
ตัว Lasse เองก็พูดว่าไม่ได้เร่งด่วนอะไรเลย และจะไม่เข้าใน merge window รอบนี้ อีกทั้งคนปกติก็ไม่ได้กล่าวหา Lasse ว่าเป็นผู้ไม่หวังดี
เหตุการณ์นี้มีจุดที่คล้ายกับ กรณี Audacity เมื่อหลายปีก่อนอย่างน่าเหลือเชื่อ
Cookie guy อ้างว่าตัวเองถูกแทง และตำรวจสหพันธรัฐมีส่วนเกี่ยวข้องกับคดีนี้ ซึ่งชี้ว่ามันอาจเชื่อมโยงกับผู้เล่นที่ใหญ่กว่า 4chan มาก
ตอนนั้นหลายคนคิดว่าเกี่ยวข้องแค่ Muse Group แต่บางทีอาจเป็นปฏิบัติการของรัฐรัสเซียก็ได้
ก่อนหน้านั้นเขายังอ้างว่าใน Audacity มี telemetry และ backdoor อยู่มาก และถูกลบออกตั้งแต่คอมมิตแรกหลัง fork
บางที Audacity อาจมี backdoor จริง ๆ ก็ได้ คงต้องไปตรวจดู source code
ช่วงนี้ดูเหมือนกำลังรวมปฏิบัติการกับ APT29 และถ้าเป็นผมคงไม่ไปปลุก cozy bear
ถ้าไม่มี honeypot ตอนคอมไพล์อย่าง openssh.patch https://github.com/amlweems/xzbot/blob/main/openssh.patch ก็สงสัยว่า exploit จริงทำสิ่งนี้ตอนรันไทม์ได้อย่างไร
ลำดับคือ
opensshd -> การแจ้งเตือน systemd -> xz ที่ถูกดึงเข้ามาเป็น dependency ชั่วคราวแต่หลังจากliblzma.so.5.6.1ถูกโหลดเข้าเมมโมรีแล้ว อยากรู้ว่ามันไล่ย้อนกลับไปถึงopenssh_RSA_verifyเพื่อ hook หรือ patch ได้อย่างไรถ้ามันถูกโหลดก่อน libcrypto มันจะลงทะเบียนตัวจัดการ symbol auditing ซึ่งน่าจะเป็นฟีเจอร์เฉพาะของ glibc เพื่อให้ได้รับการแจ้งเตือนตอน symbol ของ libcrypto ถูก resolve แล้วค่อยชะลอการ patch GOT
อยากรู้ว่ามีใครทราบไหมว่า exploit นี้ทำงานเฉพาะตอนมีการเชื่อมต่อ SSH เข้ามาหรือไม่
ในรายการสตริงบน GitHub มี
DISPLAYกับWAYLAND_DISPLAYอยู่ด้วยhttps://gist.github.com/q3k/af3d93b6a1f399de28fe194add452d01
สิ่งเหล่านี้ไม่ได้เกี่ยวกับ SSH อย่างชัดเจน จึงอาจเป็นไปได้ว่ามันทำอะไรบางอย่างแม้ไม่มีการเชื่อมต่อ
เรื่องนี้อาจสำคัญสำหรับคนที่รันโค้ดแล้วแต่คิดว่าปลอดภัยเพราะไม่ได้เปิดเผย SSH server สู่อินเทอร์เน็ต
กล่าวคือเป็นกลไกเพื่อหลีกเลี่ยงสถานการณ์ที่มีคนพยายามตรวจจับ ทำซ้ำ หรือดีบักมัน
ถ้าไม่ปิดเวลาเชื่อมต่อไปยังเครื่องที่ไม่น่าไว้ใจ ฝั่งผู้เชื่อมต่อจะมีช่องโหว่ด้านความปลอดภัยที่พบได้บ่อย
คำว่า “successful exploitation does not leave a log message” หมายความว่าถ้า exploit นี้ไม่ถูกพบ ผู้โจมตีจะสามารถรัน คำสั่งตามอำเภอใจในสิทธิ์ root บนโฮสต์ที่ถูกเจาะได้ โดยไม่มีแม้แต่ sshd log สำหรับ “การเชื่อมต่อ” นั้นเลยใช่ไหม
การรันโค้ดจากระยะไกลเกิดขึ้นใน ขั้นตอนการเชื่อมต่อ ก่อนที่จะมีการบันทึกล็อก
เลยสงสัยว่าระบบตรวจจับความผิดปกติแบบไหนจะจับสิ่งนี้ได้บ้าง
สงสัยว่าถ้าแยกไฟล์ทดสอบไปไว้ในรีโพแยกต่างหาก เพื่อไม่ให้ใช้ได้ในขั้นตอน build จะทำให้การโจมตีนี้ยากขึ้นหรือไม่
แนวคิดคืออะไรก็ตามที่มีส่วนกับ build ควรเป็นสิ่งที่มนุษย์อ่านได้
เราควรมองการโจมตีนี้เหมือนอุบัติเหตุทางการบิน และออกกฎใหม่เพื่อลดโอกาสที่มันจะสำเร็จอีกครั้ง
ถึงเราจะตรวจสอบผู้มีส่วนร่วมทุกคนไม่ได้ทั้งหมด แต่ข้อมูลทดสอบที่ส่งเสียงดังแบบนี้ก็ควรถูกแยกออกได้ง่าย