Injeção de código malicioso no Facebook

Neste blog, apresentarei a vocês um estudo que realizei junto com Dikla Barda. Neste estudo, fomos capazes de fazer upload de um arquivo malicioso para o CDN do Facebook manipulando o navegador e criando um Fuzzer simples que nos ajudou a contornar o Analisador de Imagens do Facebook, que excluía o código malicioso da imagem..

Neste estudo, apresentamos um método interessante pelo qual um arquivo malicioso pode ser inserido em uma imagem e armazenado no CDN do Facebook e, assim, basicamente contornar todos os mecanismos de assinatura, uma vez que o Facebook é considerado um domínio confiável, não havera nenhum bloqueio de links provenientes dele-Facebook.
Para tirar proveito da falha de segurança, tivemos que localizar e contornar uma série de fatores:

  • Falha de segurança do lado do servidor
  • Realizar manipulação e pesquisa sobre o próprio navegador
  • Contorne o intérprete de imagem que comprime as imagens novamente e reduz a imagem

Vídeo de demonstração:

https://youtube.com/watch?v=sGlrLFo43pY

Part 1

Quando você carrega uma imagem para o Facebook, a imagem é carregada para o CDN para desempenho e tempos de resposta mais rápidos, uma vez que você carrega uma imagem com uma resolução acima de 962×541, a imagem é carregada para o CDN sem parâmetros que identificam a imagem com qualquer conta. assim que é:

“ https://scontent-lhr3-1.xx.fbcdn.net/t31.0-8/14102894_1137188709676282_2198558191558447569_o.jpg`

Se a resolução for menor do que isso obteremos um link com parâmetros que não podem ser modificados:

“ https://scontent-lhr3-1.xx.fbcdn.net/t31.0-8/14102894_1137188709676282_2198558191558447569_o.jpg?_nc_cat=0&oh=2d6cfd727aa2e63c31b3a10e34872ffa&oe=5BA12209`

Nosso primeiro passo foi fazer upload de diferentes fotos no Facebook em diferentes resoluções e examiná-las. Assim que encontramos a resolução apropriada, que é acima da resolução de 962×541, passamos para a próxima etapa, a de inserção de código na imagem. Apenas para fins de demonstração:

    WshShell=new ActiveXObject("WScript.Shell");
    WshShell.Run('c:/windows/system32/calc.exe',1,false);

O resultado fica assim:

Carregamos o arquivo no Facebook e baixamos de volta depois que passou pelo processo de conversão de imagem do Facebook pelo leitor de fotos (Image Parser).

Esperávamos que pelo menos parte do código inserido permanecesse, mas o resultado foi um tanto problemático: Todo o código que inserimos na imagem foi substituído pelo intérprete de imagem que compactou a imagem para economizar espaço.

Como contornar algo assim?

Primeiro tentamos pegar o arquivo compactado que passa pelo interpetre e inserir o código nele, porque um arquivo que já foi compactado usando o lado do servidor do Facebook não será compactado novamente na mesma extensão e mais caracteres permanecerão no arquivo.

Depois de modificar o arquivo compactado, pudemos ver pelo menos uma pequena parte do código que inserimos.

Como progredir a partir daqui?

Depois de muito pensar, ocorreu-nos escrever um fuzzer que usa uma API gráfica que executa as seguintes etapas:

  1. Define um local no arquivo e insere nosso código nele.

Abaixo está a principal função do código escrito:

                                                ``
def payload_check():
    # Send Picture to Facebook
    r = requests.post("https://graph.facebook.com/v2.7/me/photos?access_FBToken={}&caption={}&url={}".format(FBToken,message,URLToImage))

    # Get the Picture Back
    r = requests.get('https://graph.facebook.com/v2.7/me/posts?access_FBToken={}&debug=all&fields=message,object_id,type&format=json&limit=1&method=get&pretty=0&suppress_http_code=1&with="{}"'.format(FBToken,message))
    image_code = json.loads(r.text)["data"][0]["object_id"]

    r = requests.get('https://graph.facebook.com/v2.7/{}/picture?access_FBToken={}&debug=all&format=json&method=get&pretty=0&redirect=false&suppress_http_code=1'.format(image_code,FBToken))
    # This URL will not contain the Payload, We need the full size one 🙂
    # print json.loads(r.text)["data"]["url"]

    # This url may contain the Payload!
    good_url = str(json.loads(r.text)["data"]["url"]).replace("s720x720/","")
    download_file(good_url)

    # Check if we see some part of the payload to enhance the positions
    with open("image.jpg","r") as f:
        if half_payload in f.read():
            print "[+] Found! Values:"
            print "Counter1: {}, Counter2: {}, Cursor: {}, half_payload: {}, Index: {}".format(counter1, counter2, cursor, half_payload, index)
            return True

        else:
            return False` 
                                            

Observe que a API Graph retorna uma imagem no formato s720x720, a imagem é menor, o que significa que estão faltando caracteres! Para acessar a imagem original, devemos excluir o s720x720 do URL e, em seguida, acessar a imagem.

Após a primeira execução, conseguimos encontrar um local que nos permite fazer upload de quase todo o código em sua totalidade, enquanto na segunda execução nosso Fuzzer foi capaz de encontrar um local exato que nos permite fazer upload de todo o código! No qual ficou assim:

 Os caracteres ‘A’ O que você vê é, na verdade, o padding que o fuzzer executou.

Então, conseguimos fazer o upload do código para uma imagem .jpg próxima ao servidor, mas isso é apenas o começo, agora temos que fazer o arquivo finalizar com a extensão .hta para que o arquivo seja executado, caso contrário, o que faríamos com ele?

Se examinarmos a resposta do servidor, parece que seu content-type (tipo de conteúdo) é image/jpeg Então, se baixarmos o arquivo, obteremos um arquivo de imagem, então precisamos encontrar uma maneira de converter o content-type para outra coisa, como application/octet-stram


Depois de várias tentativas, descobrimos que é possível adicionar um ponto à extensão da url para obter o content-type desejado:

Uma vez que tenhamos conseguido controlar o tipo de conteúdo, temos que fazer a imagem ser baixada pela URL, para fazer isso vamos adicionar o parâmetro dl=1 O que indica fazer download, o que será assim:

Voltamos para burp suite para analizar o pacote, você pode ver que o o parâmetro dl=1 Adiciona o título à URL
“ Content-Disposition: attachment`

O que indica o download do arquivo

Como o Facebook não anexou o nome do arquivo ao content-dispostion, o navegador terá que decidir qual será o nome do arquivo que pretende baixar do site..

Part 2

Para entender em que base o navegador conclui qual será o nome do arquivo, vamos olhar o código-fonte do projeto chromium:

“ https://cs.chromium.org/chromium/src/content/renderer/loader/web_url_loader_impl.cc`

Começaremos com a função PopulateURLResponse, que sua função é abordar as informações provenientes do servidor.


Esta função gerencia todas as informações recebidas e responde aos títulos de acordo, ela também determina o nome do arquivo se tivermos content-disposition nos títulos da resposta do servidor.

Se houver realmente um content-disposition, esta função chama a função GetSuggestedFileName :


Na função headers->EnumerateHeader Verifiqqa se existe um nome de arquivo no content-disposition e se não existe o valor que é a segunda variável da função GetSuggestedFilename estará realmente vazio.
Se olharmos para esta função, parece que ela apenas envolve o GetSuggestedFilenameImpl:
 Esta função verifica se content_disposition está vazio e, se estiver, entra na função GetFileNameFromURL

Esta função verifica se a chamada é realmente uma chamada normal e não data:// ou about:// Em seguida, chama a-ExtractFileName

Que só envolve DoExtractFileName
 
A função DoExtractFileName é assim: 
Ele tenta localizar o nome do arquivo do final da URL até a última barra, mas se tivermos um ponto e vírgula “;” Portanto, o nome do arquivo é determinado da última barra até a vírgula dessa forma:
“ /filename.extension;`

Part 3

Com base nessas informações, podemos basicamente controlar o nome do arquivo que queremos selecionar pelo navegador. por exemplo:

O URL ficará assim:
“ https://scontent-lhr3-1.xx.fbcdn.net/t31.0-8/14102894_1137188709676282_2198558191558447569_o.jpg/facebook_password.exe;.?dl=1`

É claro que um arquivo exe não será executado porque o cabeçalho do arquivo não corresponde ao cabeçalho PE, mas o que podemos fazer é alterar o arquivo para uma extensão .hta que executará o código javascript que inserimos no arquivo anteriormente na primeira parte.

que será assim:
 Só nos resta copiar e enviar o link, e quem clicar no link executará nosso código armazenado nos servidores do CDN. Facebook.

Baixar o arquivo e clicar nele irá ativar a calculadora, pois foi isso que fizemos em nosso código:

Part 4

Ao esconder o link no messenger, tentamos verificar se é possível ocultar o link no messenger de alguma forma e chegamos ao seguinte truque:

Fazemos upload da imagem que contém o código JavaScript que mostrei antes, mas desta vez fazemos upload para-Messenger:
 Modificaremos o tipo de conteúdo e extensão de arquivo, para o tipo de conteúdo escolhido image/svg+xml E para o nome do arquivo é selecionado novamente .hta então:

Vamos enviar o arquivo, e parece que nem precisamos de um link, podemos enviar uma foto regular em-Messenger:

Clicando na imagem vai baixar o arquivo e renomeá-lo para .hta pois é o sufixo que escolhemos para o arquivo e desta vez ele aparece em-content-disposition:

Clicando na imagem baixa o arquivo My_Facebook_password.hta

Clicar no arquivo ativará o código e, assim, executará calculadora:

Compartilhe este post


Sobre nossa equipe

A equipe do site está envolvida na segurança da informação há anos e trabalha em empresas-chave no campo cibernético, tentamos participar de desafios e aprender coisas n

Outros artigos que você pode ter interesse: