Walking over to this talk, I was thinking to myself, I'm a, you know, full-time job as a developer, and I'm thinking, you know, if there's one thing that developers love doing and we're super competent at, it's string handling. It's not like there's an entire industry based on just how poorly we've mangled that up over the years. And if there's a second thing we really love doing and are great at implementing correctly, it's Unicode. So, I'm sure we're in for something exciting here with Unicode or emoji shell coding, so help me welcome Adrian and George. Hello, DEF CON. How are you today? Great. Welcome on board. Before we depart, please make sure that your seat is up, your tray table is toad, and And your seat belt is securely fastened. If you have any electronic device, please switch it to DEF CON mode now. Please be careful when opening your overhead bins, as emojis may fall on you. Thanks, and enjoy your talk. So let's present ourselves. So this is Hadrian. HADRIEN LAMBERT- Hello. It's his first DEF CON talk. And I'm Georges. It's my second DEF CON talk. So security researchers at Ecole Normale Supérieure in Paris, France. We are both French, as you may have guessed. And we both managed to fail at the Turing test, but that's another issue. What we're going to see today. So we're going to talk about some generic tooling and methods to write shell codes under heavy constraints. We'll dive into the dark art of shell coding. We'll play with emojis to make computers do things they definitely shouldn't do. By the way, you'll also see the first emoji shell code ever created. Brace yourselves. We're going to turn emojis into merciless payloads. So let's begin. Who has ever written a shell code? Just raise your hand. Come on, just raise your hand. Don't be shy. Feds won't arrest you. Well, about 20%, 10%. 10% or 20%. So for the remaining, let's explain what a shell code is from the beginning. So a shell code is some code that you either found or managed to inject in your target. This code generally gives you some power. Usually, it pops a shell. That's why we call it a shell code. Now, it can do a lot more things than simply spawning a shell. And generally, what you do is after injecting it in your target, you jump onto it using what we call a vulnerability. So you can have before overflow, use after free, and so on and so on. You just go to the CVs list, and you have all of them. And the typical scenario is you send a carefully crafted string to your target, and you profit, and you have your shell. However, there are many issues with shell codes. So the first thing, for example, if you have a before overflow using a scanf, so you need to input a C string. And a C string, you can't have a new character inside, because it breaks everything. OK. Same. If you treat it as input, same thing again about scanf. You can't have white spaces, because it will just break your shell code in half. You can have other constraints. So for example, your name can only contain letters. So you can't have a name with digits. Or you can also have string escaping. So if you put it in a form, generally, you have string escaping, which tends to break your shell code. And of course, trying to pass your input with containing slash bin slash sh as your first name is not really credible. So it does not look legit at all, so it gets detected. What can we do? So here, we go into the domain of constraint shell coding. So generally, many hackers want to pass their shell codes as pretty stealthy. So the idea is you want to reduce the set of characters that are used for your shell code. One of the normal constraints that have been studied for long is alphanumeric shell coding. So your shell code. The shell code must only use letters and digits. Here, we're going to see on x86. And x86 has been solved quite a long time ago. So here, it has been solved by Rix in 2001. And the idea is really simple. So you take your letters and your digits, you disassemble them, and you look what happens. So here, on x86, it's pretty easy, because you have single letter instructions for push and pop, which allow you to manipulate the stack. You can also increase increment and decrement registers, with only single letter instructions. You also have control flow instructions, like jump and comp. So this is pretty easy. They are all alphanumeric. And you also have the XOR operation, which does an exclusive OR, with many, many operands that are all alphanumeric. So it's pretty easy shell coding on x86. And if you want to switch to x64, so it's pretty easy. You just put a capital H in front of every instruction you want, and it works, except for increasing and decreasing. You're just increasing registers. So x86 is pretty easy. Actually, it's even so easy that other people managed to go much further than that. So people tried. So this is not alphanumeric anymore, because here we add some spaces and punctuation. But people managed to write shell codes entirely in English. So the idea is you take English words, punctuation signs, and so on, and you want to make a shell code that looks like English. So this will be our first demo. So in this scenario, what we'll be doing on an x86 computer, so we will do a set user ID exploit. So this happens on a lot of computers. So take, for instance, changing your password. To change your password, so any user can change his own password, for this you have to modify the password file, which is on Linux on etc shadow. And for this, you do not have the right permission to do so. So someone has to change the password, for example. This is for you. For this, we have a specially crafted program, which is called a set user ID program. So it's a program with a special permission that a user can execute that performs actions at the administrator level, which is root on Linux. So here we can check for this program here. We can see it's called main. It's in red. And we have the s bit in the permission that describes that it is a set user ID program. So if this program has a vulnerability, instead of changing just your password, what you can do is send a shell code to have other actions. So here. We want a root shell from this vulnerable program. So it's purposely vulnerable here. So I just take my shell code. So you can see, indeed, that it looks like English. If you try to understand it, actually, it does not mean anything. And then when I paste it, I press Enter. And here I have my root shell that just spawned. So I can check, indeed, I have UID 0, which is root. All right. Thank you, George. So we have seen that doing alphanumeric shell coding on x86 works fine. What about other architectures? Well, let's look typically at RISC architectures, a.k.a. reduced instruction sets. Well, then we don't have single characters for instruction anymore. And we also have very few addressing modes. So no way to do instruction from memory to memory. And moreover, we have the right to make instructions. have very heavy constraints on the operand side of instruction. So basically, this means that the previous technique doesn't work anymore. So let me present you three ways to get around that, namely compilation, emulation, and unpacking. So the first one is compilation. And the idea is that you want to write a compiler which, instead of targeting your usual architecture, will target the constrained architecture. It has been done in the past for a slightly different thing, which is called a single instruction set computer. Typically, the Morpheus 14 is a compiler which takes any arbitrary C code and compiles it for only the move instruction on x86, because this instruction happens to be Turing-complete. So it works quite fine for one instruction set computer, much less when it comes to our RISC setup, because then the constraints are on the operands and not on the opcode. So writing a compiler, it's much more difficult, and nobody really knows how to do that. So this approach is a bit dead for us. Second one is the emulation way. And this time, the idea is to spend quite some time to write a small interpreter for some language. This interpreter will pass the filter. Typically, it would be alphanumeric. And then, since you have a filter, you can pass it. If you have an interpreter, you can encode any payload, arbitrary payload in your target language, and interpret it. This is typically what Yunnan and others did in 2009. So they did it for ARMv7 and wrote a brain-fuck interpreter. So it works fine. But there's an issue with this approach. Mainly, it's that the harmfulness relies on what you can do with an interpreter. So if you want to use it for an actual exploit, it means that you need to. You need to escape the interpreter, or that the interpreter has to be able to write outside of its own box. And typically, in Yunnan case, it's not the case. So we're a bit stuck here, too. Third one is unpacking. So this time, we want to encode our payload in a constraint-compliant way. Typically, I want to find a way to encode my payload in alphanumeric. Base 64 would be almost good for that. And then, identify, like, high-end, high-end, high-end, high-end, high-end, high-end. High-level constraint, and try to write some alphanumeric unpacker from that, which will be able to unpack my payload, then jump on it and execute it. So you spend some time writing this unpacker, which is alphanumeric. You have your payload, which is encoded in an alphanumeric way. Unpack it, jump on it, and then you can run arbitrary payloads. So this is what George, me, and some of our colleagues did previously. From ARMv8. So we are going to show you a small demo of that. Thanks. Good. So in this setup, we have a small account manager, which is very badly written and has an obvious buffer overflow vulnerability in it. So it's asking for your username. And obviously, before getting to the buffer overflow, there is some check to make sure that the username looks like a valid username. So alphanumeric works fine here, because a username which consists only of letters and digits is quite legit. So we are going to use our payload here. As you can see, it is only made of letters and digits. And we're going to use that as our username and take control of this account manager. Here, the idea is to dump the etc shadow file, which is a Linux machine, the file which contains the hash of all user account passwords. Takes a bit of time, because the terminal is a bit slow. Good. That was finished. And then we have a win. Our payload just dumped the slash etc slash shadow file, and we have the hashes of all passwords. So we can now use our So in a sense, we can, I think, almost say that alphanumeric shell coding is solved. It even works for the RISC-V architectures. And this is what some colleagues, George and I, presented in DEF CON 27. So don't hesitate to jump back in time and see our talk again. Back to emojis. A few months ago, Bloody Tangerine, which is a security researcher, asked George and I, well, you have done alphanumeric shell coding. Could you maybe do emoji shell coding? I didn't think about it, and found a very good reason why it was impossible. So just gave this reason, and I was happy with it. Then, 2 AM, next morning, I just wake up and realize that my reason is totally wrong. Like, I did a mistake. And so I try to fix it, and after a few hours, I realize that, well, I can't fix it. So I'm only left with one possibility, that is to prove that doing emoji shell coding is possible. And then I woke up at 8 AM, I opened my mailbox, and what I found, a horde of wild emojis that were just saying hello world to me in Khemu. So when you see that in your mailbox, the first thing you say is, what the fuck is this? And then you look into it. Then you ask for the source code, then you look more into details, and you find bugs. And with all the bugs that are found, I said, how the fuck does it even work? There are bugs everywhere. And then I just went and saw Adrian, and we sat together on a table, and we said, let's clean this shit and send it to DEF CON, and here we are. Good. So we want to execute. Emojis has some architecture, so execute emoji code. For that, we first need to look at emojis and try to define a bit what emojis are. Well, let's say I wanted, like, 10 years ago to send a nice text to my girlfriend. So I type my nice I love you. The probability that she would get this instead was unfortunately quite high. And well, this is not exactly the same meaning. So we needed to do better. And fortunately, we have Unicode, which is now quite an old standard to the rescue. We can take the Wikipedia definition, which is that Unicode is a standard for the consistent accounting, representation, and handling of text expressed in most of the world's writing systems. Namely, that, like we want, that everything displays quite the same and in a consistent way for everyone on any system. So what do we have in Unicode? Well, obviously we have Latin letters, such as, you know, as this capital A. And on this part, Unicode is actually compatible with ASCII. Then you can find many other scripts, such as Japanese paragoners. More obscure stuff, such as the whole set of alchemical symbols. Yes, it's in Unicode. You have also the whole set of playing cards. Spilies. And some odd characters, such as cuneiform. So this is a single Unicode component. So you... This very wide character is a single Unicode character. And then more stuff such as the Holy Horn Grenade of Antioch, or Kim Kardashian. Well, actually, not the last two ones. At least, well, not yet. So, we have a very small sample of Unicode characters, and now the question is, well, what's an emoji in this? Capital A? Well, clearly not an emoji. Smiley? Clearly an emoji. Not an emoji. But maybe, what about the Jack of Spades? Well, you look into the Unicode standards, well, it's not an emoji. But let's replace the Jack of Spades with the Black Joker. Then, the Unicode standard says that it is an emoji. Starting to get a bit confusing. And let's switch it again with, from the Black Joker to the White Joker. Then Unicode says this is not an emoji. Even more confusing. So we are going to settle for a very, very, very, very simple, yet a bit subtle definition, which is that, if Unicode says it is a qualified emoji, then it is an emoji, period. And in UTF-8, which is a way to represent Unicode code point in bytes, emojis are at least three bytes, such as this nice smiley face, and at most, 35 bytes. Moreover, they add new emojis every year, and we are currently at Unicode version 14. So for today, we are going to talk about the Unicode version 14. And then, we are going to consider doing shell coding with UTF-8 emojis on Unicode version 14. Good, so we have emojis here, and then we need to run the emojis on something. So we are going to use RISC-V. What's RISC-V? Well, RISC-V is the architecture of the future. Or, well, this is what we said three years ago at CON 27. I think now we can even say that RISC-V is the architecture of the present. Now, since 2021, you have some real RISC-V products that you can buy as a customer, so maybe you already have RISC-V CPUs at home. And in more technical terms, let's see what we have in RISC-V. Well, this is a simple RISC architecture. They push a lot to have open source ISAs, and also they push a lot for open hardware. And in RISC-V, you have two and four byte instructions, and it is little engine. Remember that for later. Well, so now I need to execute my emojis on RISC-V. Let's try to look back at the method we saw previously, the previous work, and see if it still works. So, alphanumeric x86. You have to take the whole set of single letters. It gave you a set of instruction. You realized it was too incomplete, so then you could shellcode in that. Alphanumeric x86. You take other RISC architecture, such as ARMv7, v8, or RISC-V. You took the set of quadruplets of letters and digits. It gave you a set of instructions. Again, you figure out it was too incomplete, so you were good. What about emoji RISC-V? Well, let's use the first method. I take my list of all emojis, see how many of them can be executable as RISC-V. Too bad, only 10 of them are. So that's not too incomplete at all. Let's try this. Let's try the second method, like pairs of emojis. We still have very, very few pairs of emojis which are valid RISC-V code. And if I go along and take triplets, quadruplets, it's not getting better at all. So we are stuck, and the previous method will not work here. So I'm going to try to present you the way it works, how we were able to fix this issue with an example. So on the bottom of the screen, you have this AUI PC, instruction, which is in RISC-V a way to load the PC relative offset into the RA register. And if you look at how it is at this hexadecimal representation, it is 97, F0, 9F, 97. And this is not valid emoji. This UTF-8 is not valid emoji. So we are going to split it in two, like this. Left, 97, and right, the other parts. So let's try to tackle the left part. I want an emoji which ends with 97. So I look into my big bag of images, find one. This is the OK emoji, which starts with F0, 9F, 86. And too bad, these three first bytes are not RISC-V code. Typically, this is three bytes, and as I told you, there are no three bytes instruction in RISC-V. But maybe we have another gadget, which, again, can do jumps. And then you can jump directly over 97 and execute our IUI PC. Let's look a bit more into our bag of images. And this time, we find the Bung emoji, which ends with 97 and starts with E2-9D. This time, it's quite nice, because E2-9D is a valid RISC-V emoji at S11, S8. So at the expense of trashing the S11 register, I can then start directly this gadget. OK. Execute my add, then my IUI PC, which is my target. Good. We have two ways to do that. And on the right side, it's going to be quite similar. So this time, I want an emoji which starts with F0, 9F, 97. Look again into my bag of images, and I find the calendar emoji. It ends with 97EFB8AF, which is a valid RISC-V instruction. So this time, at the expense of trashing the T6, 96 register, I can finish my gadget. Again, last time, let's look into our bag of images, and we find the bin emoji. This time, the last four bytes are not valid RISC-V, but 91EF is a small forward jump. So again, I can end my gadget by jumping out of it. In the end, this is quite good, because I can use any combination of OK bin, OK RISC-V, RISC-V, RISC-V. OK calendar, bin bin, or bin calendar, and execute things. And I have quite of a trade-off between the gadget size and trashing registers or not. Good. So the question is, well, I could do it for one specific instruction. How can I do that? How can I find a way to generate all possible gadgets and a whole list of emoji-compatible RISC-V instructions? Well, for this, we have to take a little step back. So we'll be talking about code reuse attacks. So for the people who know it already, think about return-oriented programming or just-in-time sparing. But here, let's present for those who do not know it. So return-oriented programming has been published originally by Shahram and others in 2007. And the idea is really simple. So you take a huge binary, typically the C standard library, and then you just scan it. And you find a small, little, reusable code reuse attack. It's called snippets. So those little code snippets are called gadgets. And the idea is once you have a whole collection of gadgets, what you do is you try to assemble them together. You chain them in order to have your shell code. So you just rebuild the shell code in the form of what we call a rope chain. Then you send this rope chain as input in the vulnerable program, and you still have your shell that pops. There is a variant of it, which is called jitspring. That has been published by Blazakis in 2010. And the idea is instead of scanning your binary to find gadgets, what you do is you just create them by controlling some code that then is compiled using a JIT just-in-time compiler. So here, the attacker, for instance, you can see it on the right side of the screen. On the top part, the attacker can control and write any code he wants. So this is typically JavaScript, for instance. So what the attacker does? He assigns to the variable y some immediates that are absorbed together. If you send it to Firefox, this goes through the just-in-time compiler and is then compiled as the binary code that you see below. And in this case, it's pretty easy, because the attacker manages to control four out of five bytes of the final program. So here, you can generate gadgets as you want, and you can just do your return-oriented programming as before. So this is jitspring. So here, we have to do the same with emojis. So we can control emojis, and we need to build gadgets from it. So how do I create gadgets from emojistream? Here, it's a little bit complex, because do you remember the infinite monkey theorem? It states that if you take a monkey, you give it a keyboard, and if the monkey types on the keyboard for an infinite amount of time, then almost surely, he will type any given sequence. So here, we do the same, and we take a monkey, and we give it a key. And we give him an emoji keyboard. Only problem is that there is no algorithm to look for gadgets in an infinite stream. So previous methods were only scanning binaries, but not infinite streams. So we need to scan the output of a monkey here. We need to invent a new algorithm. So let me explain this algorithm. So here, we have three lines. So on the first line, what we have is the emoji stream. So this will be a stream of emojis. This will be what our monkey will type. Then, this emoji stream will have a hexadecimal representation that is on the middle. And we must synchronize it with some executable stream. So this is RISC-V instructions. And both should coincide on their hexadecimal representation. So let's start with an instruction. So let's take, for instance, add S7, S7, S7, S8. So here, I have an excess of my instruction stream. So what I must do? What I must do is find an emoji which starts with E29B. So let's take our bag of emojis and take, for instance, the chain emoji here. So here, I go on the opposite side. So I have an excess of emojis. So I must find some executable instructions that fits the hexadecimal representation here. So let's look what is inside. So I find an or immediate, which fits. And here is something very interesting, because here, I managed to end my emoji. I managed to end my instruction stream at the same point. So both are synchronized here. Both are synchronized here. And what I can do is I can just stop here and consider that this is a gadget, because I can reuse the chain emoji independently without taking care of what comes before or after. So this is a gadget. Let's go to a second case. If I take the basketball player emoji. So again, I have an excess of emojis here. So I must find an instruction. So this B9EF is a branching instruction. So it's a small branch forward. So this is another very interesting case, because now I can escape out of my gadget. And I do not need to end my basketball player with other prescribed instructions. So this is another gadget. So you can see here the second gadget that we managed to generate. And we can reuse it as it is. Let's go to the third case. So let's take the snowman emoji. So again, I look for an excess of emojis. An instruction that starts with 84. So let's take, for instance, store word. And then here, I come back to the beginning where I have an excess of instructions. And I complete it with an emoji. So the copyright emoji here. And again, I can do the same as before. And here, I have a branch which ends my gadget again. So I do this for every possibility. And in the end, I have a whole list of gadgets that are usable for shell coding, and so on and so on. Just a side note for people who know a little bit about grammars. So the method we use, actually, is really generic. As you may have guessed, the algorithm follows derivations of a grammar in the form we have a non-terminal that produces a terminal followed by a non-terminal. Here, our terminal is emojis. So for people who know about it, this exactly describes what we call a right linear grammar. And there has been a very interesting property detailed by Patel in 1971. Who says that any regular expression, which is typically for computer scientists what we call a regexp, can be converted to a right linear grammar. Of course, adapting the tool is left to the reader as an exercise. Well, let's get back to emoji now. So I managed to generate all gadgets that can be implemented using emojis. Now we just have to chain them. Good. So let's try to write our emoji chain. Using the output of a string. We have our previous algorithm. We want to look at which instructions are indeed emoji compatible. And well, at the beginning, it looks quite fine because we have a bit more than 4,000 RISC-V instructions, which I can find in at least one of the gadgets. And if you look at what kind of instruction we have, first we have some logic instructions, like add, end, sub, and more. So that's quite good. We have branches, both conditional and conditional, both forward and backward. That's very good, because for control flow that's very useful. We can address many registers. So here you can see we can address 14 registers, but not the stack pointer. Don't worry. Even if we cannot touch the stack pointer, we can still find ways around. And in practice, it's not that much of an issue. However, we have very few immediates, so very difficult to load constants. And actually, in this 4,000 instruction, we have quite a lot of floating point instructions, which we, of course, don't want to use. To give you more idea of what we have, for some families of instruction, such as the CSR family, we only have a single one of them. So this is the one displayed here. And in the end, what we see is that we have a tiny bit of everything. So the set of RISC-V, FOMG-compatible RISC-V is very diverse and very difficult to work with, because it's very difficult to combine one instruction with the other. Yet, we do manage to do this. And I'm going to show you the overall view of what our shellcode looks like. So we use the unpacking method that we showed you previously. And basically, we have a small initialization part, then our unpacker, which embeds the encoded payload. So the unpacker will unpack this payload in memory, then jump on it. And it means that it's very easy to change the payload, because we can generate the unpacker easily from the payload. Let's focus on the unpacker. So in our last talk, we went very far into finding ways to do very efficient unpackers. And we've many gadgets to do that. For this time, we're going to go the very opposite and write a very simple unpacker. So for that, we will only use three gadgets. One is increment the A1 register. Second one is write the value of A1 in the pointer pointed to by A3. And the last one is incrementing the A3 register. So with that, we can write an unpacker. I will show you how. So for this, we take an initial payload, which is a very dummy payload of only three bytes. Three, 20, and 10. And if we go back to initialization, what we do here is that we make sure that A1 is 0, and then A3, is set to the first address of where we want the payload to be decoded at. So let's get back to it. We have A1 equals to 0. Let's output three times increment A1 for the gadget, so then A1 is free. And we can store A1 in A3, increment A3. From 3 to 20, it's 1D, so we output 1D times for increment A1 gadget. A1 is now 20. We can write it. We are good. And from 3. From 20 to 10, well, we have to output the A1++ gadget eight zero times so we can write it in memory. So this is the way that we generate our encoded payload. If we execute this, well, then we start from A1 equals zero, A1++ three times, A1 equals three, we write three to memory, then we add 1D to three, it's 20, we write 20 to memory, and add eight zero to one, so A1 is 10, write it to memory. It means that the initial payload is equal to the decoded payload, and we have found a way to encode any payload into our encoded payload, which will decode at the exact same initial payload. And since all of this gadget, A1++, A3++, and store A1 is in A3, are emoji compatible, it means that the whole encoded payload is only made of emojis. Let's present you, like, a bit in detail how we do this A1++, A3++ gadgets. Okay, time to switch to GDB over Bimmer. So here, let me present you what we have. So on the first line, we have the emoji stream, as before, and we have its hexadecimal representation on the bottom. So here, let me show my binary code. You have the two gadgets on the left part of the screen. You have the registers on the right part of the screen, and you have the memory where we want to store, and you have the memory where we want to store, our payload on the bottom right part of the screen. So let's start with a NOP-like operation for the first gadget. So this will just trash the value of S3. If we go to the next one, we store A1, which has been initialized to AB before, to the address pointed to by A3, which is 8000. Then we just take our branch, which will jump to the next gadget, and we start with another NOP-like instruction. Same, we have a branch to the next, which is 8000 bytes, which is also a NOP-like instruction. And in the end, we add the value of T2, which is 1, to A3, which will increment A3. And we can continue as we want. Here, I have a small gap between the two gadgets, and I can fill it with whatever I like. So here, I can fill it, for instance, with three England flag emojis. Let's switch to the demo. So here, I will present you a demo on a Hi5 Unleashed board. So this is a quad-core RISC-V 64-bit board. So now it has been discontinued, so you can't buy it anymore, unfortunately. And it manages to run on Linux. So if it has a Linux, let's play with it. So in this case, what I made is a small network interface that is vulnerable to a shell code. And here, I can check that I have my shell code, which is all emojis. So let's see. So you can see my emoji shell code here. And what I will do is I will just send it through the network to my Hi5 Unleashed board on its vulnerable interface. So let's send the shell code to the Hi5 Unleashed. So I just cut it through netcat. I press Enter. And here, I managed to get my shell. So let me check if the CPU is correct. So let's see. I can see that, indeed, I have a RISC-V CPU from C5. And if I just check who am I, I am going to say, OK. I'm ROT. So I managed to get my ROT shell. Well, I went a little bit quickly over one thing. So I have my gap between my two emojis. So what I can do instead of just putting England emojis. So I can put whatever I want. I just need to find emojis whose size is compatible with the gap. So this is just a variant of the subset sum. It's not a problem. So you can look in Wikipedia how we solve it. And in our case, it's very easy, because we have three and four bytes emojis. And as we know, every number above six can be written as a sum of three and four. So it's really easy to solve it. If we want to have a little bit fun, so we can instead, for example, try to look at how I can fill this gap with the minimal number of emojis. And this is standard dynamic programming. It took me exactly four minutes and 13 seconds to solve it and implement it. So. You can try it, and it's pretty easy. Well, and now I can fit with whatever I want. And this gives me something very interesting, which is called polymorphism. And this is almost trivial to have polymorphism. What is the purpose of polymorphism is to generate many variants of the same shell code that all behave the same. And this is very useful, because now you can pass antiviruses that generally use the notion of signature. So they compute the hash of your shell code and check in the database if it's already known. So now I can generate as many shell codes as I want, whose hashes are all different. So for example, I can put five emojis here. I can also put these 10 emojis, or more emojis, as much as I like. When you do shell coding for HIRL, sometimes it happens that pointer is a bit flawed, and you don't know exactly where the vulnerable program will jump on your payload. So. So to make up for this, you start your payload with a long list of knobs, which makes that even if it doesn't jump exactly where you want, it will just follow the list of knobs until it gets to the start of the meaningful part of your payload. So this is what we call a knob sled. And in Unicode, we have something which is called, we have like a sled emoji. So obviously, as hackers, our first question was, well, can we do our knob sled using the sled emoji? Well, the answer is, yes, we can. So we have to copyright it, but a few knobs, and as many slides as you want. So believe it or not, but this is perfectly executable RISC-V code. And with that, we can do another demo. So this time, this is on the RISC-V code. It's 532 bits. So we have the board with me. So this is a nice, expressive ESP32C3 board. If it is too small for you to see, well, just look at the slides. You have a picture, and it's bigger. And the nice thing about this one is it costs nothing. You can get it for less than $10 from your favorite distributor. So it's very nice to hack with. Good. So for this demo, we have decided to use a very bad coin wallet. So this one is, again, vulnerable to an obvious buffer overflow. And it's going to ask for the passphrase to just access it. And so obviously, we want to use our emoji shell code to get the private key of the wallet. And if we can get that private key, we are rich, because we get all coins. Let's get that. This will be a bit slow, because . Good. So here you have the end of our emoji shell code. We have used polymorphism here. This is why it looks a bit nicer than the previous. And at the end, you can see that it worked. So we have dumped the private key of the wallet. And so now we are rich, unless you manage to get the private key and extract the money before the end of the talk. So it worked. Good. So as a conclusion, well, we have shown you that we are able to do RISC-V emoji shell coding on both 32 bits, 64 bits. That's nice. And that's the method that we presented you, even if it was a bit technical, could be used for other kinds of filters, other kinds of constraints. And we have to talk a bit about what happened during the making. of this project. So it might not be easy to see, but all of these slides are made with LaTeX. So we had many issues with LaTeX. And typically, we did crash Text Studio. We did crash to LaTeX, and more. Most PDF readers cannot display our slide correctly. Acrobat Reader is probably the worst. And all other PDF readers have various glitches. We even had issues with VLC. We found that recording the demos would not play on VLC. So we had to convert them all from MKV to MP4. We found a way to break Firefox. So this is Firefox. It's playing currently the .. Yes. Good. And if you press F5 with PDF in full screen, then you lose crawling in the tab. Like, I cannot crawl anymore in the PDF. And if I press F5 again, this is this. Yeah. Yeah. Yeah. Yeah. Good. Well, it is still broken. So I have persistently broken this tab. So, well, I'm good to switch to the new tab, the Backup tab, to finish this talk. Well, other things such as terminal slowness and cuts, if you know about it, this is what we use in Linux to print things. So I did dare Georges to print the slides. Tell us what happened. So I tried to print the slides. And I managed to break both. Both my computer and the printer at the same time. And since then, I was not able to use my printer for any task at all. I have to do a factory reset, I think. Good. We also have a GCC segfault, which we need to investigate. And very big things for Windows for the occasional BSOD when compiling the slides. Bottom line, emoji support is hard. There are still many issues in libraries. There is software dealing with emojis. If you want to find exploiting them, it's probably a good bet to look at them. In the end, obviously, we do release all of what we did. So you have the GitHub repository. Don't hesitate to click on the link. If you cannot click on the link on the big screen, just remember the short link. You have our email address if you want to contact us. And if you have any questions, don't hesitate to come to the bottom of the stage. And we will happily answer them. Thank you a lot. Thank you.