โจมตี Node.js ผ่านการดัดแปลงต้นแบบออบเจ็กต์ JavaScript

นักวิจัยจาก Helmholtz Center for Information Security (CISPA) และ Royal Institute of Technology (Sweden) วิเคราะห์ความสามารถในการบังคับใช้เทคนิคการสร้างมลพิษต้นแบบ JavaScript เพื่อสร้างการโจมตีบนแพลตฟอร์ม Node.js และแอปพลิเคชันยอดนิยมที่ใช้แพลตฟอร์มดังกล่าว ซึ่งนำไปสู่การเรียกใช้โค้ด

วิธีการสร้างมลภาวะต้นแบบใช้คุณลักษณะของภาษา JavaScript ที่ช่วยให้คุณสามารถเพิ่มคุณสมบัติใหม่ให้กับต้นแบบรากของออบเจ็กต์ใดๆ ได้ แอปพลิเคชันอาจมีบล็อกโค้ด (แกดเจ็ต) ซึ่งการดำเนินการได้รับผลกระทบจากคุณสมบัติทดแทน ตัวอย่างเช่น โค้ดอาจมีโครงสร้างเช่น 'const cmd = options.cmd || "/bin/sh"' ซึ่งตรรกะจะเปลี่ยนไปหากผู้โจมตีสามารถแทนที่คุณสมบัติ "cmd" ในต้นแบบรากได้

การโจมตีที่ประสบความสำเร็จต้องการให้แอปพลิเคชันสามารถใช้ข้อมูลภายนอกเพื่อสร้างคุณสมบัติใหม่ในต้นแบบรูทของออบเจ็กต์ และการดำเนินการดังกล่าวจะพบกับอุปกรณ์ที่ขึ้นอยู่กับคุณสมบัติที่แก้ไข การเปลี่ยนต้นแบบทำได้โดยการประมวลผลคุณสมบัติของบริการ “__proto__” และ “constructor” ใน Node.js คุณสมบัติ "__proto__" ส่งคืนต้นแบบของคลาสของวัตถุ และคุณสมบัติ "ตัวสร้าง" ส่งคืนฟังก์ชันที่ใช้ในการสร้างวัตถุ

หากรหัสแอปพลิเคชันมีการมอบหมาย “obj[a][b] = value” และค่าถูกตั้งค่าจากข้อมูลภายนอก ผู้โจมตีสามารถตั้งค่า “a” เป็นค่า “__proto__” และบรรลุการติดตั้งคุณสมบัติของตนเอง ด้วยชื่อ “b” และค่า “value” ในต้นแบบรากของวัตถุ (obj.__proto__.b = value;) และคุณสมบัติที่ตั้งค่าไว้ในต้นแบบจะมองเห็นได้ในวัตถุทั้งหมด ในทำนองเดียวกัน หากโค้ดมีนิพจน์เช่น "obj[a][b][c] = value" โดยการตั้งค่า "a" เป็นค่า "constructor" และ "b" เป็น "prototype" ในอ็อบเจ็กต์ที่มีอยู่ทั้งหมด คุณสามารถ กำหนดคุณสมบัติใหม่ด้วยชื่อ "c" และค่า "value"

ตัวอย่างการเปลี่ยนต้นแบบ: const o1 = {}; const o2 = วัตถุใหม่ (); o1.__โปรโต__.x = 42; // สร้างคุณสมบัติ “x” ในรูทต้นแบบ console.log (o2.x); // เข้าถึงคุณสมบัติ "x" จากวัตถุอื่น // เอาต์พุตจะเป็น 42 เนื่องจากต้นแบบรูทถูกเปลี่ยนผ่านวัตถุ o1 ซึ่งใช้ในวัตถุ o2 ด้วย

ตัวอย่างของโค้ดที่มีช่องโหว่: ฟังก์ชัน entryPoint (arg1, arg2, arg3){ const obj = {}; const p = obj[arg1]; พี[arg2] = arg3; กลับพี; }

หากอาร์กิวเมนต์ฟังก์ชัน entryPoint ถูกสร้างขึ้นจากข้อมูลอินพุต ผู้โจมตีสามารถส่งค่า “__proto__” ไปยัง arg1 และสร้างคุณสมบัติด้วยชื่อใดก็ได้ในต้นแบบรูท หากคุณส่งค่า arg2 เป็น "toString" และ arg3 เป็นค่า 1 คุณสามารถกำหนดคุณสมบัติ "toString" ได้ (Object.prototype.toString=1) และทำให้แอปพลิเคชันเสียหายในระหว่างการเรียก toString()

ตัวอย่างของสถานการณ์ที่อาจนำไปสู่การเรียกใช้โค้ดของผู้โจมตี ได้แก่ การสร้างคุณสมบัติ "main", "shell", "exports", "contextExtensions" และ "env" ตัวอย่างเช่น ผู้โจมตีสามารถสร้างคุณสมบัติ “main” ในต้นแบบรูทของออบเจ็กต์ โดยเขียนเส้นทางไปยังสคริปต์ของเขา (Object.prototype.main = “./../../pwned.js”) และ คุณสมบัตินี้จะถูกเรียกในขณะที่ดำเนินการในโค้ดของโครงสร้าง need("my-package") หากแพ็คเกจที่รวมอยู่ไม่ได้กำหนดคุณสมบัติ "main" อย่างชัดเจนใน package.json (หากไม่ได้กำหนดคุณสมบัติไว้ จะได้มาจากต้นแบบราก) คุณสมบัติ “shell”, “exports” และ “env” สามารถทดแทนได้ในลักษณะเดียวกัน: ให้ rootProto = Object.prototype; rootProto["exports"] = {".rol./changelog.js"}; rootProto["1"] = "/path/to/npm/scripts/"; // เรียกใช้ทริกเกอร์ need("./target.js"); Object.prototype.main = "/path/to/npm/scripts/changelog.js"; Object.prototype.shell = "โหนด"; วัตถุต้นแบบ.env = {}; Object.prototype.env.NODE_OPTIONS = "—inspect-brk=0.0.0.0:1337"; // เรียกใช้ทริกเกอร์ต้องการ ("ไบต์");

นักวิจัยวิเคราะห์แพ็คเกจ NPM 10 แพ็คเกจที่มีจำนวนการขึ้นต่อกันมากที่สุด และพบว่า 1958 แพ็คเกจไม่มีคุณสมบัติหลักใน package.json, 4420 แพ็คเกจใช้พาธสัมพัทธ์ในคำสั่ง need และ 355 แพ็คเกจใช้ command substitution API โดยตรง

ตัวอย่างการทำงานคือการหาประโยชน์สำหรับการโจมตีแบ็กเอนด์ Parse Server ที่แทนที่คุณสมบัติ evalFunctions เพื่อให้การระบุช่องโหว่ดังกล่าวง่ายขึ้น จึงได้มีการพัฒนาชุดเครื่องมือที่รวมวิธีการวิเคราะห์แบบคงที่และแบบไดนามิกเข้าด้วยกัน ในระหว่างการทดสอบ Node.js พบว่ามีอุปกรณ์ 11 ชิ้นที่สามารถใช้เพื่อจัดระเบียบการโจมตีที่นำไปสู่การรันโค้ดของผู้โจมตี นอกเหนือจาก Parse Server แล้ว ยังมีการระบุช่องโหว่ที่สามารถใช้ประโยชน์ได้ XNUMX รายการใน NPM CLI

ที่มา: opennet.ru

เพิ่มความคิดเห็น