DLLs e seus riscos de Segurança

DLLs e seus riscos de Segurança

DLL (Dynamic Link Libraries) é uma biblioteca compartilhada do sistema operacional windows que tem como função facilitar e otimizar a execução de diversas funções, nas quais podem ser utilizadas por diversos softwares ao mesmo tempo quando utilizada de forma dinâmica.

As DLLs trazem funções de API para o windows, facilitando o desenvolvimento de software ao permitir que aplicações reutilizem código pré-existente e interajam com o sistema operacional, tendo em vista que a aplicação pode utilizar de bibliotecas nativas ou de terceiros para realizar tarefas especificas dentro do sistema, como a criação de serviços do Windows, gerenciamento de processos e threads, manipulação de arquivos e comunicação de rede.

Tendo em vista que este não é um recurso by-yourself, a execução desses binários dependem da execução de um outro software, dessa forma, há alguns tipos de chamada:

  • Implicit Linking: O binário carrega as funções em memória, dessa forma, cria uma tabela de importação (IAT - Import Address Table) no qual já é listada antes da execução, logo, o sistema controla quando a DLL é importada, não necessitando de uma função dentro do código.
  • StaticLink: O código da biblioteca é incorporado diretamente no binário durante a compilação, eliminando a necessidade de um arquivo DLL separado. Dessa forma, o programa não depende de bibliotecas externas para rodar. Como consequência, o binário final pode ser maior e qualquer falha na biblioteca exigirá a recompilação e redistribuição do software.
  • RuntimeLink: é um processo onde o ator importa a DLL durante a execução do processo, sem estar declarada, nesse caso, em análises estáticas, nem todas as funções importadas irão aparecer, tendo em vista que o ator irá fazer o import dentro de alguma função do binário. Métodos que definem o Runtime:
    • LoadLibraty - Processo de execução dinâmico, no qual o software realiza a chamada da DLL durante a execução do processo. Essa função faz o load da dll durante a execução e retorna um handle.
    • GetProcAddress - Processo de execução dinâmico, no qual o software realiza a chamada da DLL durante a execução do processo. Essa função pega o endereço da função da DLL após ela ter sido carregada pelo LoadLibrary e retorna um ponteiro para uma função ou variável importada da DLL para o processo.

A estrutura de uma DLL é dividida em dois módulos:

  • Funções externas: funções que serão importadas por outros softwares.
  • Funções internas: funções que garantem o funcionamento da DLL e que não podem ser importadas por outros processos.

Esses módulos e funções são definidos por 3 arquivos que constituem essa formação: o header, o código da DLL e o arquivo de definição de módulos que serão exportados.

Header: Arquivo com a extensão “.h” no qual tem a declaração das funções que podem ser exportadas:


// Definição de macro para exportar ou importar funções, dependendo de quem estiver compilando
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif

// Funções exportadas (externas) que poderão ser usadas por outros programas
extern "C" {
    MYDLL_API int Add(int a, int b);   // Soma dois números
    MYDLL_API int Subtract(int a, int b); // Subtrai dois números
}

Código da DLL: contém o código de módulos internos, externos e a função DLLmain, no qual é responsável pelos processos de call da DLL, o arquivo possui a extensão “.cpp”.

// MyDll.cpp

#include "MyDll.h"
#include <windows.h>

// Módulo interno: Funções não exportadas (privadas)
namespace InternalModule {
    // Função interna privada para multiplicar
    int Multiply(int a, int b) {
        return a * b;
    }

    // Função interna privada para dividir
    int Divide(int a, int b) {
        if (b != 0) {
            return a / b;
        } else {
            return 0; // Evitar divisão por zero
        }
    }
}

// Módulo externo: Funções exportadas (públicas)
extern "C" {

    // Função para somar dois números, exportada
    MYDLL_API int Add(int a, int b) {
        return a + b;
    }

    // Função para subtrair dois números, exportada
    MYDLL_API int Subtract(int a, int b) {
        return a - b;
    }

}

// Função especial DllMain chamada pelo sistema quando a DLL é carregada ou descarregada
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    switch (ul_reason_for_call) {
    case DLL_PROCESS_ATTACH:
        // Código quando o processo é anexado (DLL carregada)
        break;
    case DLL_THREAD_ATTACH:
        // Código quando uma nova thread é criada
        break;
    case DLL_THREAD_DETACH:
        // Código quando uma thread é finalizada
        break;
    case DLL_PROCESS_DETACH:
        // Código quando o processo é desligado (DLL descarregada)
        break;
    }
    return TRUE; // Indica que a DLL foi carregada com sucesso
}

Arquivo de definição: código de extensão .def no qual realiza a definição de quais funções vão estar acessíveis a outros processos, realizando o controle, sendo assim, mesmo que haja outras funções declaradas nos arquivos .h e .cpp, caso não esteja nas definições de export, não poderão ser utilizadas por outros binários.

; MyDll.def

LIBRARY "MyDll"          ; Nome da DLL
EXPORTS
    Add                  ; Exporta a função Add
    Subtract             ; Exporta a função Subtract

Conceito e funcionamento da IAT (Import Address Table):

Ao realizar a compilação de programas utilizando Implicit Linking, o linker realiza um “de-para” das funções utilizadas no programa e as DLLs das quais a função está agregada. Após essa compilação, o software forma a IAT, no qual possui todas as DLLs e as funções que serão utilizadas pelo código.

Ao executar o software, o Windows carrega a DLL na memória (caso ainda não esteja carregada por outro processo) e mapeia suas funções em endereços de memória. Após esse mapeamento, o Windows Loader preenche a IAT com os endereços reais das funções importadas. Dessa forma, quando o programa chama uma função da DLL, ele acessa o ponteiro armazenado na IAT para executar a função necessária.

Resumindo, a IAT é uma tabela com duas partes principais, o endereço de memória (Definido pelo loader do sistema operacional (processo de execução da DLL em memória) e o nome da função necessária pelo processo (no qual é definido durante a compilação).

Ataques no DVWA
  • Problemas que podem ser identificados e riscos de segurança:
    • DLL Hijacking: O ator malicioso realiza um processo onde altera a ordem de execução de dll de um processo vulnerável ao ataque, tornando o processo malicioso Stealth
      • Árvore de procura de uma DLL caso o path completo dela não seja declarado no código da aplicação (passando somente o nome, não o caminho completo):
        1. O diretório do qual a aplicação foi lançada
        2. O diretório de sistema (C:\Windows\System32)
        3. A versão 16-bit do diretório de sistema
        4. O diretório do Windows (C:\Windows)
        5. O diretório atual
      • Dll Search Mode off:
        1. O diretório do qual a aplicação foi lançada
        2. O diretório atual
        3. O diretório de sistema (C:\Windows\System32)
        4. A versão 16-bit do diretório de sistema
        5. O diretório do Windows (C:\Windows)
    • DLL injection: utiliza funções para fazer com que um processo carregue uma DLL maliciosa.

Por fim, agradecemos a leitura e esperamos que este post tenha te ajudado de alguma maneira! Caso tenha alguma dúvida, entre em contato conosco pelo Telegram , Facebook ou Instagram ! Veja mais posts no IronLinux !

Posts relacionados

Do IIS User ao SYSTEM: Escalação de Privilégio Windows

Do IIS User ao SYSTEM: Escalação de Privilégio Windows

Neste post será demonstrado como escalar privilégios no IIS com Juicy Potato (MS16-075): Um exemplo de como uma má configuração pode levar ao comprometimento total de um Webserver.

Ler post completo
Hack The Box – Optimum Writeup (Usando Metasploit)

Hack The Box – Optimum Writeup (Usando Metasploit)

Hoje veremos o writeup completo da máquina Optimum do Hack The Box!

Ler post completo
Utilizando clientes RDP no Linux

Utilizando clientes RDP no Linux

Já precisou se conectar em um servidor Windows via Remote Desktop Protocol (RDP) através do Linux e teve dificuldades?

Ler post completo