Cairo Security 101 - Let's have a look at Cairo attack vectors

ETHCC 5 · July 2022 · 21:19

Joran Honig examines potential attack vectors in Cairo smart contracts. The speaker considers vulnerabilities that could affect Starknet applications and approaches to preventing them.

Speakers

Topics

From ETHCC 5, hosted by Ethereum France.

Transcript

thank you hi everyone uh my name is johan and uh well if you're here you probably already know what i'm gonna talk about but basically uh i'm gonna have a look at this new cool language oh well it's relatively new i guess uh called cairo and how easy it is to mess things up and what kind of bugs you should be looking out for as both a developer or a bounty hunter or a hacker um so first things first let me do a very very short introduction of what cairo is i assume most of you already know but it's this quite cool programming language built by the guys

over at starkware and its goal is to [Music] allow for verifiable execution and what this means is that outside of doing some computations which regular code does which literally does or your regular javascript and python what it will also do is produce a zero knowledge proof showing that the result that is computed was actually computed using the code this in our space is very very useful because um what we have right now to do this is a very naive method which is just rerunning the code to see if you get the

same results and in this case it's easy you can just trick the zero knot of truth and so i want to dispel a myth and i'm not sure why this is a myth but a guy was talking to me the other day and he's like verifiable execution doesn't that mean that the code is always verified so there's never any bugs um this is not the same thing so verifiable execution is about being able to show that what you did was the code it says nothing about whether the code was correct um and it's actually not that difficult to mess up and have incorrect code um yes

so the main thing to note here is that cairo is still relatively young language i'm not sure how many people here remember the solidity 404 days there were a lot of quirks in salinity at that point and we've come a long way and this very very same thing is happening in the cairo ecosystem a lot of the like weak points i'm going to talk about today are super well known and i know that these people are already working on improving it so i don't want to by any means say that cairo is a bad language but when i first saw it or my first experience with cairo was

like this i was kind of scared because i saw food guns everywhere it's still relatively low level and there are some quirky things that make it easy to mess up very much the same as solidity which by now has become super mature and i'm sure that will happen to cairo too but that is kind of like a disclaimer so in this talk i'm gonna go through seven sources of vulnerabilities that you as a developer or a bounty hunter or an auditor should probably keep in mind and the first group of vulnerabilities or attack factors that i'll talk about are things that arise purely from the language so

cairo works outside of stark net has many applications or has many potential applications outside it and these vulnerabilities affect all of these use cases and the first is probably the simplest vector that i'm going to talk about which is arithmetic arithmetic problems you're probably familiar already with the overflow that you don't need to worry about anymore since the limited eight in cairo you kind of still need to worry about it but what's more is the arithmetic happens in the finite field over much low sum prime which is weird or well it's not

it's actually quite simple once you get the hang of it but at first it has some really weird behaviors or seemingly weird behaviors for example if you take a number you multiply it by two the end result might not be an even number it can be an odd number and this is because a prime is always an odd number so the result of the modular operation could be an odd number and actually it's about half of the numbers in the finite fields have this effect where you multiply it by two then it overflows and then you get an odd number another thing is division doesn't work the way you would think it to based on

like experience with regular languages so seven divided by three you would either or usually you would assume it's like two like that four or like maybe it rounds up who knows in this case that's not true you always get a whole number but this whole number will have the property that when you multiply it by three much lower the prime it will give that other number um which is a huge number so basically you need to worry about overflows and divisions and multiplications too but that's kind of kind of obvious the way to get around this kind of is to use one of these uin 256 libraries that

are out there already that kind of model the uni-256 behavior they are going to be a little bit less efficient than using pure [Music] uh in cairo but they they will kind of solve this issue for you then um this is my favorite uh attack factor or type of vulnerability it's super unique to cairo or uh really like zero knowledge based languages and it's it's uh the result of the fact that cairo is actually not an imperative language it looks like it but it's actually not what the program a cairo program is is a series of steps with constraints things that must be true and um

it's up to the prover to figure out a way to make everything fit so this is very abstract here's an example this is uh something that you could do imperatively or reason about imperatively you have a variable you add one to it you put it in y and you return y what actually happens is that the prover gets this challenge there is a variable y and it's equal to x plus one come up with this variable y which is super easy to do in this case because you just take the very one add one that doesn't hold always have to be the case so here's a program where uh you gotta find

two factors of a larger number and you can see that i first declare two local variables then say that x my input value is equal to p times q which is like this just looks super weird from an imperative standpoint and basically what you see here is a function that tells to the prover you return two numbers whose multiple if you multiply them then that must be my input and it tells the prover nothing else and well you're probably familiar with this problem it's super hard to solve and this basically if you do this the problem will usually not be able to come up with the result but in most

cases where you would use this is if you can make the constraints super small uh by saying like yeah i don't have a good example sorry but what you can do is write these short python snippets called hints that that give a hint to the compiler on what kind of values it would be able to choose for these unknown pnq the problem is the compiler or the proverb has the ability to choose whether to use your hint or not it could use anything else so if if you assume that it will run the hint and then not sufficiently constrain what it's allowed to be what the output

of your hint is allowed to be then the prover can just do anything they want so for example if you have a right to like a storage variable called balances and you don't tell the approver like it needs to be computed correctly then they might just update their balance to something uh that they would prefer to have themselves even though they don't have the tokens um an example of this is dick accesses where this could happen the way that store like a dictionary works in cairo is that you basically have a list of updates instead of an actual dictionary in memory and this list of updates uh has is basically a bunch of three tuples

i write to this value it was this value and now it's the other value if you accidentally add an unconstrained update into your dict access array and um and assume that that is not unconstrained then you basically give an arbitrary right to the approver and so if this is kind of the balances example where the approver would be able to write any value into their balances and basically become rich cool number three this is probably one of the most scary ones it's the fact that they're

it might be unclear what these built-ins or cisco's are doing um and this is kind of a vague statement so i'm just going to give an example this is me writing a function that wraps the verify ecdsa signature and it adds to a search that are super weird you wouldn't write this but does anybody think that this could ever execute successfully like show of hands the stargate guy over there is like yes um okay so what happens is the built-in guarantees one thing that there is a message there and that there is that it knows a

signature by the key that's provided the matches that message it doesn't guarantee that these rns values match up with the signature it knows well this one is pretty hard to actually have a vulnerability around but it does illustrate the fact that there's unexpected or undocumented behaviors that you should become aware of so that you don't mess up in your own code or that you should be aware of so that you can find bugs in other people's code and then yeah cool so this is kind of a limitation of knowledge problem as well like you don't know what you don't know so read documentation uh read the code

that's really the only thing you can do finally in the in the language class of bugs there's low level memory accesses and this is difficult to exploit generally but it's super easy to mess up so in cairo you have access to this ap register and you can write to it increment it um you can do neat things with it but it's really easy to mess up and in general you should avoid using these low level things same with the dict axes having an unconstrained element in there super disastrous and really the easiest way to avoid it is to

just use standard library interfaces to work with the code cool then instagram specific vulnerabilities there's gonna be two here and these are two that are well known that people have suggested improvements for that are probably not gonna be interesting for long but at least right now you should be very much aware of them so the first one is the way that storage variables work it's basically the way it works is you have a storage variable and if it has a particular name that identifies its location so if you have two cairo files in your start net contract and they both use a variable with the

same name it will act as if it's the same location this can be a nice feature but it's also an easy source of bugs if you don't know it because like both of these pieces of code might assume that the other doesn't exist and updates can be uh like inconsistent and incoherent um the other thing is accidental imports and this one is going to be such a nice source of bugs i think if you actually if you import a file with an external call or if you import the external call it automatically gets added to your own smart contract as a callable function

so imagine accidentally importing uh change ownership function or an initialize function even though you don't use any initialization code and this function overrides the owner variable this is like an easy way to lose all of your money so i like really like the the the structure that the open supplement cairo contracts have where the inherited code or code that you would want to inherit is separated from the external calls of a particular like archetype so if you have an erc20 the external calls are in a separate file from the implementation at least

last i checked and this provides this nice guarantee that you need to like be very very intentional if you want to import external calls then finally there is the the vulnerabilities that arise from the domain so and this is really the easiest slide for me because i'm just going to say that all the things that are important facilities smart contracts are going to be important here too you'll see your flash loan attacks market manipulations reentrancy so for cairo contracts as well it's really important to be aware of all the types of attacks types of best

practices we have our own you can check them out there there's tons of articles on this uh but that's gonna be a really really good source of bugs or something to be aware of all right so after this call or call a talk i will post on my twitter account an erc20 contract that i wrote when i just started out right playing around with cairo it has a ton of vulnerabilities in there so it's good practice for you to see like if you can find any in there um and if you find a bug that i didn't find myself yet and i hope i i know about all of them by now

uh then then i'll probably buy your beer or something uh cool so this is kind of the summary of the like the seven vulnerabilities that i i think are going to be like a good source of bugs in cairo contracts uh on the language side arithmetic it's a an old thing but it's gonna stay uh under constrained execution probably the most interesting thing in this field right now or at least i think so um cisco's undocumented behavior or weird behavior always a nice source of bugs and then low level code i mean yeah people here probably remember c um

then starting at these two things to be aware of are the storage variables and accidental imports but i think that's going to be a short-term thing where a lot is going to be improved in the language and finally of course always be aware of d5x or d5 attack vectors that's it um well good luck if you're a a bounty hunter or an auditor finding these bugs uh if you're a developer we a diligence do cairo audits now so if you need one hit us up and that's it so i'll open it up to questions now [Applause]

hi um are you aware of some shared registry of typical cairo security issues like swc for ethereum for solidity but basically for especially for cairo um no are you aware of any i mean yeah we should probably start a startup joint effort thanks another question hey uh regarding point number six um which was about yeah

i think was yes yeah accidental imports doesn't using namespaces help with this to reduce i know i've seen open zeppelin use namespace a lot i think i'm so i'm not sure to what extent it really fixes it in any case you'll have the problem that um you can import a function that you didn't intend to so just having like a list of things that you want and then accidentally having one in there um this happens if like you don't know what you're inheriting um but i'm not sure right now if

if you import the namespace that doesn't import all the the external calls in there yeah i might be completely wrong but what i had in mind is that the issue was that when you call a function it's so maybe i'm completely wrong but uh what i have in mind is when you call a function you uh call the hash of the function and that using the name space you would actually append like you would compute the hash of the name space plus the name of the function and that will solve the problem but maybe i'm completely wrong so i'm not sure exactly how the function dispatching works at the moment sorry but

wait wait so that wouldn't fix it so the problem or the problem here is that the call is available not that there is a particular that you don't know how to call it uh it's about other actors being able to call initialize even though you didn't want initialize to be there but good question not a question more a comment because i'm the one that makes the pr an open disabling for adding namespaces so basically the thing is that we opens up in care of contracts we don't export anything the namespace

are just is for clarity and to make sure that if two ercs define the same function there is no clash when you import basically but once you import those basically the libraries are just like namespaces with internal stuff everything you need to export on your own which is one of the challenging carrow is that it's very easy to forget to export one of the functions and only notice after the fact great thanks okay perfect [Applause] you