Analysis of the Vulnerability Found in the vaults.sx Smart Contract

Our analysis on the EOS SX vault attack in May 2021.

We are investigating an attack on the vault. The majority of the EOS and USDT in the vault have been stolen.❗️SX Vault attackDO NOT DEPOSIT in vaultWe will update EOSX so as to stop people from depositing further ASAP.We'll provide a complete-post mortem as soon as we complete our investigation.
EOS Nation is offering a 100,000 USDT bounty to the white hat hacker who identified the re-entry attack exploit on the flash.sx smart contract.The reward will be transferred to the account of your choice once the 1,180,142.5653 EOS and 461,796.8968 USDT are returned to the flash.sx account.
https://www.bloks.io/account/potghpfcmocs
# Just an example
potghpfcmocs → vaults.sx 2 USDT
vaults.sx → flash.sx 2 USDT
token.sx issued 20 SXUSDT to vaults.sx
vaults.sx → potghpfcmocs 20 SXUSDT
// deposit - handle issuance (ex: EOS => SXEOS)
if ( deposit_itr != _vault.end() ) {
potghpfcmocs → vaults.sx 10 SXUSDT
require_recipient( from );
require_recipient( to );
// withdraw - handle retire (ex: SXEOS => EOS)
} else if ( supply_itr != _vault_by_supply.end() ) {
const extended_asset out = calculate_retire( id, quantity );extended_asset sx::vaults::calculate_retire( const symbol_code id, const asset payment )
...
const int64_t S0 = vault.deposit.quantity.amount;
const int64_t R0 = vault.supply.quantity.amount;
const int64_t p = (uint128_t(payment.amount) * S0) / R0;
return { p, vault.deposit.get_extended_symbol() };
// update internal deposit & supply
_vault_by_supply.modify( supply_itr, get_self(), [&]( auto& row ) {
row.deposit -= out;
row.supply.quantity -= quantity;
row.last_updated = current_time_point();
transfer( account, get_self(), out, get_self().to_string() );
// send underlying assets to sender
transfer( get_self(), from, out, get_self().to_string() );
flash.sx - borrow 0.0001
flash.sx → potghpfcmocs 0.0001 USDT
potghpfcmocs → flash.sx 0.0002 USDT
vaults.sx - update id: USDT
// get balance from account
const asset balance = eosio::token::get_balance( contract, account, sym.code() );
...
// update balance
_vault.modify( vault, get_self(), [&]( auto& row ) {
row.deposit.quantity = balance + staked;
row.staked.quantity = staked;
row.last_updated = current_time_point();
});
const int64_t S0 = vault.deposit.quantity.amount;
const int64_t R0 = vault.supply.quantity.amount;
const int64_t p = (uint128_t(payment.amount) * S0) / R0;
// Just an example
potghpfcmocs → vaults.sx 10 SXUSDT
vaults.sx → potghpfcmocs 2 USDT
// Just an example
token.sx - retire quantity: 10 SXUSDT
vaults.sx → potghpfcmocs 1 USDT
token.sx - retire quantity: 10 SXUSDT
Block producers reached consensus to uphold the intent of code.Approximately 1.2M EOS and 462,000 USDT was stolen in a re-entry attack exploit on the flash.sx flash loan smart contract that began on May 14 at 11:28 UTC.The vaults.sx and flash.sx smart contracts were open-source, MSIGed, and passed security audits, however the re-entry exploit was not identified.All of the funds are safe under control of eosio.prods and will be returned to depositors.

EOS Block Producer candidate in the heart of the Americas. We stand for liberty and equality. Mainnet BP: costaricaeos · https://t.me/eoscr