Jump to content
  • Editando executáveis com o GNU poke - Parte 1

       (1 review)

    Ano passado eu assisti à uma palestra sobre esse novo utilitário da suíte GNU chamado poke. Ele é um editor de dados binários de linha de comando bem diferente dos que costumo usar (HT Editor, Hiew, etc). Hoje decidi testá-lo e curti bastante. Tá em mega beta, então não tá nem perto de ter pacote disponível nos repositórios oficiais das distros Linux, mas consegui compilar e neste artigo vou dar as instruções, que podem variar em cada ambiente, até porque o poke está em constante desenvolvimento. Usei um ambiente Debian testing aqui.

    Instalando as dependências

    A dependência mais chatinha de instalar foi a gettext, porque o pacote pronto dela não foi suficiente. Então tive que clonar e compilar:

    $ sudo apt install perf fp-compiler fp-units-fcl groff build-essential git
    $ git clone https://git.savannah.gnu.org/git/gettext.git
    $ cd gettext
    $ ./gitsub.sh pull
    $ ./autogen.sh
    $ ./configure
    $ make
    $ sudo make install

    Com a gettext instalada, agora podemos partir para as demais dependências do poke:

    $ sudo apt install build-essential libgc-dev libreadline-dev flex libnbd-dev help2man texinfo

    Só então podemos seguir para a compilação do poke.

    Compilando o poke

    $ git clone git://git.savannah.gnu.org/poke.git
    $ cd poke
    $ ./bootstrap
    $ ./configure
    $ make
    $ sudo make install

    Criando links para as bibliotecas

    Como instalei as bibliotecas do poke em /usr/local e o meu sistema não tinha este diretório configurado para que o loader busque as bibliotecas, precisei criar dois links para elas em /usr/lib:

    $  sudo ln -s /usr/local/lib/libpoke.so.0 /usr/lib/libpoke.so.0
    $  sudo ln -s /usr/local/lib/libtextstyle.so.0 /usr/lib/libtextstyle.so.0

    Sei que há outras maneiras de resolver isso, mas fiz assim pra acelerar, afinal eu queria mexer no poke logo! ?

    Abrindo um binário PE no poke

    Baixei o executável do PuTTY para brincar um pouco e abri assim:

    $ poke putty.exe
         _____
     ---'   __\_______
                ______)  GNU poke 0.1-beta
                __)
               __)
     ---._______)
    
    Copyright (C) 2019, 2020 Jose E. Marchesi.
    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.
    
    Powered by Jitter 0.9.212.
    Perpetrated by Jose E. Marchesi.
    
    hserver listening in port 47209.
    
    For help, type ".help".
    Type ".exit" to leave the program.
    (poke)

    Gerenciando os arquivos abertos

    O poke permite trabalhar com múltiplos arquivos de uma vez. Você pode ver a lista de arquivos abertos com o seguinte comando:

    (poke) .info ios
      Id	Mode	Size		Name
    * #0	rw	0x0010b990#B	./putty.exe

    ios signifca "IO Spaces". Não tem nada a ver com o SO da Cisco ou com o da Apple. hehe

    Se quiser abrir outro arquivo, pode usar o comando .file <arquivo> e aí pode selecionar em qual você quer trabalhar com o comando .ios #n onde n é o número que identifica o arquivo, mas vou seguir o artigo com somente um arquivo aberto mesmo, então só teremos a tag #0.

    Dumpando dados

    Um dos principais comandos do poke é o dump (perceba este não começa com um ponto) que basicamente visualiza o conteúdo do arquivo, mas este tem várias opções. Vamos à mais básica:

    poke_dump.png.a306fee9e691569c325d5357f46cffc3.png

    A primeira linha na saída acima é só uma régua pra te ajudar a encontrar os bytes.

    Fiz questão de colar uma captura de tela aí acima pra você ver que o poke colore a saída, mas nos exemplos seguintes vou colar a saída em texto pelo bem da sua largura de banda. ?

    Por padrão, o dump exibe 128 bytes do arquivo, começando do seu primeiro byte. O número de bytes pode ser alterado na própria linha de comando:

    (poke) dump :size 64#B
    76543210  0011 2233 4455 6677 8899 aabb ccdd eeff  0123456789ABCDEF
    00000000: 4d5a 7800 0100 0000 0400 0000 0000 0000  MZx.............
    00000010: 0000 0000 0000 0000 4000 0000 0000 0000  ........@.......
    00000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00000030: 0000 0000 0000 0000 0000 0000 7800 0000  ............x...

    A sintaxe pode parecer um pouco estranha no início, mas você acostuma rápido. O sufixo #B diz que a unidade usada é bytes. Você pode testar outros valores como 2#KB ou 1#MB por exemplo.  ?

    Dumpando a partir de posições específicas

    Para dumpar a partir de uma posição específica, podemos usar a opção :from do comando dump:

    (poke) dump :from 0x30#B :size 32#B
    76543210  0011 2233 4455 6677 8899 aabb ccdd eeff  0123456789ABCDEF
    00000030: 0000 0000 0000 0000 0000 0000 7800 0000  ............x...
    00000040: 0e1f ba0e 00b4 09cd 21b8 014c cd21 7468  ........!..L.!th

    No comando acima eu pedi para o poke me mostrar 32 bytes a partir da posição 0x30. Seria o equivalente a fazer hd -n 32 -s 0x30 <arquivo>.

    O poke mantém um ponteiro de leitura no arquivo, por isso se você comandar somente dump novamente, o dump ocorrerá a partir da última posição lida (no caso, 0x30). Se quiser voltar o ponteiro para a posição zero, é a mesma sintaxe: dump :from 0#B.

    Interpretando dados

    O dump sempre te entrega uma saída em hexadecimal, mas e se quisermos interpretar os dados e exibi-los de maneiras diferentes? Para  isso a gente larga de mão o comando dump e começa a operar com o jeito do poke de ler e interpretar especificamente, assim:

    (poke) byte @ 0#B
    77UB

    O sufixo UB significa Unsigned Byte.

    Se eu quiser a saída em hexa por exemplo, basta eu setar a variável obase (output base):

    (poke) .set obase 16
    (poke) byte @ 0#B
    0x4dUB

    Eu poderia querer ler 2 bytes. Tranquilo:

    (poke) byte[2] @ 0#B
    [0x4dUB,0x5aUB]

    Posso interpretar o conteúdo como número também:

    (poke) uint16 @ 0#B
    0x4d5aUH

    O prefixo UH significa Unsigned Half (Integer). Perceba que o poke sabe que um uint16 tem 2 bytes e por isso os lê sem a necessidade que especifiquemos o número de bytes a serem lidos.

    À essa altura você já sacou que equivalentes aos tipos padrão da linguagem C (da inttypes.h na real) estão disponíveis para uso né? Fique à vontade pra testar off64, int64, int32, etc.

    Lendo strings

    Além dos tipos numéricos, o poke tem o tipo string, onde ele lê até encontrar um nullbyte:

    (poke) dump
    76543210  0011 2233 4455 6677 8899 aabb ccdd eeff  0123456789ABCDEF
    00000000: 4d5a 7800 0100 0000 0400 0000 0000 0000  MZx.............
    00000010: 0000 0000 0000 0000 4000 0000 0000 0000  ........@.......
    00000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00000030: 0000 0000 0000 0000 0000 0000 7800 0000  ............x...
    00000040: 0e1f ba0e 00b4 09cd 21b8 014c cd21 5468  ........!..L.!Th
    00000050: 6973 2070 726f 6772 616d 2063 616e 6e6f  is program canno
    00000060: 7420 6265 2072 756e 2069 6e20 444f 5320  t be run in DOS
    00000070: 6d6f 6465 2e24 0000 5045 0000 4c01 0700  mode.$..PE..L...
    
    (poke) string @ 0x4d#B
    "!This program cannot be run in DOS mode.$"

    Patch simples

    Vamos fazer um patch simples: alterar o "T" desta string acima de maiúsculo para minúsculo. Basicamente é só colocar à esquerda o jeito que acessamos uma determinada posição do arquivo e igualar ao que a gente quer. Sabendo que para converter maiúsculo para minúsculo na tabela ASCII basta somar 32 (0x20), podemos fazer:

    (poke) byte @ 0x4e#B = 0x74

    Perceba que fui na posição 0x4e, porque na 0x4d temos o '!' e não o 'T'. Só pra checar se funcionou:

    (poke) string @ 0x4d#B
    "!this program cannot be run in DOS mode.$"
    (poke)

    Legal né? Mas dá pra ficar melhor. O poke suporta char, então podemos meter direto:

    (poke) char @ 0x4e#B = 't'
    (poke) string @ 0x4d#B
    "!this program cannot be run in DOS mode.$"

    Por hora é só. Fica ligado aí que postarei a parte 2 em breve, onde vou mostrar mais recursos do poke que tô achando bem úteis para engenharia reversa. Até lá! ?


    User Feedback

    Join the conversation

    You can post now and register later. If you have an account, sign in now to post with your account.
    Note: Your post will require moderator approval before it will be visible.

    Guest

    • This will not be shown to other users.
    • Add a review...

      ×   Pasted as rich text.   Paste as plain text instead

        Only 75 emoji are allowed.

      ×   Your link has been automatically embedded.   Display as a link instead

      ×   Your previous content has been restored.   Clear editor

      ×   You cannot paste images directly. Upload or insert images from URL.



  • Similar Content

    • By Bruna Chieco
      O Cisco Talos, grupo global de inteligência de ameaças de cibersegurança da Cisco, descobriu uma vulnerabilidade de divulgação de informações no kernel do Linux.
      A vulnerabilidade, rastreada como CVE-2020-28588, pode permitir que um invasor visualize a pilha de memória do kernel, o que significa que dados ou informações que não deveriam ser vistas possam ser acessadas. O problema foi visto pela primeira vez pelo Cisco Talos em um dispositivo Azure Sphere (versão 20.10), um dispositivo ARM de 32 bits que executa um kernel do Linux corrigido.
      O kernel do Linux é o núcleo livre e de código aberto dos sistemas operacionais do tipo Unix. A vulnerabilidade existe especificamente na funcionalidade /proc/pid/syscall de dispositivos ARM de 32 bits executando Linux.
      Um invasor pode explorá-la lendo /proc/<pid>/syscall, um arquivo legítimo do sistema operacional Linux, podendo aproveitar esse vazamento de informações para explorar com êxito vulnerabilidades adicionais não corrigidas.
      O Cisco Talos trabalhou com o Linux para garantir que esse problema seja resolvido e uma atualização já está disponível para os clientes afetados. Os usuários são incentivados a atualizar esses produtos afetados o mais rápido possível para o Kernel Linux versões 5.10-rc4, 5.4.66 e 5.9.8.
    • By Bruna Chieco
      Pesquisadores da Sayfer fizeram engenharia reversa no WhatsApp Web e acabaram encontrando "sem querer" um recurso desabilitado por padrão, ou seja, que ainda não está liberado. A função pre-released descoberta permite que a velocidade de um áudio seja acelerada. 
      Os pesquisadores, na verdade, estavam fazendo uma pesquisa sobre outro projeto quando descobriram acidentalmente que o WhatsApp tem um sinalizador para o recurso oculto que permite aos usuários alterar a taxa de reprodução de mensagens de voz.
      Uma das ferramentas de pesquisa utilizadas pelos pesquisadores permitiu essa alteração, sendo cada mensagem de voz é essencialmente uma marca de áudio com um blob de uma fonte de áudio. Para alterar a velocidade, contudo, foi necessário fazer engenharia reversa no código-fonte minimizado do WhatsApp para a web.
      Eles descobriram que o WhatsApp tem três velocidades pré-determinadas para os áudios, porém, desabilitadas. Em publicação, os pesquisadores explicam o passo a passo do que fizeram para conseguir alterar a taxa de reprodução dos áudios.
      E se você quiser saber mais sobre engenharia reversa, o Mente Binária tem um curso com 24 aulas que pode ser acessado por meio do nossos canal no YouTube:
       
    • By Felipe.Silva
      Injeção de código é uma técnica que consiste em adicionar instruções extras em um executável. Essas instruções podem ser adicionadas em vários lugares do programa, inclusive executar antes do entry point original.
      O uso mais comum para injeção de código é para fins maliciosos, onde um shellcode poderia ser inserido no executável e dar acesso remoto para um atacante. Mas um exemplo de uso "justo" para essa técnica é para fins de patching no executável quando você quer que algo seja alterado em tempo de execução no binário.
      Se você já tentou injetar código em um executável manualmente deve saber que não é uma tarefa tão divertida. Pensando neste tipo de impasse, imaginei que seria interessante ter uma ferramenta para automatizar esse tipo de manipulação de um executável. Por esse motivo criei o pei, uma ferramenta para automatizar injeção de código e outros tipos de manipulações em executáveis PE de 32-bit e 64-bit.
      O pei foi programado pensando na automação e por isso é fácil usar a ferramenta a partir de um script. Com ela você pode obter e modificar valores no executável, e é claro, injetar código.
      Antes de qualquer coisa você pode instalar o pei em seu Linux rodando os comandos abaixo:
      git clone https://github.com/Silva97/pei cd pei make sudo make install Nota: Caso use Windows e não esteja usando WSL ou um MSYS2 da vida, você pode compilar o projeto instalando o make e o MinGW (recomendo usar o Chocolatey). No entanto, o “sudo make install” não vai funcionar no Windows, você vai ter que adicionar o executável ao PATH manualmente.
      Se você não estiver a fim de compilar o executável, fiz o favor de adicionar o binário da ferramenta compilado para Windows nos releases dela. Você pode baixá-lo no link https://github.com/Silva97/pei/releases/latest.
      O uso básico da ferramenta segue o seguinte formato:
      pei [opções] <operação> <executável> [argumento] Se você quiser ver mais detalhes de como usar a ferramenta você pode rodar “pei -h”.
      Operações
      As operações são o que determinam o que a ferramenta irá fazer com o executável, indo desde exibir informações sobre ele até modificar campos dos cabeçalhos.
      show
      A operação show serve para exibir informações sobre o executável e campos dos cabeçalhos. Se você não passar argumentos para a operação por padrão ela irá exibir informações básicas do executável:

      Você também pode especificar uma combinação de letras para escolher quais campos serão exibidos, dentre elas: c (COFF header), o (optional header), d (data directories) e s (section). Exemplo:
      $ pei show test.exe co Esse comando, por exemplo, exibe o COFF header e optional header do executável.
      get
      A operação get pega o valor de um campo individual de um dos cabeçalhos (coff, optional ou section) do executável.
      Seguindo uma notação com pontos, semelhante à acessar campos de estruturas em C, você pode especificar o cabeçalho e o nome do campo para ser obtido. Por exemplo, para obter o entry point do executável o comando ficaria:
      $ pei get executavel.exe optional.entry_point '%x' 14f0 Dica: Veja o nome dos campos dos cabeçalhos usando a operação show.
      O argumento após o nome do campo é uma string de formatação idêntica a da função printf() da libc, que aceita quaisquer flags de formatação disponíveis para a função.
      Para acessar os campos de uma seção é necessário especificar o número da seção também, como demonstrado no print abaixo:

      edit
      A operação edit serve para modificar o valor de campos, onde o nome do campo é especificado de maneira idêntica à operação get.
      Você pode utilizar os operadores `=`, `|=` e `&=` que fazem exatamente a mesma coisa que na linguagem C. Exemplos:
      $ pei edit executavel.exe section.0.name = .code $ pei edit executavel.exe optional.entry_point = 0xabcd1234 Esta operação aceita números em decimal, hexadecimal ou octal na hora de definir o valor de campos numéricos.
      zeros
      Esta operação simplesmente exibe uma lista das maiores sequências de bytes nulo em cada seção do executável. É este espaço que é utilizado para injetar o código, tornando a operação útil para você poder escolher em qual seção injetar o código.
      $ pei zeros executavel.exe Section #0 '.text': 0x00000000000022fb of 13 bytes Section #1 '.data': 0x000000000000242c of 1012 bytes Section #2 '.rdata': 0x0000000000002a5b of 37 bytes Section #6 '.idata': 0x0000000000003a26 of 22 bytes Section #7 '.CRT': 0x000000000000420b of 21 bytes Section #9 '/4': 0x0000000000004649 of 23 bytes Section #10 '/19': 0x0000000000004cbe of 10 bytes Section #12 '/45': 0x000000000003e2fc of 5 bytes Section #13 '/57': 0x0000000000041019 of 8 bytes Section #15 '/81': 0x0000000000043c33 of 44 bytes Section #16 '/92': 0x0000000000045509 of 23 bytes inject - A cereja do bolo 🙂
      Esta é a operação que injeta o código. Você pode usar a opção -f para especificar o arquivo contendo o código a ser injetado, que seria um raw binary. Onde esse arquivo deve conter apenas as instruções de código de máquina a ser injetado, como um shellcode por exemplo.
      Opcionalmente, você pode usar a opção -s para especificar o número da seção que você quer injetar o código. Se a opção não for especificada, por padrão o pei vai injetar onde houver mais espaço disponível.
      $ pei -f my_code.bin inject executavel.exe Writed code of 12 bytes on offset 0x0000000000043924 of section #15 '/81' Após o código injetado, o pei insere um jump absoluto para o entry point original do executável, fazendo com que após o código injetado ser executado o fluxo de execução do programa continue normalmente.
      Outra modificação que o pei faz é desabilitar o dynamic base do executável, para evitar que o jump aponte para o endereço errado.
      Dica: Injetando Código Muito Grande
      Se você precisar injetar um código maior do que o espaço disponível apontado pela operação zeros, você pode dividir o código a ser injetado em várias partes e injetar cada parte por vez, seguindo a ordem da última até a primeira parte. Isso funciona porque o pei irá adicionar um jump no final do código para o endereço que está sendo indicado como entry point. Se você já injetou código antes, esse endereço é o endereço do código anteriormente injetado. 🙂
      Dessa forma você pode fazer um chain de saltos da primeira parte até a última e então saltar para o entry point original. Exemplo:
      $ pei inject -f parte-3.bin executavel.exe Writed code of 87 bytes on offset 0x0000000000043833 of section #15 '/81' $ pei inject -f parte-2.bin executavel.exe Writed code of 80 bytes on offset 0x0000000000044924 of section #11 '/23' $ pei inject -f parte-1.bin executavel.exe Writed code of 32 bytes on offset 0x0000000000401a15 of section #1 '.text' Isso irá criar a seguinte ordem de execução: parte-1.bin -> parte-2.bin -> parte-3.bin -> entry point. Onde as setas “->” representam os saltos.
      diff
      Esta operação exibe diferenças entre dois executáveis. Ela compara cada campo dos cabeçalhos do executável e o conteúdo das seções, e depois exibe estas diferenças no terminal. Você pode usar a opção -c (ou --color) para que a saída da operação seja colorida:

      Em vermelho são os valores no executável original que foram modificados e em verde são os valores novos no executável modificado.
      patch
      Essa operação lê um texto de entrada no mesmo formato que a saída da operação diff e replica as mesmas modificações no executável. Exemplo:
      $ pei patch executavel2.exe diff-output.txt Caso você não especifique um patch file, o pei irá ler de stdin. Assim, é possível que você faça um pipe entre uma operação de diff e patch:
      $ pei diff original.exe mod.exe | pei patch outro.exe A diferença de fazer isto e simplesmente fazer uma cópia do executável modificado, é que a ideia é replicar somente as diferenças entre os executáveis. Quaisquer outros campos não são tocados pela operação patch, o que te permite salvar alterações e replicá-las por meio de um script. 🙂
      Se você quiser, também é possível escrever um patch file manualmente, bastando imitar a saída de diff. Uma dica é que os valores dos campos originais (em vermelho) não são necessários para a operação patch, então você pode inserir somente os valores novos. Seguindo o print da saída do diff que utilizamos agora pouco como exemplo:
      optional.entry_point xxx 0x4ca33 section.0.name xxx .code section.15.characteristics xxx 0x62100040 // @O texto não faz diferença, só importa o @ no início da linha // Daqui para baixo qualquer linha que não inicie com + será ignorada. +0x43830 00 30 9f 61 62 63 0a b8 f0 14 40 00 ff e0 00 00 // O formato é: +0xoffset bytes em hexadecimal Nota: Onde há o “xxx” seriam os campos dos valores originais (em vermelho) que não são lidos pelo pei porém são necessários para imitar a saída da operação diff. Você pode colocar qualquer valor aí que não dará erro já que são ignorados.
      Você pode usar a operação patch em scripts para automatizar várias modificações em um executável ao mesmo tempo, inclusive em bytes de um determinado offset.
    • By Bruna Chieco
      Três vulnerabilidades foram encontradas no subsistema iSCSI do kernel do Linux, permitindo que invasores locais com privilégios básicos de usuário obtenham privilégios de root em sistemas Linux sem patch. Segundo o BleepingComputer, os bugs de segurança só podem ser explorados localmente, o que significa que invasores em potencial terão que obter acesso a dispositivos vulneráveis explorando outra vulnerabilidade ou usando um vetor de ataque alternativo.
      O mais impressionante é que essas vulnerabilidades já existem há 15 anos. A descoberta foi feita por pesquisadores do GRIMM. "Ao contrário da maioria das coisas que encontramos acumulando poeira, esses bugs revelaram-se ainda bons, e um acabou sendo utilizável como um escalonamento de privilégio local (LPE) em vários ambientes Linux", diz publicação feita no blog do GRIMM.
      De acordo com o pesquisador de segurança do GRIMM, Adam Nichols, as falhas afetam todas as distribuições Linux, mas felizmente o módulo de kernel scsi_transport_iscsi vulnerável não é carregado por padrão. No entanto, dependendo da distribuição do Linux que os atacantes estejam focando, o módulo pode ser carregado e explorado para escalonamento de privilégios.
      Saiba mais sobre as vulnerabilidades: 
      CVE-2021-27363: vazamento do ponteiro do kernel (vazamento de informações) CVE-2021-27364: leitura fora dos limites (vazamento de informações, negação de serviço) CVE-2021-27365: estouro de buffer de heap (escalonamento de privilégio local, vazamento de informações, negação de serviço)
    • By Fernando Mercês
      Batizado com nome herdado da estrela binária VV Cephei, o Linux.Cephei é provavelmente o primeiro file infector para executáveis ELF (utilizados nos sistemas baseados em Linux, entre outros) escrito na linguagem Nim. Isso mesmo, o autor é um tanto excêntrico e disse em seu blog que o Linux.Cephei é inofensivo (por enquanto) e fez somente para participar de um concurso de programação.
      O vírus é do tipo que chamamos de prepender, ou seja, ele adiciona algo "antes" da execução de um programa saudável, no caso, de um binário ELF. A técnica para isso é a alteração de seu entrypoint. 
      Nos testes que fizemos aqui, o Linux.Cephei só funcionou com binários compilados estaticamente: 
      $ uname -a Linux malinux 4.9.0-4-amd64 #1 SMP Debian 4.9.51-1 (2017-09-28) x86_64 GNU/Linux $ cat /etc/debian_version 9.2 $ cat h.c #include <stdio.h> int main(void) { printf("ola mundo do bem!\n"); return 0; } $ gcc -static -o hello h.c $ ./hello ola mundo do bem! $ chmod +x linux.cephei $ ./linux.cephei $ ./hello Did you know that VV Cephei, also known as HD 208816, is an eclipsing binary star system located in the constellation Cepheus, approximately 5,000 light years from Earth? It is both a B[e] star and shell star. Awesome! https://en.wikipedia.org/wiki/VV_Cephei The more you know... :) ola mundo do bem! $ gcc -o hello h.c $ ./linux.cephei $ ./hello ola mundo do bem!  Perceba que ele injetou seu código com sucesso no binário hello, mas somente quando foi compilado estaticamente.  
      Além da linguagem exótica, ultimamente não se vê muitos file infectors já que a moda de infectar executáveis passou. De qualquer forma, é bom ficar de olho. Com códigos como o do Linux.Ceiphei, vírus podem permanecer ocultos num sistema por muito tempo. E pouca gente usa antivírus no Linux, mesmo tendo uma alternativa livre como o ClamAV.
×
×
  • Create New...