 To those of you who haven't seen me before, my name is Ted, and I work in Tinkerbox. Today will be a serious talk, okay? It will have some code in it. Hopefully you can see if I was hoping the projected screen would be bigger, but I can also read it out to you so you can follow along. So the name of the talk today is Open Sesame. I'm going to touch on the dangers of the Open URI module from Ruby and how it can be used for remote code execution. So the background to this talk is one of our junior developers came to me with an exception in one of our projects that he couldn't really make sense out of. He couldn't find out where this was coming from. So he came to ask me about it. The exception looked something like this. I don't know if you can see it, but essentially it says no such file for directory. Which seems very strange to him because he wasn't doing anything like this in the project. But it turns out he was using a gem called Refile. I don't know if anyone has used it. Essentially it's a gem for uploading files. And this gem also gives you the option to enter our URL to our remote file. And the gem will essentially download it for you and then upload it again so you get it on your own server. But obviously this error wasn't coming from Refile. So I went into the Refile source code and checked what was going on. And it turns out they were using this Open URI module from the Ruby core to facilitate this feature where you can put in a remote URL. So let's see what the documentation says about Open URI. So it literally says Open URI is an easy to use wrapper for NetHP, NetHPS, and NetFTP. This is everything it says in the description. Obviously this error wasn't coming from Open URI either. So I started wondering how does this really work? How does Open URI do this magic? And the answer to that is monkey patching. So what exactly does Open URI monkey patch? It turns out it monkey patches the kernel method Open. But obviously when you do this you don't only get the stuff that you put in there, you get the stuff that was there in the first place as well. So let's move to the documentation on kernel.open. Firstly it says if the path provided does not start with a pipe character, treat it as the name of a file to open. So this kind of gives some hints that maybe it's just something more. What if it is a pipe character? It continues. If path starts with a pipe character, a sub process is created connected to the caller by a pair of pipes. We're starting to get somewhere, but there's more in there. If the command following the pipe is a single minus sign, Ruby forks, and this sub process is connected to the parent. And already here, I mean I'm no security expert, but it sounds like you could do something like a fork pump to create a denial of service attack on anything using this. But actually it gets even better. What if it's not a minus sign? If the command is not a minus sign, the sub process runs the command. So this is why I think the big revelation that this is probably not a good thing. Especially if you're using this with any kind of user input. So the reaction then became let's go and break something. And the most obvious candidate, of course, is the gem refile. So I'm going to show you guys a short demo of something that I made up using the refile gem. So I created a very extremely simple application that is essentially a bare bones application with refile in it. And an input field that takes this remote URL that it allows you to input. So this is how refile did this at the time. Maybe you don't see it from here, but they basically call open that has been monkey patched by open URI. They pass in a URL parameter. And it turns out this URL parameter is just an unescape string from the user whatever they put in the input field. So this sounds like a recipe for success to me. So I'm going to show you the demo. Hopefully it works well now. So this is the very makeshift Rails application that has refile in it and it has an input field for the remote URL. So we know that if we put a pipe character, we can make it execute a command. So let's see what happens. Say that we want to echo hello world. So does anyone think that refile will take the output of this command and upload it to S3? The answer is actually no. I don't know if you can see it here, but what happens in refile is once it read your remote file, it accesses some meta information on it. Because we used it to execute a command, the result doesn't have this meta information. So it just throws an exception. So this is rather boring, I would say. Because we need some way to get this information out that we can access by running commands on the application. So I did something else. I created another makeshift Rails application that basically has a single model. It routes the create action and takes some JSON and just stores it in the database and the root URL shows you the lost result. So obviously it says nothing here yet because we haven't done anything yet. But now we're going to use this application together with the refile to make something interesting. So we're going to do the same thing again. We're going to do the echo hello world. But we're going to format this to the format that is expected by my other application. And then obviously, because this is essentially just bash commands, I just pipe the result from this into, say, a curl. And send a post request to my other application. So again, you're getting the same exception here. Let's see, hopefully I got something in my other application. All right, seems like it's working. So let's see what else we can do here. Just writing some text for other application seems kind of boring. So let's see if we can find out what user this is running as. Okay, getting the same exception. All right, it turns out we can actually see what user we're running as. So let's try something more exotic, like traversing the directory tree and reading some files. Okay, same old exception. All right, it seems like I've read a file on the remote server and I've sent the results to my own catcher application. So you can see there's a bunch of stuff here that I read from the etc slash password file. So actually just after I discovered this, I went to Ruby Tea Party in Trade Gecko and Moe there, he said that, okay, you can never do that in Heroku because probably the user is shut down. But actually this demo that I just showed you is running on Heroku already. So it turns out he was wrong. So I actually raised this to Heroku but it seems like their entire security idea revolves around dino isolation. So as long as you get owned in your own dino, they don't really care. So the intro solution to this was the following. Let's just sanitize the remote URL field. So we're using the remote URL module. We're parsing the URL and we're calling valid on it. If it's not valid, we just remove it. It turns out this is only slightly less unsafe because for some reason the string http colon slash dot dot slash etc slash password is still valid. So if for some reason you can plant a folder in the application named http colon, you can still exploit this. And actually this is the approach that carrier wave is using at the moment. So the real solution is obviously you should update your refile now if you're using it. I sent an email to the maintainer and it turns out he's a really nice guy. He replied to me within hours and he patched this within a week I think. So if you're using refile make sure you're using 0.5.4 which is the latest version. Right, that's all. So previously they used I think REST client. So he just reverted the code back to using that. But 0.5.0 to 0.5.3 is still unsafe if you're using this feature. Thank you very much.