Aventuras em S3 Scraping — Parte 1

Nov 29 2022
Você não acreditaria nos arquivos estranhos que as pessoas compartilham nos baldes S3. Coisas que provavelmente não deveriam.

Você não acreditaria nos arquivos estranhos que as pessoas compartilham nos baldes S3. Coisas que provavelmente não deveriam. Coisas que definitivamente não deveriam.

AWS, S3

Apenas este mês eu vi arquivos como:

  • Chaves de licença para o software de uma empresa.
  • Documento do Excel de uma conferência relacionada à mídia contendo nomes, e-mails e números de telefone de funcionários da mídia local em 9 estados diferentes.
  • Documento em Powerpoint para uma divisão latino-americana de uma grande empresa de tecnologia marcado como “Proprietário e confidencial” (meu espanhol está um pouco enferrujado) que, acredito, listou custos/taxas/margens para os serviços propostos.
  • 45.000 a 50.000 documentos PDF contendo PII de clientes (nomes completos, endereços de e-mail, números de telefone e locais) de uma empresa que faliu nos últimos 2 anos. (Relatei o depósito ao AWS Support e discutirei o problema com mais detalhes SE eles agirem e protegerem os dados em um futuro próximo.)
  • Digitalização de alta resolução da carteira de motorista, cartão de seguro e registro do veículo de alguém de 2015 a 2016. (Em uma tentativa de decência, tentei entrar em contato com essa pessoa procurando-a no LinkedIn e no Google apenas para encontrar alguém com o mesmo nome, data de nascimento e endereço foi condenado em 2021 por um crime bastante grave e agora está cumprindo pena, portanto Não vou perder o sono por seus documentos de 7 anos estarem na Internet).

Presumi que nos últimos 10 anos as pessoas estariam melhor equipadas e teriam melhores ferramentas para bloquear seu conteúdo e compartilhar apenas o que deveria ser compartilhado. Eu estava errado. Então, por que se preocupar 10 anos depois em repetir essa atividade? Para diversão e conscientização. Também já faz alguns anos desde que escrevi uma quantidade substancial de código, então queria revisitar e aprimorar algumas habilidades.

Como você pode visualizar os arquivos de um bucket?

Primeiro, eu queria identificar os buckets S3 existentes que podem estar em uma região específica da AWS (us-east, us-west etc.). Se eu tivesse um nome válido para um bucket, poderia tentar em um navegador da web e veja se listou o conteúdo do balde. Isso não significa que os próprios arquivos possam ser visualizados, mas pelo menos deixe-me ver os caminhos de arquivo para investigação futura desses arquivos.

Por exemplo, se o nome do bucket “MyObviouslyFakeBucket” fosse visível publicamente e localizado na região AWS “US East 1”, você poderia ver o conteúdo em seu navegador da Web visitando https://myobviouslyfakebucket.s3.us-east-1. amazonaws.com/

Isso retornaria uma listagem semelhante à seguinte imagem parcialmente editada.

Arquivos contidos no bucket myobviouslyfakebucket S3.

No documento XML exibido como resultado, você pode ver as entradas de arquivo em cada tag “Conteúdo”. Para cada nó “Conteúdo” existe um
nó “Chave” que exibe o caminho do arquivo e o nome de cada arquivo. Portanto, para o arquivo “interessante-texto-arquivo.txt”, você poderia testar o acesso ao arquivo anexando o caminho no final do URL do bucket, como o seguinte:

https://myobviouslyfakebucket.s3.us-east-1.amazonaws.com/interesting-text-file.txt

Se o arquivo pudesse ser visualizado, ele abriria no navegador ou acionaria um download automático (dependendo do tipo de arquivo e do seu navegador). Se você não tivesse acesso, veria um resultado XML mostrando essencialmente uma mensagem de “Acesso negado”.

Apesar dos melhores esforços da AWS, ainda existem pessoas que definem o conteúdo para ser visualizado publicamente quando não deveriam. Não trabalho com S3 há alguns anos, então posso estar perdendo alguma coisa, mas parece haver MÚLTIPLAS etapas envolvidas antes que seu balde possa ser facilmente enumerado e visualizado em um navegador da web.

Ao criar um novo bucket, a opção "Bloquear todo o acesso público" é marcada por padrão. Você deve desmarcá-lo e, em seguida, selecionar a caixa de seleção "Reconheço" mais abaixo. Veja abaixo.

Configurações de privacidade para o novo bucket S3.

Mesmo com meu bucket configurado para não 'bloquear todo o acesso público' quando adicionei dois arquivos de texto de amostra, ainda não consegui listar o conteúdo do bucket em meu navegador da Web até adicionar uma política de bucket JSON explicitamente concedendo acesso público.

{
    "Version": "2012-10-17",
    "Id": "Policy1669653712601",
    "Statement": [
        {
            "Sid": "Stmt1669653708988",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::myobviouslyfakebucket"
        }
    ]
}

Abordagem global

Então, como você lista o conteúdo do balde sem adivinhar aleatoriamente? Minhas duas principais opções para conseguir isso além de um único nome de balde de minha própria invenção eram usar um arquivo de dicionário emparelhado com o AWS SDK ou HTTP GET simples.

Eu escolhi escrever código Java usando HTTP GET's simples para uma primeira versão. Isso evitou a necessidade de aprender o AWS Java SDK v2. A última vez que usei o AWS Java SDK foi em um v1 e mudou o suficiente para que eu não quisesse que uma curva de aprendizado atrasasse meu progresso. Também pude evitar a necessidade de configurar as credenciais da AWS para usar o SDK e quaisquer erros ou esquisitices específicas do SDK que surgiram durante o teste. Mantenha simples.

Comecei com um arquivo de dicionário existente que tinha em um projeto pessoal anterior. Este era um arquivo de texto plano com uma palavra por linha. A certa altura, dividi-o em 8 ou 10 arquivos separados, com cada arquivo contendo as entradas de 1 a 3 letras, dependendo do número de entradas. Isso me permitiu processar um número menor de entradas por vez com mais facilidade. Você pode pesquisar online por um arquivo de dicionário, pois há muitos disponíveis.

Esbocei as etapas que precisava programar em 2 post-its. Eles foram os seguintes:

  • Analise o arquivo de dicionário para recuperar cada entrada de palavra.
  • Para cada palavra na lista, construa a URL a ser verificada usando a palavra como um nome de bloco e a região da AWS (codificada por enquanto para “US East 1”).
  • Tente conectar-se à URL para executar uma operação GET.
  • Recupere o código de resposta enviado do servidor.
  • Se o código de resposta indicou sucesso (o depósito existe), adicione a palavra a uma estrutura de dados.
  • Salve as palavras bem-sucedidas na estrutura de dados em um arquivo de texto plano para investigação posterior.

Analisar o arquivo de dicionário

private void populateList(List<String> words, String dictionaryFile) {

   BufferedReader br = null;
   try {
      br = new BufferedReader(new FileReader(new File(dictionaryFile)));
      String line;
      while ((line = br.readLine()) != null) {
         words.add(line);
      }

   } catch (Exception e) {
      e.printStackTrace();
   } finally {
      try {
         if (br != null) {
            br.close();
         }
      } catch (Exception e) { }
   }
}

Construir o URL

String currentRegion = "us-east-1";
int wordSize = words.size();

for (int i = 0; i < wordSize; i++) {

   String bucketName = words.get(i);
   
   String sUrl = "https://" + bucketName + ".s3." + currentRegion + ".amazonaws.com";
   URL url = new URL(sUrl);

   // do something with the URL
}

Executar uma operação GET

String sUrl = "https://" + bucketName + ".s3." + currentRegion + ".amazonaws.com/";
URL url = new URL(sUrl);

HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.connect();

Recupere o código de resposta e armazene-o

int respCode = connection.getResponseCode();

if (respCode == 200) {
   code200s.add(bucketName + "," + currentRegion);
}

Armazene os nomes de bucket válidos

private void writeCode200s(List<String> validBuckets, String parentDirectory) {

   if(validBuckets == null || validBuckets.isEmpty()) {
      return;
   }
        
   BufferedWriter bw = null;
   
   try {
      File parentDirectory = new File(parentDirectory);
      if (!parentDirectory.exists()) {
         parentDirectory.mkdirs();
      }

      FileWriter writer = new FileWriter(new File(parentDirectory, "valid_buckets_" + System.currentTimeMillis()+ ".txt"));
      bw = new BufferedWriter(writer);
      
      for (int i = 0; i < validBuckets.size(); i++) {
         String bucketName = validBuckets.get(i);

         bw.write(bucketName);
         bw.newLine();
      }

   } catch (Exception e) {
      e.printStackTrace();
   } finally {
      try {
         if (bw != null) {
            bw.close();
         }
      } catch (Exception e) { }
   }
}

É isso. Simples e direto. Pode não ser a solução mais elegante, mas funcionou e serviu de base para que eu pudesse expandi-la e melhorá-la. Identifiquei milhares de baldes S3 válidos apenas raspando várias letras do alfabeto. Nas minhas primeiras tentativas, encontrei catálogos de arquivos MP3, milhões de imagens, incontáveis ​​arquivos de log e muito mais.

Desde então, excluí meu balde de teste “myobviouslyfakebucket”, portanto, sinta-se à vontade para reivindicar o nome, se desejar. Nas próximas partes desta série de artigos, destacarei etapas adicionais para aprimorar essa solução, como:

  • Manipulando e armazenando códigos de resposta para baldes além de 200(OK) e o que eles significam e o que você pode fazer com essas informações.
  • Usando a lista de nomes de bucket válidos para ver se você pode enumerar a lista de arquivos nesse bucket.
  • Analisar a lista de arquivos de bucket para capturar o caminho e o nome de arquivos individuais.
  • Filtre arquivos por extensão de arquivo para ignorar ruídos indesejados.
  • Resultados de arquivo de paginação para buckets S3 com mais de 1.000 arquivos.
  • Verificar listas de arquivos para ver se arquivos individuais podem ser baixados.