Como um estouro negativo de buffer pode levar à execução remota de código em 64 bits?

Tenho o seguinte pseudocódigo resumido em C89 de uma biblioteca de servidor ssh que fornece acesso apenas a coisas como git ‑ shell (/bin/bash é substituído pelo programa a ser executado, portanto, não é possível fazer outra coisa) :

struct _raw_uncapped_ssh_string { // no limit on the size of the string; uint32_t len; char non_null_terminated_string[]; // by protocol design it have a minimum length of 1 }; typedef struct _raw_uncapped_ssh_string raw_ssh_string; union buffer { void * uncapped_zlib_decompressed_network_data; // yes, the size is uncapped, so it’s possible to put 4Gb of // data in it that would be copied later into memory. zlib // allow easily to turn some Mb in Gb of data, but it’s not // the point of the question. raw_ssh_string st; }; get_command (compressed_network_data) { size_t len; char * command; buffer string=uncompress_to_buffer(compressed_network_data); len=ntohl(string.st.len)+1; command=malloc(len+1); command[len]=0; // here’s the point, both the string length and content as // well it’s supplied size is controlled by the attacker. memcpy(command,string.st.data,len); return command; } 

Aqui está como o comando é executado posteriormente (a string command permanece inalterada após get_command()) .

const char *args[]={"/bin/bash",command,NULL}; // /bin/bash isn’t the shell, it has been replaced by git‑shell. // redirect the program output to the network. dup2(stdin, 0); dup2(stdout,1); dup2(stdout,2); close(stdin); close(stdout); //if this return execution failed and print an error message return execv(args[0],(char * const *)args); // I don’t know which is the system, so I can’t know about the libc behaviour. 

Não posso fazer memcpy(command,string.st.data,0) porque o terceiro membro de memcpy tem um tamanho mínimo de 1 e, no meu contexto, size_t usa um número inteiro de 64 bits, não posso realizar um estouro de buffer, pois há len.

Tudo que posso fazer é definir len para um valor maior do que o alocado para string.st.data. Este é um estouro de buffer que me permite ler a memória não alocada.
Eu posso ler a memória do servidor, no entanto, não consigo ver quais dados confidenciais um servidor SSH público pode conter (no meu caso, a lista de usuários que pode executar ssh é público) .

O mesmo acontece com o estouro de buffer em memcpy permitir remoto execução de código?

Comentários

  • Se o código realmente funciona command[len] = 0 então isso é um estouro de buffer, já que o índice máximo para um buffer de comprimento len é len-1. Alternativamente, se o código real faz um malloc(len+1) em vez de um malloc(len), então você pode fazer um estouro de buffer massivo definindo o len valor para 0xFFFFFFFF.
  • @ThomasPornin: Não, é um erro da minha parte, na verdade ele chama várias funções para fazer esta. Suponha que o restante seja verdadeiro.
  • @ThomasPornin: Estou falando de 64 bits, já que len é size_t, ele usa um inteiro de 64 bits. Portanto, não há como ultrapassar o número inteiro.
  • @ThomasPornin Eles alocam len+1, portanto, a configuração para 0 deve ser válida.
  • @ RoraΖ: Ele estava falando sobre definir string.st.len como -1.

Resposta

Em geral, não, um estouro negativo de buffer não pode ser usado para execução remota de código. Como os dados controlados pelo invasor nunca deixam o espaço alocado para eles, eles nunca têm a capacidade de assumir o fluxo de execução do programa.

Um estouro negativo de buffer tem potencial para outros tipos de ataques, como divulgação de informações (se o programa estiver contando com o conteúdo original do buffer sendo apagado pelos novos dados).

Comentários

  • Ou como eu vi recentemente no git, ser transformado mais tarde em um estouro de buffer.

Resposta

Quando eu escrevi esta resposta originalmente , parece que encobri alguns mal-entendidos que você mantém. Esses mal-entendidos provavelmente o impediriam de entender minha resposta. Para ser claro, vou deixar este texto grande para chamar a atenção, não para ser desrespeitoso:

Muitos dos termos que você usa não significam o que você parece pensar que significam.

Por exemplo:

  • “o terceiro membro de memcpy não existe porque memcpy é uma função, não uma struct ou union.
  • “Não posso realizar um estouro de buffer, pois há len e não posso “ficar sem gasolina, porque há gasolina. Estou implorando o pergunta? Parece uma analogia adequada para mim, especialmente porque, teoricamente, seu código poderia invocar um estouro de buffer.
  • “Tudo o que posso fazer é definir len para um valor maior do que o alocado para string.st.data. Este é um buffer underflow … “ Não, essa não é a definição de buffer underflow. Um buffer underflow ocorre quando você acessa fora dos limites uma matriz usando um índice negativo, ou um índice que causaria Ocorrerá quebra aritmética de ponteiros. Esta última pode ser possível para algumas configurações (mesmo em “sistemas de 64 bits”, o que quer que isso signifique), mas duvido que seja isso o que você quis dizer quando estava escrevendo estas palavras, porque você as seguiu com:
  • “… permitindo-me ler memória não alocada.” Acho que talvez você quisesse dizer “memória não inicializada”. Para ser claro, acho que você quis dizer que “ve alocou um excesso e deixou o excesso não inicializado e talvez você queira fazer algo a respeito (consulte calloc ou memset).

Vamos considerar char *fubar = NULL; por um momento … esse tipo de ponteiro geralmente tem um zero valor.A desreferenciação é considerada uma desreferenciação de ponteiro nulo , mas se escrevermos algo como 0["hello"], obtemos o mesmo que "hello"[0] (isto é "h"). Assim, uma desreferência de ponteiro nulo pode ser usada em ataques quando o invasor controla a expressão entre colchetes (como eles fazem em sua situação).

Voltando ao fubar coisa; digamos que memset(&fubar, UCHAR_MAX, sizeof fubar);, agora fubar seja todo de 1 bit, o que significa que pode ser um endereço esse é o maior endereço que nosso sistema poderia (teoricamente) acomodar. E se acessarmos fubar[1]? Como você pode acessar o elemento após o maior endereço? endereço e depois voltar? Tecnicamente, tudo isso é comportamento indefinido, mas se eu fosse nomear isso na arquitetura comum, seria:

  1. Estouro aritmético de um ponteiro, levando a …
  2. Desreferência de ponteiro nulo e / ou potencialmente um underflow de buffer .

um buffer underflow em memcpy permite a execução remota de código?

Um buffer underflow pode permitir que um invasor sobrescreva ponteiros de função localizados nas regiões da memória antes da matriz em questão. Se esses ponteiros de função forem feitos para apontar para shellcode, esse shellcode pode ser executados quando forem chamados mais tarde.

Neste trecho um tanto obscuro de código, parece haver “um risco de estouro de buffer quando int tem um maior domínio do que uint32_t, já que ntohl(string.st.len)+1 faria com que o valor uint32_t fosse convertido em int tipo. Considere, por exemplo, se INT_MIN é -4294967296 (que é um a menos que 0 - UINT32_MAX) e INT_MAX é 4294967295 … seria essencialmente um int de 33 bits com padding para preencher até a largura de um byte; incomum, mas possível. Nessa circunstância, a expressão ntohl(string.st.len)+1 não é uint32_t no tipo; é “s int no tipo, e em vez de voltar a 0 quando ocorre estouro de inteiro sem sinal , provavelmente volta a -4294967296 quando ocorre estouro de número inteiro assinado .

Se você “está procurando uma garantia contra estouro insuficiente de buffer , use o U sufixo literal inteiro (ou seja, ntohl(string.st.len)+1U) Então, nessa situação, você “acabará com a expressão sendo uint32_t ou unsigned int (dependendo de qual tipo tem o maior domínio).

Se você considerar que ntohl(string.st.len) pode retornar um a menos do que o valor máximo para esse tipo não assinado (seja ele qual for), então len=ntohl(string.st.len)+1 resultará no valor máximo, malloc(len+1) causará empacotamento não assinado , então você” acabará invocando malloc(0) e então command[len]=0 escreverá bem e verdadeiramente além do final do array. Então, é claro, você também terá um problema com memcpy(command,string.st.data,len); (isso é um estouro de buffer).

Estouro de buffer e transbordamentos não são o único risco. Se você não “t verificar o valor de retorno de malloc e malloc retorna NULL então um ponteiro nulo desreferenciamento pode ser usado para causar a execução arbitrária do código. Isso implica que você deve ter um método para comunicar malloc falhas de volta para o chamador. Isso também implica que você pode usar o mesmo método para verificar e comunicar o problema de quebra de volta para o chamador.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *