Um, anyway, this is Steve, Sam and the door. Let's give him a big DEF CON welcome. Afternoon, everyone. Welcome. Thanks for having us here. You may not believe it, but not only was that our first shot at DEF CON, it was actually our first shot ever since, since we got away from breakfast this morning anyway, so, all right, well, welcome guys. Uh, thanks for having us. We're here to talk about the conclusion of our research, feels like it's been a long time, into hacking an access control system. So this is called Perimeter Breach. My name is Steve Pavolni, I'm head of advanced threat research and principal engineer at Trellix, and my Twitter handle's here if you want to connect afterwards, I, most of the team makes fun of me since I'm a manager. Usually they, you know, joke that I'm not allowed to do anything but Excel spreadsheets, which I can write a mean function in, but when I'm doing technical stuff, I'm interested in vulnerability root cause analysis. Right? Reverse engineering, uh, exploitation, and actually with this project, got some experience for the first time with hardware hacking. And my name is Sam Quinn. I'm a senior security researcher at Trellix. Um, I could have summed all this up, which is hacking. I really like all aspects of it, but some of my core technical interests are listed here. Um, I like exploitation, hardware hacking, um, looking at systems like IOT devices and things like that, as well as just OS fundamentals. Now, Sam and I have interests. Outside of just hacking and computers, and we're both avid mountain bikers as well, as you'll see when we're not in the lab finding crashes, we're somewhere out there and crashes manage to find us either way. So this is real footage, by the way. Like we're not, we're not faking anything here. All right, let's get right to it. Uh, the target for today and what we chose is the HID Mercury access control panel, which is this beautiful bright red board that you see. We chose the Linnell 4420, which is a partner of HID Mercury, and the reason we got interested in it, and we're going to talk about the how, the what, the whys of this project, but we got interested in it because obviously access control is one of those areas of critical infrastructure and industrial control systems that is really prevalent in this increasingly targeted attack space and really has not been widely researched, especially over the past few years. So we got interested in it for that reason. Before we get into the why and the how we did this project, let's set the stage for how this is typically deployed. Well, you see this. Beautiful door on stage that we, uh, unceremoniously brought in halfway through the last person's presentation. Uh, this is actually the Linnell, uh, card reader controller is, is plugged in behind there and we have managed to a badge reader. This is typically going to be deployed on a local network. It is possible to make this cloud facing or internet facing, but highly non recommended. The, the panel itself can manage up to 64 different devices. In this case, we're using a badge reader and being on a local network, we use a local network manager or an LSM to actually manage the door operations. And for Linnell, that's this server called on guard. That's the server that's responsible for facility for facilitating all of the user control badges, access all of that provisioning. And then the board itself is kind of a dumb control relay. It essentially just, uh, flipped electric relays. And we'll talk about that quite a bit more. Well, one of the things that first caught our eyes when we were exploring this target, this 4,400 panel was some marketing material from Linnell. That. Specified that this had been approved for use in government facilities following quote, rigorous security, vulnerability and interoperability testing. Well, obviously this really kind of intrigued us. We found out through the research process that this as intended to specify physical security testing, but really led us down the track of saying, how does this hold up from a cybersecurity perspective? And that's what led us to these findings. Now, we did originally disclose all of this, uh, just a couple of months ago at hardware IO, where we spent a lot more time focusing. On the hardware hacking process and getting to a root shell, which we'll show you is going to be our starting point for today. And so for the purposes of this talk, we're going to focus on just three of the eight CVEs, but we submitted and ultimately got patched eight CVEs, four of which are zero day unauthenticated, uh, remote code execution vulnerabilities, or were at the time we disclosed them. These are now all fully patched. The who in this scenario is who was affected by this. And as you can see here, the 4420 panel. Was the one that we chose and ordered off of eBay for a few hundred bucks. But throughout the research process, we did find out that additional eight boards manufactured by HID were also vulnerable to these same issues. And so there was a much wider install base than we had originally thought. Furthermore, there's more than 20 partners of HID Mercury, including Linnell, the board that we looked at there, uh, that use these boards and rely on them. And, uh, all of them were vulnerable to the same issues as well. So this. Since millions of controllers at tens of thousands of sites worldwide, and really the vast majority of the fortune 100 companies. So the install base is pretty impactful. We chose to approach this project and what we call choose your own adventure, a little spin on the, uh, the classical phrase here. And what that means to us is we're going to explain the process we took at various iterations of the project and the decisions we made at those steps. And why now, again, I mentioned how we have already disclosed these issues at hardware IO. So for today. We're going to start assuming we have a root shell on the device, which we do, and we're going to actually live hack it in iterations on stage here throughout the process. So, of course, what could go wrong? We're not doing just one demo, but like three live ones. So we're going to really tempt the demo gods. This is all a network, uh, vulnerability. So you have to be on the same network as the device, which, uh, many of you know, is kind of the low bar of exploitation. But either way, these are unauthenticated RCEs as far as standard operations for the panel. One of the first things we looked at was, of course. Doing a network scan, and we used Nmap to find just these three open ports. I know with the lights, uh, it might be kind of hard to read. So just like throw your hand up if you can't see you're interested in something, but we, uh, the ports that were that showed up on the scan were port 80 and 443 for the embedded web server that runs and a separate port 3001, which at the time we didn't know what it did and found out later. That was that management server, the on guard management server, how it communicates to the panel directly, but because we bought it off eBay. We had kind of this. Half baked solution, and we didn't have the on guard set up at this point when you boot up the web server on the device, which comes on automatically and by default, you're greeted with a typical login screen admin password. You'll see this in a moment. It does pretty standard session management, including, uh, cookies, uh, for the most part, uh, little, uh, preview of things to come there. It does it quite well. Configuration pages are primarily what you see inside of this web server. So there are, if you look closely, there's, there's almost nothing. That's related to actual badge usage or anything related to doors. This web server is used to configure the panel, and then it becomes kind of useless from that point on where you'll take over with the on guard server and do all of the door specific functions. The network set up page really caught our attention. It has a number of inputs. We'll show you why it was really interesting in a moment here for right now. You can just see that it's managing the host name of the device, and we'll come back to that. All right. So that brings us to choose your own adventure. Number one. We're early in this project. We were just trying to look for an RCE, our, our first zero day, and hypothetically unauthenticated. Three different areas that we could have explored here, the first one being an operating system vulnerability. This would include things like the vendor and product specific libraries and files, keys, creds, crypto, you know, the typical stuff here, maybe configuration flaws. We chose to go with more of a combination of web based and network vulnerability, and we decided to look at things like command injection because we had a web server that took user input and directly configured it. We decided to go with K Dip, we contacted the board and we worked with the board. So bypassing the network stack which would have really been looking for probably an end day or maybe a really cool ODAY in third party libraries or networking stack itself. So the process we went about as we decided to look at the web form, and of course, we're shortening this a little bit, but one of the areas that was interesting was command injection, and our injection candidate here was the device host name itself. If you look at one of the root processes that spun up when the board boots here, there's a DHCP call where it does all the network support. factoring and one of the parameters to that DHCP call is dash H or host name of the controller. In this case it's controller one and we can see by catting out the host name file that it is coming from the controller one. So if we can control input through the web server and get it to actually execute commands as root, that's a pretty cool command injection. Now before we talk about that some more, let's look at what are the restrictions. Everybody has client side JavaScript restrictions. This is no different. What you'll see here if you can't read it is this is a alphanumeric and period and hyphen are the only characters allowed on the client side. When we look at the actual JavaScript it matches up very well to the string. It's only allowing alphanumeric and period and hyphen. On the server side there's a function called XSS string test for cross site scripting string test and that looks for some additional kind of funky characters here. Of course tied to cross site scripting and other injection attacks but for us the ones that were most harmful to work around. Of course if we wanted to do a path and a command injection. The ampersand and the semicolon character for delimiting commands. One final thing here was this parse forms data function and the thing to pay attention to here is this string tokenizer function which will split our host name every time it sees either an equals or space. Equals we probably don't care about but the space is a really useful character in command injection and so we had to find a way around that. Before we get into that. Let's talk about authentication and how it's handled for getting post this is kind of funny here so get get get requests are handled exactly as you'd imagine a session ID is created by calling get session ID and return and then as you'd expect that session ID is compared to the session ID that's taken from the cookie value. If it checks out the pages authenticated now for post requests it's done slightly differently and pretty poorly here so it calls get session ID from cookie so good or so far so good. But then it never compares that to anything. So we can use any arbitrary session cookie value you'll see Sam use 1337 1337 in a moment here and we can send any post data completely unauthenticated and it'll actually redirect to get requests looking like it failed but it'll actually send the data. All right. Let's tempt the demo gods and Sam you want to take over and do a little dive demo for us. Oh yeah. Thank you. All right. So here we go. Let's get the correct window where it should be. It is. Over here. Let's see that I'm going to. Yeah I'm going to go a little bit bigger. Hopefully that's better. All right. So didn't clear this from our test before but we're going to start a listening server. This is going to be the kind of command and control aspect of this. We'll kind of dive into this a little bit more in detail. However we'll start that off and had that run in the background when we now go to the we'll actually bring up the. The Web server. So this is actually the. The Web site that is running on the Linux system on the board itself and of course as you can see there is a log in page as normal and if we go ahead and log in. It will indeed give us that cookie that Steve mentioned earlier. All right. So logging in. And it might be hard to see but over here there are some session IDs getting appended to the get requests. And if we looked into the post primers there's also a cookie there as well. But we'll go over to that network host name that we wanted to command eject into. As you can see it is controller one at the moment. And now if we go ahead and actually try to do our own authentic command injection by running this command where it is. We saved it off. See I won. So this is the command injection where it's now bypassing the JavaScript that Steve showed earlier where it's restricting characters and whatnot. We do have the cookie as just a bogus one three three seven super elite cookie there. And then the host name has this command direction that will cover in more detail in just a second. So hold on for that. But as you can see there's really nothing here that should have allowed this to take place. So if we go ahead and run this. It looks like it failed. That's because the post data actually did take effect. But then when it tries to refresh the page the get request actually fails. So that's why we see this timeout error here. But if we go back to the web page and actually refresh this by just clicking on network again you can see now the host name is that big long command injection that we actually sent. That was all unauthenticated. Anyone could have done that without knowing the password or anything. So now we'll go back into our slides. And I'll kind of explain why. We chose that set of command injection characters and whatnot. So as Steve mentioned this is not a space but it works just as just the same in Linux interpreters. So that is actually a tab character and that was not bypassed or checked in that XSS string test function. And as Steve also mentioned this is the function that we're trying to get into. More importantly into the host name. And because this is when you're injecting into a process. It's a. It's a halting operation. So and it's kind of a hard place to halt when you don't even have internet yet. So we wanted to actually reach out to that command and control server to download more commands not having to go through the host name with all those restrictions for forward slashes and things like that. So the way that we wanted to get this command to actually run where it's actually W getting more commands from that listener that we set up is to actually nest another DHCP call in here. To actually have the network. Finish its upbringing and actually get a valid IP address. So the desired command injection can take place. And that worked actually pretty well. And that's why we filed it as our first CVE unauthenticated command injection. The only reason this is only rated as a CVSS 9.0 is because we can't trigger it on like on command. It had to actually be on a reboot call where that DHCP process gets or pulls in our host name. And that's kind of where our second choose your own adventure took place where we needed to find some sort of reboot primitive. And there's a few ways that we went about looking at this. The first was looking at the web server as we just found that command injection. Is there any valid ways to cause a reboot? And the apply settings tab did indeed at the end reboot the device. However it did check for cookies correctly. So we couldn't use that for this unauthenticated command injection here. The next thing we could have looked at is looking for a system exhaustion where we're trying to trigger some of the watchdogs on the device to actually forcefully reboot the device or the controller. And that did work. It took about an hour to actually do. And it was really noisy. It was thousands and thousands of requests. But it did actually trigger the watchdogs. That was always a fallback. But we didn't really like that. We wanted to be a little bit more meticulous about it and actually try to find something better. But what we ended up looking at was trying to find some sort of crash that would cause a segfault and then look for more memory corruption vulnerabilities where we can actually use that to actually reboot the device. And the reason a segfault here would cause a reboot is when we started to look at that as an avenue, all segmentation faults on the device called a custom core dump handler. And you can always check what core dump handler is on your machine by cutting out this core pattern file in your file system. And as you can see here, this one even says custom MSC core dump handler. So that was kind of interesting. And that is just a bash script that if you look at the very bottom, it eventually does call reboot. And this was perfect for us. Since all of the web sites or web pages on the device are CGI bin compiled files, any of them, if you can cause a memory corruption vulnerability, will dump their core. So that's where we started to look. As much as possible. And to do that, we wanted to really kind of improve our odds with automation. Any time I get to write a tool, I get pretty happy. So using Python here, we started to look for some of these dangerous functions that you can most familiar kind of or most common ways to actually get memory corruption vulnerabilities. So there's a list here. I'm not going to go through them. But looking for these. And with the power of automation. To write a Python, we wanted to actually find where these were user input could actually hit these before any authentication took place. So the Python script would find where these were being ran before any sort of authentication or cookie check would take place. And as you can see, we did find a few. However, all of them were actually programmed perfectly fine with static size fields, things like that. Not very helpful for us to find memory corruption vulnerabilities. However. On the next page here, as the list continues, the last one is an advanced network CGI, which did have eight stir copies that we could actually use for memory corruption vulnerabilities where they source in destination pointers were actually attacker controlled or user. What we passed in as the user, we could modify. The way we wanted to test this, because it's never good to test live on the only board that you bought off eBay, is we actually set up an ARM QMU virtual machine to actually emulate these. And the way that we emulate it is, since we have that root access on the machine, we actually dumped the whole MTD partitions of all of the file system, the boot, all of that. And then actually CH rooted into it through this ARM VM. And that means that all of the configuration files and all of the other files that it's looking for are actually in the right spot. Since now we're into the CH root environment. And we can execute these binaries as usual. So you can see this is kind of a joke, but we're actually interacting with that advanced network CGI bin file that we identified statically could have these memory corruption vulnerabilities in by just echoing parameters into it. So this is how the CGI bin files take post parameters. And you can see right below, there is the timeout window that we saw when we did the live demo earlier. So it is working properly. Now we wanted to fuzz it. So to do that, we used Radamza. It's a pretty really simple fuzzer that just mutates whatever you pass into the standard in. And then on its standard out, we'll have something modified. So you see here, this is a test passed directly into Radamza, and then it gets mutated to something else. They changed some bytes to non-ASCII printables. And to do this for the CGI bin files. Since they take the post parameters over standard in as well, we can take a valid post parameter string just pulled right out of the web server and then pass it into Radamza. And as you can see, it flipped one of the last octets on this DNS server to negative one, which is a pretty good test in my opinion. So we didn't want to run this fully, you know, manual every single time. That's not how fuzzing works. So we set up this little fuzzer that's, I think, pretty cute. It's like 13 lines of code or something. But it is all written in shell, and it uses the Radamza mutator. So you pass in the normal post parameter files here to get mutated. As soon as it gets mutated, we actually pass that directly into whatever CGI bin file we want to look at. And then, of course, if it crashes, which means it returned a return code other than zero, we rerun the whole test case with the mutated data inside of GDB and then call backtrace on it. So while it looks super simple, it actually gives great results, where this is a crash that we found from that. So you can see that the account str here has a much longer string than it usually has. And that is what Radamza decided to mutate on this run. And you can see that it crashed right below in str copy, right as we suspected from our static analysis, which kind of goes to show that we spent all this time getting this like emulator set up and whatnot and didn't actually found any more crashes than we did. It did statically, but it was still kind of a fun learning experience. And this is where we filed this as our second CVE for this talk here, that it's an unauthenticated denial of service, where it is just because it can cause a reboot on command. That's why it's just a denial of service. The crash in str copy was actually just an out of bounds read, so we couldn't use it to write anything. But I'll let Steve cover how we used these together as a chain to kind of continue our explanation. Okay. So now we've got two vulnerabilities which work in concert. We have a command injection which is triggered at reboot, and we have a way to trigger reboot via segfault. So we have a way to get full unauthenticated code execution. And that meant it was time to upgrade to the latest firmware and kind of bypass our eBay YOLO route and see if we could make this a little bit more official. So we actually called up, we live in Portland, we called up one of the actual legitimate third party installers for Linnell systems, a company called Convergent. We put heavy air quotes around social engineering. We're not like doing a ton of social engineering here, but we may have done a little cloak and dagger when we described the project to them of what we wanted them to build out. This monstrosity that you see on stage here. So they're like, you want us to build what? Why? Okay. You're paying us how much? Okay. We're good. All right. So they came to the lab and actually built out this full system, and they integrated in the on guard as a standalone server, and you can kind of see some of the pictures here. It was wired up with a controller and card reader. So this is essentially an installation that mirrors a production building install, minus the fact that we have no glass in there, but we'll mime that. And it actually works just like you would. And our controller is actually on the back on a panel where you can't see it above the door. That would be in a server closet typically, but we obviously don't have room for that. So otherwise this is a production system. They also gave us some training and hands on experience with how to provision doors and badges and users and all the fun stuff that came with the software. That was good for us to learn, but it paled in comparison to the usefulness of being able to finally download and push firmware to the device. So we could test whether our vulnerabilities survived from an older version of firmware to the latest one, just doing this. And we also had a copy of a legitimate firmware on the file system, despite being encrypted on the Linel that we had, or on the on guard system that we had. All right. So that brought us to a success and failure point here. Let's start with the failure. And the before, as you recall Sam saying. There was a segfault on every system binary, meaning it would reboot the system. Well afterwards, they had restricted this just to the two main Linel system, or Mercury system binaries here, and actually that meant that we had lost the segfault that led to our reboot, and we couldn't arbitrarily trigger our command injection anymore. And those are the two, I know you can't read it, but those are the two files that are now in the new crash dump handling file. However, our command injection did survive. An update to the latest firmware version, and that was really the higher bar here. We knew we could find some way to get this thing to crash or reboot, and that was really the next step of this process. Choose your opponent venture number three. Now we needed an alternative method of getting that unauthenticated RCE back, because all we had is code execution, unauthenticated, but not on demand. So we could have looked for a new standalone vulnerability, a zero or one click kind of do it all in one here. We could have looked for a new reboot primitive or a crash. To replace the one that we missed in this chain here or that we lost in the upgrade process. And I suppose we could have updated the ‑‑ we could have actually moved on and just done our malware or door exploit, because at some point the board is going to go down or going to be rebooted for maintenance and it's going to trigger that command injection. Obviously we're way too lazy to do that. So we went ahead looking for a new toy to play with. Now luckily with this upgrade came some new fun functionality here. Specifically the firmware upload option. And what you'll see. The diagnostics page on the web server added the ability to upload firmware. And it says specifically will reboot board. It's almost like they were in our heads here. This was exactly what we were looking for. So we're going to go ahead and do another live demo here or attempt to, kind of showing you how we managed to play with this a little bit. All right. Cool. So we're back at the web server here. Where is it? To the left. To the left. Okay. All right. Cool. It has timed out. So I'm going to try my best to log back in. Can't see anything down there. But am I on it? Anybody read it? Okay. Cool. That made that really hard to see. Let me drag it over and I'll pull it back. Okay. Wrong address. That's why. Okay. So what I'm going to show you here is that we're going to log into the web server itself. And we're going to basically try to show you two things. The ability to arbitrarily reboot the board. Sam runs everything in the Dvorak keyboard, which is really fun to switch back and forth from English to Dvorak. I knew this would bite me somewhere. All right. So we're going to show you basically trying to log in the device, do a bypass of the login, and actually show you what the session management looks like. Problem with that? Yeah. It's eight. Oh. Cool. All right. This is fun. Okay. All right. Let me pull this back on screen so you can see it here. Okay. So we're back at the login screen. And I have the developer tools up so we can see session management a little bit here, just like Sam showed. I'm going to log in for the first time legitimately so we can get a valid session cookie. Then we're going to capture the get request. Log out and see if it handles it properly. So if we go over to the diagnostics tab that was added here, that's the one that I mentioned was added that has the firmware upload feature. And you probably can't see this. I'm going to highlight in developer tools. There is a diagnostic CGI tab that was requested, and the session ID is validly appended on to there. So I'm going to right click and copy essentially the get request so we can test that logged out as well. Before we do that, if you look just a little bit further. Further down here, what you might see is there's a request to another CGI page called view firmware update.cgi, and you may have sharp eyes and see that that does not have a session ID appended on to it. So let's look at that a little bit closer. We'll log out and we'll try this get request to the diagnostics page and see if it fails, which we expect it to. Well, yes, the session cookie was not valid. It handled that properly, so it was unable to actually log in. So we've got to bring this back over. And I'll pull it over in just a minute so you can see this. All right. So now I'm going to go directly to that CGI page that I showed you before that was loaded, which is view firmware update directly. And what you'll see is we can browse to that page completely unauthenticated without logging in whatsoever and randomly upload files to it, which we could not believe. Thank you. Thank you. Yes. All right. Thanks. Thanks for bearing with me through that demo train wreck. All right. So now we can upload a file. What I'm going to do is I'm going to choose a file from the file system here. And this is actually going to be running in the background. It will cause a segfault, which we're going to explain to you shortly here. So the people in the front row may actually hear it reboot. The device as we load this is going to actually segfault and reboot. And we'll talk about how and why this works in just a moment. All right. Let's click load file. Installing. And sometime in the next maybe 30 seconds some of us will hear a little beep there. All right. Let's go back to slides now and continue on and explain what's happening in the background. All right. We have a file that we've now controlled the file input to. There's a few areas of interest. We could look at the file contents itself. You might have just heard the board reboot or the controller reboot. We can look at the file contents itself or some kind of an overflow, the file name, the characters that are input, even the things like the file size. And we started to play around. around with could we control one of these to get a crash? The first thing that we did on this diagnostics page is upload an empty file called foobar.txt. It doesn't matter what it is. It didn't have any contents in it. We're just trying to see what happens. As it went ahead and tried to install that, it eventually errored out, of course. It would be really awesome if it didn't, but it errored out and it threw this error invalid signature size. So we knew that it was looking for some kind of a file signature and more so that it was somehow parsing a size. Well, maybe that was interesting to explore a little bit further. So the next thing that we did is we pulled up that actual valid firmware file from the on guard system and it's an encrypted file, so for the most part it's uninteresting until you get to the end of the file. And the last hex 158 bytes, if you have sharp eyes, are a base 64 encoded blob. As you might have guessed, this specifies the file signature and more so at the very end there are three ASCII bytes, 158, that specify the size of that file signature. So we pulled up that actual valid firmware file from that file signature. So it's not doing it dynamically, parsing in byte by byte and determining a size mathematically. It's taking, in this case, what we call a user controlled value here because we can upload this file and it's using that to calculate the size of or to compare the size of the file signature. So the next thing we did, of course, was go in and look at the code responsible for parsing this firmware file signature size check. So the first thing that happens is there's a malloc, a static malloc of 190 hex bytes and, of course, then the file is opened and saved off using a file open command. So far so good. Now this is going to be hard to read so I'll just dictate it as we go. The next function here is an F seek or a file seek. It will go to the end of the file minus three bytes, of course, to retrieve those last three ASCII bytes, which are a size field. And using F read will read those in as a size value. Now we'll take a string to long or str to L and change that ASCII value to a string to long or str to L and change that ASCII value to a long value. And as you guessed it, shortly later in the F read function, that will be used as a parameter here to the size. And, of course, if we have a static buffer and we have a dynamic size that we control, that's prime for overflow and, of course, this is where a heap overflow does happen. Shortly later in the file close function is where we actually see the crash happen and we can start to leverage it. And we're going to show you a little bit more detail on that. All right. Our steps to overflow or to test this. We created a fake file. Basically a copy of this. And we're going to test this. We created a fake file. Basically a copy of this. The firmware file. And we filled it with a unique and predictable pattern. Just think of your Metasploit pattern create. That's what we're filling it with to see if we actually control any registers in here. Now because this is an encrypted file, it needed the salted underscore underscore underscore file header. So it does check that. And we added that to the beginning of the file. And then we changed the file size or file signature size here to 999. Anything over, what was it, 190, the static buffer size will work. And then we changed the file size or file signature size to 999. Anything over, what was it, 190, the static buffer size will work. And then finally we set a break point here in we'll talk about, you know, we're going really in the context of the emulator Sam was talking about earlier. But we set a break point here before we thought we controlled execution in the file close function. And if you can see what's highlighted, it's a branch instruction, an unconditional branch, an arm to the R3 register. Well, we know we're going to hit this. And why is that important? Well, if you look to the right side at the registers, you can see R1, R3, and R6 all have our pattern data. So they've been fully crushed with the overflow. And R4 has a pointer to the pattern data that we've overflowed as well. And that's going to be important here. Now, before we go into ROP, and we're going to spend a few minutes talking about the ROP we did, we actually managed to get this working really beautifully in ROP. The next few slides will show you how that works. When we try to transfer it back to the physical hardware, we had all sorts of issues. Like we were just ‑‑ it was unbelievable. We had heap confusion issues and process threading and just would not transfer back to physical hardware. So you'll see what we ended up doing is we just used that file to cause a reboot and essentially trigger our command injection. But we do have full code execution using the ROP gadget and the emulator. One is all we needed, just a single ROP gadget. And what we have on screen here is two commands. The first one or the top half is just a script using ROPR to search for ROP gadgets in all the binaries and third party or imported libraries. We did use that. And then we put ‑‑ we put in the script, we pushed it into a regular expression to filter it down to a set of useful gadgets. Now, even if you can read these closely, you may not know why these are useful gadgets. Obviously we're going a little bit out of order here. You'll see that in a moment. The gadget we chose here is just these five instructions. And we'll talk about why these are important here. The approach that we took is we wanted to ‑‑ so there's an unconditional branch to R3, as I talked about, and that's where we get the start of control. If we can get into R3 and address that points to the beginning of our gadget, which of course we can because we control that register, we can potentially call a system call here. All right. So the first step was getting an address of our gadget into R3. Because it gets ‑‑ because R0 is moved into R3 here. So the first step here is we want to get R0 as an address to our system call. It's moved into R3 and then five instructions later that unconditional branch calls it directly. So it will be calling system. However, to get an argument for system in ARM, we need to get an argument for system in ARM. The argument has to be still an R0. Well, that's beautiful because the second instruction of this gadget is moving R4 into R0. And it's not mangled in between then. So not only can we use R0 to get system, but we can use R4 to get our parameter system or our reverse shell into R0 and it will be called in the unconditional branch to R3. Okay. So good things come in small packages here. When we started to put these into the correct offsets in our pattern file, you can see that we used a W get command. Sam talked about this in the command injection earlier. This is going to be our attempted reverse shell. You can see the locations of where we have R4 and R0. There's only 28 contiguous bytes for us to get execution before R0 is messed up. Now, as we were executing this, we thought it was working perfectly and we'd be able to get our reverse shell. But for some reason, four bytes of that reverse shell string get copied and basically pasted into eight bytes later mangling our IP address. And so that caused an epic fail here. However, in the process of running that, Sam just, because he likes to YOLO everything, just like let's change it from, I don't know, like whatever it was, IP address to AAAA. Like that will make some difference. And of course, it did. And it caused a similar crash, but now R4 was located way earlier in the pattern file. So instead of having 28 contiguous bytes, we had something like 400 plus contiguous bytes. So if you can see the pattern here that R4 points to. It says QKAA. That's the ASCII part of the pattern. And you'll see it here in our pattern file. So the rep gods kind of smiled on us. So now R4 is way earlier. And we don't even have to bother with this reverse shell that we essentially push commands to from our C2 server. We can actually just call system with a direct reverse shell as the argument to it. And if we go back, that's the right side of the screen. So where R4 is, we just did a straight up SOCAT reverse shell and connection. So that's the way we're connected to that directly. And that's what we'll show you in a minute here. All right. So let's summarize here. We have a break point to our at the call to ROP, which is our branch to R3. And R3 has to be the address of our gadget. Okay. So we can see that that does match up. The address of our gadget works. I know we're moving quickly, but you can go back and check some of these later. I know it's hard to see. There's our gadget. Once we get into executing that address, we have just those five instructions. And inside of it, the last thing is another thing. So our complete pattern file here looks like this. R4 has our reverse shell in the very beginning. Some 400 bytes later, we have a system address in R0 and our gadget address in R3. And that's really all it took. And we'll see here from the complete ROP that we now have system execution with our arbitrary SOCAT as the parameter to it. And you see that we're on the break point where it's about to get called. And that, of course, does work beautifully. In an emulator, it doesn't go back real well to the hardware. We might go back and check some of the other things. We might go back and figure that out at some point. Either way, all we cared about was kind of learning this process and getting the door to crash so we could pick up our command injection. That was then filed as our highest CVSS score of 10.0. It is arbitrary unauthenticated code injection code execution, excuse me. And that was also patched. So for the last part before we do our demo, Sam's going to take us through exploitation of this. We call it malware with heavy quotes. Obviously, we're not releasing malware. In fact, we're not even really writing malware. But we are trying to interact with the door directly. And Sam's going to show you how that's done. All right. So we'll wrap this up here and then do the final demo. So this kind of brought us to the Choose Your Prone Adventure 4 where now we got back our on command command injection with that reboot and chaining the two together. And we could have gone about creating our quote unquote malware in two different ways. The people that were actually probably hacking this in their life would probably want to do some ransomware thing. But that's not cool. So we wanted to do the more James Bond, Ghost in the Shell type approach where we actually could gain, like, you know, secret access into a facility, bypassing all monitoring or whatnot. So that ended up that we went down that path. But to do either of those, we actually needed to figure out how the door locks even work. I'm going to kind of go quickly through this section. These are through the use of relays, which are electrical switches. You can see them. There's four here right now. And then the second one is this one. This is one of our secret accesses. All these are switch is hooked up or each of the door locks are actually hooked up to these outputs. To find out how the software actually controls those relays you can look in the binary that has all of its symbols for relays and you can see that there are one function for on and off for each of these. And they're even more simple when you look at it. There is a simple kernel module where you can interact with iOctl, give it the file descriptor for the GPIO, I don't know, and then the GPIO device and then a parameter and then a zero indexed array of which relay you want to actually control. So to actually make this into our malware and to open the door whenever we want we cross compiled it for ARM with just copying that iOctl call and ran it on the device. However, you can probably see that there's this while loop here and this was our brute force while loop. And this was our brute force while loop. And this was our brute force while loop. And this was our actually a way to actually keep the door unlocked. Whenever we would trigger an unlock it would instantly relock. So calling it every millisecond was the right way to get it to stay persistent which is kind of a fun hack right there. But now we'll go on to the demo and I just want to summarize kind of what we've done up here on stage live. This could have all been done with our single click exploit but we wanted to separate it a little bit and try to explain a little bit more. The first thing we did was that command injection through the host name unauthenticated. We then uploaded an arbitrary firmware update file that we can control the signature size to which caused a segfault initiating that reboot that we heard just a little bit ago. At that point at the device start up it's getting an IP address and it's calling our command injection through that host name parameter. And at this point it's actually downloading more scripts from this laptop up here to run the reverse shell. So this is a little bit more complicated than we thought. And then that reverse shell gets opened and now we'll show you live connecting to that reverse shell and opening the command. So here we go. Let's see if we can do it. Alright. Switch to Dvorak. Let me clear this. So we're going to go back to the server and we're going to go to the server. Goons were joking that we could make this a lot easier and just save you guys 50 minutes by doing this. But I think they were being smart asses. So. Alright. So here's the reverse shell connection. We'll go ahead and connect it and you can see that we do have a shell. And if I run WhoAmI. We are root. And then. And to finalize it we're going to run our super elite open door hack. So here we go. Alright. So that was that was everything we have today. Thank you guys so much for coming. We're going to stick around for a couple minutes for questions. I want to thank Carrier as well who handled the disclosure process to Mercury. They're here today. Thanks to that team. Thank you guys for attending. If you want to connect with us on social media or just stop by for questions feel free. Thank you so much. Enjoy the rest of your DEF CON.