เมื่อเวลา 19:48:59 น. ของวันที่ 18 ตุลาคม 2023 ตามเวลาปักกิ่ง กลุ่มการให้กู้ยืมของ Hope.money ถูกโจมตีจากการดำเนินการของสินเชื่อแฟลช
Hope.money ได้สร้างแพลตฟอร์มการให้กู้ยืม HopeLend, การแลกเปลี่ยนแบบกระจายอำนาจของ HopeSwap, สกุลเงินที่มีเสถียรภาพ $HOPE และโทเค็นการกำกับดูแล $LT เพื่อให้ผู้ใช้ได้รับบริการทางการเงินแบบกระจายอำนาจแบบเต็มรูปแบบ
โปรโตคอลที่เกี่ยวข้องกับการโจมตีนี้คือ HopeLend ซึ่งเป็นแพลตฟอร์มการให้กู้ยืมแบบกระจายอำนาจที่ผู้ใช้สามารถจัดหาสภาพคล่องให้กับโปรโตคอลหรือรับรายได้จากสินเชื่อที่มีหลักประกันมากเกินไป
เรื่องราวทั้งหมด
ในการใช้งานโค้ดของ HopeLend มีช่องโหว่ที่สามารถใช้ประโยชน์ได้ในกลุ่มการให้ยืม เมื่อทำลายใบรับรองการฝากเงินเกิดปัญหาการหารจำนวนเต็มไม่ถูกต้องซึ่งทำให้ส่วนจุดทศนิยมถูกตัดทอนส่งผลให้ใบรับรองถูกทำลายจำนวนน้อยกว่า ที่คาดหวัง ได้รับและคาดหวังโทเค็นมูลค่าที่สอดคล้องกัน
ผู้โจมตีใช้ประโยชน์จากข้อบกพร่องนี้เพื่อระบายแหล่งเงินกู้ต่างๆ ที่มีเงินทุนอยู่ใน Hope.money
ในหมู่พวกเขา กลุ่มการให้กู้ยืม hEthWbtc ถูกใช้งานเมื่อ 73 วันที่ผ่านมา แต่ไม่มีเงินทุนอยู่ในนั้น ดังนั้น แฮกเกอร์จึงอัดฉีดเงินจำนวนมากเข้าไปในกลุ่มการให้ยืมเพื่อเพิ่มอัตราคิดลดอย่างมาก ด้วยเหตุนี้ จึงทำให้เงินกู้อื่น ๆ ภายในหมดอย่างรวดเร็ว ธุรกรรมแบบบล็อก กองทุนรวม
สิ่งที่น่าทึ่งยิ่งกว่านั้นคือแฮ็กเกอร์ที่ใช้ประโยชน์จากช่องโหว่นั้นไม่ได้รับเงินทุนเพื่อใช้ประโยชน์จากช่องโหว่นั้น ธุรกรรมการโจมตีของเขาถูกค้นพบโดย front-runner นักวิ่งหน้าเลียนแบบพฤติกรรมการโจมตีของเขาและขโมยการโจมตีทั้งหมดได้สำเร็จ (527 ETH) ในท้ายที่สุด 50 % ของรายได้จากการโจมตี (263 ETH) ถูกใช้โดยนักวิ่งแนวหน้าเพื่อติดสินบนนักขุดที่บรรจุบล็อก (น้ำหนักบรรทุก)
แฮกเกอร์กลุ่มแรกที่ค้นพบช่องโหว่ได้สร้างสัญญาการโจมตีในบล็อก 18377039 และเรียกสัญญาการโจมตีในบล็อก 18377042 ในเวลานี้ front-runner ติดตามธุรกรรมในพูลหน่วยความจำและจำลองสัญญาการโจมตีในฐานะ front-runner ข้อมูลเข้าในสัญญาที่กำลังทำงานอยู่ถูกนำไปใช้ประโยชน์ในบล็อกเดียวกัน 18377042 และธุรกรรมของแฮ็กเกอร์เริ่มต้นในบล็อก 18377042 ล้มเหลวในการดำเนินการเนื่องจากถูกเรียงลำดับตามหลัง front-runner
เงินทุนไปไหน.
หนึ่งชั่วโมงหลังจากที่นักวิ่งแถวหน้าได้รับผลกำไร เขาก็ย้ายเงินทุนไปที่: 0x9a9122Ef3C4B33cAe7902EDFCD5F5a486792Bc3A
เงินทุนไปไหน.
หนึ่งชั่วโมงหลังจากที่นักวิ่งแถวหน้าได้รับผลกำไร เขาก็ย้ายเงินทุนไปที่: 0x9a9122Ef3C4B33cAe7902EDFCD5F5a486792Bc3A
เมื่อเวลา 13:30:23 น. ของวันที่ 20 ตุลาคม ทีมอย่างเป็นทางการที่ต้องสงสัยได้ติดต่อไปยังที่อยู่ดังกล่าว โดยอนุญาตให้นักวิ่งแนวหน้าทิ้ง 26 ETH (กำไร 10%) เป็นรางวัล และได้รับคำตอบจากนักวิ่งแนวหน้า
เงินสุดท้ายถูกโอนไปยังห้องนิรภัยแบบหลายลายเซ็นของ GnosisSafe หลังจากผ่านไปหนึ่งชั่วโมงของการสื่อสาร
ด้านล่างนี้เราจะแสดงช่องโหว่ที่เกิดขึ้นจริงและรายละเอียดว่าแฮกเกอร์ใช้ประโยชน์จากช่องโหว่ดังกล่าวอย่างไร
ข้อมูลล่วงหน้า
*การใช้โปรโตคอลการให้กู้ยืมของ HopeLend นั้นแยกจาก Aave ดังนั้น ตรรกะทางธุรกิจหลักที่เกี่ยวข้องกับช่องโหว่จึงมีอยู่ในเอกสารไวท์เปเปอร์ของ Aave
0x00 เงินฝากและสินเชื่อ
Aave เป็น DeFi ล้วนๆ ธุรกิจการให้กู้ยืมดำเนินการผ่านแหล่งรวมสภาพคล่อง เมื่อผู้ใช้ฝากสภาพคล่องใน Aave พวกเขาคาดหวังว่าจะได้รับรายได้จากการให้กู้ยืม
รายได้จากเงินกู้จะไม่ถูกแจกจ่ายให้กับผู้ใช้ทั้งหมด รายได้ดอกเบี้ยส่วนเล็ก ๆ จะรวมอยู่ในสำรองความเสี่ยง สัดส่วนนี้น้อย และรายได้เงินกู้ส่วนใหญ่จะแจกจ่ายให้กับผู้ใช้ที่ให้สภาพคล่อง
เมื่อให้กู้ยืมเงินฝากใน Aave นั้น Aave จะใช้ส่วนลดเพื่อแปลงจำนวนเงินฝาก ณ จุดต่างๆ ตามเวลาให้เป็นจำนวนเงินฝากที่จุดเริ่มต้นของกลุ่มสภาพคล่อง ดังนั้น ผลรวมของเงินต้นและดอกเบี้ยที่สอดคล้องกับสินทรัพย์อ้างอิงสำหรับแต่ละรายการ จำนวนหุ้นได้โดยตรง โดยคำนวณโดยใช้จำนวน (หุ้น) * ดัชนี (อัตราคิดลด) ซึ่งอำนวยความสะดวกในการคำนวณและทำความเข้าใจอย่างมาก
เข้าใจได้ว่าเป็นกระบวนการที่คล้ายกับการซื้อกองทุนมูลค่าสุทธิเริ่มต้นของกองทุนคือ 1 ผู้ใช้ลงทุน 100 หยวนเพื่อรับ 100 หุ้น สมมติว่าหลังจากช่วงระยะเวลาหนึ่งมูลค่าสุทธิจะกลายเป็น 1.03 ในเวลานี้ ผู้ใช้ลงทุน 100 หยวนอีกครั้งและได้รับ 100 หุ้น คือ 97 และส่วนแบ่งของผู้ใช้ทั้งหมดคือ 197
นี่เป็นการลดราคาสินทรัพย์ตามดัชนี (มูลค่าสุทธิ) เหตุผลที่ทำเช่นนี้ก็เนื่องมาจากผลรวมที่แท้จริงของเงินต้นและดอกเบี้ยของผู้ใช้คูณด้วยดัชนีปัจจุบันโดยใช้ยอดคงเหลือ เมื่อฝากเงินครั้งที่สอง ยอดรวมเงินต้นและดอกเบี้ยที่ถูกต้องของผู้ใช้ควรเป็น 100 * 1.03 + 100 = 203 หากไม่ได้ดำเนินการประมวลผลส่วนลด เงินต้นและดอกเบี้ยหลังจากที่ผู้ใช้ฝากเงิน 100 ในครั้งที่สองจะกลายเป็น (100 +100) * 1.03 = 206 ผิด หากดำเนินการลดราคา ผลรวมของเงินต้นและดอกเบี้ยจะกลายเป็น (100 + 100 / 1.03) * 1.03 = 103 + 100 = 203 ผลลัพธ์ของ 203 ถูกต้อง
กระบวนการโจมตี
0x25126......403907(hETHWBTC พูล)
กระบวนการโจมตี
0x25126......403907(hETHWBTC พูล)
0x5a63e......844e74 (สัญญาโจมตี-ถอนออก)
ผู้โจมตี:
1. ให้ยืมกองทุนแฟลชกู้ยืมเริ่มแรกและจำนำ
ผู้โจมตียืม 2300WBTC จาก Aave Flash Loan เป็นครั้งแรก และให้คำมั่นสัญญา 2,000 WBTC ให้กับ HopeLend เงินจะถูกโอนไปยังสัญญา hEthWbtc ของ HopeLend (0x251...907) และจะได้รับ 2,000 hETHWBTC ที่เกี่ยวข้อง
2. ใช้กลุ่มสินเชื่อที่ว่างเปล่าเพื่อจัดการอัตราคิดลดเริ่มต้น (liquidityIndex)
ยืม 2,000 WBTC ผ่านการกู้ยืมแฟลชจาก HopeLend
ค่าปัจจุบันคือ 1 heTHWBTC = 1 WBTC
ตามการดำเนินการปกติของการฝากและถอน ETHWBTC เพื่อแลกเปลี่ยนเป็น WBTC อัตราส่วนการแลกเปลี่ยนจะไม่ได้รับผลกระทบ (อัตราส่วนการแลกเปลี่ยนจะได้รับผลกระทบก็ต่อเมื่อมีการได้รับดอกเบี้ย และ 1 heTHWBTC จะได้รับ WBTC มากขึ้น)
ณ จุดนี้ แฮกเกอร์เริ่มจัดการอัตราคิดลดผ่านชุดการดำเนินการที่ซับซ้อน:
• แฮกเกอร์โอน WBTC จำนวน 2,000 WBTC ที่ได้รับโดยตรงไปยังสัญญา hEthWbtc ของ HopeLend (0x251…907) ผ่านการโอนโดยตรง ขั้นตอนนี้ไม่ใช่การชำระคืนเงินกู้
• จากนั้นแฮกเกอร์จึงถอน WBTC ส่วนใหญ่ (1999.999…) ที่ให้คำมั่นสัญญาในขั้นตอนที่ 1 ดังนั้นขั้นตอนก่อนหน้านี้จำเป็นต้องโอน WBTC กลับเพื่อเติมเต็มสินทรัพย์ในพูล
• สุดท้ายแล้วแฮกเกอร์จะคงไว้เพียงหน่วยที่เล็กที่สุด (1e-8) ของ hEthWbtc เท่านั้น ไม่สามารถกล่าวถึงได้ครบถ้วนในที่นี้เพราะต้องทิ้งเอาไว้นิดหน่อย เมื่อคำนวณ อัตราคิดลด (liquidityIndex) จะขึ้นอยู่กับ อันที่มีอยู่บวกอันใหม่ , หากเคลียร์ อัตราคิดลด (liquidityIndex) จะกลายเป็น 0 และสัดส่วนในพูลไม่สามารถไม่สมดุลได้
• ใช้ wBTC ที่แลกเปลี่ยนเพื่อทำลาย hEthWbtc ส่วนใหญ่ในขั้นตอนก่อนหน้า บวกกับ wBTC ที่เหลือจาก flash Loan ก่อนหน้า และคืนแฟลช Loan ที่ให้ยืมไปยังกลุ่ม HopeLend โดยจ่าย WBTC ทั้งหมด 2,001.8 WBTC (รวมดอกเบี้ย wBTC 1.8)
• กระบวนการข้างต้นทำลาย hEthWbtc ส่วนใหญ่ โดยเหลือเพียง 1 หน่วยขั้นต่ำ (1e-8) ของ hEthWbtc ในบัญชีแฮ็กเกอร์ ซึ่งจะช่วยลดจำนวน hETHWBTC ทั้งหมด แต่มี 2,001.8 wBTC ในกลุ่มการให้ยืม ในขณะนี้ ส่วนลด อัตรา (liquidityIndex) สูงถึง 126,000,000 อย่างน่าประหลาดใจ
สิ่งนี้เกี่ยวข้องกับความรู้ที่ว่าดอกเบี้ยของผู้ใช้เงินฝากโดยพื้นฐานมาจากการเติบโตของสภาพคล่องใน Pool Lending Pool จะปรับอัตราดอกเบี้ยการยืมและเงินฝากแบบไดนามิกตามอัตราดอกเบี้ยเงินฝากและอัตราการใช้
ที่นี่ เมื่อพูลได้รับสภาพคล่องเพิ่มเติมจากดอกเบี้ยเงินกู้แฟลช (1.8WBTC) เจ็ดสิบเปอร์เซ็นต์ (126,000,000) จะรวมอยู่ใน liquidityIndex (liquidityIndex) ซึ่งใช้ในการคำนวณมูลค่าลดต่อหน่วยเงินฝาก (hEthWbt)
เนื่องจากพูลว่างเปล่าก่อนการดำเนินการของแฮ็กเกอร์ สภาพคล่องทั้งหมดหลังจากการชำระคืนจึงเป็นเพียง 1 จำนวนคือ 126000000 และสภาพคล่องดัชนีเริ่มต้นคือ 1 ผลลัพธ์คือ 126000001
3. ขยายอัตราคิดลดต่อไป
แฮกเกอร์ยังคงยืม 2,000 WBTC จาก HopeLend ผ่านการกู้ยืมแบบแฟลชและส่งคืนเพิ่มเติม 1.8 WBTC ในแต่ละครั้ง ทำให้ดัชนีสภาพคล่องสามารถสะสมได้ 126,000,000 ในแต่ละครั้ง
แฮกเกอร์ทำขั้นตอนนี้ซ้ำ 60 ครั้ง และในที่สุดสภาพคล่องดัชนีก็สูงถึง 7,560,000,001 มูลค่าส่วนลดของ hEthWBTC ขั้นต่ำ 1 หน่วยที่ผู้โจมตีถืออยู่อาจสูงถึง 75.6WBTC (ประมาณ 2.14 ล้านดอลลาร์)
นอกจากนี้ยังช่วยให้แฮกเกอร์สามารถควบคุม hEthWBTC และบิดเบือนมูลค่าของมันได้
4. ล้างกลุ่มการให้ยืมของกองทุนอื่นที่มีอยู่และสร้างรายได้
ผู้โจมตีใช้ hEthWBTC ขั้นต่ำ 1 หน่วยเป็นหลักประกันในการยืมสินทรัพย์จำนวนมากจากกลุ่มโทเค็นอีกห้าแห่งของ HopeLend
รวม:
- 175.4 - เวธ
- 145,522.220985 - USDT
- 123,406.134999 - ดอลล่าร์สหรัฐ
- 844,282.284002229528476039 - ความหวัง
- 220,617.821736563540747967 - stHOPE
โทเค็นเหล่านี้ถูกแปลงเป็น WBTC และ WETH ผ่าน Uniswap เป็นรายได้ หลังจากหักค่าธรรมเนียมต่าง ๆ แล้ว กำไรสุดท้ายของแฮ็กเกอร์จะอยู่ที่ประมาณ 263 WETH (ไม่รวม 263.9 WETH ในเพย์โหลดการติดสินบน)
ทำไมแฮกเกอร์จึงสามารถยืมเงินจำนวนมากจากแหล่งอื่นได้:
ในการกู้ยืมเงินหรือถอนเงินฝาก สัญญาการให้กู้ยืมจะตรวจสอบสถานะทรัพย์สินจำนองของผู้ใช้บริการเพื่อให้แน่ใจว่าเงินกู้ไม่เกินจำนอง
เนื่องจากแฮกเกอร์เคยใช้อัตราคิดลดมาก่อน และอัตราคิดลดจะรวมอยู่ในการคำนวณมูลค่าการจำนองโดยใช้ตัวคูณรายได้ปกติ มูลค่าการจำนองของหนึ่งหน่วยของ hEthWBTC ในมือของเขาจึงสูงถึง 75.6WBTC
ทุกครั้งที่เขายืมเงินจากแหล่งอื่น แฮกเกอร์ก็ผ่านการตรวจสอบทรัพย์สินหลักประกันได้อย่างง่ายดาย
ในเวลานี้ ผู้โจมตีลงทุนทั้งหมด 2,000+1.8*60 WBTC ใน HopeLend เพื่อจัดการ liquidityIndex เหลือเพียง 1 หน่วยของ hEtthWBTC
5. ใช้ประโยชน์จากจุดอ่อนที่สำคัญ (ข้อผิดพลาดในการหารจำนวนเต็ม) เพื่อถอนเงิน
ในการถอน wBTC ที่ลงทุนไปก่อนหน้านี้ ผู้โจมตีได้ปรับใช้สัญญาการโจมตีอื่น: 0x5a63e......844e74 และเรียกใช้เมธอด withdrawAllBtc() ในนั้น
กระบวนการช่องโหว่มีดังนี้:
1 ฝากครั้งแรก 151.20000002 wBTC ตามสภาพคล่องดัชนีปัจจุบัน (1 หน่วยขั้นต่ำ hEthWBTC=75.6wBTC) ผู้โจมตีจะได้รับ hEthWBTC ขั้นต่ำ 2 หน่วย
② ถอน 113.4 wBTC คำนวณย้อนกลับส่วนแบ่ง hEthWBTC ที่สอดคล้องกัน และดำเนินการเบิร์นบน hEthWBTC
3 113.4 wBTC กำหนดให้ทำลาย hEthWBTC ขั้นต่ำ 1.9999999998 หน่วย อย่างไรก็ตาม เนื่องจากความแม่นยำของฟังก์ชัน div ทำให้ hEthWBTC ขั้นต่ำเพียงหน่วยเดียวเท่านั้นที่ถูกทำลาย จึงกลายเป็นช่องโหว่ที่สามารถหาประโยชน์ได้ แฮกเกอร์ยังคงรักษา hEthWBTC ขั้นต่ำได้ 1 หน่วย
ช่องโหว่ที่สำคัญ
วิธีการเบิร์นของ hEthWBTC เรียกว่า rayDiv การหารที่มีความแม่นยำสูง
ที่นี่:
a=11340000000 (WBTC วางแผนที่จะถอนออก)
b=756000000100000000000000009655610336 (อัตราคิดลด)
แม้ว่า (a*1e27+b/2)/b = 1.9999999998 แต่วิธี div ของ solidity จะตัดทอนและส่งกลับ 1 ซึ่งเทียบเท่ากับ 11340000000 / 7560000001 ตำแหน่งทศนิยมจะถูกตัดทอนหลังการหาร
แม้ว่า (a*1e27+b/2)/b = 1.9999999998 แต่วิธี div ของ solidity จะตัดทอนและส่งกลับ 1 ซึ่งเทียบเท่ากับ 11340000000 / 7560000001 ตำแหน่งทศนิยมจะถูกตัดทอนหลังการหาร
0x5a63 (สัญญาการโจมตี - เงินสดออก) ยังคงฝากเงิน 75.60000001WBTC และได้รับ hEthWBTC ขั้นต่ำ 1 หน่วยพอดี ดังนั้นยังคงถือ hEthWBTC ขั้นต่ำ 2 หน่วยต่อไป
ในรอบการถอน 113.40000000wBTC และฝาก 75.60000001wBTC นี้ ผู้โจมตีสามารถรับ 37.8 wBTC จากอากาศในแต่ละครั้ง
หลังจากผ่านไป 58 รอบ ผู้โจมตีก็ถอน wBTC ทั้งหมดที่ลงทุนในระยะแรกออก และคืนเงินกู้ยืมแฟลชของ Aave ได้สำเร็จ
สรุปแล้ว
เนื่องจากไม่ได้เริ่มต้นกลุ่มการให้กู้ยืม hEthWBTC ผู้โจมตีสามารถจัดการสภาพคล่องดัชนีและเพิ่มเป็นสูงสุดได้อย่างง่ายดาย หลังจากที่อัตราการถอนถูกขยายอย่างมากเป็นตัวหารเนื่องจากข้อผิดพลาดในการตัดทอนของการหารจำนวนเต็ม จึงง่ายกว่าที่จะถอนตัว การลงทุนครั้งก่อนในบล็อกเดียว .
ในกลุ่มสินเชื่อที่มีการดำเนินงานที่ดี ไม่ใช่เรื่องง่ายที่การเพิ่มดอกเบี้ยเงินกู้เล็กน้อยจะทำให้อัตราคิดลดเพิ่มขึ้นอย่างมาก เนื่องจากมีสภาพคล่องอยู่ในกลุ่มอยู่แล้ว
ความคิดเห็นทั้งหมด