Cloudflare TV

Hacker Time

Presented by Evan Johnson
Originally aired on 

Join Evan Johnson as he speaks with security professionals about recent security news!


Transcript (Beta)

All right. Hello. Welcome to Cloudflare TV. This is Hacker Time, the number one security show in the world, anywhere, anytime, any place.

And I'm your host Evan Johnson coming at you from the Cloudflare product security team here in Austin, Texas.

And we're picking up where we left off last week. And I want to go over where we left off last week.

And it culminates with this. This is my front door right now.

And working backwards from there, go back to last episode. I just tweeted a link to last episode.

If you're not familiar with where we are, I told the whole story of how I moved into my house and there was a mysterious box in the closet.

And that mysterious box turned out to be like a video video security system. And what I did was I wanted to get it working.

It wasn't working. It wasn't plugged in.

There was no wires plugged in. So I had no idea what it was. I read the I Googled the model number, Googled what it was.

It was a security system. I tinkered around with it.

I downloaded the installation guide and I was able to hack into it with the default password as the default password always works.

That's rule number one in the world of information security.

And then and then after I got access to the system, I tinkered around with it and started learning about it.

I learned about the web.

It's running a web service here at 192. Let's grab another tab here.

192. Oh, I actually don't remember the IP address. Oh, I have it right here.

192.168.86. We were able to figure this out. And from there, username Claire Admin default password is secure seven.

From there we were able to then poke around and see what types of services it was running.

So we see that network settings.

It's running all sorts of things. Where is it? 8554 is RTSP.

That's the magic port. There's an 8000. There's an HTTPS. There's an 80 RTSP being the big one.

And then we were able to configure RTSP to get it working. And I have the camera here, but I've gone around and installed the last episode.

I had the camera like physically with me and was moving it around.

And this week I've got the cameras all back where they belong, except for one.

And trying to trying to now the end goal is I'm trying to make these cameras.

I want to be able to check on things at my house when I travel or when I'm not at home, I want to be able to see images.

I don't need I don't need something always on or always recording or I don't need to see live video.

Just a still image every few seconds would be great.

And I'm hacking that together today in the spirit of Hacker Time, hacking something together using Cloudflare services today, the end goal.

So we've got this.

This is going to be a fun episode today because we have this, which is a bunch of like FFmpeg shell scripts.

And instead, what we're going to do is we're going to take this FFmpeg program.

We are going to we are going to write a little Go program that uploads still images from the camera to Worker's KV and try to make this accessible all in 25 minutes.

This is going to be a sprint. Let's see how we do. Let's get started, because if we don't get started now, we just won't finish so., no CD, Go source.

I don't have any Go programs here, just a main probably leftover from another day.

And the first thing we need to do is since this is a sprint, we're going to go really fast.

So we need this this FFmpeg program. And then a wise man named Albert once told me that any time you want to write a shell script, don't do it.

Instead, write a Go program that shells out to to the command line. And that was a very wise thing because my shell scripts are Go programs, a lot easier to maintain.

And then I have all the good string operations from Go that you'd want.

So I'm going to run this copy paste. Great.

OK, this is what I want. So thanks, Albert, for that wise advice many, many years ago.

So, oh, man, I'm not used to programming on Windows and we've got tabs everywhere.

This is ugly, but it's going to work.

So let us run this. So there's a lot of arguments here, this might take me a minute.

So many arguments, so we've got to parameterize each argument here.

And this is actually a security feature of Go's that it's requires each one of these.

Really, actually very good.

Makes it so, you know, that you know that any injection attack.

So let's say I have any untrusted input in like a variable here. If I had a variable here, it'd be really hard to hijack this command as an attacker and run your own shell command.

So this is all of this parameterization is actually a security measure, and it works very well.

Go made a really great design decision there.

OK, now we want to run this and see. If this actually works, but I need Go.

I might open up Visual Studio's code here because my Go environment isn't set up right.

Open folder. No, we're just going to roll with it, which means that I've got to do my imports on the fly.

Import, we need thumped. It's not going to be pretty, we need log, we need exec, we need, is it OSCMD, what is it?

CMD, I don't think it's any, it's a variable. OK, log, log. This might work. Oh, run main.go.

And we should, should, OK, import it and not use thumped. I'll actually use that one, should run for a second.

And then there should be an image there.

OK, I ran for a second, there's an image here.

Let's see, img.jpg, that's my front door now.

Great. So it's working. Got my FFmpeg program working. And now really all we have to do from here, actually, we're ahead of schedule here, we might finish our project on time, under budget.

I need to, within Cloudflare, get my environment set up with Workers KV and everything.

I'm going to make these API calls by hand.

So Workers KV API, so we're going to go to Workers, we're going to go to Workers KV namespaces.

I've got a bunch of them. We're going to add another, we're going to create a new one and it's going to be called Cameras.


Cameras was added. I've got the ID, we got it all here. Cloudflare v4 API, Workers KV.

And then Workers KV.

Request namespace, write key value pair.

Perfect. Okay. Write key value pair with metadata.

No, I just want to write. So copy and pasting, this is what the curl looks like.

Set no AI, no auto-indentation when I paste.

Here's to not have worked. Something with tabs here. Okay, we've commented it out.

This is what the request looks like. And we need to do a new request, http new request.

And value is going to be image.jpeg namespaces.

We have our namespace ID. So I'm not super familiar with this API, but I'm sure it's not too complicated.

F214, F214. And my account ID, I need my account ID.

That's a good security feature to include in your API design, make people specify their account ID, makes preventing direct object reference attacks pretty easy.

Because you just check that the person who's making the request owns the account ID and we new request, request, right?

This needs to be a post, I believe.

It needs to be a put.

And then some data needs to be all the data from img.jpg. Okay, post.

It needs to be a put. Golang, request with body.

New request.

This looks slightly old, but it'll work. Okay, new request works differently than I remember.

Http method put. Let me just verify that this is actually how it works.

Golang, new request. Http docs.

And that should tell us we should be able to see the API. Yeah, okay. First thing is the method, then the URL, then the reader for the body.

So first thing, the method.

The method is put, like we just said.

The URL, which is hard-coded here, and then the body, which we will need to read that image.

So the body is a bytes reader. New request takes in an IO reader.

Yeah, what's the easiest way to do that?

I can just read the entire file here. Ioutil, read all, read file.

Is it read file? Yes, it is read file. I'm gjpg, and we're going to get a big buffer.

No time to check error messages.

This will just always work. Famous last words. We need Ioutil, which is part of IO, I believe.

And then HTTP. I think it's under net.

Is that correct? Net HTTP, yep. Okay. Ioutil going. Let's check the docs there. Yep, it is under IO, so we've got our package correct.

And then the last thing we need to do is take all of these bytes.

First of all, let's test where we're at. Let's add format here and print out everything.

Let's print out this image to the screen.

Should print out a bunch of junk and the request for good measure.

15 minutes.

I'm amazed that we might actually do this. New request returns two values, not enough to call new request.

Yep, you're right. You're right, Go. The compiler is always right.

That's something that you need to learn as a programmer. Not, yes, doesn't like this.

Let's see.

All right, it's running, it compiled. Should print out a bunch of junk.

That's the actual image on my front porch. And yeah. It seems to be working properly.

So what we need to do is convert these IOUtilReader thing, or this buffer into a reader.

Golang bytes buffer to reader. Convert byte slice to IO reader.

I can never, doesn't matter how much Go code I write, I can never remember how to do this.

Yeah, this looks right.

So this is a reader. So we're creating a new request.

We're saying that we're calling the Cloudflare API, Worker's KB API, passing it all of this data from this buffer, from this buff.

And then saying that the data is in this reader.

So when it makes the request, read from the reader.

Okay, but now we need to make a client and to make a request. And then we should get to the part where this doesn't work.

And that will be because we don't have, we haven't done the authentication and authorization yet.

So I have my API key all ready to go and my bash RC here.

So we have a client.

We need to run client do in the request. All the minutes, you know, we might not get it authenticated with Cloudflare access.

I sure hope we do in time, but we'll probably get it written to Worker's KB.

And then remember, you don't need to check errors when you're going fast. This will probably work.

I just need bytes. Okay, that actually compiled.

Who would have guessed? Then just print out some, yep, a 400 meaning bad request.

Perfect. Perfect. Why is the request bad? Probably because it's not authenticated.

So I need to add two headers to this request. Add header.

So rec.header, add. That's off email.

This is the email I use for my personal Cloudflare account. I'll probably change it after this.

X off key.

One, two, three, four, five.

That's my API key. That's not actually my API key, but let's see if we get a 401 instead of a bad request.

No, interesting.

Let's read the body here.

ioutil.readall rest.body.

Then print out our variable.

Let's see what the body says. And we want to get, the last thing we need to add is the authentication here.

Okay, we printed out the body, but it's not a string.

So let's try that one more time. I can't read hexadecimal as quickly as some people.


X off key, X off email or authorization headers. Perfect. Okay, so it's just that we're missing our key.

So we are going to read the API key right now.

And then I have it as API token.

I'm not going to show it to you. You're just going to have to believe me.

And we probably don't want to do it every time.

We can do it as a var. API token.

What did I call it? API key equals os.getenv API token. Let's see if it works.

Nope. Undefined OS. Oh yeah, it isn't defined.

You're right. I have os.exec, but not os. os.getenv.

Oh, I do this.

This is one thing that I do every time I write getenv. I do capital E. I do camel case getenv instead of, yeah, that's an issue.

All right. Last things last. I need to set these before I make the HTTP request.

That's a problem. So we're going to set our headers before we make the actual HTTP request.

And we should now like either get a 401 or something a little more relevant.

So I was not actually setting the authorization headers there.

Okay. We got a 200 with eight minutes left, but did it work? Let's see if it worked as we expected it to.

Let's check our workers KV namespace. Let's check cameras and there's an IMG JPG.

Heck yeah. Okay. So let us put this in a for loop and run it.

Go front main.go, go.go.

I always get worried that when I output this, it's, I'm just not going to worry about it.

I'm not going to worry about formatting. Go run main.go. It's in an infinite loop.

Should update constantly. We have seven minutes to get a workers thing running.

It's running over here, We're going to get a worker. Manage workers, create new worker.

All we want is to do a Cloudflare worker read KV.

All we want is to read KV, set the correct content type and respond to the HTTP request.

So all we need is it's perfect.

Handle requests.

This is all we need. The docs have everything that I need and we want to get IMG.jpg.

This is what we want.

I think this is our whole program. How do I save it? Deploy to go live.

Where's deploy? I haven't looked at this image in a while because I've actually been using like the command line tools save and deploy.

Great. This should just break because I didn't actually mount the KV namespace, but we want to settings, add binding, cameras.

We'll call it cameras and save.

Then we'll go back to the actual program and update the KV namespace name because that was not included in there.

Quick edit. Cameras.

And then save and deploy. Okay. This should actually work. Should just be a bunch of junk.

Yeah, there we go.

Okay. Now we just have to set the content type. Cloudflare Workers content type.

Okay. We just need to set a header. When we create a new response, we then need to do headers.

I forget how to do this. Response header.

Alter headers. Okay. All we need is, this is perfect. We can copy and paste our way to success here.

New response.


And then we want to do, it's not a const, it's a let. I don't know if there's a difference.

And the new response, we need to do dot headers, dot add, append.

Right? And then we need to do content type, image dot jpeg. Image. Is that the right content type?

Jpeg. I can't remember. Image jpeg, not img jpeg. It's one of those things where I knew it didn't look right, but I knew it was simple enough.

Okay. Did this work?


Not quite. Not quite. Instead of focusing on getting this exactly working, this is the actual image.

I want to get this end to end. I want to get Cloudflare Access involved in our last three minutes here.

So what I want to do is I want to do frosty poetry.

Okay. We want to go to ejcx .dev. We want to do workers.

We want to do add a route with frosty poetry. Save. Save.

Got Argo tunnel error right now left over from something I previously was doing.


We've got all these left over. I hard -coded these in. Let me just delete this. Delete.

It's amazing how fast your DNS updates. All righty.

Now this should be back to normal. Should download something funky and I can put it behind Cloudflare Access here real quick.

Access. We're going to create an access policy in the last minute.

Cameras, and we're going to call this cameras.

We are going to allow emails. This is my GitHub email address that I use and should be able to save here.

And then Cloudflare Access. I think we got it working end-to -end.

Should authenticate me with GitHub. Connection timeout. No, I think I messed up my KB or my worker, running my worker here.

Let me check. Well, we got 30 seconds and I'm really happy with how much we got done here.

We got end-to-end basically.

We got the whole thing mostly fleshed out. My program, taking images, making them accessible and protected by Cloudflare Online.

And I'll put all of this on GitHub and I appreciate you watching.

Thank you so much for joining me this week.