Back
Featured image of post [PT-BR] O desafio de gerenciamento de memoria está resolvido?

[PT-BR] O desafio de gerenciamento de memoria está resolvido?

Rust e Go estão ai, sera o fim de bof's? O tipo de falha mais conhecida, ocorre basicamente quando podemos escrever mais bytes que um buffer pode comportar, assim, fazendo-o transbordar.

Buffer Overflow é passado?

Podemos olhar para linguagens mais novas como Go e Rust, que prometem um gerenciamento de memoria seguro e com otima performace, principalmente rust, que é uma linguagem emergente que visa prevenir bugs de gerenciamento de memória sem sacrificar muita eficiência e pensar:

Buffer overflow é passado.

Porem, sera que podem prometer tal proteção?

Esse post ira focar no estudo de CVE’s da linguagem Rust

Overview

Gerenciamento de memoria seguro

De acordo com o relatório estatístico do MITRE, os bugs de gerenciamento de memoria são enumerados entre os principais tipos de vulnerabilidades de software.

Em conceito, os bugs de gerenciamento de memoria são causados ​​por acesso arbitrário à memória, podemos enumerar os principais tipos a seguir:

Use-after-free

Apos liberar um ponteiro, o buffer apontado pelo ponteiro sera desalocado ou reciclado.
No entanto, o ponteiro ainda aponta para o endereço de memória desalocado, conhecido como “ponteiro pendente”.
Desreferênciar um ponteiro pendente pode causar problemas de Use-after-free.
Esses bugs são perigosos porque a memória liberada pode já ter sido realocada para outros fins, ou pode permitir que o usuário controle a lista linkada de blocos de memória livre.

Double free

Ao liberar um mesmo ponteiro duas vezes ira causar uma vulnerabilidade de Double Free.
Semelhante ao Use-after-free na heap, o double free deixa espaço para que um atacante manipule a lista linkada de blocos de memória livres.

Buffer overflow

O tipo de falha mais conhecida, ocorre basicamente quando podemos escrever mais bytes que um buffer pode comportar, assim, fazendo-o transbordar.


Agora que temos uma noção das principais vulnerabilidades relacionado ao gerenciamento de memória podemos continuar

Gerenciamento de memoria do Rust

Rust é uma linguagem que visa prevenir bugs de segurança, sem sacrificar o desempenho.
Ela aborda o objetivo introduzindo um conjunto de regras semânticas estritas no nível do compilador.
Desta forma, Rust pode ser mais eficiente do que outras linguagens de programação (como Go, ex) que dependem muito da verificação de memória em runtime e garbage collection

Basic Design:
Rust é por natureza uma linguagem híbrida, incluindo uma parte segura e uma parte insegura.
A parte segura garante que os comportamentos de todos os códigos e APIs sejam bem definidos, e os programas que usam APIs seguras não devem ter nenhum risco de problemas de segurança de memória.
A parte insegura não tem essa garantia e pode levar a comportamentos indefinidos, mas é necessário atender a algumas necessidades específicas, por exemplo, acesso eficiente à memória para desenvolvimento de software com requisitos de desempenho rigorosos.
Qualquer código que possa levar a comportamentos indetermindados deve ser declarado como inseguro, como desreferenciar ponteiros brutos, chamar FFIs (foreign function interfaces) e APIs não seguras.
Na verdade, muitas APIs seguras também empregam APIs inseguras internamente, e essas APIs podem ser seguras porque eliminaram todos os riscos de segurança da memória, por exemplo, por meio de código condicional.
Uma função que chama APIs inseguras pode ser declarada como segura ou insegura, o que depende principalmente da decisão do desenvolvedor. Rust não pode verificar se a declaração está correta ou não.
Portanto, declarar falsamente uma API como segura é perigoso e pode prejudicar a integridade da segurança do Rust.


Ao estudo

Agora que pude passar uma ideia melhor sobre a linguagem Rust e sobre gerencialmento de memoria, iremos fazer um estudo nos CVE’s catalogados até o momento

Irei abrangir apenas um CVE para não alongar demais esse post, mas dependendo do feedback posso refazer esse estudo com outros CVE’s

A vulnerabilidade

O CVE que sera estudado sera o CVE-2021-31162, uma vulnerabilidade de Double Free na stdlib do rust, a qual ocorre ao lidar com Vetores.
Escolhi tal CVE por ser uma falha relativamente recente, sendo relatada pouco menos de 5 messes atras(momento que estou escrevendo esse post), por ser, em essência, simples o entendimento e por se tratar de uma falha na stdlib do Rust, podendo assim falar um pouco sobre Rust internals

Oque é a falha?

Na stdlib do Rust antes da versão 1.52.0, um double free pode ocorrer na função Vec::from_iter ao liberar um elemento em panic.

Vamos explicar melhor…

SpecFromIter<T, I> para Vec<T> chama Vec::IntoIter::drop_remaining().
drop_remaining() chama drop_in_place() antes de substituir o ponteiro.
Como resultado, os elementos descartados não são invalidados e descartados novamente em panic.

// rust/library/alloc/src/vec/source_iter_marker.rs:71

// drop any remaining values at the tail of the source
src.drop_remaining();
// rust/library/alloc/src/vec/into_iter.rs:88

pub(super) fn drop_remaining(&mut self) {
    unsafe {
        ptr::drop_in_place(self.as_mut_slice());
    }
    self.ptr = self.end;
}

PoC:

#![forbid(unsafe_code)]

use std::iter::FromIterator;

#[derive(Debug)]
enum MyEnum {
    DroppedTwice(Box<i32>),
    PanicOnDrop,
}

impl Drop for MyEnum {
    fn drop(&mut self) {
        match self {
            MyEnum::DroppedTwice(_) => println!("Dropping!"),
            MyEnum::PanicOnDrop => {
                if !std::thread::panicking() {
                    panic!();
                }
            }
        }
    }
}

fn main() {
    let v = vec![MyEnum::DroppedTwice(Box::new(123)), MyEnum::PanicOnDrop];
    Vec::from_iter(v.into_iter().take(0));
}

Esse CVE pode parecer um pouco complexo em um primeiro momento, mas vamos olhar melhor.
Oque acontece, em outras palavras, é que ao ser liberado, o elemento não é invalidado, assim, sendo liberado novamente, criando a falha de Double Free
Bem simples, não?

Podemos perceber que gerar uma falha de gerencialmente de memoria é extremamente dificil, sendo necessario um esforço para gerar tal erro.
Quis trazer esse CVE também para demonstrar isso, uma falha desse tipo muito dificilmente poderia ser usada em um cenario real para exploração de um binario.

É Seguro ou não?

Podemos dizer que sim, o Rust consegue fazer um otimo trabalho para o gerencialmente de memoria, limitando com sucesso os riscos de problemas de segurança, e essa mágica toda está na solidez das APIs e seu compilador que ira veementemente te alertar sobre possiveis falhas e más praticas.
Como resultado, muitos bugs de segurança são problemas leves de solidez, como citado acima.

Referencias


Autor do post: R3tr0

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy