 Thank you guys all for coming here. Bright and early first talk of the day, Track 3, Rockin' It. This is our first speaker, Jonathan. He is going to be presenting an introduction to the Witchcraft Compiler Collection. I hope you guys really enjoy it. And I'll let him take it away. Good morning, Devkan. It's a tremendous pleasure to be here with you. We're going to talk about some badass reverse engineering, hopefully. As you saw we got some technical problems, but hopefully with a bit of imagination we managed to see everything. So my name is Jonathan Brossard. I have a whole lot of content to share with you today. So I'm going to start with the boring things a little bit and hopefully we'll switch very quickly to full demo stuff. I cannot give the full feedback, I mean the full prerequisites and go through why everything works. So I'm going to stick to the demos and try to outline why the things which are a little bit touchy work. But I can assume that you already know about the ELF format. You're familiar with POSIX. And basically the more reverse engineering you've done previously, the more I think you'll be able to appreciate this talk. Okay, let's get started. So thank you very much for coming this morning. I know it's 10 am. Devkan is starting, so you should all be exhausted by now. The real motivation of this talk is to share with you a new toolkit to do reverse engineering which really facilitates a whole bunch of things that I thought were impossible before even trying to do it. So it's published under MIT license, I mean a dual MIT and BSD license because I'm reusing components which were already licensed this way. And I would very much like if you contributed to this witchcraft compiler collection. You don't have to be very familiar with CIO assembly to do this. As a matter of fact, you can write all the code you need on top of this framework using Lua or web programming language that stems from Lua and that we created specially for this tool which I call Punk C. Okay, so quickly, who am I? So to introduce me, I thought it would be good to tell you about my love relationship with Devkan. My first talk ever was at Devkan eight years ago. He was on bias vulnerabilities. Basically, we found a new class of vulnerabilities on the bias which allowed us to pop using a single exploit, TrueCrypt, BitLocker, McAfee endpoint and a fair bit of bias Firmware. Then I've been rejected pretty consistently at Devkan until four years ago where I gave this talk on a hardware back during. This is a talk I really liked because I did it with the people who engineered an open source bias called Coreboot. And the real pretext of this talk was to work with them. We did this for a conference in Paris called No Such Conference. And basically they dropped the project and I pursued it a bit and it ended up pretty cool. The MIT technology review gave me a review on it and said that's a computer infection that can never be cured even if you change the hard drive or erase your bias and things like this. Incidentally, that's where my ex-teachers in engineering school stopped talking to me. The thing also got featured in more mainstream press. It got featured in Forbes, which is interesting. They had pretty much the same feedback. I'd like you to pause for a second and imagine the kind of online presence this gives me. Imagine I get to meet a girl on Tinder or whatever and she Googles me. She's like, oh, also I've been on Forbes. Yeah, for writing malware, baby. So I'm telling you I've been pretty consistently rejected. That's a message to you if you submit to conferences. Being rejected is part of it. It's normal. It's completely expected. So stuff that didn't make it to DevCon but that I presented at Black Hat. That was a pretty cool debugger I thought. It's called PMCMA. You can find the code base online if you're interested. Basically the normal way it works is you have one debugger with debugging a debuggy and you get a one shot. If you crash the debuggy, like you have to restart everything again. So instead of doing this, my debugger was attaching to the debuggy, forcing it to fork to replicate itself in memory. And then we'd work on one fork at a time. That's the routine you can see over there. Finally, last year we did a presentation with my team from Salesforce. We pretty much redirected SMB relays and our contribution was to show that it doesn't work just on LANs. It actually works from the internet. I saw on the schedule this year that there were more talks on SMB at Black Hat. So yeah, I hope we resurrected something. If you run Windows networks, you want to watch this talk because this is still unpatched and this is affecting every version of Windows. Okay, this slide is brought to you by the security vacation club. What do you do once you've done all the work to create a bad-ass presentation and that you have massive content you'd like to share? Well, do it more than once. There's all this list of very cool conferences. So I keep doing some in Paris. I'm a bit biased because I created it. I keep the box in Malaysia, which is really awesome. H2HC in Brazil, it's insanely good. Side scan in Asia, things like ShakaCon in Hawaii. I'm also part of the program committee of that one. Infiltrating Miami, Recon in Canada, only reverse engineering, very, very cool. KiwiCon in New Zealand, EcoParty in Buenos Aires, Zero Nights in Russia. So basically, if you do all the work to create one presentation, I suggest you enjoy your security vacation club tour and give it to all these conferences. There is one left I didn't put in the slides because it's a trap. It's called Raxcon in Australia. I was supposed to go there for one week to speak and I stayed four years. So it's a trap. Okay, a bit of disclaimer and then let's go to the meet. So my employer does not wish to be named even though I just did. They're not associated with this talk and this is my personal research. On the upside, it means I'm free to share it with you. There's no intellectual property problem. But on the downside, it means you want to do reverse engineering and you're on your own. So in this case, you call EFF to the rescue. And before anything, I'm going to ask you to give a big clap to the people of the EFF because they've been amazing. Thank you very much. I know some of them are talking today, so they're definitely around. If you manage to catch them, pay them a beer because they're really helping us when in situations like this where we cannot afford legal advising. And if you do reverse engineering, they have a nice fact that I suggest you check. The URL is on the slides. Okay, so my employer doesn't want to be named but he's recruiting. It's a badass security team. If you saw the attacks on like breach and things like this, denononymizing SSL, ravage, which was a very cool tool to provide Java. Or the research we presented last year at Black Hat. Feel free to decrypt those slides. I think we call the legal department and the PR department are very unlikely to decipher this slide. In terms of agenda, I'm going to talk to you quickly about why I started this project and why it's relevant, hopefully. And then we're going to do some really serious black magic with the Witchcraft compiler collection. I'll show you what are the main tools in it. I'll show you how to transform an executable into a shared library. Okay, that's not supposed to fly but that's super helpful when you do reverse engineering or exploit writing. Then we go further and we'll completely unlink an ELF to a relocatable file. And then we'll extend that and go a bit crazy. Okay, I don't get to see my slides so it's a bit touchy for me too. Quickly, let's imagine we're working on an application and we don't necessarily have the source code. Honestly, even if you have it, it takes time to recompile things, possibly to adjust your tool chain. You have missing dependencies and things like that. You often don't have the right headers and things. So working with source code isn't easy. I'd really like to work with binaries directly. As an example, I took a very old version of SMB. It's called SMB server 1532. It's basically a predecessor of Samba. So if you do static analysis, that's a pet project I run on the side with another company I work with full-time. So I have two full-time jobs, right? The one with the company who doesn't want to be named, one with a startup in France. And I have so much free time that it allows me to do more research and share it with you. So basically, that's the thing we run with our startup in France. You can register for free. It's only for security researchers for the time being. It does full-featured static analysis. So you upload your binary and you get back a bunch of metrics. On this slide, the more red you get, the better it is. You can see that it's checking, you know, five different aggregated metrics. Without going into the details, the bit we're going to care about today is like, okay, you can see that the binary, for instance, is compiled without ASLR, without Fortify hardening and things like this. But what we're really going to look at is like finding proper vulnerabilities from this binary without using source code. One which looks pretty cool is the one with Fopen right here, which is opening, I mean, it seems like it's opening a file with a predictable name in write mode in slash TMP. Since this thing is running as root, there might be, you know, possibility for a sibling attack and basically your local privilege escalation. The thing which is interesting here is that we don't have a full-back trace. So the way the tool works internally is like, it does like, you know, de-compilation, I mean, disassembly, de-compilation, full static analysis on the binary by using symbolic execution, rub the engine or cells, so it does it straight on binary. And a problem you commonly face when you use advanced techniques like this is that you don't have a full stack trace. So I have only a partial stack trace here and I'd like to verify these vulnerabilities does exist. Okay, another way to do this is to hit this problem is when you do fuzzing, like often, like in this case, this is taken from the bugzilla of Red Hat. It's also SMB, which seems to be crashing. And we have only a partial stack trace. And this thing is actually bizarre, right? Because it's not called from main, so we don't really know what's happening. What I know is that I don't want to call the whole thing from remote by sending packets. I'd like to verify the vulnerability exists by calling directly an arbitrary function inside the binary or inside a shared library. So that's our problem statement today. I want to be able to call any function inside any binary without even to craft an input to reach it and without necessarily going through main and whatever layer of function and then reach it. I want to be able to call those functions directly. Okay, so here are the components of the Witchcraft compiler. It has a linker, which is a badass tool which patches one bytes in binaries, but its power is incredible. You're going to see this. We got the WCC, which is the core compiler, which is a tool which takes a binary as an input and gives you relocatable files that you can later on relink to generate new shared libraries or relink against your own code and get new executables. And finally, we have the Witchcraft shell, which is by far the most complex. It's a full dynamic interpreter and scripting engine built on top of Lua. And yeah, we're going to see that this is pretty cool. With this shell, you can write your own script. So an example of scripts we're going to see today are WCCH and WLD. Let's go to the Meet. Libification. So if you did one class of computer science and C programming, the first thing people tell you with that, what we're going to do today is impossible. Let's start with a demo. I don't see anything. Yeah, yeah, I know you're not seeing this. Okay. There we go. Okay, so for instance, to give you an idea of where the state of the art of decomposition and why we're not going to take this route. So this is a illo.c file. Okay. And I'm going to show you the equivalent code generated by possibly the best decompiler you can get for free. It's called Snowman. It works in two mode, either on top of IDAR, or it has a mode where it can run by itself autonomously, which is free. So to give you an idea like when you decompile these two lines of code program with Snowman, you get something like this. Okay. So that's the source code which is supposedly equivalent. So if you take that path to decompile a real binary, let's take poor FTPD for instance. It's 200,000 lines of code. Okay. You're going to get about 200,000 times this. So decompiling this and recompiling this is not reasonable. If we give it a try just for fun, let's try to recompile the one from Snowman. I'm just going to grab the errors. Maybe. I don't see shit. Okay. Let's drop this. I think you got the idea. Okay. So what's libification? Basically we're going to take an ELF file. This is the main IDAR, the ELF IDAR. We're going to patch one byte. We're going to tell it you're not an executable binary anymore. You're a shared library. So patch it from ET exec to ET din. So let's do it with poor FTPD for instance. Let me get this full size. Can you see any of this? That flies. Yeah. Okay. So as an example, let's take poor FTPD. And let's assume we don't have the source code. Let's copy to slash TMP. If we check the type of the file. Okay. It's obviously an ELF executable as seen right here. I'm going to use WLD. So the first tool from the Witchcraft compiler collection. It takes only one option which is Libify. And then the name of the binary. So I'm going to run this. Let's now check the type of this file. Okay. And surprisingly it's a shared object. Okay. But what's crazy now is that it really is a non-relocatable shared library that I can re-link against my own applications. So let's check what you do this. Okay. So that's a simple program which is going to load slash TMP poor FTPD.SO into memory. So let's start with renaming poor FTPD. We just patch into poor FTPD.SO. It's going to DL open it. So load it in memory. Which returns a handle which allows you to call DMCIM and find the address in memory of any of the symbols in there. What I'd really like to call is this function called pr underscore version underscore get underscore string which returns inside poor FTPD the version of poor FTPD. So what's very crazy. Let me show you the make file quickly. There's nothing very crazy. Okay. We're just using a link script to avoid GCC giving by default the same address to this program and to poor FTPD.SO. So they do not collide in memory. So what I do is basically my demo is mapped much lower in much higher in memory to make space for this non-relocatable shared library. And what's very crazy is that this thing actually works. Okay. So if you look up there it really returned like 1.3.3D which is essentially the version from poor FTPD which we got from the shared library. Isn't this insane? Okay. So that's exactly what we just did. You can see that we really just patched one byte in memory. So without going into the crazy details there's a couple of reasons why this works. The first reason is that oh and I need to show you something else which is very crazy is that the library we just patched is still a valid executable that is completely unexpected, right? So the reason that this works, the reason it's still a valid executable is that basically the way we pushed for the SLR in the Linux kernel is by allowing you to compile your binaries as a shared library and execute them at any address in memory. So yeah, you can have a binary with technically a shared library. You can see that a bit more on this later. The other thing which is a bit crazy is that this newly recrafted like shared library cannot be remapped in memory, right? Because it was never meant to be relocated that way. So we have a shared library but that can only be mapped at a single address in memory. This actually does exist in the normal world. It's called prilinking is the fact of giving a base address to speed up booting essentially to shared libraries. And yeah, so that's exactly what's happening in here. Okay, let's do a demo with Apache. I'm going to show you that you can actually re-link against Apache, which is not a shared library, but pretend it's a shared library. Okay, so let's look quickly at the source code. Okay, so it does nothing crazy. It tries to call ap get server banner, which is a function which is defined inside Apache. And to do this, we're going to link straight away against Apache as if it was a shared library. So let's tap make quickly. Okay, it's complaining. Boom. And if now I run this application, it's very crazy, but that's also works. So Apache is not really a shared library, right? It's an executable, but it's compiled as ETDN to have like full ASL on. And if you link directly against it, you can actually call functions directly inside it. This is so amazing that I'm tempted to end my talk here. Okay, if you look at like the way, you know, if you try to print like which shared library is this recursively, this application is linked with, you see that the first one is very much Apache and that looks very crazy, but it works. Okay, let's go one step further. So this technique we just used is by far my favorite because it's cross-platform. It works across architecture and it's super stable. There's no such thing as like complicated analysis that my fail and stuff like that, like all we did was patching one byte, which, you know, incidentally is pretty reliable. If you wanted to go one step further and instead of transforming an executable into a shared library, go back to the output of a compiler before the application is linked. So we call this unlinking. Well, we're going to use this tool called WCC, which is a lot more complex and less portable. And, you know, basically what it tries to do is recreate the relocations that we're missing from the final executable and add them to the binary so that you can relink it. So the normal way to do reverse engineering and to solve this problem would have been this. When you have compiled source code, you would use a decompiler, which is totally not what we're going to do. We're just going to go back to the relocatable files. And once I have the relocatable files, I'll be able to relink them again. So the command line is made so that there is zero learning curve. If you know how to use GCC, it takes essentially the same parameters as GCC. Finally, it doesn't take source code as an input. It takes a binary. Okay. I'm going to give you a demo on this. So I have a small file here, which is a small C file, and it has a whole bunch of relocations. Okay. It has a number of imports. It has strings, which are in the read-only data section. It has strings, which are, you know, past as arguments. So yeah, it has pretty much all the type of relocation that do exist. You know, I'll share the source code so you guys can verify this. But essentially what we're going to do is first compile it. Then, so the normal compilation will give us a binary called small. Then we'll use the wishcraft compiler to go back to a relocatable file from this final binary. And we link it as another binary using GCC called small2. And the question is like, does small2 actually run? Because that would be very crazy. So let's compile it quickly. So if I run small, which is the original binary, it tells me something like hello from Defcon. You can see that WCC. So let's redo this manually, actually. Let's re-delete this and this. Okay, so I start, I have only this small binary. I'm going to use WCC, which is what I just told you about. If I play it stupid and I assume, you know, if I wanted to use GCC, so I would like to do something like this, essentially. Until it give me, you know, small wcc.o, and I'd like it to be a relocatable file. If I run this, like GCC is going to be like, I don't understand what you're trying to do. The input is not a source code. But if you do it with WCC, it's going to be like, yeah, okay, let's do this. And it gives you this relocatable file that then you can absolutely re-link into small2. And what's amazing is that small2 does the same thing as the original binary. It sounds very crazy, doesn't it? So if you like it, I'd really like you to contribute back to this tool and extend it. For the time being, it works mostly on AMD64. I'm trying to port it on ARM. And the drawback as opposed to the first tool which, you know, worked everywhere because we were just patching one binary. It's a bit of work because we have to deal with all the type of relocations for all the type of CPUs. So I need your help with this. Okay, I'd like to explain you one thing. I choose, so to architecture this, the front end is basically libBFD. So it's like objdump and tools like this, so objcopy, which has a nice, I mean, the drawback is not very precise. But the advantage is that it works with many type of binaries, not just ELF files. So technically at this stage, we can try to do very crazy things like let's do witchcraft, baby. Let's try to transform a PE file into an ELF file. I'm running late, so I'll let you play with this at home, but that basically works. We have one problem here. It's like, I don't know what you're reeling this with. Right? So an idea would be to use, like, the libraries shipped with wine, which already give us, you know, the Windows API combined as an ELF file. This is left as an exercise to the reader. We're going to do something way more crazy right now. Can you run openBSD binaries natively on Linux? I mean, if you've been to, you know, if you've done a bit of engineering, like, that's not supposed to be possible. Like, technically, you're supposed to have a virtual machine. Let's do it without a virtual machine. Proper witchcraft. Okay, so we could do this manually. I'm going to do it with a make file. Um... So the original binary... So here the original binary we're going to play with. You can see that it's very much an openBSD binary. Okay. The source code is trivial. It's basically this. So it's doing a bunch of printf in a row. Okay. Here comes the main file. Fuck the display today. So we're going to copy the binary, blah, blah, blah. Okay, basically, there's a couple problems. The dynamic linker is expecting to find, does not exist. So to show you manually, if I do, like, a S-trace on this particular binary. That was not supposed to work, actually, because I actually patched it. Okay. Okay, let's just run it. I'll explain you how it works later. Basically, the dynamic linker is not the right one. This is outcoded inside the binary, inside the interpreter section. I could patch the binary to put my own dynamic linker. Instead, what I'm going to do is copy my real dynamic linker to the location it's expecting, the dynamic linker to be encountered. Okay. Right. So then it has a little bit of a problem. I'm looking for a libc, which is called libc.so.62.something. And my libc is not called like this. So I could patch the binary there again to tell him, like, no, look at, like, the libc I've got. But instead of this, I could also copy my own libc to the libc he's expecting. And if you do all that, I share all the material so you can reproduce yourself. But essentially, amazing. Okay. If you, there's a last problem. It's like it has a missing dependency on, right. If I try to run it now, it has a missing dependency on atexit. So what we're going to do is use LD preload to give him, you know, a shared library with all the dependencies is missing, like atexit. We don't really care, right? Atexit is a function which is called, basically, when you exit to called, you know, deconstructed and things like this. So I don't really care. Long story short, if you look at the last line, you do the LD preload. You run this openBSD binary, which has been slightly adapted inside a Linux machine. And it does print what it was supposed to print, which is also very crazy because there's no virtual machine. This was never supposed to work. Okay. Now let's go to proper witchcraft. And I'm going to introduce you to a programming language that comes from basically, I mean, implementing a sort of reflection in C inside a Lua interpreter. So what I'd like to do is basically to get reflection-like features, but on binaries. Reflection is typically something which only exists on a virtual machine, right? It allows you to load classes or binaries in memory and instrument them, like get a bunch of information on them, like what are, you know, what type of argument they're expecting and things like this, and then to invoke them directly in memory. So I'd like to do that, but without a virtual machine, straight from binary. So it's based on DL open, which is a function we used earlier to link with POSTPD. You can compile it with Lua JIT, which gives you just-in-time compilation in memory. There's no P trace, and it typically doesn't work like any, you know, debugger you might have used before. I don't see the slides, so I'm going to go quickly on this, and we're going to stick to the demos. Ten minutes. Okay. So it's time to activate. Okay. Since I don't see the slides, I'm just going to run it for you. Okay. That's the SMB server I showed you earlier with the static analysis thing. So let's, let's talk, let's copy the SMB. Fuck that shit. Okay. Nothing works. So I'm going to load like the POSTPD binary we've patched before, and I'm going to, you know, showcase you the capabilities of WSH. So we just loaded it in memory. It has a bunch of built-in functions. When you don't know one of the functions, you can use, you know, the app, which is the only documentation you'll get in this respect. Okay. So let's call this function, for instance. Hey, everybody. It's Jonathan doing a good job. Thank you, folks. Okay. I really want to show you this because it's beautiful. So we can do crazy things like print the section headers that the output you would get from NM typically. Only this is dynamic and this is recursive over all the shared libraries inside your memory. This is a feature I always wanted because it's not built-in in GDB and it's always a problem when you're trying to do this. You can have the program headers. You can look at like what are all the functions which are loaded right now in memory. Let me show you this first. You can, you can ask him to print what are all the libraries that you have loaded in memory. I just loaded pre-ftp, but that also loaded all its dependencies. So if we look at like all the functions that this has actually loaded in memory, there's a fair bit of them in a bunch of, you know, libraries, and we can now invoke them straight away. So already we got like 6,000 functions to play with in memory. I don't even know the prototype. I know nothing about them. So let's look for instance if there is a function which print the version or something like this inside proftpd. So like before, we find this version called pr-version-get-number. I'm going to print this inside a variable. And if I print the variable like before, that's not what I expected because that's not the right one. Let's try with the string one. Yeah, so like before, that's printing me internally the version number. No, no, no, no. You've seen nothing. The real good thing, the real good thing is, so the real bad thing and way to clue out, I'm going to try to activate, is that you can have more than one return value. So I'm going to tell him, okay, I want A, the result of this function, but also give me a context. So now if I looked at the context, it tells me a whole bunch of things is down, I mean, of the context of this execution so that there's L number, you know, with zero, there's no sec fault and stuff like that. I had not planned to do this, but let's do something crazy. I mean, this allows you basically to build static analysis on top of WSH very simply. I won't have the time to explain everything today. Let's just say we want to test all the functions inside ProFTPD, I'd like you to file them and call them a hundred times each. So he's going to list all the functions in memory and what he's doing right now is like calling them. Okay. Okay, let's stop this for a sec. Dear God, I've been too ambitious. Okay. A hundred times is too much. Let's run it like once. And what happens is that he's recording all this stuff in memory and you can see them right after. Okay. I don't have the time to see you all this. I don't want to sabotage this talk, but that's too bad. We're looking to invite me to your local conference. Yeah, I won't talk about this. I won't talk about this. Future work. What I'd like you to help me with. Yeah, I know, we were super late. So I think this is a very cool base to do reverse engineering. There's a whole bunch of stuff I don't have the time to do. So I don't know what to do. You don't have to go to the computer to get in touch. Do you have any questions or do you want me to stick to demos? Oh, there was one thing I need to show you, because it's amazing. What if I wanted to analyze an arm binary on my Linux machine? So typically, to do this, there again, you would need like, you know, a full hypervisor and things like this. What I did is just cross-compiled my WSH shell to WSH arm. Okay. I've registered QMU in a weird mode, um, to do me, when I execute natively, um, binary on my machine, um, in-memory binary translation. So this is really a um, binary, I'm running on my own machine right now. And what we're going to do with this is load this libesele next thing, which comes from ARM. So it's complaining because some dependencies are missing. Typically you would do this inside a ch-root to have all the dependencies. But you can see that like before, we have a bunch of functions and we can call them directly. Now let me show you something which is really insane. Um, for instance, okay, let's print, let's me get my PID. So that's my PID. And it didn't work. That's amazing. Okay, uh, let's get it another way. I cannot get a shell. That's beautiful. Okay, uh, I can have it another way. Um, let's just look at like the libraries mapped inside memory. So my process is really a ARM process. It's loading a ARM binary and it's running natively on Linux. The binary translation is also inside the same process, thanks to QMU and the other mode it's using. And it's all, all of this is just one program in memory. You don't need a virtual machine. You don't need all that garbage. And that facilitates a lot like, you know, the fact of sharing this tool or, or even like a ARM binary and calling functions inside it, uh, from other application without any reverse engineering. Last but not least, I'm almost done. I told you SMB server, I wanted to write an exploit for it. So let's do that quickly. We saw before that, uh, we wanted basically to call this particular function called reply close inside SMB server. So let's do WSH of SMB server. If I want to call reply close directly, I can. This is amazing. Um, uh, we get to know that it writes, but we didn't decompile. We didn't retrace. We get that basically by writing a custom signal and law and passing but are like, you know, the update we can get out of it. Let's give it a couple arguments and let's see why it's crashing. Okay. So right here, it's crashing because basically the second argument as you can see is trying to write. So let's map something here. Okay. Lua strings are supposed to be immutable, but in punk C, which is, you know, what's happening in this script, you can break all the Lua rules in particular. Yeah. You can absolutely write two, two strings. So I'm going to try to this string. It did reply close. Okay. And if you remember like the static analysis we saw, uh, this is supposedly creating a file in slash TMP with a predictable name, which is G something. And you can see here that yeah, this file was actually created. And what's amazing here is that we can verify results of static analysis without any reverse engineering basically. And we can call any function we like in the stack trace, even if we have, we don't have the full stack trace. And even if we don't know an input to take it there. With this my friends, I think I'm done. Thank you very much for your attention today.