← All talks

Secure Coding with Rust

BSides Canberra · 201840:08127 viewsPublished 2018-07Watch on YouTube ↗
Speakers
Tags
StyleTalk
About this talk
BSides Canberra 2018 Slide deck: https://securecode.rs/bsidescbr18/slides Code, slides and video are available at https://securecode.rs/.
Show transcript [en]

next up we have Cameron Ford who will be talking about secure Russell let's all welcome cam to the stage thanks very much today I'm gonna be talking about rust let's get into it so first a bit about me I do red teaming when I do read to me I like working on the tools that we do to our red teaming occasionally I do a bit lecturing in cybersecurity and probably about 12 or 18 months ago I started to get a bit curious about rust seeing a lot of hacking news articles out there and a lot of claims so I just want to start to explore some of those so some ground rules before we start this spoilers I

kind of like rust having looked at it a bit now but you may not that's okay so every program image is kind of designed for a different problem right some of them our problems better than others so even if you don't like rust at the end of this and if you knew this I don't expect to convince you within an hour but maybe there's one or two things you can pick up and adapt into the library or something for your own favorite programming language okay so what is rust the name comes from rust is always kind of close to bare metal so it's a programming language that's supposed to give you control but at the

same time the three things that they emphasize the language is its language it focuses on safety speed and concurrency we're gonna be looking a bit at a couple of those today particular the safety a bit of the concurrency and the speed we're not gonna go into but you can find that information elsewhere it started in 2006 there was Mozilla employee who kind of got a bit sick of seeing the similar sort of mistakes being made in their codebase time and time again so he wondered apart from all the tooling that I have maybe there's something to programming though you can offer to improve things by 2009 it became an efficient Mozilla project and at this

stage we still invest a lot of effort into it they hired people who design programming languages they took it really seriously and there's lots of viruses lots of things put into the language taken out of the language and by the time we get to 2015 we've stabilized we set on what we want in the language a year later Firefox for 48 starts running Russ code around the world - I think Parrs head of headers for keiki container for video files and since then it's been used in plenty of other places and Firefox continues to use it more they rewrote the CSS CSS engine styling and rust stuff like that Stack Overflow - a developer's survey

every year where the people using Stack Overflow yeah answer a bunch of questions about their job in their work past three years for us has been voted the most left loose most loved language on that survey so there's some people out there who like it that's good its kind of influenced by C and C++ to one degree as I said it kind of wants to give you that control the language doesn't have garbage collection and things like that it wants you to kind of be able to account the resources that you're using but at the same time it's kind of influenced by ML type languages so camel and Haskell and stuff like that it uses strong typing to

offer a lot of the safety it's kind of similar to Swift in that sense Swift also has its strong types and D as well I'd say and there's reasonable IDE support out there I use Ben that's fine for everything so why bother but that's that doesn't mean all right here's some of the things people have been doing with it the Russ compiler uses LVM so we get all the benefits of talking toting all the stuff LVM does over here we've got an operating system that people have been running in rust called redox so that's coming along someone's here written an iOS app and people using it for embedded development but if using it for embedded development

gonna lose a bunch of the stuff and Stan library obviously but still it can do things there and if you want to find out more about who's using it you can go to this URL down the bottom and there's a bunch of company logos they often link to blog posts and they explain how they're using it but I've seen interesting things like Coursera was easier to bootstrap container runtimes so they could run untrusted containers and stuff like that and Dropbox I think rewrote a bunch of their storage engine in it so people are using it beyond just Mozilla so today we're going to go through four bugs where I'm gonna show a code that's not really exploitable code

for these bugs but it's going to show you the primitive that might kind of lead to this sort of bug if you and I'm gonna use different languages to make these points if you use one of these languages C C++ go you're probably not going to like the code that appears on the slide that's okay that's not the point the point is that someone we all kind of work in one way or another with this guy this guy is nelson bighetti from a TV show called Silicon Valley he's kind of this hapless programmer who kind of falls through life he's by his own admission he's not a great coder and even though we might write perfect code

nelson bighetti doesn't it's also called bighead i make only big-headed sometimes and maybe your teens great maybe your team doesn't have a nelson bighetti in it but maybe one of the open source libraries is something that you depend on maybe the getty made a commit there or something at some stage who knows but there was curly out there that's trying to harm us so that's what we're trying to help to that end we kind of need some sort of model to see how we're going to defend ourselves against such code so I've kind of got these concentric will not concentric circles but conjoined circles and any program bighead rights kind of lands somewhere in this big circle right and so a bunch

of those sometimes are going to be a perfect program without bugs most of the time they're not they're out here and what we kind of want to do is kind of force these programs in into not reaching into our production environment okay so we can put one barrier behind that we're gonna make him Musa compiled language right so he can't bash his head on the keyboard anymore and run a Perl program or something so he has to actually now compose programs he didn't pick up naming variable errors and stuff like that you might get an interpreted language we're also gonna make him run tests and we might have some sort of static code analysis or something so

we're kind of each time Co se and closer and closer so now I buy random if his program lands anywhere in here you know we getting floating close to this small circle so let's start off with buffer overflows he's a program bigot he's written we create a array length five he wants to look at the last element and he wants to look at the ninth element for some reason so what happened by the way just down in the bottom right you'll see text that you probably can't read these slides going to be online straight away afterwards and there's a github repo this is the folder where you can find each code each file for the

examples and you can build them and play with these things yourself so that's that's what that is but yeah he's written this code but we're forcing him to compile so what happens when he uses his toolset to compile he gets a warning the warning is kinda useful right so we get index known as past the end of the array that's that's pretty informative and it shows you exactly where it's happening what's the problem with this is we can't guarantee what's going to happen if you do this if it's reading obviously memory somewhere the program may crush it might not in this case is just reading the memory but if he's writing it it could be far far worse

right so that's what we're dealing with here but we can implement another control we can just tell the compiler with the W error flag all right all your warnings and our errors it's not going a producer binary for this bit of code well this is kind of simple code there right we can obviously see what interesting past thee into the array but what if we try and take out index from standard input well it's kind of hard to analyze this now for the compiler to consider all cases because there's going to be valid indexes that are submitted as well so we have to through some other form of static code analysis or something encourage bounds checking and

of course we've got runtime protections like stack cookies and stuff like that to prevent you know writing over other variables in the stack but beyond that it's good to exercise these sorts of things so here's some Russ code very first bit of rust code we're still declaring an array of five variables of five elements the size is here those things are signed 32-bit integers as it says here we want to index the last element and the ninth element and again compiled language okay compiler throws out a warning it's very similar right to the C one that's just a warning again so we can do some linting on this code and we can turn that warning into an error

the linter in rust is the separate tool called clipping and like other linters that lets you conditionally turn sort some sorts of warnings into errors and others not but again what if we take our index from standard input we can't kind of detect that sort of thing so we write that program we run it with a an audience and out of bounds index and the program crashes but it kind of crashes gracefully it's got information it tells us you're trying to get to the index 10 but the only length is only 5 and it even tells us if we set this environment variable that we could get a whole stack trace for this so that's interesting what's

actually happening here is because Russ knows the length the array it can automatically build in a bounced check in the compiled program if you're doing a bounce it's going to throw you over to the standard routine for balance checking otherwise it's going to continue doing normal things and just as an aside we can see here the array being built on the stack and you can see some of the optimizations we get out of LLVM to improve performance and speed so what have we done we're compiling music compiled language and on top of tests and static code analysis that may exist we've got a linter that we can use there's nothing particularly special at this stage this is similar to other

languages but that's what we have for this kind of bug about no pointer dereferences so here's an awesome C program that big a DS written we've got some pointer a bunch of stuff happens with that pointer and a bunch of code that have excluded might have tried to allocate some memory to it or something and when we try and write to that our whole programs gonna explode right so what should you be doing should be checking for nails before we try and use them this actually does matter and rust despite all the safety and everything in rust you can also do unsafe things if you want to talk directly to hardware or if you want to optimize things somehow

that you feel you need even more control yeah you can do that but you just have to be explicit and put it in this unsafe block yeah so let's have a look at some unsafe code we've got a string that we build up a rough string which has the pointer which it starts with and its length we get the pointer we print both those out so you can see hello world the address of it then we create a C string okay so we can do that a nice nail terminate string here and then we get to some unsafe code so we can actually use printer from Lib C and take this format string chuck it into

printf give it our pointer and now C code is printing out our string you can this is nice we've got Lib C here for us in this case but if you've got your own C code that you want to call out to there's a tool called Bonjean as well so you can generate rust bindings for your C code and whatnot and there's plenty of function foreign function interface is for other languages as well so that's what we do there that's a bit of C code that we consider unsafe because we're calling out but there's also Russ code that's unsafe apart from the stuff we're creating in the standard library we can say hey create the string from raw parts

here's where it starts it's going to go through this long and then when we have a look at that we can see the original string is still fine but we've got this corrupters dream where it just keeps a reading past memory so we can do stuff like that that's kind of why we care about null pointer dereference and still so he's an equally bad program in rust where we create a pointer that's a null pointer and we actually explicitly have to create it as mutable by the way another kind of safety / optimization thing all your data files by default are immutable so we have to say hey this seems to be mutable because I want to change it

later on and then we try and dereference it and change it what happens segfault in rust okay so Russ gives us a tool to kind of deal with this in the sea world we're kind of dealing with these pointers but Russ gives us an alternative which is we have what's called an option type which in turn wraps around a non-normal type which wraps around a tee which is just any any title so like that point it could point to and in a string whatever that T can be what okay so we have two different types in two different languages of dealing with this and when we look at what can be in that C type we get heaps and heaps of

values right these are all valid values for that type anything from zero through the it max when we talk about the RUS type there's only two valid values so those two valid values are none or some and so if I ever want to use something that is an option type my first step is I have to check whether I've got the none version or the some version and I'll show you in code how to do this in a second I just thought explain this a bit first so I have to do that check first and then I can access the underlying value in C I get the value first and I have to on my own remember

to check for that null pointer of course there's things that can help you with this again in static analysis and whatnot but generally this is what you're doing with and also I want to point out in terms of rust performance and things it talks about zero cost of distractions a lot so here we have a zero cost abstraction and the programming language we have these kind of like rich types which you know an option type which becomes two types which then I actually get a value but this is actually stored we're still talking something that's just four bytes long on a 32-bit system and if it's zero it's what we consider none if it's not

zero it's the some time so underneath it's all the same but when accessing it via the programming language it's we've got these there across the instruction on top so he's our program now we've got our pointer and then immediately we convert that to a option type of an unknown pointer scientific point of science into bit number and if we want to access the underlying value here we have to do this this is kind of like a switch statement it's called a match statement or pattern matching so we take this good pointer and we want to check if we have the son version or the version if you've got the non version we're just going to print out or log or

something the fact that this happened if we've got the son version this is where we now have permission to get the underlying value this P is the what's the non null pointer and then we can get non null pointer has a method on it called as pointer that's the actual value we can dereference that okay so this is all still happening in my unsafe block I'm providing protections around it but because I provided those protections I can also now move my unsafe block further in okay so you can be more explicit about what might be a source of memory corruption or something like that so unsafe code can cause bugs anywhere else in your program because

you can put the programming in invalid state but if you're trying to track down those bugs it's probably a reasonable step to start with looking in the unsafe blocks so that's going to help you with that sort of stuff so what have we done we've got again that compilation step we've got our tests and code analysis but now we've actually reduced the size of that circle of programs that will compile there's no way that he can access that underlying pointer without going through that option type so the type checker is giving us a bit more safety there all right so dangling pointers we might see these creep up in use after free or double free sort of

bugs where we kind of lose clarity over who has responsibility for a particular piece of data so here we have a string being created called foo and pointer also points to foo so now immediately these two things both have responsibility for it they both have ability to do things to this dream we can print them both out to give us the name of the variable the the address of it and the value that's there true then decides that it wants to clean itself up so we delete through we get rid of null pointer we set it to null pointer and now we go to print both out proves invalid that all makes sense that's kind of what we wanted that point

is still pointing to that but a memory and now what it was suspecting to be a string is whatever so C++ gives us something to deal with this called unique pointer in more recent C++ versions so I've written a function here this is called take ownership this is something that's in the example it's not part of C++ and all this function does is take any arbitrary type and does nothing with it but it could do something with it it could free whatever it's getting or decent called the Deconstructor on it it could create another alias or something like that so the point is it has control of it so we create a new string but we wrap this

up in a unique pointer and then we print out string should work and then we pass the string over to our take ownership function and this is what happens we actually get this template error which is telling us that the copies instructor whereas a constructor is implicitly deleted because we're using a unique pointer so that's actually kind of useful so we're normally that value we copied into this function by wrapping an unique pointer we've disabled the ability to copy it and we have to be more explicit about our intentions here so c++ gives you something for working with unique pointers and that is move semantics so we say all right this function it now gets to take ownership of foo and from

there I should no longer be able to access it so in this program we've got the string we use it with transfer ownership we try and use the string again and it bugs out so we've got a crash but it's a bit more of a controlled crash we're not we're not opening ourselves to other use after fries or other things like that in fact what this is is a null pointer dereference so if we check true for a null pointer and run the program again indeed through is now a null pointer so what happens is three is created we use it as soon as foo gets moved in here our version of food becomes null okay their

version of food they keep using and that's fine so that's what we've got there to kind of protect and C++ rust this is kind of one of the differentiating features of rust I would say it has this ownership model and four years from the very start at the language in 2006 is kind of a consideration to take make take advantage of this so it's built into the language and I sorry hang on let me go back to that finish off the C file first stuff there's we can also access the underlying pointer in C++ so if we want to get the three pointer we can a lease it to a three pointer using this gap

function so now we've made a copy of it ourselves explicitly we can use it and we can give our ownership of it to someone else but when we give ownership they still may free whatever that memory and our food pointer could be invalidated in a way so we don't know what's gonna happen when we get to this line here the better way to do this sort of thing is to use the release API and get a weak now get the memory address into food pointer but when we try and transfer ownership through set to null straight away when we try and transfer ownership we're transferring ownership of null and therefore our own copy of reigns fine

from here you forgo on an alias that pointer and have other problems but it's the preferred way of doing things here at least you're being honest and saying that unique pointer is unique because it's it's not valid anymore here you're saying I've got a unique pointer but really there's actually other pointers as well so as I was saying rust has its own ownership model so it tracks ownership here we've got a string food and we assign that to another variable stolen so immediately to two people they're responsible for this in this version we're transferring that variable into another function just like we were in the C++ example so what happens here we try and compile we get a

compilation error and it says use of moot value value was moved here and you used it here after you've moved it so I find that this is a pretty simple example but I find that a really kind of nice useful message it's pretty clear to me what's happening so I can clean that up and try and use through other ways I can stop using through after that function but that's kind of a hassle for my programs I'm going to have to be passing off these variable D functions and then never ever see them again so rust also gives us ways of transferring ownership and transferring it back so we have here we create a string we now clone this string so this

is implemented for the type so I know this guy gets his own copy of that string and we can safely keep using our own so that's an option we can return ownership so the function signatures for these are here by the way return ownership we take it but then we give it back at the end so we assign that to something else and we can keep using that new thing borrowing I can say hey you can have this but when you're done with it instead of destroying it like you normally would I'm gonna keep using it so we can do that as well the ampersand that's fine if everyone's reading from it we can have

multiple borrowers but if anyone wants to change that while other people are trying to read from it the compiler is going to complain so you have to be explicit if you're making a mutable borrow and if the post any possibly exists that there could be a mutable borrow at the same time or a couple mirrors at the same time the compiler just won't have it so we've got a compilation step again we've got programs that pass through our continuous integration continuous deployment pipeline hopefully we've got the type checker which has protected us from a few things and now we've got this borrowed checker which is further rejecting the number of programs that are actually going to end up so you know

if if pressures on spaghetti and he needs to get this fixed into production and he's just hacked something up in his books and he's ready to push out and subvert whatever CI city pipeline we have he can't do it because the program's not going to exist so the final bug I wanted to look at was data races here's some go code immediate apologies to people that write go it's not pretty very good it's not idiomatic code it's the first bit of go code I've written though and what we're doing is we've got a variable here we've got n times we're gonna spin up this go function this lightweight thread and every one of those is going to increment

this variable and we wait till they're all done and we want to check what the value is at the end and we write a bit of rapid code around this because we kind of I know it just seems like we're just talking about multiple mutable references feels like this has a bit of codes me all about it feels like something should be wrong here but yet he's done his research these times don't a bit of disassembly he says no look there's this that single line of code it comes down to that one in construction so those are instructions they can interleave we're going to be ok here you think okay I'm gonna write my I'm still gonna write this little

harness around it and find out what happens and this is what happens so if everything is awesome we print it out if it's if we didn't get to 100 for some reason we find out what the value is and that value each immediately changes and the best sort of errors right the ones that just aren't consistent so that's what's happening here now apart from this not being actually I'll get to that in a second the reason this is happening by the way is the lock in the in construction isn't guaranteed to be atomic unless we use a lot of prefix so that kind of intuitively makes sense you know there's a few things going on with

an Incan instruction we read it we change it write it back you can see how that would happen but so we need some sort of safety around this and go has channels in the preferred way of actually sharing memory to do that but even if someone did progress with this sort of go code go does give them another option they use a data race detector so you can run your tests with a - race flag and you'll hopefully find these data races in your code add this as a step in your CID pipeline and you can start to track down these sorts of bugs and reject those builds so let's try and do this in rust we've got we're

splitting up ten threads this time and each one is incrementing and we're using proper threads not lightweight threads just cause let's see what happens so in this case we get 0 all the time at least it's consistent maybe I think that's good consistent that's consistently wrong

so what's going on here yeah so because this is a primitive data type when I transfer ownership of the primitive data type to this thread it actually gets its own copy okay this is not a pointer to some other bit of memory this is just a single value and it gets its own copy each one of the 10 threads adds one to that zero that they get and then throw it away and then we still get zero at the end so that's what's happening here so to kind of understand this a bit better let's do it with something that might be heap-allocated like a vector vectors a variable size arrays and rust so we're going to have every single

thread add to the vector and see how this all behaves all kind of mutating this thing at the same time right well Australia we get a compilation error it says you tried to move the value into the closure and by the way I should explain some syntax this is a closure so this is a function that we're giving to the thread this move says we're giving ownership of these values to the closure to the anonymous function these pipes is where you put any arguments to that function but there are no arguments it's just capturing this variable from its environment so it gets ownership of it and then it tries to use it and we get some sort of compilation

error this might be kind of initially confusing I mean it's its function can it use that so I split this out into two separate threads rather than have a loop and let's see what happens when we compile this program okay so the first one the first thread that spins up it receives ownership it's the second thread when it tries to use that variable for which ownership has already been transferred it has I fit and won't compile that makes more sense now I can see that so the question is how we're going to fix this if this is a normal function the ownership is kind of obvious you know if we call this function first we transfer ownership it

transfers it back Gudrun back in the main function transfer ownership to the second function transfer it back to refine but with threads there's a whole new world of possibilities right so we may get a scenario where that execution pattern happens again so it may transfer ownership there and back and there and back to that second function but those functions could happen at any time in this timeline so they could end up both running at the same time or yeah in different order or something like that so we have to solve that you know how are we gonna give these threads concurrent access to this bit of memory other languages obviously have things for dealing with this as well so we have

to solve that but we also have to solve the problem of once everyone's finished with this resource who's gonna clean it up so we need to work out when when everyone's finished with it and again that could change in time we could happen quicker sometimes and later at other times so there's a couple things going on here at the same time and Russ gives us ways to deal with both of those in terms of allowing that controlling that concurrent mutability mutexes you know pretty much real languages have them nothing surprising there but if we just try to write this program with the mutex and compile it and it's in that github repo but didn't put a slide for

it it still won't compile because we still need to answer that question of who's going to clean this up when we finish with it so to deal with that problem we need to create a reference count in this case it's an atomic reference count and through those two kind of container types in use simultaneously our program will finally compile and we get data as compiled time safety so here's what it looks like so we have our mutex we're wrapping the original inter-gang and we're wrapping that mutex abstain in a atomic reference count we make sure everyone gets their own copy so that's how we allow this the simultaneous access when they reach this actual clone is of the atomic reference

counter it just incorrect increments the reference counter that's the definition of clone for arc and when it's done when this closure finishes rust the rust compiler knows all right I can clean up this variable which ran reference counter means I decrement that reference counter I need to acquire a lock to the mutex obviously and that lock acquiring that lock could fail or it could succeed so I in good rust code I should be checking that here to fit on the slide I'm just using his unwrap function to assume that it's worked this if I'm wrong in that assumption the program will blow up here and it will tell me why but for convenience I'm for

prototyping and we need unwrap here and then we increment the value of this and so in this way everyone gets their own copy of something it's a reference count as far as it doesn't know any better it's a reference count to that Munich's which then they try and each get their own lock - and with all this we actually get a compiling program that works move any element of that and it won't work a couple of things I wanted to point out on that example obviously I'm talking about data races not race conditions rust mates no trains against race conditions data races were talking simultaneous access to memory race conditions you know there's far more

complex state that we're dealing with you know within the same program or not we're not looking at that and we were communicating by showing memories so I have said that that was no idiomatic go code go to my understanding would prefer you to share memory by communicating through channels rust you can also use there's a channels API there us is kind of giving you a choice to do one or the other and if you try in the showing memory model it's trying to guide you and help you do things more securely okay so here we are we've got the compiled step we've got that test step in this case we had the type checker preventing us

[Music] it didn't like it if we just using mutex we just used an arc but once we use them together the type checker was happy the borrowed checker was happy so that's kind of it if you if I despite your curiosity enough there's pretty good documentation out there for us there's this book which is also in high copy but you can just read online for free if you want to look at unsafe code there's the Russell nomicon that is also kind of interesting to read for security minded people to find out what's more of what's happening underneath in rust there's a rust for C++ programmers and C programmers Reaper which kind of kinds tries to transfer those concepts from

one to the other and there's a bunch of others this week and rust is kind of interesting to see what libraries coming out what new features that non null pointer example in there is only a couple weeks old in the stable version of the compiler so there's always change happening people have tried a bunch of things as I said a particular point out this one this guy's got really great tutorial and writing your own operating system and rust not the redox sort of operating system that we saw which is far more fully featured but you end up with something that's an ISO and you can print your own stuff to screen it's pretty cool pretty fun

as punching better development environments and stuff that people are trying but again you can see that this is kind of trying to be close to bare metal they said people use for us to write rest api's and things like that this all works because the maths so there's a field of maths which I don't know a lot about called linear logic and it's kind of you can use in probably butcher this but you can use variables in your proofs but once you've used that variable once you can only use it in your other assumptions or clauses that's what the compiler is using underneath to do all that borrowed checking so it's not just kind of fluked or kind of

hacked together there's actually really solid math underneath it and there's also some people have done some work to prove that those container types a couple of which you saw the used bit of formal methods to show that those do indeed have the properties that they claim to have doesn't mean that's necessarily always secure it's just what it says and then some people are trying to build a bit of a rust ecosystem on top and so forth which I kind of stumbled across but it could be interesting I think it's early days for that went to my understanding okay so for this talk I've just got website where I put up the code these slides are available right

now and when there's a video for this I'll put out the video as well but you can go if you want to explore more of these yourself there and that's kind of it thanks to Carlene Silvia for organizing the conference and you guys are coming so let's take care [Applause] you