ข้ามไปยังเนื้อหา

ความเสี่ยงต่อพื้นที่จัดเก็บจาก delegatecall

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

อัปเดต

จัดทำขึ้นเพื่อการศึกษาเท่านั้น ไม่ใช่คำแนะนำการลงทุน การลงทุนอาจทำให้ขาดทุนได้

คำตอบโดยตรง

delegatecall เรียกใช้โค้ดจากสัญญาเป้าหมายภายในบริบทของสัญญาผู้เรียก สัญญาผู้เรียกยังคงใช้พื้นที่จัดเก็บ ยอดคงเหลือ และ address(this) ของตนเอง ส่วน msg.sender และ msg.value ยังคงมีค่าจากการเรียกครั้งแรก

พฤติกรรมนี้ทำให้สร้างพร็อกซี ไลบรารี และโมดูลกระเป๋าเงินอัจฉริยะได้ แต่ก็ทำให้โค้ดที่ได้รับมอบสิทธิ์มีอำนาจโดยพฤตินัยของสัญญาผู้เรียกด้วย การเขียนข้อมูลลงพื้นที่จัดเก็บ การโอนสินทรัพย์ การอนุมัติ หรือการเรียกสัญญาภายนอกจะดำเนินการในนามของสัญญาผู้เรียก ไม่ใช่เป้าหมายที่ให้โค้ดมา

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

วิธีการทำงาน

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

จึงมีขอบเขตสี่ด้านที่ต้องตรวจสอบ:

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

ERC-1967 ลดการชนกันในพร็อกซีด้วยการเก็บที่อยู่ของอิมพลีเมนเทชัน beacon และผู้ดูแลระบบไว้ในสล็อตมาตรฐานที่อยู่นอกการจัดสรรตามปกติของคอมไพเลอร์ แต่ไม่ได้พิสูจน์ว่าอิมพลีเมนเทชันปลอดภัยหรือการอัปเกรดที่ได้รับอนุญาตไม่มีอันตราย

ตัวอย่าง

สมมติว่ากระเป๋าเงินเก็บ owner ไว้ใน slot 0 ปลั๊กอินที่คอมไพล์โดยมี counter อยู่ใน slot 0 จะเพิ่มค่าตัวนับเมื่อกระเป๋าเงินเข้าถึงปลั๊กอินผ่าน delegatecall

การเขียนข้อมูลนี้เปลี่ยนค่า owner ของกระเป๋าเงิน เพราะพื้นที่จัดเก็บเป็นของกระเป๋าเงิน หากค่าที่ได้เข้ารหัสที่อยู่ซึ่งผู้โจมตีควบคุม การตรวจสอบสิทธิ์ในภายหลังอาจยอมรับผู้โจมตีเป็นเจ้าของ แม้ว่าปลั๊กอินจะไม่เคยถือครองสินทรัพย์ของกระเป๋าเงินเลย

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

ความเสี่ยง

  • การเรียกใช้เป้าหมายใดก็ได้: เป้าหมายที่ผู้ใช้ควบคุมหรือผ่านการตรวจสอบไม่รัดกุมอาจเรียกใช้โค้ดอันตรายด้วยสิทธิ์ของสัญญาผู้เรียก
  • การชนกันของพื้นที่จัดเก็บ: อิมพลีเมนเทชันอาจเขียนทับข้อมูลเจ้าของ ยอดคงเหลือ สถานะหยุดชั่วคราว หรือแม้แต่สล็อตที่ใช้เลือกอิมพลีเมนเทชันถัดไป
  • การอัปเกรดที่ไม่ปลอดภัย: ผู้ดูแลระบบหรือกระบวนการกำกับดูแลที่ถูกเจาะระบบอาจแทนที่โค้ดที่เคยผ่านการตรวจสอบแล้ว หลังจากผู้ใช้ฝากสินทรัพย์หรือให้สิทธิ์อนุมัติ
  • การเริ่มต้นใช้งานล้มเหลว: พร็อกซีหรืออิมพลีเมนเทชันที่ยังไม่ได้เริ่มต้นใช้งานอาจเปิดทางให้บัญชีอื่นยึดบทบาทที่มีสิทธิ์ระดับสูงหรือกำหนดค่าการพึ่งพาที่เป็นอันตราย
  • การตรวจสอบที่ทำให้เข้าใจผิด: การตรวจสอบเฉพาะซอร์สของพร็อกซี อิมพลีเมนเทชันปัจจุบัน หรืออินเทอร์เฟซ อาจพลาด beacon การอัปเกรดที่รอดำเนินการ รีจิสทรีโมดูล หรือเส้นทางการทำงานอื่น

ก่อนลงนาม ให้ระบุอิมพลีเมนเทชัน ณ บล็อกล่าสุด ตรวจสอบว่าใครเปลี่ยนได้และต้องรอนานเท่าใด ตรวจสอบไบต์โค้ดที่ยืนยันแล้วและโครงสร้างพื้นที่จัดเก็บของเป้าหมาย จำลอง calldata ทั้งหมด และเปรียบเทียบพื้นที่จัดเก็บที่ละเอียดอ่อนกับการอนุมัติโทเค็นก่อนและหลังการดำเนินการ สำหรับกระเป๋าเงินอัจฉริยะ ให้ตรวจสอบด้วยว่าโมดูลถูกเปิดใช้ ปิดใช้ และได้รับอนุญาตให้เลือกเป้าหมายอย่างไร

ความเข้าใจผิดที่พบบ่อย

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

หัวข้อที่เกี่ยวข้อง

แหล่งที่มา

การนำทาง

ค้นหาในวิกิ...