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