Defense Against (the Dark Arts) - Contract Runtime Mutability by 0age (Devcon5)
Devcon·Fri, Oct 9, 2020, 12:00 AM
Visit the https://archive.devcon.org/ to gain access to the entire library of Devcon talks with the ease of filtering, playlists, personalized suggestions, decentralized access on IPFS and more. https://archive.devcon.org/archive/watch/5/defense-against-the-dark-arts-contract-runtime-mutability Smart contracts are no longer guaranteed to have immutable runtime code, and can be redeployed with new code using a variety of methods involving the CREATE2 and SELFDESTRUCT opcodes. In this presentation, we will investigate how this is done and how to protect against malicious mutable contracts. We will also explore ways these new techniques can be applied in order to enable new use-cases and to improve the user experience. Speaker(s): 0age 0age Skill level: Intermediate Track: Security Keywords: technical Follow us: https://twitter.com/efdevcon, https://twitter.com/ethereum Learn more about devcon: https://www.devcon.org/ Learn more about ethereum: https://ethereum.org/ Devcon is the Ethereum conference for developers, researchers, thinkers, and makers. Devcon 5 was held in Osaka, Japan on Oct 8 - 11, 2019. Devcon is organized and presented by the Ethereum Foundation, with the support of our sponsors. To find out more, please visit https://ethereum.foundation/
Transcript
[Applause] you may as well just dive in because there's a lot to to get through here so today we're gonna be talking about the dark parts of contract run time new ability more specifically defending against that I'm 0h thank you guys for being here alright so here's the gist of it deployed contract code is not immutable here's the link to the repo that's got all of the code that we're going to go through today so you can kind of review async or follow along so we won't get too bogged down in like working through it and fly it by mind a lot to digest but that this will have everything all right so here's we're going to talk about today first contracts can be destroyed and then redeployed zombie contracts is another sort of term for this secondly depending on the way that you deploy them these contracts can actually be redeployed brand new runtime code third we're going to discuss some techniques that you can use to protect yourself against these mutable contracts finally we'll explore any ways that this is actually you can build cool new stuff doing this if you're willing to sort of Trailways okay so just so we're all on the same page go over some of the four teens basically understand here first off contact creation code is the byte code that you feed into your creating a contract whereas the runtime code is what gets returned from the creation code and will actually run when you call the contract secondly we've got contract storage which is a storehouse load standard storage but then you've also got runtime code from before which in addition to running logic all of that is where you're gonna hold the actual like constants and your contracts in that matter okay so the og original method here is create or actually deploying contracts that's what happens when you're actually deploying at a contract via transaction to the Bill aggress uses the employers address and the nonce to determine the address the new kid on the block create two instead of using the nonce of the the deployer it's basing that address and poised to off of the deployment address the salt and the initialization contract creation code okay self-destructs I'm sure you guys are all familiar with this to actually feature in my original aetherium foundation hello world that this was like seen as you do your civic duty on the block chaining cleaning up back to yourself and then of course the parody multi-state hack and a whole bunch of very sharp edge cases of that most developers - just saying avoid this like the plague I'm not most developers there are a couple of cool things you can do with it if you're willing to take along a wild side but always tread with caution when you're using self-destructed one of those things that self destruct can enable or exploit is reliance so the gist of a contract redeployment let's go over how this sort of comes down to be if I try an employee to an accountant that's already got a contract this is a scenario that in a creative only world isn't going to happen without a hash collision because the non-singer minute every time but pretty to world if you try to feed in those exact same arguments it's just gonna fail right there's already a contract there whether or not there's code this is invalid but when you self-destruct a contract what's actually happening it's more than the runtime code getting in there now the entire thing is white you've got no nuts no residual balance no it stinks everything is going as far as the blockchain is concerning this contract it's never intestine so then once it's already been wiped what happens when I haven't went to me again well in that case refine the contract can be deployed no problem but if I was relying on this estate or the dogs of whatever property you know that contract before this you got white and then look back well now you're in trouble right so let's look at an example of how this might work and bear in mind that the code won't actually even be changing this is just an example greatly simplified like a more realistic world use case would be like this add X or a swap contract or something like that but this is a contract that we approve to transfer our ship coins okay and initially it's in testing mode so don't approve and it's a testing mode because anyone can move your your funds around but then we turn the testing mode off but then the contract can be destroyed retired and once you reapply it it's back in testing mode and into the fund stolen okay so this is a problem and really low hanging fruit if my series be careful with your giving me approvals out you guys already know that but let's look at that so remediation to this which here's our same coin mover contract and we've stripped out the testing mode so you're good you can approve this contract you're gonna be the only one that's able to call the transfer from that bit we're all set we've fixed this problem everyone you can rest easy except you can't because if it was deployed via some of these tricky methods then you're you're backing the same problem where they could deploy a contract vendors can move your funds again and the phenomenon here is it the phrasing I started using for this particular type of redeployment where you've got brand new code coming in it is metamorphism there's enough this also goes by like malleable contracts this there's a few different ways of describing this a metamorphic contract is one that has non-deterministic creation code so you feed it the same Dick's creation code that you're sourcing but how you're going to derive the runtime code from something other than that creation code payload so here's the basic rules of how this happens first off you have to destroy the existing contract you have to call self-destruct on second you have to use create - and the interesting thing about this is it doesn't have to be deployed directly from creating - because and anywhere along the chain some contract uses created - and then all those intermediate contracts get destroyed that you can redeploy all the way up third the deployment address the creation code and the salt can't change because otherwise you're going to have a new address and lastly once those three rules are are adhered to anything is fair game and is good so let's talk about some of the main ways that this would work first off a straightforward way to do this is to go slow man under contract so what I could do is I can store the address that I want to clone in contract storage and then my creation code is going to go get that address and clone the contract now this has some limitations you can't use a constructor sort of like with common upgradability patterns you have to call into an initializer another way to do it is using what I call a transient deploy and the transit deployer is going to use this trick where it's going to rather than getting if an address it's gonna go get the actual creation code that wants to deploy that it's gonna deploy its own contract via create using that creation code and create doesn't have the same limitations on being you can change the creation code all you want but then if if the transient contract immediately destroys itself after it's done so then if you want to go back and redeploy remember the nonce of that contract is reset so create is going to hit the same ad that's again this has the limitation you want to do this there's two contract appoints so there's some overhead but it's a way to do it and more flexible fashion and support constructors finally my personal favorite pattern for this I call the metamorphic delegator pattern I'm going to what you do here is you can actually take your target creation code and you put it into the runtime code of another contract then when you deploy this metamorphic contract it's going to delegate call into that that creation code in runtime code contract and it'll set up all the initialization stuff in the constructor and then return your target code which had been rights to my time so first let's look at what a cloner might look like this is a standard non metamorphic loner where the creation code changes this is just going to take our target and put it on the stack and then copy the code and this is what a factory that spits out that clone might look like as you can see our creation code is gonna change so if we try to clone a new address it's not going to work so here's a metamorphic version of that clone army and what this is going to do is we store the address you want to clone on the factory and then this calls into the fall back on the contract it deploys it does a static column gets that address and then uses that to copy the code contract and this is what that might look like it does simply fall back that is just going to grab the target address and storage and give it back so then you can see here your reasonable never changes but every time you call this as long as we can destroy it an existing clone we can keep adding upgrading to new cloned contracts let's look at the transient deployment this is a little more involved but what we're basically going to do here is we're going to call in to the fallback same thing but we're not going to add this we're getting the creation code then we take that we don't know the size of it ahead of time so we're gonna use return data size when turning data copy to put it into memory and then we create now if this if it reverts your bill and grab that revert the reason and return it otherwise we're gonna self-destruct this contract so that we can do it again and then all we got to do is self destruct this up contract and redeploy so this one it looks pretty similar to the last example of a factory rather than storing an address restoring a binary and the other the other stuff of this that's important is we have to we know what the nonce is gonna be one on the transient deployer so we can use that our LT encoding to figure out what the address is and we're pointing to alright and then the blesses metamorphic delegator this one is basically two steps step one is that you need to insert that creation code into the runtime code of another contract this is what I called preload or block of code you can insert in front of whatever arbitrary runtime you want to be deploying and it will play it for you you can also use like libraries and the cool thing about this is you can do this ahead of time you can even reuse the same creation code in runtime code blocks then once you've got that that contract deployed then here's a metamorphic delegator payload here we're just gonna put a bunch of zeros on the stack to use later then we go and get the address from the fallback the creation and runtime address then we're going to delegate call in to that address and either bubble up the revert reason or return the runtime code because we're in a constructor by returning that code we write it to storage or to run times so here's the factory for this it's basically going to take our creation code and deploy a creation and code in run time storage contract once again this is something you could do ahead of time and reuse if you wanted to really choose select the efficiency out of it and then we're gonna take it and deploy our fix non-deterministic byte code that goes graphs that address and delegate causing so this obviously if you want to go down this rabbit all there's a lot to it if these are kind of minimal examples where you can supply your own salts you can do all kinds of tricks so I put together a sort of utility contract of homework that is it uses this a metamorphic delegator pattern but what it does in CNF T's but you Clark addresses ahead of time and then deploy or redeploy contracts to those addresses and also sort of makes the Plumtree you're welcome to check it out and also have you answer any questions about it now a hope this film might make you a bit squeamish I know most the people I explained this to it really goes against the standard immutable principles of what we're doing so let's talk about how you can protect against this because the truth is it's impossible so there's three big things first off the best protection you can provide is to make sure the contract cannot be destroyed it's not quite as simple it's like no self-destruct opcodes because you can also if there's delegate calls with call codes those could trigger a self-destruct in the context of your contract and in this world where it's upgradeable everything and proxy everything this is actually a harder guarantee to provide in many cases than many of us would like what you can do though if you can provide this guarantee if you give your contracts to this this registry indestructible buddy it'll scan them and stamp them it's like this is indestructible you're good so that's one way you can protect another thing you can do is you can ensure that the contracts are being deployed and mutable it's not just quite as simple as create has to create all the way down to like something that has never touched create two because you can bubble them all up or you know it's created by something that is itself indestructible finally you can go through beautiful create two factories there is a example of one that you can use they to factory Dottie and other contracts that do similar stuff and then you basically just track once you've used a particular assault and an info or whatever and don't let your be the third thing here is that you can check the run time the Xcode hash into the contracts that you want to interact with them make sure they haven't changed at the injury now this isn't going to protect against a standard read employs the zombie deploys it's only gonna protect against a metamorphic contract so you so be careful about steak and might get wiped in between if something is destructible and you can register just ahead of time and use a registry like this one code cache cache as an example and now if you combine two and three you've covered most of your bases if one bit is not available and the biggest thing here is educate people about this slate this should be in the checklist during an audit to make sure that just like can't you have the right trust assumptions or dealing with ownership you have the trade trust assumptions when you're doing their deployment methods if they're susceptible to this all right all your white hats can take breath for the rest of us let's talk about some of the cool things that you can do with this technology so first off contract deployments it's actually really cool to be able to park an address ahead of time and to find an address that's efficient without needing to tie that into the creation code so you use an address that has lots of zero lights you can save quite a bit of gas of servicing areas also we can know the deployment address of any of our contracts ahead of time so you can deploy a whole family of contracts that refer to each other as constants and are interrelated and save gas that way or just have coordinated plays about having two buses trackers all of the nonces appear an address isn't a really granular way finally if this makes it easy to deploy different contracts that are slightly different to the same addresses on local test meds main nets other chains as long as you're coming out of the same factory its deployed across chance second run time storage so there's a lot of talk with a new assemble artwork that the price of s low is going to quadruple so you can use run time storage and certain breathe heavy applications to make storage a lot cheaper and also more accessible so usually when you go through contract storage you have to go through again right and the contract is dictating how that's gonna be read back to you but if you're using run time storage you're just copy whatever portion or section of the data that's relevant to you and so you can access storage on your own terms it's like true public storage in that regard and it's also cheaper to do you can also by transferring ownership over to accounts that you can deploy to you can give ownership of a region of storage in a way that's really future proof right it's not tied into any particular implementation on your contract finally it's economical as we were sort of discussing you can save a lot on not only reads which is like big savings but also on initial storage rates especially for larger payloads because in effect you pay twenty thousand gasp a word if you're storing on contract storage or as you're only paying 6,400 a word for every read time storage order plus the overhead of the contract and you can still do updates by using some kind of system third smart walnuts so the theory moxie doesn't have native transactions script support it's just calling into the contracts but what you can if you use one of these Clark addresses as your smart wallet you can deploy contracts that will do throw out an arbitrary bark byte code and then blow up and then you can do that at na z to sort so sort of having the transaction scripts behavior it's also really secure because there's no code to hack in between these transactions it's happy to code so there is not the same like risk at rest for your fights and this leads to being able to support really user friendly stuff where you can do these complex actions in a way that includes conditionals and atomic operations and all of that and is only one transaction from the perspective of the user forth you can't organize accounts this way so in effect as long as I own the rights to deploy a little ticular address and I give that to you I can reassign controller this stuff and you're not tied in to any particular smart wallet implementation or whatever you can put whatever code you want there once you want it's your account as long as it can be destroyed so this way you can create portfolios baskets of assets that are to go beyond just like a particular in the year see 20 years of 7.1 whatever it may be the ownership reputation all of that because the accounts is the ultimate abstraction for doing that and it's a like a reliable way to do as a future perfect manners so that if you do need to make changes down why you whatever your tokenize accountants yeah this is outfit like the this sort of new use cases for this they're weird but if we weren't willing to get weird like what are we doing here this this is a theory anyway right so I'll leave you with a quote on the dark arts and maybe a slight modification then I think it's more applicable to less here and this moves the talk thank you guys very much I'll put up one more time [Applause] [Music] so when you're accessing contract storage there is an extra you have to go not only to the contractor then you send it into that patrician 3 as well so there is an overhead on the machine level they're doing it that way now is it does it make sense that's like a preferred storage method I don't know I think that it might be an indication that there's more work that needs to be done to out and maybe a gas correction and I think that's also important to bear in mind with if you decide to use one like exotic storage method over another that you should basically anticipate that things might change in terms of gas pricing online and you might be stuck with something that's really esoteric and out there but as long as couple that with good upgrade ability and then it's maybe not so much an answer yeah to use like this transaction script type functionality so your your right out of the gates you're dealing with 32,000 gasp overhead for deployment then you can deduct out the twenty four thousand rebate it starts to make a lot more sense as you get into more complex operations and especially stuff it like you couldn't go through another another helper or something that would do that matching for you but the big thing is most like batch transaction type things removal tool calls that don't really have a strong ability to hook into conditionals alike both based on this I'm going to go do this other thing on this other back this way you can like the sky's the limit and there's also the security consideration of the KOAT action yes probably what did you into code so you don't change the deployer the salt or the creation code but the creation code is non-deterministic meaning it's fixed creation code but during that creation step rather than a standard like solidity and employment that's going to basically have the runtime code and just copy that over you're going to say oh instead I'm gonna go ask this other contract for their code that they have right now or some variation okay last question is the hands of the premise contracting to destroy those who destroy all you have to kind of move it out yes so whenever you're self-destructing contracts are to be very careful with balances token balances aren't good because they live on the tokens right that's not so much of a concern but if you have either at the contract when it's destroyed that your ether is burned and not like Sentinel address its annihilated your body isn't gone so obviously self-destruct you specify a recipient and it will immediately forward the funds to them but if any funds make their way back within that same transaction the contract hasn't been deleted until the end of the transaction at which point it's only been scheduled for donations it's another limitation to this is that you're limited to one redeployed per transaction that's a pretty extreme case that you would be doing multiple redo poisoning in a single transaction but that's something to be aware of so there's like a slight additional delight with fairness okay I'm happy to chat Laura take some questions after this but thank you guys for being here and go teach people about this [Applause] you
Automatic transcript — names and jargon may be misspelled.