Introduction to Linux shellcode writing (Part 1)

Introduction

This is very brief and basic list of steps to follow if you want to write a shellcode under Linux operating system.

1. Craft the shellcode

The first step and by far the most important one is to find a vulnerability and to write the shellcode that’s exploiting the vulnerability. In this tutorial we will write a dummy shellcode represented by the “Hello World” program. The easiest way to write a shellcode is first to write it in the C language and then in order to have a more compact version, to translate it or to rewrite the shellcode in assembler.

1.1 Craft the shellcode in C

The main goal of writing the shellode in C is to have first working version of the exploit without (yet) bothering about the constraints of the shellcode execution (see later the chapter about the validity of the shelcode). In our case, the C version of our dummy shellcode is the following one:

#include<stdio.h>
int main(){
    write(1,"Hello World \n", 14);
    return 0;
}

After the compilation (gcc -o hello hello.c) we can take a look at the generated assembly code (objdump -d ./hello -M intel) and we would see that for a very small C program the assembly version is quite long (this is mainly due to the C preprocessor); it’s 228 lines length ( objdump -d ./hello -M intel | wc -l).

Now, we would like to “translate” the C version of our shellcode in the assembly version and the most straightforward way is by finding the system calls that hat are made by the C version of the shellcode. In some cases the system calls are quite obvious (the case of the write function) but sometimes it’s not so easy to guess. The tool that will give to you the system calls is the strace. In our case the strace ./hello will have the following output (the parts that are interesting for us are in bold):

execve("./hello", ["./hello"], [/* 62 vars */]) = 0
brk(0)                                  = 0x8826000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb76fa000
....
....
mprotect(0xb771d000, 4096, PROT_READ)   = 0
munmap(0xb76dd000, 117407)              = 0
write(1, "Hello World \n\0", 14Hello World 
)        = 14
exit_group(0)                           = ?

1.2 Craft the shellcode in assembler

Now that we have the system calls it is possible to get some infos like the parameters needed by the each system call (using man) and the system calls numbers (all the system calls names and number are in /usr/include/i386-linux-gnu/asm/unistd_32.h file).

So, the number of the write call is 4 (using cat  /usr/include/i386-linux-gnu/asm/unistd_32.h | grep write) and the parameters are the following one (using man 2 write):

ssize_t write(int fd, const void *buf, size_t count);

For the exit system call the number is 1 and the call parameter are :

void _exit(int status);

Having all the needed information, we can write the assembler version of our dummy shellcode.

In order to call system calls in assembler, you must fill the tax register with the system call number and fill the register ebx, ecx, edx for every parameter that the system call need.

For example the write have 3 parameters so the tax register will be filled with 0x4 (the system call number), ebx register will contain the file descriptor (1 for sysout), ecx register will contains the address of the string to print, and edx register will contain the length of the string to print (if you don’t have any knowledge of linux assembler you can take a look to this very good introduction Assembly Language and Shellcoding on Linux ):

global _start
section .text
_start:
    ;execute write(1,"Hello World \n", 14);
    mov eax, 0x4
    mov ebx, 0x1
    mov ecx, message
    mov edx, 0xD
    int 0x80

    ;execute _exit(0);
    mov eax, 0x1
    mov ebx, 0x5
    int 0x80
section .data
    message: db "Hello World!", 0xA

Now, we can create an executable (using nasm and ld) using the following commands:

nasm -f elf32 -o hello.o hello.asm
ld -o hello hello.o

2. Test your shellcode

In order to test your shellcode you can use the following C program (which is a slightly modified version of the code from the (famous) “Smashing the stack for fun and profit“):

#include<stdio.h>
#include<string.h>
unsigned char shellcode[] = \
"replace this with the hex version of the shellcode";
main()
{
    printf("Shellcode Length:  %d\n", strlen(shelcode));
    int (*ret)() = (int(*)())shellcode;
    ret();
}

The above lines are simulating a vulnerable program by overwriting the return address of the main() function with the address of the shellcode, in order to execute the shellcode instructions upon exit from main().

The HEX version of the shellcode can be obtained from the binary file using the objdump utility and a much smarter version of the command can be found on commandlinefu.com

Lets compute the HEX version of our dummy shellcode and then test it with our  test program.

The HEX version of our assembler version of the dummy shellcode is the following one:

"\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\xb9\xa4\x90\x04\x08
\xba\x0d\x00\x00\x00\xcd\x80\xb8\x01\x00\x00\x00\xbb\x05\x00\x00\x00\xcd\x80"

We add the new shelcode to the test program and then compile the test program:

gcc  -z execstack shellcode.c -o shellcode

We execute the shelcode program and we have the following output:

Screenshot from 2015-08-19 23-33-15

As you can see the execution didn’t went very well for a number of reasons that will be explained in the second part of this small tutorial.

Book review: Hacking – the art of exploitation, 2-end edition

This is a review of the Hacking – the art of exploitation, 2-end edition book.hck2ed

Chapter 0x100 Introduction

Very short chapter (2 pages and 1/2) in which the author gives his definition of a hacker; person that find unusual solutions to any kind of problems, not only technical problems. The author also expresses very clearly the goal of his book: “The intent of this book is to teach you the true spirit of hacking. We will look at various hacking techniques, from the past to the present, dissecting them to learn how and why they work”.

Chapter 0x200 Programming

The chapter is an introduction to C programming language and to assembler for Intel 8086 processors. The entry level is very low, it starts by explaining the use of pseudo-code and then very gradually introduces many of the structures of the C language: variables, variables scopes, control structures, structs, functions, pointers (don’t expect to have a complete introduction to C or to find advanced material).

The chapter contains a lot of code examples very clearly explained using the GDB debugger. Since all the examples are running under Linux, the last part of the chapter contains some basics about the programming on Linux operating system like file permissions, uid, guid, setuid.

Chapter 0x300 Exploitation

This chapter it builds on the knowledge learned in the previous one and it’s dedicated to the buffer overflow exploits. The most part of the chapter treats the stack-based buffer overflow in great detail using gradual complexity examples. Overflow vulnerabilities on other memory segments are also presented, overflows on the heap and on the BSS.

The last part of the chapter is about format string exploits. Some of the string vulnerabilities use specific GNU C compiler structures (.dtors and .ctors). In almost all the examples, the author uses the GDB to explain the details of the vulnerabilities and of the exploits.

One negative remark is that in some of the exploits the author use shell codes without explaining how these shell codes have been crafted (on the other side an entire chapter is devoted to shell codes).

Chapter 0x400 Networking

This chapter is dedicated to the network hacking(s) and can be split in 3 parts. The first part is rather theoretical, the ISO OSI model is presented and some of the layers (data-link layer, network layer and transport layer) are explained in more depth.

The second part of the chapter is more practical; different network protocols are presented like ARP, ICMP, IP, TCP; the author explains the structure of the packets/datagrams for the protocols and the communication workflow between the hosts. On the programming side, the author makes a very good introduction to sockets in the C language.

The third part of the chapter is devoted to the hacks and is build on the top of the first two parts. For the  package sniffing hacks the author introduces the libpcap library and for the package injection hacks the author uses the libnet library (ARP cache poisoning, SYN flooding, TCP RST hijacking). Other networking hacks are presented like different port scanning techniques, denial of service and the exploitation of a buffer overflow over the network.  In most of the hacks the authors it’s crafting his own tools but sometimes he uses tools like nemesis and nmap.

Chapter 0x500 Shellcode

This chapter is an introduction to the shellcode writing. In order to be injected in the target program the shelcode must be as compact as possible so the best suitable programing language for this task is the assembler language.

The chapter starts with an introduction to the assembler language for the Linux platform and continues with an example of a “hello word” shellcode. The goal of the “hello word” shellcode is to present different techniques to make the shellcode memory position-independent.

The rest of the chapter is dedicated to the shell-spawning(local) and port-binding (remote) shellcodes. In both cases the same presentation pattern is followed: the author starts with an example of the shellcode in C and then he translates and adapts (using GDB)  the shellcode in assembler language.

Chapter 0x600 Countermeasures

The chapter is about the countermeasures that an intruder should apply in order to cover his tracks and became as undetectable as possible but also the countermeasures that a victim should apply in order reduce or nullify the effect of an attack.

The chapter is organized around the exploits of a very simple web server. The exploits proposed are increasingly complex and stealthier; from the “classical” port-biding shellcode that can be easily detected to more advanced camouflage techniques like forking the shellcode in order to keep the target program running, spoofing the logged IP address of the attacker or reusing an already open socket for the shellcode communication.

In the last part of the chapter some defensive countermeasures are presented like non-executable stack and randomized stack space. For each of this hardening countermeasures some partial workarounds are explained.

Chapter 0x700 Cryptology

The last chapter treats the cryptology, an subject very hard to explain to a neophyte. The first part of the chapter contains information about the algorithmic complexity, the symmetric and asymmetric encryption algorithms; the author brilliantly demystifies the operation of the RSA algorithm.

On the hacking side the author presents some attacks linked to the cryptography like the man-in-the-middle attack of an SSL connection (using the mitm-ssh tool  and THC Fuzzy Fingerprint) and cracking of passwords generated by Linux crypt function (using dictionary attacks, brute-force attacks and rainbow tables attacks).

The last part of the chapter is quite outdated in present day (the book was edited in 2008) and is dedicated to the wireless 802.11 b encryption and to the weaknesses of the WEP.

Chapter 0x800 Conclusion

As for the introduction chapter, this chapter is very short and as in the first chapter the authors repeats that the hacking it’s state of mind and the hackers are people with innovative spirits.

(My) Conclusion

The book it’s a very good introduction to different technical topics of IT security. Even if the author tried to make the text easy for non-technical peoples (the chapter about programming starts with an explanation about pseudo-codes) some programming experience is required (ideally C/C++) in order to get the best of this book.