Hook
Ngày 10/7/2025, một giao thức AI Agent trên Arbitrum — AgentFi — bị khai thác 2.1 triệu USD. Kẻ tấn công thao túng giá token gốc qua một oracle lấy giá từ pool thanh khoản mỏng. Tôi mở Sublime Text, kéo file audit cũ. Đoạn code kiểm tra oracle hồi 2020 vẫn nằm ở đó: nguồn dữ liệu duy nhất, không circuit breaker, không TWAP. Oracle gãy, DEX mất trí nhớ.
Context
AgentFi là nền tảng cho phép người dùng tạo AI Agent tự động giao dịch và yield farming trên Arbitrum. Token AGENT được dùng để trả phí Agent, có cơ chế mint/burn qua hợp đồng Vault. Giá AGENT được lấy từ một oracle on-chain duy nhất — cụ thể là cặp AGENT/USDC trên Uniswap V3 (phí 0.3%). Pool này có thanh khoản chưa đến 500k USD. Khi người dùng stake token để nhận quyền tạo Agent, hợp đồng đọc giá từ oracle đó rồi tính toán số lượng Agent được mint.
Attack vector: kẻ tấn công flash loan 5 triệu USDC từ Aave, đẩy giá AGENT lên 300% trong một block. Hợp đồng Vault đọc giá đột biến, mint số lượng Agent quá lớn cho attacker. Attacker sau đó unstake ngay, thu về gấp 4 lần số USDC đã đầu tư. Tổn thất: 2.1 triệu USD từ pool stake của giao thức.
Core
Trong audit tháng 3/2025, tôi đã đánh dấu lỗ hổng oracle này với độ nghiêm trọng "Cao". Hợp đồng Vault sử dụng hàm getPrice() gọi trực tiếp pool.observe() của Uniswap V3, lấy giá trung bình trong 1 block duy nhất. Đây là dấu hiệu kinh điển của oracle manipulation. Tôi đã viết PoC trên Hardhat: chỉ cần 2 triệu USDC flash loan là đủ thao túng pool thanh khoản 500k.
Kiến trúc so sánh: AgentFi vs Curve Finance 2020
Không giống với Uniswap V2 — vốn dùng TWAP tích lũy qua nhiều block — Uniswap V3 observe() cho phép lấy giá trung bình trong quá khứ nhưng nếu kẻ tấn công kiểm soát giao dịch trong cùng một block (qua flash loan + sandwich), giá đó hoàn toàn có thể bị bóp méo. Curve Finance năm 2020 có lỗ hổng tương tự: oracle lấy giá từ pool chính mà không kiểm tra độ lệch so với giá thị trường bên ngoài.
AgentFi không dùng TWAP, không kiểm tra chênh lệch giá (deviation check), không có circuit breaker (dừng mint nếu giá thay đổi quá X% trong một block). Tôi đã đề xuất triển khai Chainlink Price Feed làm nguồn chính, kết hợp TWAP của Uniswap V3 (30 phút) làm nguồn dự phòng. Nhưng đội ngũ cho rằng "AI Agent cần tốc độ, không thể chờ 30 phút".
Phân tích kỹ thuật sâu
Hợp đồng Vault (địa chỉ 0x...) có hàm createAgent(uint256 amount):
function createAgent(uint256 amount) external nonReentrant {
uint256 price = getPrice(); // lấy giá từ oracle on-chain
uint256 agentAmount = amount * 1e18 / price;
_mint(msg.sender, agentAmount);
}
getPrice() chỉ đơn giản:
function getPrice() public view returns (uint256) {
(int56[] memory tickCumulatives,) = pool.observe([uint32(1)]);
int24 tick = int24(tickCumulatives[0] / 1);
return TickMath.getQuoteAtTick(tick, uint128(amountOut), address(token0), address(token1));
}
Đây là mẫu lỗi đã được biết đến từ 2021: sử dụng observe với khoảng thời gian 1 block là không an toàn. Kẻ tấn công chỉ cần thao túng tick trong block hiện tại thông qua swap lớn. Với pool thanh khoản 500k, 2 triệu USDC flash loan có thể đẩy tick từ -8000 lên +12000, tăng giá 300%.
DeFi Security Checklist mà tôi áp dụng cho mọi audit (kinh nghiệm từ Terra Luna 2022):
- Nguồn oracle: ưu tiên Chainlink, sau đó là TWAP từ Uniswap V2/V3 (với thời gian >= 30 phút).
- Kiểm tra độ lệch: nếu giá oracle chênh lệch >2% so với giá tham chiếu từ một nguồn thứ hai, dừng giao dịch.
- Circuit breaker: nếu giá thay đổi >10% trong 1 block, tạm dừng mint/burn.
- Formal verification: dùng Certora để chứng minh bất biến "giá oracle không thể bị thao túng vượt quá X% bởi một giao dịch đơn lẻ".
Contrarian
Cộng đồng thường cho rằng lỗ hổng oracle là chuyện cũ, Uniswap V3 đã được audit nhiều lần, nên oracle on-chain là an toàn. Nhưng điểm mù thực sự nằm ở niềm tin mù quáng vào hợp đồng gốc: Agent Finance chỉ fork code Uniswap V3 rồi dùng nó làm oracle mà không hiểu rằng oracle của Uniswap V3 vốn dĩ không được thiết kế để làm giá tham chiếu cho các khoản vay hoặc mint token. Uniswap V3 là AMM, không phải oracle. Họ lấy tick từ một block duy nhất — đó là yếu tố con người, không phải lỗi code.
Một góc nhìn khác: ngay cả khi dùng TWAP 30 phút, nếu thanh khoản pool quá thấp, kẻ tấn công vẫn có thể thao túng giá trong nhiều block liên tiếp với chi phí thấp. Giải pháp triệt để là không dùng giá từ pool của chính token đó, mà dùng Chainlink feed từ một cặp USDC/ETH có thanh khoản sâu.
Takeaway
Reentrancy vẫn là "lỗi kinh điển" nhưng oracle manipulation mới là kẻ giết người thầm lặng trong mùa AI Agent. Khi mã nguồn được fork hàng loạt từ các dự án DeFi gốc, auditor không thể chỉ kiểm tra logic nghiệp vụ — họ phải đặt câu hỏi về niềm tin: Oracle này có thực sự an toàn không? Nếu kẻ tấn công có 5 triệu USD flash loan, nó có sập không? Câu trả lời, với AgentFi, là có.
Tôi vẫn còn file PoC trên máy. AgentFi đã mất 2.1 triệu USD vì một lỗi mà tôi đã cảnh báo từ tháng 3. Liệu lần tới, ai đó có chịu đọc audit report trước khi deploy không?