Hacker Time
Presented by: Evan Johnson
Originally aired on March 7, 2021 @ 8:00 PM - 8:30 PM EST
Join Evan Johnson as he speaks with security professionals about recent security news!
English
Security
News
Transcript (Beta)
I'm going to start and just assume I am live. Thank you for having me this morning on Cloudflare TV.
My name is Evan and this is the number one security show on all of Cloudflare TV.
And that is probably the highest population of security shows anywhere in the world.
And today we're going to pick up where we left off last Friday at the same time.
This is my code repository for the show Hacker Time. You can find it at github.com slash ejcx slash hacker time.
And we're going to be doing some live security programming, just some morning fun to warm up, talk about different security issues that you might come across while writing some software.
And let's pick up where we left off last week.
So I was working on this hacker time .go with you live on TV.
And this, the main thing we were working on was we were talking about this encrypt function and walking through one line at a time, how to build a good encrypt function.
We were talking about what dependency to use and all of all of these details around how to use a non securely.
And so I'm going to recap that just quickly.
And because I think that the details are important and I want to make sure that if you are watching now, then you still have the context as to why we've made certain decisions.
And first quickly, we're using this secret box thing to do the encryption and decryption operations for a symmetric key crypto operation.
And you might wonder why is that? Doesn't doesn't go link have really good support for encryption and their crypto library.
I know that they have quite a large and really smart going team working on the standard packages working on this on, on these packages in go for AES and RSA.
Why aren't we using those? And the answer is because those are a little too low level for what we want.
There's a lot of ways to do things wrong with those.
And if you're writing a program with and using the AES package, you're going to come across all of these design decisions like, well, what mode of AES do I need?
And do I need to do any authentication of my cipher texts?
And, and it can be really problematic if you make a mistake.
so that's kind of the reason why we're using the secret box the secret back box package instead of the built into go go crypto AES packages.
The next thing you might wonder is, well, what is the secret box thing?
And secret box is what's called an AEAD and Wikipedia has a great there's, there's a lot of details online about AEADs, but it stands for authenticated encryption and associated data.
This is probably a little small for you to see. And and the reason why you want to use an AEAD is because it kind of abstracts away all of those details that unless you're a cryptographer, you don't really care about.
You really just want to securely encrypt and decrypt with a symmetric key, then look no further than an AEAD.
And I can't believe that rhymed. That was a catchy campaign slogan and didn't mean even mean to do that.
I'm going to have to remember that if you need to encrypt, what did I say?
Look no further than an AEAD. Oh, if you need to encrypt with a symmetric key, look no further than an AEAD.
That was great. So it's, it couldn't be more true.
There's a lot of information online about it. If you just Google AEAD, you can read about this for days.
And it's really, really fascinating because it's all built on it's, it's really just all of those primitives that are in the Go that Go provides you.
But there's some, some really strong opinions on how to do, how to use those and implement symmetric key cryptography securely.
So those are great. And so let's, let's pull this code up and see if it runs.
So here's my terminal. I'm in tmux and vim. And we've got hacker time here should be the exact same state as in the repo.
Let's do a good status and we've got a clean branch.
So the way we've been running these, I'll pull up my test and the code side by side.
And over here, let's get my test going. And then down here we can run the tests.
So let's run the test.
And we've just encrypted the sentence. You'll see over here, what gotest-v does is it'll run all of the tests in, in this package.
And there's only one test encrypt.
And we're encrypting the word, be sure to drink your Ovaltine.
It's going over here into the, so it's going into the encrypt function here.
And it is, the first thing we do is generate a random nonce. That's super important.
Just because you want to nonces are one time use, random bytes 20 and in the super box package, it has to be exactly 24 bytes long and it should be generated randomly every time you do a, every time you do an encryption operation.
And so we generate those bytes. And if we can't, if we can't generate the bytes, then we just fail because there's, there's no reason to continue this kind of a toy example.
No errors to handle here. Next, we are generating a random key.
And this is very problematic. We're going to have to fix this right about now, because if you generate a random key, every time you encrypt, you might want that.
But if you want to decrypt the message, you've kind of got to keep track of a lot of keys if you're encrypting a lot of things.
So instead we're going to want to change this encryption function.
So that a key is passed into it along with the string that you're encrypting.
And then lastly, we're printing a lot of, these are just debug print statements that we have to, to print the key, print the nonce and and then we are calling secret box seal, which is what actually does the encryption and printing out the ciphertext.
So it would be great today if we could get to a place where we have refactored encrypt to take in a key.
And this test encrypt also does decryption.
So we can encrypt, be sure to drink your Opal team, see the ciphertext and then decrypt that ciphertext and see the be sure to drink your Opal team string again.
So let's try to go about that. And to do that, that means I have to build in order to, to, to get to that goal.
We're going to have to refactor encrypt to, to pass in a string.
And then also we're going to have to write the decrypt function.
So we have a lot of work. We've got about 22 minutes and I want to cover other stuff too.
So we got to go fast and so let's get going.
First thing I'm going to do is update the encrypt function to take in a 32 byte array.
And this will be the key. This is super simple. And instead of generating it here we can take this code and generate it over here.
So let's remove all the key stuff here and we can move it to the tests.
So we've got a variable over here that's now unused in our tests and let's take out the actual generation of this.
And we actually don't need this.
I suppose for good practice, we can keep that. So make sure that we are generating some bytes here and, and checking how many bytes for if n does not equal 32.
All right.
And lastly, when we encrypt, we also want to pass in the key. And so now when we go over here, everything should work exactly the same, but as when we ran the program previously, however, the key is actually getting generated over here in the testing for function, getting passed into encrypt and everything else should, should work exactly the same.
So let's see if that worked. And what's pretty good to me.
All right. It must work. If, if your program prints information and it does it on the first try, give yourself a pat on the back because you didn't make any mistakes that you know of yet.
Okay. So now we've got to do the decrypt function. That was the encrypt function and it looks to be in pretty good shape.
I'm just going to the one thing that's missing here is we need to change the encrypt function to return data because it doesn't make any sense to print your encrypted data down here.
We actually have to return it. And so let's just return a bunch of bytes or an error and we can go back.
You know what? This is a toy example. Let's just return bytes and all the error handling the program will just fail.
And so it's plenty good enough. We want to just return buff here.
All this, give this a more descriptive name, cipher text and we want to return it and get rid of this and we should be good to go.
However, we're going to need to cipher text here.
Once again, everything should run exactly the same.
We should see the same thing. It should all be working.
However, we're slightly refactoring the code as we go. Great, great, great.
Everything is working. So now we want to, we have a cipher text current status in the encrypt function.
We have a cipher text here. We're encrypting.
Be sure to drink your Ovaltine with a key and we need to, we need to take the cipher text, pass it into decrypt and call it a day.
That's all we need to do.
So let's load up the, um, the docs on secret box and there might be one extra thing.
Okay. So open, so we called seal. This is the input function.
Now we need to call open. That's the decrypt function. Uh, and everything looks good.
There's actually going to be one extra little hitch that I noticed just now looking at the docs, but open looks a lot like encrypt.
So you take, um, should return, you just pass in your bite or your, your key, your nonce, the, the cipher text, um, and it will append the message to out, which must not overlap box, uh, produced by seal, open it encrypts and decrypts.
It box produced by seal.
Okay. Which must not overlap. Huh? What's going on here? All right. Well, we'll guess and check.
Let's learn as we go and try to, uh, let's try to open this cipher text.
Let's try to get something working. So we want to create, uh, we want to create a new function decrypt.
It's going to take in a cipher text, which is a bite bunch of bites, a key, which is also a specific number of bites.
And we want to return some bites, very specific stuff.
Uh, that's cryptography when you've got bites and, uh, it's just bites for days.
Um, so, okay, we've got the cipher text. We need to, let's first just pass it in to decrypt and then debug.
So we're going to pass in cipher text and key and this should work.
Get some. So our goal is this plain text when we print it should say, be sure to drink your Ovaltine, um, at this point.
So we're calling, we're making a decrypt function. We're passing in the encrypted, be sure to drink your Ovaltine.
And our goal should be after we decrypt, we should see that string again.
And that message again, because it means we've successfully encrypted and decrypted.
Um, and we're going to hit some little bugs to debug along the way.
And, uh, that'll be just fine.
Uh, because that's a faster way to program. In my opinion is just program really fast debug with compiler write tests and, uh, and you can move quickly.
Okay.
So open, we want out box nonce key. Great. Uh, key cipher text. Uh, we want to call secret box dot open.
Uh, let's do this out. We're going to do one more thing in a moment.
Um, out key box is the cipher text on key.
And this should return in text and a bull.
It, it, uh, and the bull indicates whether or not it worked and um, what's going on here?
It's upset with me. And see how it goes. Shouldn't work. No, why?
But that's okay. And last we need to use this full, if not, okay, let's fade a lot.
Okay.
Okay. We have a bunch of bugs to solve. Let's solve them. First cannot use nonce type 24 byte as pointer to 20.
Yes. Yes, of course. So we hit this last time as well.
We need to pass a reference to the nonce and a reference to the key. They're expecting pointers here.
Um, so we have to make sure they're happy about that.
And now this is actually operation, open operation, not okay. Uh, no. Okay. Let's make that say no.
Okay. And this is a, the last thing I think we need to do. So here in encrypt we're generating a random nonce and then here, we're decrypting with a nonce, but it's not this nonce from up in the encrypt function.
and so we actually need to move this nonce generation stuff over to the encrypt function and pass in the nonce to both, um, the encrypt and decrypt function, uh, just like we treated our key.
And so you may be wondering, doesn't that mean whenever I'm, um, writing code and doing encryption and I use a nonce that I have to treat it like a key and be passing it around like a key all the time.
And the answer is actually no, the nonce is not so sensitive.
So one thing that I have done in the past is you can actually append your nonce to your ciphertext.
And, um, so you have your ciphertext.
And since the nonce is a set length of 24 bytes, you can just always have the first 24 bytes of your ciphertext.
Uh, you, you append the nonce to the ciphertext and the first 24 bytes of the ciphertext is the nonce.
You just take read the first 24 bytes and treat that as your nonce then read the ciphertext, um, uh, read the rest of the ciphertext and, uh, you're good to go.
Uh, so you don't, you can just treat nonces as ciphertext if you concatenate the two and that's totally okay.
So, uh, let's just, let's just refactor this a tiny bit and get this thing working.
And, uh, that should be great. So we want to add a nonce to the signature of both of these functions.
I'm doing this. It's not the cleanest thing, but I'm doing this, uh, to err on the side of speed because we have a lot to fit in 30 minutes.
And, um, and so I'm trying to move fast. Let's get rid of this nonce.
Let's look at everywhere. We have a nonce. This looks right. Uh, this looks right.
This looks right. This looks right. Uh, this is not right. We need to move this to here.
We read the key, read the nonce and why is it mad about the key here?
Is it bad? No, it's quite all right. Okay. And this should work.
I think this will work. Let's give it a try. Um, oh, we didn't change our functions here.
That's the only thing we need to do. So we need to pass in the nonce here.
We need to pass in the nonce here. And this, I believe should actually work.
And what do we have? Be sure to drink your Ovaltine encrypt.
Oh, uh, one last thing.
These are all byte arrays. So be sure to drink your Ovaltine. We had to cast that as a string.
Cause it's a bunch of bytes. Great. Uh, let's clean this up a little bit so you can see what's actually going on.
And here in after we get the ciphertext, I think what we should do is we should, we should change it a little bit.
Okay. And this should be, be sure to drink your Ovaltine.
Let's clean this up just a tiny bit, all of this stuff.
So the message is going to be, be sure to drink your Ovaltine and it's going to be a constant.
And then we have the ciphertext, the plain text.
And what we want to do is kind of print out everything.
So let us, um, print the message.
Let us print the ciphertext.
And lastly, the plain text and the plain text should equal the message.
And what I do when I'm writing tests for this is I go ahead and I won't have all these debug statements.
Like all these print statements don't have anything shouldn't exist in tests like this.
Um, but it's kind of easier to see, but what I would do is have a test where if, um, if, uh, let me actually change this to a bar where I would say, if message does not equal string plain text, and we would do it, uh, a fail the test.
Uh, I think it's still. And what this verifies is that you're encrypting and decrypting and getting the same result when you, when you, when you finish decrypting.
And so, um, should shouldn't be an issue here.
Um, undefined message. These should be, this should be capitalized.
All right.
This looks horrible. And the reason is because we don't have new lines. Alrighty.
That actually looks pretty good. I like this. Um, this, we can see that the message is be sure to drink the Ovaltine.
The ciphertext is just a bunch of bytes. So let's type it to xxd and you'll see that here.
Uh, this is all the random like go debugging stuff.
But then when it says ciphertext here, you see, it's just a bunch of bytes here.
All of these should just be random bytes and then it says plain text and be sure to drink your Ovaltine.
Uh, the string in here is the same string as the one up here starting at 42.
So 42 on to here. Um, and way to go everybody.
We did it. And I think it looks really great. Uh, and also the test past, you might, you might not have noticed that, but this is actually a real go test.
And so, um, the test did pass, which means that, uh, which means that this message equal to plain text.
So let's commit this up and I want to talk about what we're going to program next.
Uh, and we won't really have time to get into it today, but I won't.
I think it's an interesting, uh, let me actually, oh boy.
Okay. Alrighty.
And I want to show you what I want to talk about next. And, uh, let's make sure that all of this made it up here.
This is some nice clean code, really short.
Uh, and we didn't really address this out thing and, uh, it could be much cleaner, but this is a good example.
Good example. Code of like, it's all right. Crypto code.
And the next thing we have here is going zigzag. And I don't want to talk about specifically go Lang's exec, but I want to talk about kind of the problem of exec thing things on the command line that you receive.
Um, and there's a lot of times when you're programming where you're writing code, whether it's go, whether it's PHP, whether it's whatever language, and you are at a point in your program and you say in your mind, this would be so much easier if I could just run this on the command line because there's so many command line tools that are really powerful.
Uh, why can't I just like use this command line tool to do this thing?
Like create a file. Uh, if you've never created a file and it's hard for you, then creating it on the command line with the touch command might be easier.
Uh, and that's kind of a toy example, but this kind of thing happens all the time.
Uh, and going has done a great job making it so you can do that safely on when you're writing a goaling program.
Uh, it's actually really hard to, to take something from your program and, and run it on a command line in an insecure way because there can be all sorts of nasty security issues when you're, when you're in a pro in programming space and you run something in, uh, your terminal or executive call system in PHP.
And, uh, that's how a lot of remote code execution bugs happen.
And I'm kind of going to kind of walk you through the anatomy of a remote code execution issue in PHP.
And then we're going to try next, next episode, we're going to do this in PHP and going, uh, kind of, we're going to make a toy program that you can totally execute arbitrary code with.
So, uh, we're going to do that in PHP. We're going to talk about why it works.
We're going to talk about, um, we're going to talk about if you can make it secure or how, um, and kind of the thing that really indicates that it's insecure, the thing that should be a glaring indication that, uh, the code that you're looking at is insecure within a second of looking at it.
And then lastly, uh, we're going to try to do the same thing and go, we're going to see that it's really difficult.
Uh, we're going to try a bunch of stuff and, um, and see if we can get it working.
And, uh, cause it's not quite bulletproof. However, it is the goings exec protection is extremely, extremely, uh, well thought out and, uh, very tough to break through.
If somebody does write a bad code and allows you to execute arbitrary code using their, their go code, that's shelling out to a, to a, to a terminal.
So that's going to be next show. And for now I leave you with a working encrypted deep crypt function and the EJCX hacker time repo.
You can check it out.
You can ask me questions. I'm on Twitter at EJCX underscore. Uh, you can send in a question to Cloudflare TV, uh, under ask the question and I'm happy to answer it.
Uh, but I hope this was educational. Don't, uh, what was the thing I said earlier?
If you're looking to encrypt with the symmetric key, look no further than an AEAD.
Don't worry about the go crypto package unless you really know what you're doing or you have the need for, um, for a primitive like AES.
Uh, and so thank you so much.
Have a good rest of your day. Happy Friday and I will see you next week for hacker time at the same time, 830 AM Pacific.
Adios.