คำเตือนความเสี่ยง: ระวังความเสี่ยงจากการระดมทุนที่ผิดกฎหมายในนาม 'สกุลเงินเสมือน' 'บล็อกเชน' — จากห้าหน่วยงานรวมถึงคณะกรรมการกำกับดูแลการธนาคารและการประกันภัย
ข่าวสาร
ค้นพบ
ค้นหา
เข้าสู่ระบบ
简中
繁中
English
日本語
한국어
ภาษาไทย
Tiếng Việt
BTC
ETH
HTX
SOL
BNB
ดูตลาด
การวิเคราะห์ช่องโหว่ของ Solidity Compiler: ข้อบกพร่องของ ABI Recoding
Eocene
特邀专栏作者
2023-04-25 10:25
บทความนี้มีประมาณ 1907 คำ การอ่านทั้งหมดใช้เวลาประมาณ 3 นาที
กระบวนการเองไม่มีปัญหาตรรกะที่สำคัญ แต่เมื่อรวมเข้ากับกลไกการล้างข้อมูลของ Solidity เนื่องจา

ชื่อระดับแรก

บทความนี้วิเคราะห์รายละเอียดเกี่ยวกับปัญหาช่องโหว่ที่เกิดจากการจัดการข้อผิดพลาดของอาร์เรย์ประเภท uint และ bytes 32 ที่มีความยาวคงที่ในกระบวนการเข้ารหัส ABIReencoding ของคอมไพเลอร์ Solidity (0.5.8<=เวอร์ชัน <0.8.16) จากระดับซอร์สโค้ด และ เสนอแนวทางแก้ไขและมาตรการหลีกเลี่ยงที่เกี่ยวข้อง

ชื่อระดับแรก

รายละเอียดช่องโหว่รูปแบบการเข้ารหัส ABI เป็นวิธีการเข้ารหัสมาตรฐานที่ใช้เมื่อผู้ใช้หรือสัญญาเรียกใช้ฟังก์ชันไปยังสัญญาและส่งพารามิเตอร์ สำหรับรายละเอียด โปรดดู Solidity official เกี่ยวกับการเข้ารหัส ABI

คำอธิบายโดยละเอียดของ

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

เมื่อรวมกันเนื่องจากการละเว้นของโค้ดคอมไพเลอร์ Solidity เองจึงมีช่องโหว่

ตามกฎการเข้ารหัส ABI หลังจากลบตัวเลือกฟังก์ชันแล้ว ข้อมูลที่เข้ารหัส ABI จะถูกแบ่งออกเป็นสองส่วน: ส่วนหัวและส่วนท้าย เมื่อรูปแบบข้อมูลเป็นอาร์เรย์ uint หรือไบต์ 32 ที่มีความยาวคงที่ ABI จะจัดเก็บข้อมูลประเภทนี้ในส่วนหัว การใช้กลไกการล้างข้อมูลในหน่วยความจำของ Solidity คือการล้างหน่วยความจำของดัชนีถัดไปหลังจากใช้หน่วยความจำของดัชนีปัจจุบัน เพื่อป้องกันไม่ให้หน่วยความจำของดัชนีถัดไปได้รับผลกระทบจากข้อมูลที่สกปรก และเมื่อ Solidity ABI-เข้ารหัสชุดข้อมูลพารามิเตอร์ มันจะเข้ารหัสตามลำดับจากซ้ายไปขวา! !

contract Eocene {

        event VerifyABI( bytes[], uint[ 2 ]);

        function verifyABI(bytes[] calldata a, uint[ 2 ] calldata b) public  {

                emit VerifyABI(a,เพื่ออำนวยความสะดวกในการสำรวจช่องโหว่ในภายหลัง พิจารณารหัสสัญญาในรูปแบบต่อไปนี้:

        }

}

b); // ข้อมูลเหตุการณ์จะถูกเข้ารหัสในรูปแบบ ABI และจัดเก็บไว้ในห่วงโซ่

ฟังก์ชันของฟังก์ชัน VerifyABI ในสัญญา Eocene เป็นเพียงการเปล่งไบต์ที่มีความยาวผันแปรได้[] a และความยาวคงที่ uint[2] b ในพารามิเตอร์ของฟังก์ชัน

ควรสังเกตว่าเหตุการณ์เหตุการณ์จะทริกเกอร์การเข้ารหัส ABI ด้วย ที่นี่ พารามิเตอร์ a, b จะถูกเข้ารหัสเป็นรูปแบบ ABI แล้วจัดเก็บไว้ในห่วงโซ่verifyABI(['0x aaaaaa','0x bbbbbb'],[0x 11111, 0x 22222 ])

เราใช้ Solidity เวอร์ชัน v 0.8.14 เพื่อรวบรวมรหัสสัญญา ปรับใช้ผ่านการรีมิกซ์ และส่งต่อverifyABI(['0x aaaaaa','0x bbbbbb'],[0x 11111, 0x 22222 ])อันดับแรก เรามาดูที่

0x 5 2c d 1 a 9 c                                                                  // bytes 4(sha 3("verify(btyes[], uint[ 2 ])"))

0000000000000000000000000000000000000000000000000000000000000060            // index of  a

0000000000000000000000000000000000000000000000000000000000011111            // b[0 ]

0000000000000000000000000000000000000000000000000000000000022222            // b[1 ]

0000000000000000000000000000000000000000000000000000000000000002            // length of a

0000000000000000000000000000000000000000000000000000000000000040            // index of a[0 ]

0000000000000000000000000000000000000000000000000000000000000080            // index of a[1 ]

0000000000000000000000000000000000000000000000000000000000000003            // length of a[0 ]

aaaaaa 0000000000000000000000000000000000000000000000000000000000            // a[0 ]

0000000000000000000000000000000000000000000000000000000000000003            // length of a[1 ]

bbbbbb 0000000000000000000000000000000000000000000000000000000000            // a[1 ]

รูปแบบการเข้ารหัสที่ถูกต้องสำหรับ:a, bหากคอมไพเลอร์ Solidity เป็นปกติ เมื่อพารามิเตอร์TX

เมื่อเหตุการณ์เหตุการณ์ถูกบันทึกไว้ในเชน รูปแบบข้อมูลควรจะเหมือนกับที่เราส่งไป จริงๆ เรียกสัญญาแล้วเช็ค log on chain ถ้าเทียบเองก็เช็คได้

หลังจากการโทรสำเร็จ เหตุการณ์ของสัญญาจะถูกบันทึกดังนี้:

0000000000000000000000000000000000000000000000000000000000000060            // index of  a

0000000000000000000000000000000000000000000000000000000000011111            // b[0 ]

0000000000000000000000000000000000000000000000000000000000022222            // b[1 ]

0000000000000000000000000000000000000000000000000000000000000000            // length of a?? why become 0??

0000000000000000000000000000000000000000000000000000000000000040            // index of a[0 ]

0000000000000000000000000000000000000000000000000000000000000080            // index of a[1 ]

0000000000000000000000000000000000000000000000000000000000000003            // length of a[0 ]

aaaaaa 0000000000000000000000000000000000000000000000000000000000            // a[0 ]

0000000000000000000000000000000000000000000000000000000000000003            // length of a[1 ]

bbbbbb 0000000000000000000000000000000000000000000000000000000000            // a[1 ]

! ! น่าตกใจ หลังจาก b[1 ] ค่าที่จัดเก็บความยาวของพารามิเตอร์ถูกลบโดยไม่ตั้งใจ! !

ทำไม

  1. ดังที่เราได้กล่าวไว้ก่อนหน้านี้ เมื่อ Solidity พบชุดของพารามิเตอร์ที่ต้องเข้ารหัส ABI ลำดับการสร้างพารามิเตอร์จะเรียงจากซ้ายไปขวา ตรรกะการเข้ารหัสเฉพาะสำหรับ a และ b มีดังนี้

  2. Solidity ทำการเข้ารหัส ABI บน a ก่อน ตามกฎการเข้ารหัส ดัชนีของ a จะถูกวางไว้ที่ส่วนหัวและความยาวองค์ประกอบและค่าเฉพาะของ a จะถูกเก็บไว้ที่ส่วนท้าย

  3. ประมวลผลข้อมูล b เนื่องจากชนิดข้อมูล b อยู่ในรูปแบบ uint[2] ดังนั้นค่าเฉพาะของข้อมูลจึงถูกจัดเก็บไว้ในส่วนหัว อย่างไรก็ตาม เนื่องจากกลไกการล้างข้อมูลของ Solidity เอง หลังจากเก็บ b[1] ไว้ในหน่วยความจำแล้ว ค่าของที่อยู่หน่วยความจำถัดไปที่มีข้อมูล b[1] อยู่ (ที่อยู่หน่วยความจำที่ใช้เก็บความยาวขององค์ประกอบ) ตั้งค่าเป็น 0

การดำเนินการเข้ารหัส ABI สิ้นสุดลง ข้อมูลที่เข้ารหัสอย่างไม่ถูกต้องถูกจัดเก็บบนเครือข่าย และช่องโหว่ SOL-2022-6 ปรากฏขึ้น

ที่ระดับซอร์สโค้ด ลอจิกข้อผิดพลาดเฉพาะก็ชัดเจนเช่นกัน เมื่อจำเป็นต้องได้รับไบต์ 32 ที่มีความยาวคงที่หรือข้อมูลอาร์เรย์ uint จาก calldata ไปยังหน่วยความจำ Solidity จะตั้งค่าข้อมูลดัชนีหน่วยความจำหลังเป็น 0 เสมอหลังจากที่ข้อมูลถูกคัดลอก . . และเนื่องจากมีส่วนหัวและส่วนท้ายสองส่วนในการเข้ารหัส ABI และลำดับการเข้ารหัสก็เรียงจากซ้ายไปขวาเช่นกัน ซึ่งนำไปสู่การมีอยู่ของช่องโหว่

รหัสที่รวบรวม Solidity สำหรับช่องโหว่เฉพาะมีดังนี้:ABIFunctions::abiEncodingFunctionCalldataArrayWithoutCleanup()

ป้อนเมื่อตำแหน่งที่จัดเก็บข้อมูลต้นทางคือ Calldata และชนิดข้อมูลต้นทางคือ ByteArray, String หรือชนิดพื้นฐานของอาร์เรย์ต้นทางคือ uint หรือ bytes 32fromArrayType.isDynamicallySized()ตรวจสอบว่าแหล่งข้อมูลเป็นอาร์เรย์ที่มีความยาวคงที่หรือไม่ เฉพาะอาร์เรย์ที่มีความยาวคงที่เท่านั้นที่ตรงตามเงื่อนไขการเรียกใช้ช่องโหว่

จะisByteArrayOrString()จะYulUtilFunctions::copyToMemoryFunction(),ผลการตัดสินจะถูกส่งไปยัง

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

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

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

นอกจากนี้,นอกจากนี้,

ชื่อระดับแรก

      • event

      • error

      • abi.encode*

      • returns             //the return of function

      • struct              //the user defined struct

      • all external call

สารละลาย

  1. สารละลาย

  2. เมื่อมีการดำเนินการที่ได้รับผลกระทบจากการอุทธรณ์ในรหัสสัญญา ตรวจสอบให้แน่ใจว่าพารามิเตอร์สุดท้ายไม่ใช่อาร์เรย์ uint หรือไบต์ 32 ที่มีความยาวคงที่

  3. ชื่อระดับแรก

เกี่ยวกับเรา

At Eocene Research, we provide the insights of intentions and security behind everything you know or don't know of blockchain, and empower every individual and organization to answer complex questions we hadn't even dreamed of back then.

Learn more: Website | Medium | Twitter

ความปลอดภัย
สัญญาที่ชาญฉลาด
นักพัฒนา
เทคโนโลยี
ยินดีต้อนรับเข้าร่วมชุมชนทางการของ Odaily
กลุ่มสมาชิก
https://t.me/Odaily_News
กลุ่มสนทนา
https://t.me/Odaily_CryptoPunk
บัญชีทางการ
https://twitter.com/OdailyChina
กลุ่มสนทนา
https://t.me/Odaily_CryptoPunk
สรุปโดย AI
กลับไปด้านบน
กระบวนการเองไม่มีปัญหาตรรกะที่สำคัญ แต่เมื่อรวมเข้ากับกลไกการล้างข้อมูลของ Solidity เนื่องจา
อันดับบทความร้อน
Daily
Weekly
ดาวน์โหลดแอพ Odaily พลาเน็ตเดลี่
ให้คนบางกลุ่มเข้าใจ Web3.0 ก่อน
IOS
Android