Reentrancy Attack คืออะไรและทำงานอย่างไร?
เทคโนโลยีบล็อกเชนได้พบเห็นการละเมิดระดับสูงหลายครั้งที่เกี่ยวข้องกับสินทรัพย์ดิจิทัลจำนวนมาก ซึ่งเรียกรวมกันว่า “การโจมตีย้อนกลับ” แม้ว่าเหตุการณ์ดังกล่าวจะมีความถี่ลดลงเมื่อเร็วๆ นี้ แต่ผลกระทบที่อาจเกิดขึ้นต่อความปลอดภัยของแอปพลิเคชันบล็อกเชนและผู้ใช้ปลายทางยังคงเป็นสิ่งที่น่าสังเกต
การโจมตีแบบ Reentrant เกี่ยวข้องกับการใช้ประโยชน์จากช่องโหว่ในแอปพลิเคชันซอฟต์แวร์ที่อนุญาตให้เรียกใช้รหัสที่เป็นอันตรายซ้ำ ๆ ซึ่งนำไปสู่การเข้าถึงหรือควบคุมระบบโดยไม่ได้รับอนุญาต การโจมตีเหล่านี้มักเกิดขึ้นเมื่อแอปพลิเคชันล้มเหลวในการจัดการทรัพยากรหน่วยความจำหรือการตรวจสอบความถูกต้องของข้อมูลเข้าอย่างเหมาะสม ทำให้ผู้โจมตีสามารถจัดการกับโฟลว์ของโปรแกรมและได้รับสิทธิ์ขั้นสูง เพื่อลดความเสี่ยงของการโจมตีย้อนกลับ นักพัฒนาต้องแน่ใจว่ามีการทดสอบและตรวจสอบโค้ดอย่างละเอียด ใช้แนวปฏิบัติในการเข้ารหัสที่ปลอดภัย และปฏิบัติตามแนวทางความปลอดภัยมาตรฐานอุตสาหกรรม เช่น OWASP (Open Web Application Security Project) นอกจากนี้ ควรใช้การอัปเดตและแพตช์เป็นประจำทันทีเพื่อแก้ไขช่องโหว่ที่ทราบและป้องกันภัยคุกคามที่เกิดขึ้นใหม่
Reentrancy Attack คืออะไร?
การโจมตีของผู้กลับเข้าที่เกิดขึ้นเมื่อสมาร์ทคอนแทรคที่อ่อนแอเรียกการเรียกจากภายนอกที่ผิดกฎหมายไปยังคู่หูที่ชั่วร้าย ยกเลิกคำสั่งชั่วคราวเหนือความต่อเนื่องของการทำธุรกรรม ต่อจากนั้น สัญญาที่มุ่งร้ายจะเรียกใช้ฟังก์ชันสัญญาอัจฉริยะเริ่มต้นอย่างต่อเนื่องในระหว่างการดำเนินการอย่างต่อเนื่อง ทำให้ทรัพยากรทางการเงินหมดสิ้นไปพร้อม ๆ กัน
ประการแรก ตรวจสอบยอดเงินที่มีอยู่; ประการที่สอง โอนเงินที่ถอนไปยังบัญชีอื่น และประการที่สาม ปรับปรุงยอดเงินคงเหลือให้สอดคล้องกัน อย่างไรก็ตาม หากบุคคลที่ไม่ได้รับอนุญาตเข้าถึงวัฏจักรนี้ก่อนที่จะมีการอัปเดตยอดคงเหลือ พวกเขาอาจสามารถระบายเงินในกระเป๋าเงินซ้ำๆ ผ่านการถอนออกซ้ำๆ
เครดิตรูปภาพ: Etherscan
หนึ่งในการแฮ็กบล็อกเชนที่น่าอับอายที่สุด นั่นคือการแฮ็ก Ethereum DAO ซึ่งครอบคลุมโดย Coindesk เป็นการโจมตีแบบ reentrancy ที่นำไปสู่การสูญเสีย eth มูลค่ากว่า 60 ล้านดอลลาร์ และเปลี่ยนเส้นทางของ cryptocurrency ที่ใหญ่เป็นอันดับสองโดยพื้นฐาน
การโจมตี Reentrancy ทำงานอย่างไร
จินตนาการถึงสถาบันการเงินที่ตั้งอยู่ในชุมชนพื้นเมืองของคุณ สภาพคล่องรวมของสถานประกอบการนี้มีมูลค่าถึงหนึ่งล้านดอลลาร์ อย่างไรก็ตาม ธนาคารใช้โปรโตคอลการบัญชีที่ไม่สมบูรณ์ ทำให้บุคลากรอัปเดตบัญชีลูกค้าล่าช้าจนถึงค่ำ
เมื่อไปเยี่ยมชมเมือง คนรู้จักของคุณที่เป็นนักลงทุนก็ค้นพบความคลาดเคลื่อนในบันทึกทางการเงิน เพื่อแสดงให้เห็นถึงช่องโหว่ เขาสร้างบัญชีธนาคารและโอนเงินเริ่มต้นหนึ่งร้อยดอลลาร์ ในวันถัดไปเขาเริ่มขอถอนเงินจำนวนเท่ากัน ภายในเวลาเพียงหกสิบนาที เขาทำขั้นตอนซ้ำอีกครั้ง แม้ว่าธุรกรรมก่อนหน้านี้ยังไม่ได้ดำเนินการโดยสถาบันก็ตาม ดังนั้น เนื่องจากความล้มเหลวของธนาคารในการอัปเดตข้อมูลบัญชีของเขา เงินที่มีอยู่ของบุคคลนั้นจึงยังคงมียอดคงเหลือเป็นศูนย์ ด้วยเหตุนี้ เขาจึงเรียกเงินตามจำนวนที่ขอได้สำเร็จหลายครั้ง ทำให้ทรัพย์สินทั้งหมดหมดลงจนไม่มีเหลือ เฉพาะเมื่อสิ้นสุดวันทำงานเท่านั้น เมื่อกระทบยอดผู้นำของพวกเขา
ภายในขอบเขตของข้อตกลงอัจฉริยะ การดำเนินการจะดำเนินการในลักษณะต่อไปนี้:
นักแสดงที่ประสงค์ร้ายพบข้อบกพร่องในสัญญาอัจฉริยะที่มีชื่อว่า"X"ซึ่งเปิดโอกาสให้แสวงหาผลประโยชน์
เมื่อเริ่มต้นการทำธุรกรรมของแท้ที่มุ่งสู่สัญญา X ซึ่งมีเจตนาที่จะโอนสินทรัพย์ไปยังสัญญาที่ชั่วร้ายซึ่งระบุว่าเป็น’Y’การดำเนินการหลังจะเรียกใช้ฟังก์ชันที่อ่อนแอภายในสัญญาเดิมในระหว่างการดำเนินการ
การระงับการดำเนินการตามสัญญาของ X นั้นขึ้นอยู่กับความจำเป็นในการโต้ตอบกับเหตุการณ์ภายนอก ส่งผลให้การทำงานหยุดชะงักชั่วคราวหรือเกิดความล่าช้า
ในขณะที่การดำเนินการของโปรแกรม X หยุดลงชั่วคราว ผู้โจมตีสามารถเรียกใช้อินสแตนซ์เดียวของฟังก์ชันที่อ่อนแอภายในนั้นซ้ำๆ ได้โดยการเรียกใช้หลายครั้ง จึงทำให้ฟังก์ชันถูกดำเนินการหลายครั้งติดต่อกัน
เมื่อเข้าสู่รายการถัดไปทุกครั้ง การกำหนดค่าภายในของสัญญาจะผ่านการปรับเปลี่ยน จึงทำให้ผู้ไม่ประสงค์ดีสามารถทำลายทรัพย์สินที่โอนจากบัญชี X ไปยังบัญชี Y อย่างเป็นระบบ
เมื่อทรัพยากรที่มีอยู่หมดลง การเข้าร่วมเพิ่มเติมจะหยุดลง เนื่องจากประสิทธิภาพที่เลื่อนออกไปในที่สุดจะบรรลุผล ซึ่งจะเป็นการอัปเดตสถานะของข้อตกลงให้สอดคล้องกับการเข้าถึงใหม่ครั้งล่าสุด
ในกรณีส่วนใหญ่ ผู้ประสงค์ร้ายสามารถใช้ประโยชน์จากช่องโหว่ที่กลับเข้ามาใหม่เพื่อประโยชน์ของพวกเขา ส่งผลให้มีการลบสินทรัพย์ออกจากสัญญาอัจฉริยะโดยไม่ได้รับอนุญาต
ตัวอย่างของการโจมตี Reentrancy
สถานการณ์สมมติที่เกี่ยวข้องกับการโจมตีการกลับเข้าที่ใหม่บนสัญญาอัจฉริยะ โดยใช้ตัวอย่างของฟังก์ชันการกลับเข้าที่ใหม่ภายในสัญญา มีไว้เพื่อจุดประสงค์ในการอธิบาย สัญญาที่เป็นปัญหามีจุดกลับคืนที่อำนวยความสะดวกในการดำเนินการบางอย่างซ้ำๆ ซึ่งทำให้ผู้ไม่ประสงค์ดีสามารถแสวงหาผลประโยชน์ได้
// Vulnerable contract with a reentrancy vulnerability
pragma solidity ^0.8.0;
contract VulnerableContract {
mapping(address => uint256) private balances;
function deposit() public payable {
balances[msg.sender] \+= msg.value;
}
function withdraw(uint256 amount) public {
require(amount <= balances[msg.sender], "Insufficient balance");
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
balances[msg.sender] -= amount;
}
}
VulnerableContract อนุญาตให้ผู้ใช้ทำการฝาก Ether (ETH) โดยใช้ฟังก์ชัน ฝาก
ในขณะเดียวกัน ผู้ใช้จะได้รับอนุญาตให้ดึงเงินที่ฝากไว้ผ่านคุณสมบัติ ถอน
น่าเสียดายที่การดำเนินการหลังนี้มีความเสี่ยงต่อช่องโหว่ reentrant ภายในฟังก์ชัน ถอน
เอง โดยเฉพาะอย่างยิ่ง เมื่อเริ่มต้นคำขอถอนเงิน สัญญาจะจ่ายล่วงหน้าตามจำนวนเงินที่ระบุให้กับผู้รับที่กำหนดของผู้ใช้ก่อนที่จะแก้ไขยอดเงินในบัญชี การดำเนินการเชิงรุกนี้เผยให้เห็นข้อบกพร่องด้านความปลอดภัยที่อาจถูกทำร้ายโดยผู้ประสงค์ร้ายที่ต้องการใช้ประโยชน์จากสถานการณ์
ตัวอย่างสมมุติฐานของสัญญาอัจฉริยะที่เป็นอันตรายอาจนำเสนอเพื่อตรวจสอบ
// Attacker's contract to exploit the reentrancy vulnerability
pragma solidity ^0.8.0;
interface VulnerableContractInterface {
function withdraw(uint256 amount) external;
}
contract AttackerContract {
VulnerableContractInterface private vulnerableContract;
address private targetAddress;
constructor(address _vulnerableContractAddress) {
vulnerableContract = VulnerableContractInterface(_vulnerableContractAddress);
targetAddress = msg.sender;
}
// Function to trigger the attack
function attack() public payable {
// Deposit some ether to the vulnerable contract
vulnerableContract.deposit{value: msg.value}();
// Call the vulnerable contract's withdraw function
vulnerableContract.withdraw(msg.value);
}
// Receive function to receive funds from the vulnerable contract
receive() external payable {
if (address(vulnerableContract).balance >= 1 ether) {
// Reenter the vulnerable contract's withdraw function
vulnerableContract.withdraw(1 ether);
}
}
// Function to steal the funds from the vulnerable contract
function withdrawStolenFunds() public {
require(msg.sender == targetAddress, "Unauthorized");
(bool success, ) = targetAddress.call{value: address(this).balance}("");
require(success, "Transfer failed");
}
}
เมื่อเริ่มการโจมตี:
คลาส AttackerContract
ในระหว่างการเริ่มต้น ได้รับที่อยู่ของ VulnerableContract
ผ่านตัวสร้างและกำหนดให้กับฟิลด์ vulnerableContract
ผู้โจมตีเรียกใช้ฟังก์ชัน"ฝากเงิน"ของสัญญาอัจฉริยะที่เป็นเป้าหมาย โดยฉีด Ether (ETH) ตามจำนวนที่ระบุตามด้วยการเรียกใช้ฟังก์ชัน"ถอน"ทันทีภายในอินสแตนซ์สัญญาเดียวกัน
ฟังก์ชันการถอนภายใน VulnerableContract จะถ่ายโอน Ethereum (ethereum) จำนวนหนึ่งไปยังบัญชีของผู้กระทำความผิดก่อนที่จะปรับยอดคงเหลือในบัญชี อย่างไรก็ตาม เนื่องจากสัญญาอัจฉริยะของผู้บุกรุกยังคงถูกระงับตลอดการร้องขอจากภายนอก การดำเนินการยังไม่สมบูรณ์ ดำเนินการ
เมื่อมีการเรียกใช้ฟังก์ชัน receive
ภายใน AttackerContract จะเริ่มทำงานโดยเป็นผลมาจาก VulnerableContract ที่ส่ง Ether ไปยังสัญญานี้ระหว่างการโทรจากภายนอก
เมื่อได้รับธุรกรรม ฟังก์ชันรับจะตรวจสอบก่อนว่ายอดคงเหลือของ AttackerContract เพียงพอสำหรับการดำเนินการถอนเงินที่ต้องการหรือไม่ ซึ่งต้องใช้ Ether อย่างน้อยหนึ่งหน่วย หากเป็นเช่นนั้น ฟังก์ชัน Withdraw ของ VulnerableContract จะถูกเรียกใช้งานอีกครั้งในภายหลัง ทำให้ผู้โจมตีสามารถใช้ประโยชน์จากช่องโหว่นี้ซ้ำๆ และระบายเงินทุนของสัญญาจนกว่าจะมีการตรวจสอบอย่างเพียงพอภายในรหัสเพื่อป้องกันการละเมิดดังกล่าว
ทำซ้ำขั้นตอนที่สามถึงห้าซ้ำๆ ตราบเท่าที่ VulnerableContract ยังคงมีเงินทุนอยู่ และสัญญาของผู้โจมตีไม่สะสม Ether ในปริมาณที่มากเกินไป
ท้ายที่สุดแล้ว ผู้บุกรุกอาจใช้ฟังก์ชันของฟังก์ชัน ถอน StolenFunds'ภายในบริบทของ
AttackerContract ` ซึ่งจะเป็นการแยกและเก็บรักษาทรัพย์สินใด ๆ ที่ถูกรวบรวมโดยสัญญาที่ถูกบุกรุกระหว่างการดำเนินการ
การเกิดการโจมตีอาจแตกต่างกันไปในแง่ของความเร็ว โดยคำนึงถึงปัจจัยต่างๆ เช่น ประสิทธิภาพของเครือข่าย ในกรณีที่มีสัญญาอัจฉริยะที่ซับซ้อนเข้ามาเกี่ยวข้อง เช่น การแฮ็ก DAO ที่น่าอับอายซึ่งทำให้เกิดการแยกระหว่าง Ethereum และ Ethereum Classic การโจมตีมีแนวโน้มที่จะเกิดขึ้นในช่วงเวลาหลายชั่วโมง
วิธีป้องกัน Reentrancy Attack
เพื่อลดความเสี่ยงของการโจมตีผู้ที่กลับเข้ามาใหม่บนสัญญาอัจฉริยะของเรา จำเป็นอย่างยิ่งที่เราจะต้องปฏิบัติตามแนวทางที่กำหนดไว้สำหรับการพัฒนาสัญญาที่ปลอดภัย โดยเฉพาะอย่างยิ่ง เราจะนำรูปแบบ"การตรวจสอบผลกระทบ-การโต้ตอบ"(CEI) มาใช้ตามที่แสดงในข้อมูลโค้ดต่อไปนี้
// Secure contract with the "checks-effects-interactions" pattern
pragma solidity ^0.8.0;
contract SecureContract {
mapping(address => uint256) private balances;
mapping(address => bool) private isLocked;
function deposit() public payable {
balances[msg.sender] \+= msg.value;
}
function withdraw(uint256 amount) public {
require(amount <= balances[msg.sender], "Insufficient balance");
require(!isLocked[msg.sender], "Withdrawal in progress");
// Lock the sender's account to prevent reentrancy
isLocked[msg.sender] = true;
// Perform the state change
balances[msg.sender] -= amount;
// Interact with the external contract after the state change
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
// Unlock the sender's account
isLocked[msg.sender] = false;
}
}
ในรุ่นที่อัปเดตนี้ เราได้รวมกลไกเพิ่มเติมในการตรวจสอบสถานะของบัญชีแต่ละบัญชีในระหว่างกระบวนการถอนเงิน โดยเฉพาะอย่างยิ่ง เราใช้แอตทริบิวต์ใหม่ที่ชื่อว่าเพื่อระบุว่าบัญชีใดบัญชีหนึ่งมีธุรกรรมที่รอดำเนินการหรือไม่ เมื่อผู้ใช้เริ่มต้นคำขอถอนเงิน สัญญาอัจฉริยะจะตรวจสอบสถานะปัจจุบันของบัญชีของพวกเขาโดยตรวจสอบความพร้อมใช้งานผ่านตัวแปร หากพบว่าบัญชีถูกครอบครองด้วยธุรกรรมอื่นที่กำลังดำเนินอยู่ บัญชีนั้นจะถูกตั้งค่าสถานะเป็น และผู้ใช้จะได้รับแจ้งทันทีว่าพวกเขาไม่สามารถดำเนินการตามที่ร้องขอได้จนกว่าธุรกรรมก่อนหน้านี้จะเสร็จสมบูรณ์และได้รับการแก้ไข มาตรการนี้ทำหน้าที่ป้องกันความขัดแย้งที่อาจเกิดขึ้นและรับรองความสมบูรณ์ของระบบโดยรวมโดยจำกัดการดำเนินการพร้อมกันสำหรับแต่ละบัญชี
หากบัญชีไม่ได้ถูกล็อค สัญญาอัจฉริยะจะดำเนินการตามการอัปเดตสถานะที่ร้องขอและการโต้ตอบที่จำเป็นกับระบบภายนอก หลังจากขั้นตอนนี้ บัญชีจะถูกปลดล็อคอีกครั้ง ซึ่งจะทำให้สามารถทำธุรกรรมการถอนต่อไปได้ในอนาคต
ประเภทของการโจมตี Reentrancy
เครดิตรูปภาพ: Ivan Radic/Flickr
โดยทั่วไปแล้ว มีสามประเภทหลักของการโจมตีย้อนกลับที่สามารถจำแนกตามลักษณะที่พวกมันถูกโจมตี
แนวคิดของ"การโจมตีแบบย้อนกลับครั้งเดียว"หมายถึงสถานการณ์ที่ผู้โจมตีกำหนดเป้าหมายไปยังฟังก์ชันที่มีแนวโน้มที่จะถูกป้อนกลับหลายครั้งภายในระยะเวลาอันสั้น โดยการเรียกใช้แต่ละครั้งอาจนำไปสู่การเข้าถึงหรือควบคุมระบบโดยไม่ได้รับอนุญาต ด้วยการใช้การป้องกันที่เพียงพอ เช่น การตรวจสอบรหัสอย่างละเอียดและกลไกการล็อคที่เหมาะสม นักพัฒนาสามารถลดความเสี่ยงที่เกิดจากการโจมตีประเภทนี้ได้อย่างมีประสิทธิภาพ
ในการโจมตีข้ามฟังก์ชัน ผู้ประสงค์ร้ายจะใช้ประโยชน์จากฟังก์ชันที่อ่อนแอลงเพื่อเรียกใช้ฟังก์ชันอื่นภายในสัญญาอัจฉริยะเดียวกันที่ใช้สถานะร่วมกันกับฟังก์ชันที่ถูกบุกรุก ฟังก์ชันเป้าหมายที่เรียกใช้โดยผู้บุกรุกมีผลที่ตามมาที่น่าดึงดูด ทำให้ล่อลวงให้แสวงหาผลประโยชน์มากขึ้น ด้วยเหตุนี้ การโจมตีเหล่านี้จึงค่อนข้างท้าทายในการระบุและตอบโต้ จึงจำเป็นต้องมีการตรวจสอบและป้องกันอย่างเข้มงวดระหว่างหน่วยงานที่เกี่ยวข้องเพื่อลดผลกระทบให้เหลือน้อยที่สุด
การโจมตีข้ามสัญญาเกิดขึ้นเมื่อสัญญาภายนอกสื่อสารกับบุคคลที่อ่อนแอ ซึ่งสถานะของสัญญาหลังถูกเรียกใช้ก่อนเวลาอันควรระหว่างการมีเพศสัมพันธ์ โดยทั่วไป เหตุการณ์ดังกล่าวเกิดขึ้นเมื่อสัญญามากกว่าหนึ่งฉบับแบ่งปันทรัพยากรร่วมกัน และบางสัญญาแก้ไขทรัพยากรดังกล่าวโดยประมาท เพื่อป้องกันภัยคุกคามนี้ มาตรการรักษาความปลอดภัยที่เข้มงวด เช่น การใช้ช่องทางเข้ารหัสสำหรับการสื่อสาร และการดำเนินการประเมินความปลอดภัยของสัญญาอัจฉริยะเป็นประจำเป็นสิ่งที่จำเป็น
การป้องกันการโจมตีซ้ำจำเป็นต้องใช้มาตรการตอบโต้ที่แตกต่างกันสำหรับการนำเสนอการโจมตีดังกล่าวแบบต่างๆ
อยู่อย่างปลอดภัยจากการโจมตี Reentrancy
แอปพลิเคชันที่ใช้บล็อกเชนประสบกับความพ่ายแพ้ทางการเงินที่สำคัญ เช่นเดียวกับการพังทลายของความเชื่อมั่นเนื่องจากการแสวงหาประโยชน์กลับเข้ามาอย่างแพร่หลาย ด้วยเหตุนี้ ผู้พัฒนาจึงต้องปฏิบัติตามแนวทางที่แนะนำอย่างเคร่งครัดเมื่อสร้างสัญญาอัจฉริยะเพื่อป้องกันการละเมิดความปลอดภัยดังกล่าว
เพื่อเพิ่มการป้องกันสัญญาอัจฉริยะ นักพัฒนาซอฟต์แวร์ควรนำกลไกการถอนที่เชื่อถือได้มาใช้ พึ่งพาห้องสมุดที่มีชื่อเสียง และดำเนินการตามขั้นตอนการตรวจสอบที่ครอบคลุม นอกจากนี้ จำเป็นอย่างยิ่งสำหรับพวกเขาที่จะต้องตระหนักถึงความเสี่ยงด้านความปลอดภัยที่เปลี่ยนแปลงไป และดำเนินการตามมาตรการป้องกันอย่างแข็งขันเพื่อรักษาความสมบูรณ์โดยรวมของสภาพแวดล้อมบล็อกเชน