Do you like uncommon challenges? At least I do, and that’s the reason why I’ve signed up for the SecurityTube Linux Assembly Expert training. But what’s this all about ? The founder Vivek Ramachandran summarizes it best:
The SecurityTube Linux Assembly Expert (SLAE) aims to teach the basics of assembly language on the Linux platform from a security perspective and its application to writing shellcode, encoders, decoders and crypters, among other things.
This training doesn’t include one of those boring brain-dump style exams, which you encounter quite often in the information technology business, instead it’s a bit like the OSCP exam - you have to challenge a practical exam to pass the certification. This exam includes 7 different challenges with varying difficulty, you have to solve by presenting your solution in a blog-style format. So this article is the first in a series about my SLAE assignments.
Assignment #1: Shell Bind TCP Shellcode
The first assignment is to create a Linux shellcode which:
- binds to a port via TCP
- execs a shell on incoming connection
- is easily configurable (in regards to the port)
To solve this assignment I’ve used one really excellent resource when it comes to linux syscalls: http://syscalls.kernelgrok.com. It’s really worth a look!
Before you read any further, I’d just like to point out two things:
- My final shellcode for this assignment is 89 bytes in length! My general intention when it comes to shellcoding is always to create them as small as possible. There are a few bind shellcodes out there which are bigger in size, because they care about things like SO_REUSEADDR, which is totally fine if you have enough room in an exploitation scenario for it! (If not, use mine instead ;-) )
- My shellcode is register-aware! This means, it makes no difference which values are in the different registers at the time of exploitation, because I’m cleaning them up before their usage using different techniques :-)
In addition, you can find all my scripts and source codes related to the SLAE in my github account.
Shell Bind TCP PoC
To understand how the final shellcode should look like, it’s first quite helpful to create a correlation of the code in a higher language like C:
I think I do not need to analyse this source code in detail (as I have commented every line).
According to the C source code, the following function calls need to be translated into assembly:
- Create a socket
- Bind it to an address/port
- Listen for incoming connections
- Accept a new connection
- Redirect stdin, stdout and stderr via dup2
- Execve a /bin/sh
Create a socket via SYS_SOCKET
Using the mentioned linux syscall reference list, you quickly figure out that you need syscall 0x66 (SYS_SOCKETCALL) to basically work with sockets.
Instead of XOR’ing the EAX and EBX registers and MOV’ing the appropriate values into them, there is a more cost-efficient way using a PUSH/POP combination (yes this really saves a byte ;-) ):
The next important part - the different functions calls of the socketcall syscall can be found in /usr/include/linux/net.h:
According to that list you need to start with SYS_SOCKET (0x1), so another combination of PUSH/POP leads to a 0x1 in EBX:
The socket() call basically takes 3 arguments and returns a socket file descriptor:
To setup a proper socket() call, you need to check different header files to find the definitions for the arguments:
Using these, you can push the different arguments (socket_family, socket_type, protocol) onto the stack after cleaning up the ESI register:
…and since ECX needs to hold a pointer to this structure, a copy of the ESP is required:
Finally you can execute the syscall:
which returns a socket file descriptor to EAX. As the subsequent functions rely on this socket file descriptor, you need to put it in an unused register, where you can later pull it from again to work with it. This is needed because every subsequent call will save its result to EAX an therefore would overwrite the socket file descriptor. So moving it to another register like the EDI is required!
Since you cannot guarantee that the EDI register is controlable in an exploitation scenario, you need to fill it with something before. In this case POPing 0x2 from the stack is quite useful, because you need it in the next syscall:
(Using XCHG at this point saves another byte in comparison to a MOV instruction)
Bind it to an address/port via SYS_BIND
Great. Now there is a socket file descriptor to work with. Up next, you have to bind the socket to an ip address/port combination using SYS_BIND.
Due to the last XCHG instruction EAX now contains 0x2, which is a perfect match, because EBX needs to be 0x2:
Therefore bind() takes three arguments, and one of them needs a special attention: const struct sockaddr:
It’s pretty obvious that you need to define this first - as we need a pointer to that structure on stack and take care of the argument order (remember that you PUSH the arguments onto the stack, so if you’d first PUSH the addrlen and then PUSH the different sockaddr_in arguments, this would result in a segmentation fault, because the argument order doesn’t match the expected function order anymore!).
So the arguments are as follows:
The first argument needs to be addrlen! Followed by the sockaddr pointer and the sockfd (stored in EDX due to the first XCHG):
Last but not least:
Listen for incoming connections via SYS_LISTEN
Now that you have a bind, let’s listen for incoming connections via SYS_LISTEN:
listen() takes 2 arguments, that are pretty straight forward:
Accept a new connection via SYS_ACCEPT
A listen() feels lonely without an accept():
As you’re accepting a new client-side connection, the arguments for accept() are a bit easier than for the bind call before: PUSH a 0x0 for addrlen and sockaddr, since you do not need to know anything about the client, followed by the socket file descriptor:
finally execute the syscall, which returns a socket file descriptor for the client into EAX, when a connection comes in:
Redirect stdin, stdout and stderr via SYS_DUP2
To “see” something in your upcoming shell, you have to redirect stdin (0), stdout (1) and stderr(2) to the client socket file descriptor. This can be accomplished by using the SYS_DUP2 syscall 3 times (e.g. in a loop).
But before digging deeper into the SYS_DUP2 calls, the ECX register needs to be prepared for the loop. To better understand the next part, let’s first have a look at the current stack layout:
Due to the preceding pushes, the stack layout contains the socket file descriptor 0x00000007 and two times 0x00000000. This is getting quite important, because you can use this stack-layout to proceed with the next two syscalls.
Therefore, POPing ECX two times will return 0x00000000 into ECX:
Now the start-counter can be moved into CL:
Since the result of each call is returned into EAX, it currently holds the client socket file descriptor (due to the sys_accept), and you have to save this to EBX using XCHG. EBX containt 0x5 due to the last syscall, so you can make sure, that if the socket file descriptor is bigger than 0xFF, the next lower register MOV will do it’s job!
dup2 takes 2 arguments:
whereas oldfd (EBX) is the client socket file descriptor and newfd is used with stdin(0), stdout(1) and stderr(2). The sys_dup2 syscall is executed three times in an ECX-based loop:
Here’s the next trick to reduce the shellcode size. A decrementing counter using ECX and a JNS condition. JNS basically jumps to “loop” as long as the signed flag (SF) is not set. CL is initiated with a value of 0x2 - let’s have a look at how the SF trigger is working:
After the third DEC ECX, it contains 0xffffffff aka -1 and the SF got set and the shellcode flow continues.
Execve a /bin//sh via SYS_EXECVE
Now you have redirected stdin, stdout and stderr to the client socket file descriptor, the last thing you have to do is executing a /bin/sh:
Execve() takes 3 arguments, and just one of them is interesting: filename. So you need a pointer to the filename, you’d like to execute in EBX, which has to be in reverse order due to little endianess and terminated by a NULL, which is luckily already on the stack - have a look at the last screenshot ;-)
Finally ECX and EDX need to be adjusted to contain 0x0. ECX just by incrementing (it contains 0xffffffff due to the dec-loop) and edx by a simple MOV:
and last but not least execute the magic:
DONE :-) !
Here’s my complete, commented shellcode - currently at a size of 89 bytes :-)
Test the shellcode
The first step to test the shellcode is to assemble/link it and then extract the shellcode using a little commandlinefu:
Now you can place the shellcode in a C - template, like this:
verify if the port is open:
and netcat to the port to test if the /bin/sh is responding as intended:
Additionally you can run strace to verify which calls are executed and correlate this to the C source code. As you can see: socket, bind, listen, accept, 3x dup2 and finishing execve are executed properly:
The last part of the assignment is to make the port easily configurable. So here’s a little Python wrapper-script which accepts the port as a commandline-argument and generates the appropriate shellcode out of it. It takes care about the lower ports (<1024) as they are intended for root-usage only, and about 0x00 values as they would break the shellcode or exploit:
The Python script echoes the new shellcode, so it can be used directly
by inserting it into the C-template and test it again:
et voila :-)!
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
Student ID: SLAE- 497