ความเข้าใจเชิงลึกเกี่ยวกับช่องโหว่การโจมตีซ้ำ
คำนำ
คำนำ
แนวคิดของสัญญาอัจฉริยะ (อังกฤษ: Smart contract) ได้รับการเสนอครั้งแรกโดย Nick Szabo ในปี 1995 เป็นโปรโตคอลคอมพิวเตอร์ที่ออกแบบมาเพื่อเผยแพร่ ตรวจสอบ หรือดำเนินการตามสัญญาในลักษณะที่เป็นข้อมูล ธุรกรรมที่เชื่อถือได้ ซึ่งตรวจสอบย้อนกลับได้และไม่สามารถย้อนกลับได้ อย่างไรก็ตาม สัญญาอัจฉริยะไม่ปลอดภัย ช่องโหว่การโจมตี Re-Entrance เป็นหนึ่งในวิธีการโจมตีใน Ethereum ต้นปี 2559 เหตุการณ์ DAO ทำให้เกิดการ Hard Fork ของ Ethereum
ชื่อระดับแรก
ภาพรวมช่องโหว่
ใน Ethereum สัญญาอัจฉริยะสามารถเรียกรหัสของสัญญาภายนอกอื่นๆ ได้ เนื่องจากสัญญาอัจฉริยะสามารถเรียกสัญญาภายนอกหรือส่ง Ether ได้ การดำเนินการเหล่านี้จำเป็นต้องมีสัญญาเพื่อส่งการโทรภายนอก สัญญาที่จะดำเนินการใหม่ในทุกตำแหน่ง ข้ามข้อจำกัดในโค้ดเดิม ดังนั้นการโจมตีซ้ำจึงเกิดขึ้น การโจมตีแบบ Reentrancy นั้นโดยพื้นฐานแล้วคล้ายกับการเรียกซ้ำในการเขียนโปรแกรม ดังนั้นจึงสามารถเกิดขึ้นได้เมื่อสัญญาส่ง ether ไปยังที่อยู่ที่ไม่รู้จัก
พูดง่ายๆ คือ มีสองเงื่อนไขสำหรับช่องโหว่การโจมตีแบบย้อนกลับที่จะเกิดขึ้น:
มีการเรียกสัญญาภายนอกและสัญญานั้นไม่ปลอดภัย

ตัวอย่างข้อมูลโค้ดอย่างง่ายแสดงไว้ด้านล่าง:
ข้อมูลโค้ดด้านบนเป็นการดำเนินการถอนเงินที่ง่ายที่สุด ต่อไป เราจะวิเคราะห์สาเหตุของการโจมตีการกลับเข้ามาใหม่โดยละเอียด
ชื่อระดับแรก
การวิเคราะห์ช่องโหว่
ก่อนที่จะวิเคราะห์การโจมตีการกลับเข้ามาใหม่อย่างเป็นทางการ เราจะมาแนะนำความรู้ที่สำคัญบางประการกันก่อน
.transfer(),01 วิธีการโอนเนื่องจากการโจมตีแบบย้อนกลับจะถูกส่งไประหว่างการดำเนินการถ่ายโอน วิธีการถ่ายโอนที่ใช้กันทั่วไปใน Solidity คือ- .send() และ
- .gas().call.vale()() วิธีการถ่ายโอนสามวิธีอธิบายไว้ด้านล่าง:
- .transfer(): การโทรจะถูกส่งเพียง 2300 gas เมื่อการโอนล้มเหลว การโยนจะถูกใช้เพื่อดำเนินการย้อนกลับ เพื่อป้องกันการโจมตีย้อนกลับ
.send(): เฉพาะ 2300 gas เท่านั้นที่จะถูกส่งสำหรับการเรียก และค่าบูลีนเป็นเท็จจะถูกส่งกลับเมื่อการส่งล้มเหลว เพื่อป้องกันการโจมตีย้อนกลับ
ไม่มีฟังก์ชั่นที่ตรงกันเมื่อเรียกใช้สัญญา
ฟังก์ชันสำรอง: ฟังก์ชันสำรองเป็นฟังก์ชันเดียวที่ไม่มีชื่อในแต่ละสัญญา และฟังก์ชันนี้ไม่มีพารามิเตอร์และไม่มีค่าส่งคืน ดังที่แสดงด้านล่าง:

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

03 รหัสช่องโหว่

โค้ดต่อไปนี้มีการโจมตีแบบ Reentrancy โดยดำเนินการตามสัญญาที่คล้ายกับกระเป๋าเงินสาธารณะ ผู้ใช้ทั้งหมดสามารถใช้ deposit() เพื่อฝากเข้าในสัญญา Reentrance หรือใช้ cancel() เพื่อถอนออกจากสัญญา Reentrance ได้ แน่นอนว่าทุกคนก็สามารถทำได้เช่นกัน ใช้ balanceof() เพื่อค้นหายอดคงเหลือของตนเองหรือผู้อื่นในสัญญา

ขั้นแรกให้ใช้บัญชี (0x5B38Da6a701c568545dCfcB03FcB875f56beddC4) เพื่อทำหน้าที่เป็นเหยื่อ และคลิกปุ่มปรับใช้เพื่อปรับใช้สัญญาใน Remix IDE

หลังจากปรับใช้สัญญาสำเร็จ ให้กรอก 5 ในช่องการตั้งค่า VALUE เปลี่ยนหน่วยเป็น ether และคลิก ฝาก เพื่อฝาก 5 ether


คลิก wallet เพื่อดูยอดคงเหลือของสัญญาและพบว่ายอดคงเหลือคือ 5 ether แสดงว่าการฝากเงินของเราสำเร็จ

รหัสต่อไปนี้คือการโจมตีสัญญาที่มีช่องโหว่ด้านบน:

ใช้บัญชีอื่น (0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2) เพื่อทำหน้าที่เป็นผู้โจมตี คัดลอกที่อยู่สัญญาที่มีช่องโหว่ไปยังช่องการตั้งค่า Deploy และคลิก Deploy เพื่อปรับใช้สัญญาการโจมตีข้างต้น


หลังจากการปรับใช้สำเร็จ ให้เรียกใช้ฟังก์ชัน wallet() ก่อนเพื่อตรวจสอบว่ายอดคงเหลือของสัญญาโจมตีเป็น 0

เรียกฟังก์ชันกระเป๋าเงินของสัญญาอีกครั้งเพื่อตรวจสอบยอดคงเหลือของสัญญาที่มีช่องโหว่ และพบว่ากลายเป็น 6 ether

ผู้โจมตี (0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2) เรียกใช้ฟังก์ชันการโจมตีของสัญญาโจมตีเพื่อจำลองการโจมตี จากนั้น เรียกฟังก์ชันกระเป๋าเงินของสัญญาที่ถูกโจมตีเพื่อตรวจสอบยอดคงเหลือของสัญญา และพบว่า กลับสู่ศูนย์ ในตอนนี้ ไป กลับไปที่สัญญาที่ถูกโจมตีเพื่อตรวจสอบยอดคงเหลือ และพบว่า 6 ether ในสัญญาที่ถูกโจมตี เงินทั้งหมดถูกถอนออกจากสัญญาของผู้โจมตี ซึ่งทำให้เกิดการโจมตีซ้ำ
04 การวิเคราะห์ซอร์สโค้ด

ชื่อระดับแรก
กรณีที่เกี่ยวข้อง
ข้อความ

เมื่อวันที่ 17 มิถุนายน 2559 โครงการ TheDAO ประสบกับการโจมตีแบบย้อนกลับ ส่งผลให้ Ethereum มากกว่า 3 ล้านรายการถูกแยกออกจากกลุ่มสินทรัพย์ของ TheDAO และผู้โจมตีใช้ฟังก์ชัน splitDAO() ในสัญญาอัจฉริยะของ TheDAO เพื่อนำสินทรัพย์ DAO ของเขากลับมาใช้ใหม่อีกครั้ง เข้าโจมตี โดยแยกทรัพย์สินของ DAO ออกจากกลุ่มทรัพย์สินของโครงการ TheDAO อย่างต่อเนื่อง และโอนไปยังบัญชีของตนเอง

โค้ดต่อไปนี้เป็นส่วนหนึ่งของฟังก์ชัน splitDAO() ซึ่งมีซอร์สโค้ดอยู่ใน TokenCreation.sol ซึ่งจะถ่ายโอนโทเค็นจาก DAO หลักไปยัง DAO ย่อย อาร์เรย์ยอดคงเหลือ uint fundToBeMoved = (ยอดคงเหลือ[msg.sender] * p.splitData[0].splitBalance) / p.splitData[0].totalSupply กำหนดจำนวนโทเค็นที่จะย้าย

payOut[_account] += รางวัล ตัวแปรสถานะที่อัปเดตจะถูกวางไว้หลังจากการเรียกฟังก์ชัน payOut ในรหัสปัญหา

ออกการเรียก .call.value ไปยัง _recipient โอน _amount Wei และการเรียก .call.value จะใช้ก๊าซที่เหลืออยู่ในปัจจุบันทั้งหมดตามค่าเริ่มต้น
สารละลาย
จากการวิเคราะห์การโจมตี reentrancy ข้างต้น เราพบว่าจุดสำคัญของช่องโหว่การโจมตี reentrancy คือการใช้ฟังก์ชันเช่น fallback เพื่อโทรกลับตัวเองเพื่อทำให้เกิดการเรียกซ้ำเพื่อดำเนินการถ่ายโอนแบบวงกลม ดังนั้น มีวิธีแก้ไขดังต่อไปนี้ ถึงช่องโหว่การโจมตี reentrancy
ชื่อเรื่องรอง

01 ใช้ฟังก์ชันถ่ายโอนอื่นๆ
ใช้ฟังก์ชัน Transfer() ในตัวของ Solidity เมื่อส่งการถ่ายโอน Ethereum ไปยังที่อยู่ภายนอก เนื่องจาก Transfer() จะส่งเพียง 2300 Gas เพื่อโทร ซึ่งจะไม่เพียงพอสำหรับการเรียกสัญญาอื่น ใช้ Transfer() เพื่อเขียนต้นฉบับใหม่ การถอน ( ) ของสัญญามีดังนี้

02 แก้ไขตัวแปรสถานะก่อน
วิธีนี้มีไว้เพื่อให้แน่ใจว่าการแก้ไขตัวแปรสถานะนั้นเร็วกว่าการดำเนินการถ่ายโอน นั่นคือโหมดตรวจสอบเอฟเฟกต์การโต้ตอบ (ตรวจสอบเอฟเฟกต์การโต้ตอบ) แนะนำอย่างเป็นทางการโดย Solidity

03 ใช้มิวเท็กซ์
มีสัญญาความปลอดภัยสำหรับการโจมตีซ้ำในห้องสมุดอย่างเป็นทางการของ OpenZeppelin:
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol




อ้างอิง
ชื่อระดับแรก
https://zhuanlan.zhihu.com/p/111446792
อ้างอิง
https://blog.csdn.net/henrynote/article/details/82119116
1. การฮาร์ดฟอร์ก Ethereum หลายรายการ:
https://blog.csdn.net/Flyhps/article/details/83095036

