Skip to content
by Unity Nodes
NEAR Protocol

NEAR Live Contract Review | Episode 9: BananaSwap

about 51:54 long

Nothing is loaded from YouTube until you press play, and then without tracking cookies.

About this video

We discuss BANANAswap contract written in AssemblyScript. The contract itself is only about 150 lines of code. It implements a receiver interface for a vault fungible token transfer and integrates with the banana token from Berry Club. During this review we discuss a possibility to exploit the integer overflow vulnerability in the bananaswap contract. We also suggest a way how this exploit may lead to draining the pool.

Contract source code: https://github.com/vgrichina/bananasw...

Berry Club: berryclub.io

Banana Swap: bananaswap.berryclub.io

Follow the latest from NEAR Protocol on,

Website: nearprotocol.com Discord: near.ai/discord Medium: near.ai/medium Twitter: near.ai/twitter GitHub: near.ai/github

#WhiteboardSeries #Blockchain #FutureIsNEAR

Transcript

5,512 words

Captions as YouTube publishes them, usually machine-generated, so product and people names can be misheard.

  1. 0:04

    hi everyone uh this is another episode of live contract reviews and today we have another part of the very club series and today we're gonna review banana soap so banana swap was created by vlad and it's actually written in assembly script which is interesting because entire very club contracts is written in rust and this one is assembly script so there's some magic that happens because we use json

  2. 0:44

    but this makes it easier to interact between two different languages so banana swap is a website and a contract it acts like a unislope you have your balances and you have a pool balances except for unislope contracts you can deposit extra liquidity and for uh for this implementation the liquidity cannot be added so it only mined by the contract provided so blood in this case uh mines all liquidity and all the commission

  3. 1:31

    but the amount in the pool grows because um the commission is about 10 for every slope so the interesting part about this is like you say i want to convert near to bananas so let's say i want to get 10 bananas then it does have you call to the contract and computes how many bananas is there how much near i need to spend to get to get 10 bananas so i started playing with this yesterday to see

  4. 2:09

    if i can um some kind of abuse this implementation and one idea was like can i bring this number to overflow so i started playing like hey i want to buy like uh 700 bananas like okay the amount of near this can i buy so so much bananas and it's like okay um we get uh not enough berries in the pool like what it's saying you have enough berries like what's going on so i don't know uh why that started to happen so so i started to play in this around

  5. 2:53

    like still not enough but 20s for some reason is enough so either this balance is invalid that was displayed here there's some must happens that makes um this issue but you can like start playing and like finding this weird number until uh you you're like exceeding this number start growing and this is because how unisop works when you're trying to withdraw almost entire liquidity your product stays a large

  6. 3:34

    in a past but the denominator actually decreases to like minimum which is one so your number of near that you need to pay will be the product of bananas by near in the pool divided by one if i can find the correct number here um so i wonder if the if vlad put a protection on the overflow and started exploring the contract yesterday to to see if i can trigger an overflow such as i don't have to pay so much near but can take all the bananas

  7. 4:29

    and we'll see if it's possible once you start to view in the contract so the contract is written in assembly script which is similar to typescript and it's just one file so it starts with a bunch of constants so this is amount of near and because assembly script had weird big number implementation this contract uses u128 and it uses a trick where it divides large number by mean fraction which is 10 and the power

  8. 5:15

    of 12 and then it does the product and at the end it multiplies it back so it uses minimum balance of uh five near because the contract only compiled binary out of this contract is only something like 20 kilobytes so you only need about like two near for uh for the contract binary and a few more near four storage while storage is constant so it's not an issue now the way it works there's two methods

  9. 6:02

    start and stop the owner can start the contract and the owner can stop the contract in case something goes wrong the owner is checked if it's called by itself so i assume blood has the keys to this contract so he can stop it if needed um it initializes the amount of berries the contract has and sets this as an initial liquidity so maybe pull the number in the pool that displayed here is actually fetched uh from a

  10. 6:55

    very cloud contract not from banana subcontract so not an inner balance but actual balance that is the contract owns from the token contract that's why it might be a discrepancy so there's a few methods so one of them is like a view method to get a buy price it passes you say how many varies you want to buy and it gives you how much in year you need to pay using get price internal and then you have buy method that buys

  11. 7:37

    bananas then you have uh get sell price and you have a cell method that is internal because you can only trigger it from calling transfer with vault from banana um contract from very cloud contract so at the end we have um implementation for transfer with vault receiver so on receive this vault which is the interface that banana contract communicates like very club country communicates with

  12. 8:18

    this banana subcontract and it verifies that it can only be called by the token contract and it has two methods uh sell and deposit deposit cannot be used if the pool is already started so it only left his cell and sell it means you trying to sell some number of bananas for near so let's start with uh method buy and or we can see how the buy price is calculated because it's called get price internal

  13. 9:10

    so by internal is it takes the amount of barracks it has locally remembered from the storage divides it by this 10 in 12 so bananas uh precision of banana token is 10 in power of 18 so this one is 10 and power of 12 so it brings it in order of a million per 1 banana token so if it has around 1700 or more realistically this number then this will bring it to something like 1.7 billion

  14. 10:01

    so internal barriers becomes 1.7 billion uh berries is how many berries you want to pay it's also like adjusted to the same uh denomination then it do does it two checks uh one it verifies that the amount of berries you passed is positive so you cannot buy zero varies and then it checks that you don't want to buy more than the amount in the pool or the same amount so you cannot drain full liquidity from the pool finally it calculates how many berries

  15. 10:41

    remain in the pool it calculates the near amount without the storage required balance so it uses actual amount of near in the pool on this account so it's not remembered amount which is important it means it means this variable is can be modified outside of this contract so if i deposit five near to this contract then this value will grow by five near so it also divided by 10 and 12 in power of 12.

  16. 11:30

    uh near is the denominated in yokta which makes near to be 10 and the power of 24 so per year you need to multiply it by the 10 and 12. so we have this is 1.7 billion and this became 8.7 trillion now it does a unislope trick where the product which is current varies by current near amount is constant so we divide by our resulting berries so this is how much common in year the new year balance of the pool has to be to maintain the invariant where the product is constant

  17. 12:33

    then we calculate how many near you need to attach because this number is positive and this number will be higher because this number is a less than internal varies so this one is also positive so it calculates how how much in here you need to attach and then it multiplies it by mean fraction which is another power of 10 and 12.

  18. 13:18

    and it calculates this commission so it calculates commission the way it calculates commission is it multiplies by 110 percent divide by hundred so this is the overflow i try to abuse and the overflow here and assembly script just does a rolling overflow is if i can bring the price to a level of u128 where by multiplying by 110 i will be able to overflow it to very low number then my final price will be low um so we'll come back to this

  19. 14:06

    so that's how you calculate the price uh then you say hey did you attach enough near to cover this price if not well sorry then we just update that uh we decrease the internal near various balance and then we send the remaining deposit back so there's no overflow because it's checked here and the do transfer raw so we basically just give the predecessor barriers directly from the contract token contract

  20. 14:53

    and that's it this is the buying method any questions on the on this part it'd be like an overall question so who is putting in the berries are people putting in liquidity or does the contract itself have that varies yeah so there was no uh liquidity initially like there was very low liquidity initially but because blood put some year originally and because of one commission of like 10 percent um overall liquidity grow

  21. 15:36

    because of the commission and the pool and like near that was deposited there so it's ten percent of berries ten percent of the price you need to pay on every swap you you pay ten percent in commission so it's kind of slippage of 10 extra cool now the sale price works fairly similarly you say how much near you want to get then it calculates current near amount it calculates varies

  22. 16:21

    calculates your near it has to be positive but not and not full pool then it calculates how many new year balance so what should be the new berries balance then it divides new berries by old berries subtracts and does the commission very similarly to by the sell order works from the receiving vault so when you send some near or when you send some bananas using vault and say how many mere tokens you need um using

  23. 17:08

    an argument it calculates so it called sell and then it returns a price that you need to pay if you paid more than this price then you'll get refunded so this is not needed because vault will automatically refund you tokens this is an overkill while then it returns a promise saying okay give me the various that need it we are done with the transaction it's maybe possible to somehow trigger

  24. 17:48

    front running there but i don't think it's a reasonable way because while this is not completed internal various balance is already updated so if you try to transfer these berries maybe you can arrive first but unlikely so it calls cell the cell does the following the same sender varies near amounts so it computes the very price how much better you need to pay if you attach less berries then it will fail otherwise it's saying i received so many berries

  25. 18:27

    and here's the near amount that you've got that you asked for and how runtime works all promises that you created will be rolled back if later for example you trigger an assert so it's safe to like for example transfer near before some other assert my trigger but this not happened so um so that's pretty much it this is the contract and and that's it very simple the logic is fairly simple the only

  26. 19:19

    problem is potential overflow that we can try to abuse like i'm probably not gonna do this on my net because it's still a fairly large amount of near that needed to be attached but let's see how actually it can be done so let me present the terminal to to see where we um one second why are you doing that it's crazy that this is like 152 lines of code this whole contract is awesome

  27. 20:11

    okay all right so so what i have here is so this tab is on my net and i can say i want to know the buy price to buy that many uh berries by like how much it's a near to buy so many berries so let's say i i don't remember where we stopped i think it was like 7 2 17.

  28. 20:46

    so if i will try to adjust this number to something like close but not not overflowing then i'll be able to bring this number to bring this number to the number of the the denominator on the new balance to one and and that will allow me to get the maximum product so for this i need to know i need to know the state so because assembly script um how i actually use date is your near

  29. 21:52

    view account state and josh you know this one i'm doing on my screen what's near cla view state view state is there a view state no there's an article there's a cli way to do that there's an rpc yeah and there's some account utils that um that are in the near examples those are the two all right okay so i guess we'll have to somewhat just like quickly brute force it we can write a binary search to to actually find this number so it

  30. 22:39

    doesn't overflow so what i'm doing is kind of incrementing um one of the digits until i can find a number that's not gonna overflow and i'll have to continue doing this until we arrive to to a number or this zero digit okay so we're getting close

  31. 23:48

    um so basically once we're done with this process or this process can be um can be completed by just clearing the state and finding the exact number then doing -1 from it i just sent you the number over slack okay anyway so so this is the number uh and it will not be affected by the next digitizing because this digit is already in 10 and 12.

  32. 24:50

    cool yeah so this is exactly what i get using this number thanks so it's uh sharif pointed out here's the number that is in the state so this is what i found here by doing the and the brute force search and uh it's just this digit minus one so this is a remainder that is truncated so this is the number that we get which is the maximum price this is the commission um so what we can do next is we can divide it

  33. 25:37

    by 100 and multiply it by hundred and divided by 110 so this is the the product that is currently in the pool so what we're interested in is the following what will be 2 in 128 divided by this um one second right so so this is the number the 2200 is the multiplier of this number that you need to exceed like once the pool product became like if you increase the pool product by 2200

  34. 26:33

    2200 times then we will overflow to in 128 but as you remember the code in this commission is actually multiplies by 110 so the actual number we need to increase is only 20 so um if the inner product of the pool would became 28.5 times higher then it will trigger an overflow in a contract so this code is here price by 100 by commission percent divided by hundred right so what i'm trying to do is i need to i have this price which is

  35. 27:26

    this number i need it to be um i needed to to be such that a hundred by plus the commission so like 110 multiplication will overflow it and and and bring it as close to zero as possible so when i divide it by 100 here the number will be like as low as possible so i don't have to play here at all so i modified last contract i added this uh method view method test overflow that just simplifies it it calls view commission and par the number 8 pass is a

  36. 28:20

    and then i have multiplied by mean fraction because i cannot avoid doing this so and it will call this commission and return it i deployed it on a test net and i had this method that i started to try to figure out the number the product number that i need to pass such as that the overflow value will be as low as possible so i started doing this but haven't finished again we can do this using um

  37. 29:07

    using the binary search we basically can say like well if i add a digit here uh how the number gonna change and as you can see some digits do not change anything because overflow 110 doesn't modify it but if i modify this digit well nothing happens as well so um so some digits become significant so for example increasing this digit actually did make a difference maybe i can decrease it

  38. 29:44

    and so this did not affect uh so if i decrease too much i will not trigger an overflow or i will mess this so basically what happened is i tried to find the minimum number that i need to pass such as it will trigger an overflow and um and decrease the the number so for example let's uh look at this number that we have given oops not this one divided by 10 and 12. so this is the number

  39. 30:45

    that is currently passed here by default so this is the one that i receive on the mainnet so what i need is by how much i need to multiply this number which is around this number such as my product will grow and then it will trigger an overflow so if i for example do this multiply by this

  40. 31:47

    it just python makes it a little more complicated so so i can get this number then if i pass it i get the minimum number possible so so this is the the price that will be returned if a product can reach this amount why i'm doing all of this so let's go and look at the contract itself so and we have operation which is get buy price internal or get sell price so if i can

  41. 32:55

    send near to the contract some amount of near that i know i need to send and this current near amount will be increased by the amount that i know then this product will be computed and and this commission will be called on the new near price so basically i can control i can control near price by providing enough near to the contract itself so i can send a simple transfer and chain to increase the inner balance of the

  42. 34:03

    banana swap account which will result in increasing this product of internal barriers by the current amount where resulting varies is one which will eventually trigger this commission to overflow and say to to take all uh berries you need to pay only one like only it's like small amount of near the problem is like i don't really need bananas so i go to get sell price and i say i wanna all near so then

  43. 34:57

    near amount became so new year balance became one new berries became very large product then berry price is computed and this commission is called for the number that i want which will overflow again and it's saying to get all the near you only need to pay like a few berries which is like a few bananas so in our case um it's going to be this amount of bananas which if you divide by 10 in 18.

  44. 35:44

    18 you basically need 200 bananas to get all the near but uh obviously the bus may be slightly broken but here's the idea so in order to make this kind of overflow hack what you need to do you need to deposit a precise amount of near to the contract through a simple transfer this will not modify the inner balance of berries then you can send a purchase order to buy to sell bananas essentially to buy all

  45. 36:27

    the near so like take all the near except for one um and then it will trigger the you in 128 overflow and you will have to pay only like a minimum amount of bananas to take all the year and this is uh that essentially will allow you to uh capture the like 87 year that is currently in the pool so very fun uh hack uh at the same time it's kind of expensive because to to get this 20x multiplier at 87 year

  46. 37:07

    you need to like um i think it's 19.4 so you need to send like 1 16 or 1700 near to the contract which is if your mass is anywhere wrong then you may lose this 1700 year but if your mass is correct you can claim 87 near from the contract plus if someone like front runs you a little bit just like increasing the balance of uh bananas which will break the overflow logic then you you might like just to lose

  47. 37:46

    only get your near locked on the banana swap which will make um banana price explode so you can sell bananas to claim majority of near here's a nice everything i have for today and i think that's kind of cool that we discovered this type of um vulnerability exploit and the way to patch it is actually would be introduce just overflow protection you can do an extra check here where let's say

  48. 38:42

    well if assemblyscript would support panic on overflow like rust does then you can just enable it but if it's not then you need some kind of check either here or or even earlier you can like do division to see if uh if this number is like relatively low to make sure this product doesn't overflow but it's still kind of hard to overflow this product because the amount of bananas a year

  49. 39:24

    has to be fairly large it's still very close so you want to protect not only this but also so you don't exceed the product maybe decreasing the mean fraction or like increasing the mean fraction even more that will protect you from this overflow so i have a question if i can this is really interesting um so this is like an overflow that happens from some math in here um and on so on russ

  50. 39:56

    it's like there's a cargo.tamil file which is very similar to like a package.json and there's something called like overflow dash checks equals true and that's just like it seems too good to be true it's just like okay cool that just takes care of overflows does that in like if this were in rust would that also account for overflows in like this situation where it's like custom math inside your contract

  51. 40:20

    would it catch that kind of thing too yeah yeah so the the check overflow it basically makes rust um act like a debug so in on debug rust is actually overflowing like any overflow and debug will result in panic so you you cannot have a rolling overflow if you don't want it so they have special methods like checked add saturating add like saturating rule and then they have overflow and move which gives you a result but also

  52. 40:56

    whether it was overflowing or not as a second second boolean so you can check if it was overflowing and like some contracts use it for optimization so they actually like to overflow and then use this bit to to to pass for example one to the next element in the number on the next bit like high bit higher byte um yeah but in in order to like abuse this you probably want to modify this contract

  53. 41:33

    build some kind of like extra logic that simulates what you're doing and then make sure you execute it in proper order in one block so no one is like in the middle of you i would guess that like so at the very top he is like import a bunch of things from near sdk as and one of them is u128 i would assume that u128 has like something like max value or something like that you could use check against

  54. 42:08

    yeah maybe it even has the overflowing check that you can do so yeah but the proper way it just implement u256 which is will take care of all of these problems because you can drop this mean fraction and uh because near never exceeds you 128 and other does not exceed it either and they don't they're not even closed and you will never reach the overflow here at the same time you can first divide by 100 and then multiply by commission that will also get rid of overflow here because because

  55. 42:58

    of mean fraction it's divisible by 100 so it doesn't lose precision there are multiple ways of addressing it on this contract so and actually maybe that's another question for everyone is like um you know january 2021 is there any reason why we um don't have u256 on assembly script and rust was that a choice or just kind of like we have not just happened to get to it yet i i think it had some bug in the mask that needed to be addressed so people don't use it

  56. 43:36

    until new assembly script is published or something like this but there's some big num i think uh module that supports this type of stuff like u256 and higher cool uh any questions or we can close early today did you steal vlad's near no no i need to like do the mass and then you need to figure out where i can get this 1700 loan flash loan maybe he can pay you a bounty just a naive yeah i'll give you some near to give me my near back um just just some just some question

  57. 44:23

    naive question so like how much of an advantage do you have because you've got access to the source code like if you didn't have access to the source code would it have taken you a little bit of extra time or prohibitively more time if there is no access to access or to to to the contract but then it will be yeah it will be much harder to like read the word assembly and figure out like

  58. 44:48

    what's going on so developers are really stuck with this double-edged sword where they want to release the contract code so people can audit it and read it and kind of feel confident that they're using something that isn't cheating them out of the whatever transactions they're engaging in but it also gives attackers this kind of red carpet access to potential bugs well it's the same time like you can do the audit and like

  59. 45:12

    for example i didn't abuse this because it was still requiring me an extra deposit which we don't have access to flash loans and near on a protocol level so you cannot really execute this attack without funds but if someone would actually try to figure it out they will come up with the like a few options and be able to actually find the number they need to send in here so they can like try send it small amount see if it increase the product if it does then okay

  60. 45:51

    let's try to figure out whether it's gonna potentially overflow so but they will probably not be certain that this number will overflow and not panic so they will not execute the attack unless they they just grab the binary deploy it on a test net fund it enough and then complete the complete the attack anyway but but but you initially found this by look by playing with the front end right so it wasn't like you look at the

  61. 46:22

    code first so that is kind of like yeah you know like someone would have a hint like hey something's there's something something here yeah it gives you a hint but it doesn't give you a certainty that the attack would be successful so like um right now i proved that the attack was successful because i took the binary introduce this helper method that let me play with it um if i didn't have access to source code i could still introduce this test method uh

  62. 46:58

    by sticking it in web assembly or by like swapping the original banana balance to to my own contract which i can control or like deploy it locally and funding it so there's multiple ways to to to execute the same attack without having to do this on a magnet you pretty much can replicate the state of the contract on your local network and then grab the same binary and replace the like bananas

  63. 47:31

    like very cloud contract with some generic token which you can meet and destroy and after this you can execute the attack and i assume like ethereum does like all the like financial hacks on ethereum are done fairly in similar way even if they don't have a source code they found some bug they have a local kind of snapshot of the magnet they can execute some transaction verify it will do

  64. 48:03

    what they need which is slightly easier because you can actually do this um off chain because of synchro synchronous state so you can execute kind of you call which is without a bunch of uh function calls on near you don't really have this capability where you can take a snapshot and then do a multi-shard like multi-account transaction that is non-modifiable so as a view which is requested feature so

  65. 48:41

    it would be cool to to have it somewhere because you can potentially do this on on a note that tracks all sharks so you can just do the instant view call on multiple accounts and like keep track only in like temporary view and you can do this in the evm and um evm yes i feel like i missed something pretty important um i i i'm having a disconnect with we triggered the overflow and now i have access to that account or now i can drain the funds i'm missing that

  66. 49:22

    stop i'm i'm right so so the the the attack here the exploit here is we can do a transfer of near to this account to trigger the overflow once you trigger the overflow it basically decreases the price from like this enormous number so from like if i do this right so the from the price of ridiculous number to something that is overflow to very low number so now i can drain the entire balance back without paying for the um

  67. 50:04

    for for the say i can basically now drain my funds and the pool funds without paying in bananas for this so this is that so if the pot of money is big enough right like if there are a million near in there and you can transfer 1500 near in order to make the price overflow like this you can walk away with a millionaire you can net yourself a millionaire even right now like it just

  68. 50:36

    so your risk is very minimum it's it's about your setup like the preparation that you do then you can execute this attack right now and like take uh what um about like 82 year from this account by draining the like the all liquid near balance and pay something like 200 bananas so you can first for example acquire acquire bananas this like this required amount of bananas by paying near to the pool

  69. 51:23

    which you already want to do and then you you can execute this attack so you first can take all the bananas out of the pool by playing a ton of near and then you transfer the remaining ear and you now can drain all the uh near out of the pool so you basically first took all the bananas and then you took all the near support gonna be very low on liquidity after you complete the exploit

  70. 51:54

    make sense thank you yep thank you okay i guess we can close for today thanks for watching and listening and attending have a good day thank you