Writing shellcodes for fun (and profit)
Mayby it's time for something more difficult. Let's talk about shellcodes tonight.
Not those simple exec and setuid shellcodes, but something more funny. Something like the old "Hello World" example.
The C code
If you want to make a shellcode, the first you should do is writing your shellcode in c. So let's start with the c code:
// helloworld.c
#include< stdio.h >
int main()
{
char *buffer = "Hello World";
write(1, (void*)buffer, 11);
exit(0);
}
Looks very nice, doesn't it? Let's analise this piece of code.
We take a pointer, defining it with a char and put "Hello World" into the memory. After then the char pointer buffer will be set to "Hello World". Easy, but it's very important for our shellcode.
write(1, (void*)buffer, 11);
Now we have our function, it's a function from the unistd.h lib. Write is a suitable function for writing text to a pid. Because we want to write something on the screen we will write it to the std_out. Which is, in linux, one (1). After our desination we want our source, the buffer pointer. At last we declare the size of the buffer's text, this is 11.
exit(0);
After we write "Hello World" to the screen, we want to exit correctly. This is done by return 0;
Going down to ASM-x86
Let's start with the real work, writing from c to asm-x86. I, myself, runs an amd64 with the gentoo 64bits linux environment. But I created a 32bits x86 chrooted environment, which I use for developing 32 bits linux shellcodes. After a few minutes I created this shellcode:
BITS 32
xor eax, eax
mov ebx, 0x01
push eax
push long 0x646c726f
push long 0x57206f6c
push long 0x6c6548
push esp
pop ecx
mov edx, 0x0B
mov al, 0x04
int 0x80
mov al, 0x01
mov ebx, eax
int 0x80
Let me expain the code very carefully. First we want to clean the registry. The only registry that should be set to zero is eax. We can't use the "mov eax, 0", because shellcode shouldn't contain zero byte codes. That's why I used xor.
BITS 32
xor eax, eax ;; always becomes 0
mov ebx, 0x01
The first part of the write function is set to 1, which is the std_out pid.
push eax
push long 0x646c726f
push long 0x57206f6c
push long 0x6c6548
push esp
We push eax (0) on the stack, after 0 the hexdecimal part of "Hello World". Remember that it should be put in little-endian, last char at first. At last we push esp on the stack which contains the pointer adres to "Hello World\0"
pop ecx
The adres of "Hello World\0" will be put into ecx, which is esp. We only have one part of the function arguments left.
mov edx, 0x0B
We put 11 (yes, eleven --> 0x0B) into edx. And we can now call the write function.
mov al, 0x04
int 0x80
All unistd functions have a system call. Just look in the unistd.h file for the write numbers. Write function has the 0x04 systemcall. So we put it into al and execute the systemcall (0x80).
mov al, 0x01
mov ebx, eax
int 0x80
Now we have to exit our shellcode correctly. The unistd systemcall for exit is 0x01, it needs one argument (0) and than should be executed.
Now let's see if our shellcode is working.
linux write # nasm helloworld.s
No compile errors, that could be a good sign. I parsed the shellcode to a good looking hexdecimal shellcode and use a simple c exploit for testing the code.
linux write # cat test.c
/* The following shellcode is 40 bytes long: */
char shellcode[] =
"\x31\xc0\xbb\x01\x00\x00\x00\x50\x68\x6f\x72\x6c\x64\x68\x6c"
"\x6f\x20\x57\x68\x48\x65\x6c\x00\x54\x59\xba\x10\x00\x00\x00"
"\xb0\x04\xcd\x80\xb0\x01\x89\xc3\xcd\x80";
int main(void)
{
int *ret;
ret = (int*)&ret + 2;
(*ret) = (int)shellcode;
return 0;
}
linux write # gcc -o test test.c
linux write # ./test
Hello World
Hallo wereld, it's working! Just the way we want it to work. Have a nice day!
-Noud 'jwaixs' Aldenhoven





