Olá novamente! Amanhã iniciaremos as aulas em um novo grupo no curso
"Engenharia Reversa" . Tradicionalmente, compartilhamos com você a tradução de material útil sobre o tema. Vamos lá!
É importante para alguns invasores que a exploração seja extremamente confiável. Sempre deve levar à execução do código quando iniciado em um sistema com uma plataforma e versão conhecidas do Flash. Para criá-lo, você pode usar erros especialmente de alta qualidade. Este artigo descreve o uso de um desses erros, bem como fatores que o tornam particularmente adequado para uma operação confiável.
BugCVE-2015-3077 - o problema da confusão de tipos nos
configuradores de filtro Adobe Flash
Button e
MovieClip , que permite confundir qualquer
tipo de filtro com qualquer outro. Eu relatei no início de dezembro de 2015 e em maio foi corrigido. O bug ocorre porque o cracker pode substituir o construtor usado para inicializar o objeto de filtro. O código de amostra que reproduz o problema é apresentado abaixo:

Esse código é um pouco confuso devido ao uso do operador [], necessário para a compilação no Flash CS. O código logicamente equivalente (que não é um fato que compila) é fornecido abaixo:

Esse código define o campo de filtro do objeto: Button ou MovieClip como BlurFilter, que é armazenado diretamente no Flash. O construtor BlurFilter é substituído pelo construtor ConvolutionFilter. Depois disso, o getter é chamado e um objeto ActionScript é criado para armazenar o BlurFilter original. No entanto, o construtor já foi substituído, portanto, ConvolutionFilter é chamado. Isso produz um objeto do tipo ConvolutionFilter, suportado pelo retorno do BlueFilter original.
Por fim, os campos ConvolutionFilter podem ser acessados (leitura e gravação) como se pertencessem ao BlurFilter. Da mesma forma para qualquer outro tipo de filtro. Isso abre uma ampla gama de manipulações úteis para explorar.
O diagrama abaixo mostra o local dos objetos originais na memória que podem ser confundidos usando essa vulnerabilidade no Linux de 64 bits.

Em dois casos, ponteiros são comparáveis a números inteiros e números de ponto flutuante que podem ser manipulados. Isso significa que os ponteiros podem ser lidos e escritos diretamente. Além disso, como os campos dos objetos são ordenados e classificados por tamanho, de acordo com a definição da classe, eles estão sempre em locais previsíveis, para que a escrita e a leitura não falhem. Essas propriedades são importantes para garantir a confiabilidade da exploração.
ExplorarComo explorar esse problema requer execução repetida de confusão de tipo, comecei criando uma função de utilitário para confusão de tipo,
FilterConfuse.confuse . Ele também arruma as coisas: retorna os construtores de filtro do ActionScript de volta ao seu estado normal para chamar repetidamente uma função vulnerável sem afetar o comportamento do ActionScript fora da própria função.
O primeiro passo foi ignorar o ASLR, definindo o endereço da tabela de funções virtuais (brevemente vtable). A maneira ideal para isso é confundir um objeto com uma vtable com um objeto no qual existe um elemento sobreposto à vtable que pode ser manipulado. Mas a tabela de todos os objetos de filtro tem o mesmo deslocamento. Em vez disso, usei o
objeto BitmapData no DisplacementMapFilter para determinar o endereço da tabela.
Para determinar o local na memória BitmapData do objeto, confundi DisplacementMapFilter com BevelFilter. Isso fez com que o ponteiro BitmapData armazenado no DisplacementMapFilter se alinhasse às propriedades de cor BevelFilter (
shadowColor ,
shadowAlpha ,
realceColor e
realceAlpha ). Essas propriedades são suportadas por dois números inteiros de 32 bits (mostrados como scolor e hcolor acima e abaixo), e as propriedades da cor acessam 24 bits de cada número inteiro, enquanto as propriedades alfa acessam os 8 bits superiores. Se você ler essas propriedades e combiná-las usando a aritmética de bits, poderá extrair o endereço BitmapData imediato do objeto.

Em seguida, você precisa ler a vtable na parte superior do objeto BitmapData. Para isso, usei a propriedade
matrix do objeto ConvolutionFilter. Ele é armazenado como um ponteiro para uma matriz de números de ponto flutuante, na qual a memória é alocada ao definir a propriedade, e uma matriz do ActionScript contendo esses números é retornada quando a propriedade é recebida. Ao definir o ponteiro da matriz para um objeto BitmapData, você pode ler o conteúdo desse objeto da memória como uma matriz de números de ponto flutuante.
Para definir o ponteiro, confundi o objeto ConvolutionFilter com o objeto DisplacementMapFilter (não o mesmo DisplacementMapFilter usado acima!) E defina o local do BitmapData do objeto acima na propriedade
mapPoint . A propriedade mapPoint é um ponto com coordenadas inteiras x e y (p_x e p_y na figura abaixo) que correspondem ao ponteiro da matriz no ConvolutionFilter, o que facilitou a configuração desse valor. Depois disso, tornou-se possível ler a vtable do objeto BitmapData usando uma matriz de matriz do objeto ConvolutionFilter (vale a pena notar que, para isso, o objeto tinha que ser confundido com DisplacementBitmapFilter e depois confundido com ConvolutionFilter).

Nesse ponto, fica mais difícil manter a confiabilidade da exploração devido ao uso de números de ponto flutuante. Os valores vtable_low e vtable_high são lidos na matriz ConvolutionFilter como números de ponto flutuante, pois esse é um tipo de matriz. Infelizmente, porém, nem todo valor de ponteiro válido é um número de ponto flutuante válido. Isso significa que a leitura do valor retornará NaN, ou pior, um valor numérico não totalmente correto.
Idealmente, para resolver esse problema, você precisa acessar vtable_low e vtable_high através de um getter, que os interpreta como números inteiros, mas isso não ocorre porque os elementos de filtro geralmente são flutuantes devido à sua funcionalidade.
Felizmente, a máquina virtual AS2 é preguiçosa o suficiente para interpretar números de ponto flutuante - apenas converte um valor em um flutuador quando uma operação é executada no ActionScript. As operações originais geralmente não requerem interpretação, exceto as especiais, como a aritmética. Isso significa que, ao copiar um número de ponto flutuante de uma matriz para vtable_low ou vtable_high, ele manterá seu valor na memória, mesmo se for inválido para float, enquanto a variável na qual foi copiada não for usada no ActionScript ou para executar operações aritméticas no nativo código. Assim, se o valor de uma variável for instantaneamente confundido com outro tipo que suporte toda a faixa de valores de 32 bits, por exemplo int, é garantido que seja o mesmo que o valor original na memória da matriz. Portanto, para evitar falta de confiabilidade na exploração, é importante realizar confusão de tipo antes de manipular flutuações no ActionScript.
Para fazer isso, escrevi uma classe de conversão,
FloatConverter , usando confusão de tipo em filtros para implementar funções de inteiro para flutuar e de flutuar para inteiro. Ele confunde a propriedade ColorMatrixFilter da
matriz (não a confunda com a propriedade da matriz ConvolutionFilter), que é um conjunto de flutuadores internos, com as propriedades de
cor e
alfa do GlowFilter que acessam diferentes bytes int.

Dessa forma, você pode implementar uma conversão confiável de um float para um int, mas, infelizmente, isso não funciona de maneira confiável na direção oposta. Para acessar a matriz de cores no ColorMatrix no ActionScript, a matriz inteira é copiada, mesmo se você acessar apenas a primeira delas. Ao copiar uma matriz, cada elemento é convertido em Number, que inclui uma chamada para ponteiros (por exemplo, chamando o valor de um objeto). Como a matriz de cores é a mais longa de toda a classe GlowFilter, fica acumulada quando confundida com o GlowFilter. Isso significa que pode ocorrer a conversão de valores desconhecidos desse heap, o que causará falha se eles se referirem a ponteiros inválidos ao converter para Number. Portanto, para int-to-float, implementei um conversor de flutuação usando outra confusão ConvolutionFilter e DisplacementMapFilter, que é uma conversão direta e não causa valores desconhecidos do heap.

Isso resolve o problema de falhas causadas pelo acesso a valores desconhecidos do heap, mas, infelizmente, há outro problema de confiabilidade associado aos flutuadores nessa exploração. Isso ocorre devido à implementação do getter de matriz ConvolutionFilter. Todos os valores numéricos no ActionScript 2 são do tipo Number, que é a união de um número inteiro e um ponteiro em um número duplo. A matriz ConvolutionFilter original é armazenada como uma matriz de números de ponto flutuante, mas é copiada para a matriz do ActionScript para preservar o acesso quando o getter de matriz é chamado e os valores são convertidos para dobrar no processo. Em seguida, ao chamar o conversor flutuante, eles são convertidos novamente em números de ponto flutuante.
A conversão de um número de ponto flutuante em um número de precisão dupla e vice-versa geralmente salva seu valor, mas não se o valor de flutuação for SNaN. De acordo com a especificação de ponto flutuante, existem dois tipos de NaNs: NaN silencioso (QNaN) e NaN de sinal (SNaN). Quando o QNaN aparece, nada acontece, mas o SNaN em alguns casos gera uma exceção de ponto flutuante. No x86, a conversão de double para float sempre resulta em QNaN (mesmo que o double tenha vindo do SNaN) para evitar exceções inesperadas.
Portanto, se os bits inferiores do ponteiro forem SNaN, eles serão convertidos para QNaN, o que significa que o primeiro bit (o primeiro bit de mantissa, bit 22) será definido quando não deve. Esse problema pode ser evitado ao ler o vtable - o terceiro byte do ponteiro que contém o primeiro bit pode ser lido sem alinhamento para confirmar o valor atual. Portanto, o código será lido sem alinhamentos (após a leitura da vtable novamente com o ponteiro de Bitmap aumentado em um) e ajustará o valor int se o float for SNaN.
Usando os conversores flutuantes descritos acima, o endereço vtable pode ser convertido em um número inteiro. Agora você precisa obter o código para executar usando este endereço. Uma maneira fácil de mover o ponteiro de instrução é substituir a tabela de objetos do objeto (ou um ponteiro para um objeto que possui uma tabela de objetos). Isso pode ser feito confundindo a matriz da matriz ConvolutionFilter e o BitmapData do ponteiro DisplacementFilter.
O fim da primeira parte. A segunda parte da tradução será publicada um pouco mais tarde, e agora aguardamos seus comentários e convidamos todos para o curso de
engenharia reversa OTUS.