Em janeiro de 2015 se ouviu falar de uma falha de segurança na glibc chamada GHOST. Esta falha poderia ser explorada por um programa malicioso utilizando as funções gethostbyname()
e gethostbyname2()
da glibc. O ataque acontece por um buffer overflow dentro destas funções quando um endereço inválido é passando como parâmetro. Ao explorar esta falha, o atacante consegue executar códigos arbitrários com as permissões do usuários executando o processo.
Este problema existia na glibc desde 2000, e foi corrigido em 2013, mas como a falha não havia sido detectada, não foi criada outra versão estável da glibc após a correção. Quando a falha foi exposta, grandes distribuições como Red Hat e Debian atualizaram a sua versão da glibc. Segundo esta publicação da Cisco, a falha não é tão assutadora quanto parece, pois o software a ser explorado precisa aceitar hostnames como entrada e ainda assim existem algumas situações em que o bug pode ser explorado: o primeiro carácter precisa ser um dígito, o último carácter não pode ser um ponto e o hostname precisa ser construído somente por dígitos e pontos (um endereço IPv4 comum).
As funções gethostbyname
e gethostbyname2
foram criadas nos anos 80 e estão depreciadas a algum tempo, podendo até ser noticiadas pela leitura do man. A alternativa é utilizar a função getaddrinfo
, que além de ser a função substituta de gethostbyname*()
, trabalha de forma transparente com IPv6.
Veja abaixo um exemplo da utilização do gethostbyname2:
Segue a saída do programa: [marcos@xfiles ghost]$ ./gethostbyname facebook.com Host: facebook.com IPv4: 173.252.120.68 Host: facebook.com IPv6: 2a03:2880:2130:cf24:face:b00c:0:25de
A chamada gethosbyname2
retorna um ponteiro para struct hostent. Esta struct contém um campo chamado h_addr_list
, onde estão todos os endereços IP relacionados ao hostname informado. O campo h_addrtype
informa se o IP retornado é IPv4 (AF_INET
) ou IPv6 (AF_INET6
). Após verificar se o retorno é um IPv4 ou IPv6 é necessário decodificar o IP, que está em formato binário. A chamada inet_ntop
recebe como parâmetro uma struct in_addr
ou struct in6_addr
e retorna o IP em forma de string. Neste exemplo foi utilizado um ponteiro void
para armazenar as structs afim de simplificar o código, já que o segundo parâmetro da função inet_ntop
espera um void *
.
A chamada gethostbyname2
foi utilizada pois gethostbyname
não retornou nenhum endereço IPv6, embora as man pages digam que ela retorna.
A função getaddrinfo
trabalha da mesma forma que a gethostbyname*
, mas esta é mais transparente em relação a filtragem dos hosts retornados. Primeiramente vamos a um exemplo:
Segue abaixo a saída deste programa executado: [marcos@xfiles ghost]$ ./getaddrinfo facebook.com Host: facebook.com IPv4: 173.252.120.68 IPv6: 2a03:2880:2130:cf24:face:b00c:0:25de
A chamada getaddrinfo
recebe quatro parâmetros:
- um nó (hostname)
- um serviço (porta)
- uma struct
addinfo
, ou hints - e um ponteiro para
addrinfo
, que irá ser conter os IPs do hostname
gethostbyname
. Já o serviço especificado pode estar em formato decimal, como uma porta, ou um serviço como "http". Podemos deixar este campo como NULL
. A struct hints funciona como um filtro, informando qual o tipo de endereços que desejamos receber da chamada getaddrinfo
para o host informado. Neste caso nós informamos ai_family
como AF_UNSPEC
, pois queremos receber tanto endereços IPv4 quanto IPv6 (para buscar por somente um deles basta informar AF_INET
ou AF_INET6
), ai_socktype
como 0, pois esperamos tanto por TCP quanto UDP, e ai_flags
como AI_CANONNAME
para que o hostname descoberto seja retornado na struct addrinfo
. Já o quarto parâmetro é um ponteiro para addrinfo
que irá conter todos os endereços IP relacionados ao host informado.A chamada getaddrinfo
retorna zero em caso de sucesso, ou um código de erro em caso de falha. O erro em sua forma descritiva por ser visto pela chamada gai_strerror
, que recebe o retorno de getaddrinfo
.
Após executado o getaddrinfo
, então iteramos sobre todos os IPs retornados. O campo ai_next
sempre aponta para a próxima struct addrinfo
. O processo para imprimir o IP do host é basicamente o mesmo do gethostbyname
. freeaddrinfo
server para liberar a memória alocada da chamada getaddrinfo
.
Como podemos observar, adaptar o código para getaddrinfo
não é difícil, muito pelo contrário, e seguir as recomendações do man parecem ser sempre algo bom a se fazer!
Até a próxima!
Referências:
Red Hat CVE GHOST: glibc vulnerability (CVE-2015-0235) GHOST, a critical Linux security hole, is revealed Qualys Security Advisory CVE-2015-0235 - GHOST: glibc gethostbyname buffer overflow You shouldn’t be using gethostbyname() anyway Man inet_pton Man gethostbyname Man getaddrinfo