 This is Audrey from California and she's from the University of California, Santa Barbara Security Lab, if I'm informed correctly, and it is about automated discovery of vulnerabilities in the Android bootloader. Wow. Not really my problem, but definitely Audrey's. So here we go. Please, let's have a big hand for Audrey Dutcher. Thank you. Good evening, everybody. Today we're talking about Android bootloaders. As a brief aside, I didn't actually work on this work. I just set a cross from the people who worked on this work and I was the only one who could make it to Germany. I have do work on some of the stuff that it depends on. So this is my field, but this is not my project. Brief disclaimer. Thanks. So today we're talking about Android bootloaders. Phones are complicated. Bootloader processes are complicated. And trying to get at the bottom of this very difficult subject. If you've ever done any homebrew kernel dev or homebrew retro gaming, you know that interacting with hardware is really complicated. And trying to do this in a phone, a system connected to a touch screen in a modem and lots of complicated money sensitive things is really not, it's really complicated. But every single one of you has probably has a phone in your pocket and all of these are immensely valuable targets for attacks. So we want to be able to detect bugs in them automatically. That's the name of the game. So the bootloader in a device, it takes, it's the job of, oh, we've powered on, we need to get everything initialized. So we initialize the device in the peripherals and then the final gasp of breath, the bootloader is to take the kernel and execute it. And the kernel obviously needs to be loaded from storage somewhere. On Android specifically, this is what we worked on. Most Android devices are arms. There's no particular standard for what an arm bootloader should look like, but the arm people do give you some guidelines. There's no source implementation of what a secure bootloader should look like. There are, in fact, several bootloaders on arm will go over this later. But it's a complicated affair that needs to preserve several security properties along the way. And above all, the whole goal of this process is to make sure that things are secure and to make sure that the user data is protected. That's what we're trying to do. Like we said, the phones in your pockets are valuable targets. If you can attack the bootloader, you can get a root kit on the device, which is even more powerful than just getting root on it. If an attacker were to compromise your phone, it could break your device or I talked about root kits already. But additionally, you might want to circumvent the security properties of your phone's bootloader in order to customize it, routing, jailbreaking, unlocking is the key word in this situation. The Android bootloader establishes cryptographic integrity over basically what's happening at all times. So on your phone, there is a master key and that will, that knows that it should only run some code that has been signed with the key associated with the hardware. And then the next stage of bootloader has a key that it will verify that the next stage of the bootloader hasn't been tampled with. And this is where we get the term chain of trust, where each part establishes, oh, I'm very, very share cryptographically share assuming RSA hasn't been broken yet, that the next bit of code is going to be doing something that I authorize. Circumventing this is valuable as we've talked about. Phones have to have a way to do that built in unless you're Apple. But obviously, protecting this mechanism from attackers is a point of contention. So really, you need to make sure that only the real owner of the device can actually unlock the phone. So what this project is about, making is about discovering vulnerabilities that let us circumvent this process. So the threat model that we use for this project is that there is, the phone is rooted and the attacker has this root control. This is pretty out there, not that out there, root vulnerabilities exist. But it's enough to make you scoff at, oh, what's the point of this? Well, the security properties of the phone are supposed to extend above the hypervisor level. You're supposed to have these guarantees that things should always work, assuming the chain of trust works, regardless of what, how what's happening in the kernel. So today, we're going to be talking about bootstomp, which is a tool that automatically verifies these properties and discovers bugs. I'm going a little slow speed up. So first of the booting process in Android ecosystems is pretty complicated in multi-stage. There's the base bootloader BL1 which loads and verifies another bootloader which loads and verifies another bootloader. And this is important because the first one's in a ROM and it's very small. And the second one is probably going, is probably by the hardware vendor and the third one's probably by the OS vendor, for example. And they all need to do different things. So the important part here is these EL things. Those are the ARM exception levels, which are basically the global permission levels for an Android processor. The EL3 is basically the God mode. There's an EL2 for hypervisors on this chart. There's an EL1, which is the kernel, and the EL0, which is user space. So when we boot you're obviously in the highest execution level and gradually as we establish more and more initialization of the device, we're going to seed control to less privileged components. So the bootloaders operate very privilegedly. And one of the things that they need to do is establish what's the ARM trust on the trusted execution environment that lets people do really secure things on Android phones. This is something that is set up by the BL31 bootloader. And in the secure world, you need to do things like establish, initialize hardware and peripherals. And in the not secure world, you're running the normal kernel and the normal user space apps. And on some phones, you actually have a final bootloader, which runs an EL1, a BL33 or the Aboot executable. And that's the one that we're generally targeting for this stuff. So this is what I was talking about, the chain of trust. Each of those arrows represents cryptographic integrity. So the next stage only gets loaded if there's a valid signature indicating that we really trust what's going on here. And that's the unlocking process that we were talking about. If you, the verified physical owner of the device, wants to, you can disable that last bit and cause and allow untrusted code to run as the kernel. That's totally fine if you own the device. The unlocking process is supposed to really specifically verify these two things that you have physical access to the device and that you actually own it. Like, you know the pin to it. That's what establishes ownership over a device. So specifically when you go through that process, it does set some specific flags on your persistent storage saying this is an unlocked device now. You can do whatever. But making sure that that can only happen when it's authorized is the point of contention here. It should, typically what happens is this security state is itself cryptographically signed. So you can't just set unlocked. You have to set unlocked but signed by the people that we really trust. But generally you probably shouldn't be able to write to it just from the normal user space. So the question is we saw that the operating system is separate from the boot loader. So what we want to be able to do is get from the Android OS to affecting the boot loader. And can this happen? Of course. That's why we're here. So the, let's see. Oh, I didn't realize there were animations in the slide. So this is sort of the normal flowchart of how these things normally come about. You've got the boot loader which has to read from persistent storage in order to initialize the operating system. Like, of course, you have to read, for example, whether or not the device is unlocked. You have to load the kernel itself. There are lots of inputs to the boot loader. And the intuition is that the boot loader is, these just serve as normal inputs to a program which can be analyzed for vulnerabilities. Oh, Lord, this is a mess. So from the OS, you're allowed to, if you have root privileges in the operating system, you can write to this persistent storage, which means that you have, that this serves as another input to the boot loader. And this can cause bad things to happen. So we need some sort of tool, that's the point of this project, to automatically verify the safety properties of these boot loaders. That's bootstomp. Boot loaders are complicated. There's a lot of stuff which means you have to, the analysis has to be automated in order to really sift through something as big and complicated as a boot loader with the care necessary to actually find bugs that are sitting there. And, but these things aren't usually things that you have source code for. So it needs to be a binary analysis. And furthermore, you can't really do a dynamic execution on something that needs to run on the highest privilege level of a processor. So you have to have your step, that analysis to be static as well. And furthermore, this needs to be a fully freestanding analysis that doesn't assume anything other than, oh, we're executing code on a system because there's no known syscalls or APIs to checkpoint process or say, oh, we know what this means. We don't really have to analyze it. So it's a tall order, but you can do it with enough work. So bootstomp specifically is the tool that we built. It will automatically detect these inputs that we talked about to the boot loader, and then it will determine if these inputs can be used to compromise various security properties of the device. One such example is if you can use this to just achieve memory corruption, for example, or more abstract forms of vulnerability such as code flows that will result in unwanted data being written by the more privileged boot loader somewhere. And the important thing about this analysis is that its results are easily verifiable and traceable, and it's very easy to look at the outputs and say, oh, well, this is what's happening, and this is why I think it's happened, and therefore I can reproduce this possibly. This happens through symbolic analysis. This is the part that I know about because I work on anger, which is the symbolic execution analysis, static analysis tool that bootstomp uses in order to do its taint analysis. That kind of taint analysis and symbolic execution are kind of loaded words, so what specifically is meant is that when we discover these sources and things of behavior through some particularly static analysis and some heuristics, of course, and then we propagate these taints through symbolic execution while maintaining tractability and notice wherever we meet, wherever we can find paths from taint sources to behavior sinks that we think are vulnerable. Specifically, we think these behavior sinks are vulnerable behavior. If you can arbitrarily write to memory or read from memory, like really arbitrary, if there's a pointer which is controlled by user input, memory corruption stuff. Additionally, if you can control loop iterations through your input, that indicates a denial of service attack. Finally, the bootloader unlocking mechanism, if we can detect specific code paths which indicate bypasses, those are valuable. This is the specific architecture of the tool. There are the two main modules, one which is written as an IDA analysis, the big tool that everyone probably doesn't pay enough money for, and then there's the other component written in anger, which is the symbolic taint analysis. This is probably the point where I break out of here and actually start the live demo. That's big enough. Okay. So we're working on a Huawei boot image here, the fast boot image. We're going to load it up in IDA real quick. So here IDA has understands, oh, this is what the executable is. So if we just sort of run the initial script, find taints, it'll think real hard for a little bit. There's no real reason this couldn't have, this part couldn't have been done in anger, or binary ninja, or R2, or God forbid. But this is a big collaborative project if you saw the huge author list and people write stuff and whatever they're comfortable with. So that's IDA in this case. Realistically, because this is just a binary blob when you load it in IDA, it doesn't immediately know where everything is. So you have to sort of nudge it into, oh, here's where all the functions are. Okay, when we finished. And what it's done is we've got this taint source sync dot text, which shows us, oh, here are all the sources of tainted information. And here's a few of the things that we established. Obviously, you don't need a sync analysis to determine if you've got memory corruption or not. But we like knowing where the rights to persistent storage are on, where all the, specifically the mem copy functions are valuable for analysis. And then, if we run our taint analysis, bootloader taint, oh, this configuration file is real simple. It just says, oh, here's what we're analyzing. It's a 64 bit architecture. Don't bother analyzing thumb mode, et cetera, et cetera. Simple stuff. Taint bootloader. And it'll do this for about 20 minutes. Configure. And it'll do this for about 20 minutes. I hope it finishes before the demo is over. If not, I'll do some magic and we'll have a pre-prepared solution. So we talked about these seeds. They're used, the seeds for our taint analysis for our persistent storage and data used by the unlocking procedure. So the heuristics I was talking about, we want to identify the reads from persistent storage through log messages, keyword analysis and log messages. So the EMMC is a specific memory module used by the bootloader for secure purposes. It's a persistent storage device, basically. And you can identify these log messages and then we just do a deaf use analysis back from the guard condition on that block to its source and you say, oh, that function must be the read. It's pretty simple. It works surprisingly often. Of course, if this isn't enough, you can just manually analyze the firmware and provide, oh, here's where we read from persistent storage. Here's what you should taint. Yeah, deaf use. And cool. So the taint analysis, our tints are specifically symbolic taint analysis. So it's not just like what Triton does where you've got a concrete value that has metadata attached. This is a real symbol being used for symbolic execution. If you're not familiar with symbolic execution, it's a form of static analysis in which you emulate the code, but instead of having values for some of the things, you can just have symbols. And then when you perform operation on the symbols, you construct an abstract syntax tree of the behavior. And then when you run into branch conditions based on those things, you can say, oh, well, in order to get from point A to point B, this constraint must be satisfied. And of course, now you can just add Z3 and stir and you have inputs to generate paths to the program. So for the syncs of the taint analysis, we want to say, oh, if tainted data comes into is the argument to mem copy, then that's a vulnerability. I don't mean like it's the, I don't mean like the tainted data is the subject of mem copy. Like it's one of the values passed to mem copy. That's a memory corruption vulnerability generally. Yeah, we talked about memory corruption, and we talked about loop conditions, and we talked about rights to persistent storage with the unlocking stuff. Cool. For taint checking specifically, this is exactly what I just said. Yeah. And what I was talking about with the symbolic predicates and trace analysis means that when you see something, you automatically have the input that will generate that behavior. So the output is inherently traceable. Unfortunately, symbolic execution has some issues. I was actually at CCC two years ago talking about the exact same problem. You have this problem where, oh, you generate paths between different states, and there can be too many of them. It overwhelms your analysis. So you can use some heuristics to say, oh, we don't want to. Because it's a static analysis, we have a more powerful stepover than what a debug can do. We don't have to actually analyze a function. We can just take the instruction pointer and move it over there. And this does cause some unsoundness, but it's not a problem if you, like, make sure that the arguments aren't tainted, for example, or sometimes you just accept the unsoundness as part of the attractability of the problem. Limit loop operation, that's classic technique from static analysis, and attend that, of course. So what are the bugs we found? We evaluated this on four bootloaders, and we found several bugs, six of which were zero days. So that's pretty good. It's like, okay, so you found some bugs, but it could just be you, oh, there are some errors in initialization that don't really matter. But on the other hand, you can crash at 41, 41, 41. That's pretty serious. So as we saw, some of the bootloaders do work in RME L3, so this is pretty significant. You can do whatever you want in the device if you actually have sufficient control over it. This is rootkit territory. You could break anything you wanted. Then there's another part of the component analysis that says, can we find bypasses to the unlocking procedure? For example, this is basically one of the ones that we found. So it says, bootstom detected this flow from data that was read from the device to data that was written to the device. And what this code is supposed to do, do I have animations? Yes. It's supposed to take some input and verify that it hashes to a certain value. And if so, hash that value and write it back to disk, and that constitutes the cryptographically secure unlocking thing. However, the thing that we write to is compared to be identical to the thing that was read from the disk. So you can just, the thing that Bootstomp reported was the code flow from the disk back to the disk indicating that if you can read from the disk, you know how to produce the thing that will unlock the phone. So this isn't secure. Mitigations. So the thing that Google does in order to prevent attacks to this class is that the key, the encryption key that unlock, that decrypts the user land data, has embedded in it the unlocked state. So clearly, if you change the unlocked state, you brick the entire phone. Well, not brick, but have to lose all your data. But that's still not really good enough. But realistically, we should probably be using a more trusted form of storage that's not just the normal partitions in the SD card. In order to store this state, it should probably be part of the EMMC, or specifically the replay protective memory block, which uses cryptographic mechanisms to synchronize the, what's it called, to synchronize the writes to the memory with the authenticated process. And so that would make sure that only the bootloader could unlock it. But of course, that wouldn't protect against memory corruption vulnerabilities. And there's nothing really to be said about that other than, hey, this is a serious problem. In conclusion, all these bugs have been reported, most of them have been fixed. As far as I'm aware, this is the first study to really explore and develop analyses for Android bootloaders. And in it, we developed an automated technique to analyze bootloaders with tractable alerts, found six ODAs and various bootloaders, and our implementation is open source. I will be taking questions. Thank you for listening. Okay, we've been taking some questions from people that understood exactly what it was all about. Yes, I see somebody walking up to microphone one. Yeah, thank you very much for that talk. Are you talking to the mic? Otherwise, we can't record it. Okay, thank you very much for that work. That was really cool. Your mitigations didn't include writing the code better. Do you think it's possible to write the code so that your tools can analyze it and maybe it would be secure? Or not yet? Well, there's certainly things to be said for having things being open source, because necessarily doing analysis on source code is a much more, a much better defined field than the, than doing analysis on binary code. Additionally, you can write your stuff in languages that are safer than C. I don't know if it's, I don't know if it's safe to talk about Rust yet, but Rust is cool. Yeah, there's lots of things that you can do. I just realized I didn't show off, I didn't show the, still running the analysis, the automated results. It did not finish in time, so I will run some magic. And now we have some results, which, so here's, here's one of the analysis results. We found at this location in the program, a tainted variable, specifically the tainted, at offset 261 into the tainted buffer, this variable was used as a pointer. And that involved following the path from along, along this side. So there is a vulnerability that I discovered for you. So we can go on with questions, sorry that was a bit, any more questions from the audience? There is no question from, from the internet. Okay, one question. Go ahead, talk into the mic please. You said that the bugs you found were responsibly disclosed and fixed. Were they actually fixed in real existing devices or did they, Venice just say, oh we'll fix it in future devices? I wish I knew the answers to that question. I wasn't on the team that did this. Yeah, I can't speak to that. This was just, that was just a slide on the slides that I was given. I sure hope they were really responsibly disclosed. It's real hard to push updates to the bootloader. Okay, any more questions? Okay, so let's conclude this talk. People when you leave the hall please take all your stuff with you. Your bottles, your cups. Don't forget anything, have a last check. Thank you very much. Let's have one final hand full. Or reduction from California.