
RichFaces Vulnerability CVE-2018-12533 RF-14310
Contents
Demonstra a falha presente no RichFaces 3.3.4 de execução remota de código e como mitigar e/ou bloquear.
- Exemplo de ataque
- Clonar o repositório
- Decodificando o payload
- Deserializando a classe Java
- Gerando um payload para testar a vulnerabilidade
- Como mitigar?
- Entendendo o fluxo da execução
- Quem usa esse recurso?
- Dicas
- Links relevantes
Detalhes da CVE-2018-12533. Outras versões do RichFaces são vulneráveis, como pode ser visto aqui.
Exemplo de ataque
| |
Após o DATA/ e até antes do .seam, é o payload do ataque. Isso é apenas uma classe Java serializada e zipada e codificada em Base64. É um Base64 com o formato um pouco modificado pelo RichFaces para ser compatível com a URL.
Essa classe contém uma linha de comando no formato de Expression Language que é executada pela aplicação, mais precisamente pelo RichFaces, e aí que está o problema, pois um código arbitrário pode ser executado no servidor.
O comando que está codificado no payload acima é
touch /tmp/richfaces-vulnerability-cve-2018-12533-rf-14310-20250102-110458(apenas para efeito de demonstração) e vamos constatar isso logo a frente.
Clonar o repositório
Decodificando o payload
Converte o payload do formato de Base64 do RichFaces para o formatado padrão de Base64. Decodifica o Base64, que nesse caso é um binário zipado, então descomprime, o que vai gerar o binário da classe java serializada.
Deserializando a classe Java
Aqui vamos ver o comando malicioso que está dentro do payload.
O comando abaixo vai mostrar todos os atributos da classe e os respectivos tipos definidos na classe. Não exibe os tipos em runtime, se não sabe o que isso significa, tudo bem.
Aqui estamos executando código Java como se fosse um script graças ao JBang
O output é algo parecido com isso:
| |
⚠️ O ponto principal é o atributo _paint.savedState.m.orig.expr, onde está o código malicioso, que nesse exemplo é apenas um código inofensivo que cria um arquivo no disco: touch /tmp/richfaces-vulnerability-cve-2018-12533-rf-14310-20250102-110458
Gerando um payload para testar a vulnerabilidade
O output vai exibir os comandos no ponto de você executar contra um servidor vulnerável. Ajuste apenas a URL.
Como mitigar?
Se você realmente precisa desse endpoint
Uma opção menos simples e mais performática, é modificar o código do RichFaces para fazer algum tratamento. Mais abaixo tem a stack relevante.
Outra opção mais simples que a anterior, é criar um filtro (javax.servlet.Filter), que vai agir apenas no path em questão, deserializando o Base64 presente no path e fazendo a devida verificação para descobrir se o request pode continuar ou se tem que ser rejeitado. É importante frisar que a deserialização tem que ocorrer por meio da classe LookAheadObjectInputStream, como é feito em org.ajax4jsf.resource.ResourceBuilderImpl.getResourceDataForKey(String), para tentar evitar ao máximo problemas de segurança durante a deserialização, como por exemplo execução de código malicioso.
Se você não precisa desse endpoint
Um opção é bloquear o path do endpoint em algum proxy que esteja na frente da aplicação.
Outra opção, que acho que vale implementar, mas que não exime a primeira opção, é criar um filtro (javax.servlet.Filter), que vai agir apenas no path em questão e vai bloquear o acesso. O bloqueio consiste apenas em responder a requisição sem deixar a cadeia de filtros prosseguir a execução. Você pode colocar um texto no body e usar o http status code 410 (Gone). Lembrando que esse filtro deve ser definido no web.xml da aplicação. Um exemplo desse filtro: PathBlockerFilter.
Se usa JBoss EAP, o filtro falado aqui pode ser implementado como um valve (a mudança é mínima), e:
- será executado antes dos filtros definidos no
web.xml- deve ser definido no arquivo
jboss-web.xmlcomo sendo o primeiro<valve>, ou- pode ser definido no standalone.xml (ou domain.xml) no subsystem
urn:jboss:domain:webpor meio da tag<valve- Exemplo desse valve: PathBlockerValve
Entendendo o fluxo da execução
tl;dr: o código malicioso é executado pelo método
org.richfaces.renderkit.html.Paint2DResource.send(ResourceContext), na linhapaint.invoke(facesContext, new Object[] {graphics,data._data});.
Quando a requisição http é feita, ela chega ao método org.ajax4jsf.resource.ResourceBuilderImpl.getResourceDataForKey(String), conforme a stack abaixo:
Daemon Thread [http-0.0.0.0:8080-1] (Suspended)
owns: org.apache.coyote.Request (id=27053)
org.ajax4jsf.resource.ResourceBuilderImpl.getResourceDataForKey(java.lang.String) line: 366
org.ajax4jsf.resource.InternetResourceService.serviceResource(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 156
org.ajax4jsf.resource.InternetResourceService.serviceResource(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 141
- Na linha
dataString = key.substring(dataStart);é extraído o Base64 no formato do RichFaces (lembrando que esse Base64 no final das contas é um objeto java que foi serializado e zipado); - Na linha
objectArray = decrypt(dataArray)esse Base64 é decodificado e descomprimido; - Na linha
data = in.readObject()o objeto java com o código malicioso é deserializando, ou seja, tem-se a instância do objeto.
Em seguida a requisição chega no método org.richfaces.renderkit.html.Paint2DResource.send(ResourceContext), conforme a stack abaixo:
Daemon Thread [http-0.0.0.0:8080-1] (Suspended (breakpoint at line 187 in org.richfaces.renderkit.html.Paint2DResource))
owns: org.apache.coyote.Request (id=27053)
org.richfaces.renderkit.html.Paint2DResource.send(org.ajax4jsf.resource.ResourceContext) line: 187
org.ajax4jsf.resource.ResourceLifecycle.sendResource(org.ajax4jsf.resource.ResourceContext, org.ajax4jsf.resource.InternetResource) line: 221
org.ajax4jsf.resource.ResourceLifecycle.send(org.ajax4jsf.resource.ResourceContext, org.ajax4jsf.resource.InternetResource) line: 148
org.ajax4jsf.resource.InternetResourceService.serviceResource(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 226
org.ajax4jsf.resource.InternetResourceService.serviceResource(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 141
⚠️ Então na linha paint.invoke(facesContext, new Object[] {graphics,data._data}); o código malicioso é finalmente executado.
Quem usa esse recurso?
Vou citar um caso, o componente rich:paint2D: https://docs.jboss.org/richfaces/latest_3_3_X/en/devguide/html_single/#rich_paint2D
Dicas
É possível ver o payload recebido pela aplicação ativando o log DEBUG passa a classe org.ajax4jsf.resource.ResourceBuilderImpl.
Links relevantes
- https://codewhitesec.blogspot.com/2018/05/poor-richfaces.html (Principal)
- https://seclists.org/fulldisclosure/2020/Mar/21
- https://github.com/redtimmy/Richsploit
- https://web.archive.org/web/20200315184606/https://www.redtimmy.com/java-hacking/richsploit-one-tool-to-exploit-all-versions-of-richfaces-ever-released/
MhagnumDw




